pages235/experiments/three.ts
2025-08-01 03:35:45 +03:00

303 lines
8.5 KiB
TypeScript

import * as THREE from 'three'
import globalTexture from 'mc-assets/dist/blocksAtlasLegacy.png'
// Import the renderBlockThree function
import { renderBlockThree } from '../renderer/viewer/lib/mesher/standaloneRenderer'
// Create scene, camera and renderer
const scene = new THREE.Scene()
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000)
const renderer = new THREE.WebGLRenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
// Position camera
camera.position.set(3, 3, 3)
camera.lookAt(0, 0, 0)
// Dark background
scene.background = new THREE.Color(0x333333)
// Add some lighting
const ambientLight = new THREE.AmbientLight(0xffffff, 0.6)
scene.add(ambientLight)
const directionalLight = new THREE.DirectionalLight(0xffffff, 0.4)
directionalLight.position.set(1, 1, 1)
scene.add(directionalLight)
// Add grid helper for orientation
const gridHelper = new THREE.GridHelper(10, 10)
scene.add(gridHelper)
// Create shared material that will be used by all blocks
const sharedMaterial = new THREE.MeshLambertMaterial({
vertexColors: true,
transparent: true,
alphaTest: 0.1,
// wireframe: true // Add wireframe for debugging
})
// Create simple block models for testing
function createFullBlockModel(textureObj: any): any {
return [[{
elements: [{
from: [0, 0, 0],
to: [16, 16, 16],
faces: {
up: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
down: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
north: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
south: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
east: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
west: {
texture: textureObj,
uv: [0, 0, 16, 16]
}
}
}]
}]]
}
function createHalfBlockModel(textureObj: any): any {
return [[{
elements: [{
from: [0, 0, 0],
to: [16, 8, 16], // Half height (8 instead of 16)
faces: {
up: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
down: {
texture: textureObj,
uv: [0, 0, 16, 16]
},
north: {
texture: textureObj,
uv: [0, 0, 16, 8] // Half height UV
},
south: {
texture: textureObj,
uv: [0, 0, 16, 8] // Half height UV
},
east: {
texture: textureObj,
uv: [0, 0, 16, 8] // Half height UV
},
west: {
texture: textureObj,
uv: [0, 0, 16, 8] // Half height UV
}
}
}]
}]]
}
let currentFullBlockInstancedMesh: THREE.InstancedMesh | null = null
let currentHalfBlockInstancedMesh: THREE.InstancedMesh | null = null
async function createInstancedBlock() {
try {
// Clean up previous meshes if they exist
if (currentFullBlockInstancedMesh) {
scene.remove(currentFullBlockInstancedMesh)
currentFullBlockInstancedMesh.geometry.dispose()
}
if (currentHalfBlockInstancedMesh) {
scene.remove(currentHalfBlockInstancedMesh)
currentHalfBlockInstancedMesh.geometry.dispose()
}
// Load the blocks atlas texture
const textureLoader = new THREE.TextureLoader()
const texture = await textureLoader.loadAsync(globalTexture)
// Configure texture for pixel art
texture.magFilter = THREE.NearestFilter
texture.minFilter = THREE.NearestFilter
texture.generateMipmaps = false
texture.flipY = false
// Set the texture on our shared material
sharedMaterial.map = texture
sharedMaterial.needsUpdate = true
console.log('Texture loaded:', texture.image.width, 'x', texture.image.height)
// Calculate UV coordinates for the first tile (top-left, 16x16)
const atlasWidth = texture.image.width
const atlasHeight = texture.image.height
const tileSize = 16
const textureInfo = {
u: 0 / atlasWidth, // Left edge (first column)
v: 2 * tileSize / atlasHeight, // Top edge (third row)
su: tileSize / atlasWidth, // Width of one tile
sv: tileSize / atlasHeight // Height of one tile
}
console.log('Atlas size:', atlasWidth, 'x', atlasHeight)
console.log('Calculated texture info:', textureInfo)
// Create mock texture object that matches what the renderer expects
const mockTexture = {
u: textureInfo.u,
v: textureInfo.v,
su: textureInfo.su,
sv: textureInfo.sv,
debugName: 'test_texture'
}
// Create block models with the mock texture
const fullBlockModel = createFullBlockModel(mockTexture)
const halfBlockModel = createHalfBlockModel(mockTexture)
// Mock data for the renderBlockThree function
const mockBlock = undefined // No specific block data needed for this test
const mockBiome = 'plains'
const mockMcData = {} as any
const mockVariants = []
const mockNeighbors = {}
// Render the full block
const fullBlockGeometry = renderBlockThree(
fullBlockModel,
mockBlock,
mockBiome,
mockMcData,
mockVariants,
mockNeighbors
)
// Render the half block
const halfBlockGeometry = renderBlockThree(
halfBlockModel,
mockBlock,
mockBiome,
mockMcData,
mockVariants,
mockNeighbors
)
// Create instanced mesh for full blocks
currentFullBlockInstancedMesh = new THREE.InstancedMesh(fullBlockGeometry, sharedMaterial, 2) // Support 2 instances
const matrix = new THREE.Matrix4()
// First instance (full block)
matrix.setPosition(-1.5, 0.5, 0.5)
currentFullBlockInstancedMesh.setMatrixAt(0, matrix)
// Second instance (full block)
matrix.setPosition(1.5, 0.5, 0.5)
currentFullBlockInstancedMesh.setMatrixAt(1, matrix)
currentFullBlockInstancedMesh.count = 2
currentFullBlockInstancedMesh.instanceMatrix.needsUpdate = true
scene.add(currentFullBlockInstancedMesh)
// Create instanced mesh for half blocks
currentHalfBlockInstancedMesh = new THREE.InstancedMesh(halfBlockGeometry, sharedMaterial, 1) // Support 1 instance
const halfMatrix = new THREE.Matrix4()
// Half block instance
halfMatrix.setPosition(0, 0.75, 0.5) // Positioned higher so top aligns with full blocks
currentHalfBlockInstancedMesh.setMatrixAt(0, halfMatrix)
currentHalfBlockInstancedMesh.count = 1
currentHalfBlockInstancedMesh.instanceMatrix.needsUpdate = true
scene.add(currentHalfBlockInstancedMesh)
console.log('Instanced blocks created successfully')
console.log('Full block geometry:', fullBlockGeometry)
console.log('Half block geometry:', halfBlockGeometry)
} catch (error) {
console.error('Error creating instanced blocks:', error)
// Fallback: create colored cubes
const geometry = new THREE.BoxGeometry(1, 1, 1)
const material = new THREE.MeshLambertMaterial({ color: 0xff0000, wireframe: true })
const fallbackMesh = new THREE.Mesh(geometry, material)
fallbackMesh.position.set(0, 0.5, 0.5)
scene.add(fallbackMesh)
console.log('Created fallback colored cube')
}
}
// Create the instanced block
createInstancedBlock().then(() => {
render()
})
// Simple render loop (no animation)
function render() {
renderer.render(scene, camera)
}
// Add mouse controls for better viewing
let mouseDown = false
let mouseX = 0
let mouseY = 0
renderer.domElement.addEventListener('mousedown', (event) => {
mouseDown = true
mouseX = event.clientX
mouseY = event.clientY
})
renderer.domElement.addEventListener('mousemove', (event) => {
if (!mouseDown) return
const deltaX = event.clientX - mouseX
const deltaY = event.clientY - mouseY
// Rotate camera around the center
const spherical = new THREE.Spherical()
spherical.setFromVector3(camera.position)
spherical.theta -= deltaX * 0.01
spherical.phi += deltaY * 0.01
spherical.phi = Math.max(0.1, Math.min(Math.PI - 0.1, spherical.phi))
camera.position.setFromSpherical(spherical)
camera.lookAt(0, 0, 0)
mouseX = event.clientX
mouseY = event.clientY
render()
})
renderer.domElement.addEventListener('mouseup', () => {
mouseDown = false
})
// Add button to recreate blocks (for testing)
const button = document.createElement('button')
button.textContent = 'Recreate Blocks'
button.style.position = 'fixed'
button.style.top = '10px'
button.style.left = '10px'
button.addEventListener('click', () => {
createInstancedBlock()
render()
})
document.body.appendChild(button)
// Initial render
render()