303 lines
8.5 KiB
TypeScript
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()
|