fix rotations for torches. sr latch demo.
This commit is contained in:
123
index.html
123
index.html
@@ -8,6 +8,9 @@
|
||||
<link rel="stylesheet" href="./src/components.css">
|
||||
|
||||
<script type="module" src="./src/components.js"></script>
|
||||
<style> mc-world, mc-timeline, mc-camera, mc-frame {
|
||||
display: none;
|
||||
} </style>
|
||||
</head>
|
||||
<body>
|
||||
<main>
|
||||
@@ -109,7 +112,7 @@
|
||||
</mc-world>
|
||||
</mc-diorama>
|
||||
|
||||
<h2>Static multi-view scene</h2>
|
||||
<h2>Multi-view scene</h2>
|
||||
|
||||
<p> Phasellus tortor turpis, commodo ut tortor scelerisque, fermentum tempus nisi. Pellentesque consectetur commodo
|
||||
rhoncus. Sed ullamcorper turpis dapibus, luctus nibh eu, dapibus risus. Fusce finibus ante eleifend, venenatis
|
||||
@@ -117,25 +120,117 @@
|
||||
eros. Etiam porttitor libero a facilisis vulputate. Duis facilisis eget arcu quis ultricies. Etiam dui erat,
|
||||
luctus ut tristique vitae, fermentum quis magna. </p>
|
||||
|
||||
<mc-frame id="nonsense">
|
||||
<mc-camera orbit></mc-camera>
|
||||
<mc-frame id="d-latch">
|
||||
<mc-world>
|
||||
p -1 0 0 redstone_wire east=side west=none north=none south=none power=0
|
||||
p -1 -1 0 smooth_stone_slab type=top
|
||||
p 0 0 0 repeater facing=east delay=1 locked=false powered=false
|
||||
p 2 0 0 oak_trapdoor facing=east half=bottom open=true
|
||||
p 0 -1 0 lodestone
|
||||
p 0 -2 0 piston facing=north extended=false
|
||||
p -1 0 0 smooth_stone_slab type=top
|
||||
p 0 0 0 smooth_stone_slab type=top
|
||||
p 1 0 0 smooth_stone_slab type=top
|
||||
p 2 0 0 smooth_stone_slab type=top
|
||||
p 3 0 0 smooth_stone_slab type=top
|
||||
p 4 0 0 smooth_stone_slab type=top
|
||||
p -1 0 -1 smooth_stone_slab type=top
|
||||
p 0 0 -1 smooth_stone_slab type=top
|
||||
p 1 0 -1 smooth_stone_slab type=top
|
||||
p 2 0 -1 smooth_stone_slab type=top
|
||||
p 3 0 -1 smooth_stone_slab type=top
|
||||
p 4 0 -1 smooth_stone_slab type=top
|
||||
p -1 0 1 smooth_stone_slab type=top
|
||||
p 0 0 1 smooth_stone_slab type=top
|
||||
p 1 0 1 smooth_stone_slab type=top
|
||||
p 2 0 1 smooth_stone_slab type=top
|
||||
p 3 0 1 smooth_stone_slab type=top
|
||||
p 4 0 1 smooth_stone_slab type=top
|
||||
|
||||
p 2 1 1 light_blue_terracotta
|
||||
p 1 1 -1 lime_terracotta
|
||||
|
||||
p 4 1 1 light_gray_terracotta
|
||||
p 4 1 -1 light_gray_terracotta
|
||||
|
||||
p 1 1 0 redstone_wall_torch lit=true facing=south
|
||||
p 2 1 0 redstone_wall_torch lit=false facing=north
|
||||
|
||||
p -1 1 1 redstone_lamp lit=true
|
||||
p 0 1 1 redstone_wire east=side north=none south=none west=side power=14
|
||||
p 1 1 1 redstone_wire east=up north=side south=none west=side power=15
|
||||
p 2 2 1 redstone_wire east=side north=none south=none west=side power=14
|
||||
p 3 1 1 redstone_wire east=side north=none south=none west=up power=13
|
||||
p 5 1 1 stone_button face=wall powered=false facing=east
|
||||
|
||||
p -1 1 -1 redstone_lamp lit=false
|
||||
p 0 1 -1 redstone_wire east=up north=none south=none west=side power=0
|
||||
p 1 2 -1 redstone_wire east=side north=none south=none west=side power=0
|
||||
p 2 1 -1 redstone_wire east=side north=none south=side west=up power=0
|
||||
p 3 1 -1 redstone_wire east=side north=none south=none west=side power=0
|
||||
p 5 1 -1 stone_button face=wall powered=false facing=east
|
||||
|
||||
t 2
|
||||
p 5 1 -1 powered=true
|
||||
p 0 1 -1 power=15
|
||||
p 1 2 -1 power=15
|
||||
p 2 1 -1 power=15
|
||||
p 3 1 -1 power=15
|
||||
|
||||
t 4
|
||||
p 1 1 0 lit=false
|
||||
p 0 1 1 power=0
|
||||
p 1 1 1 power=0
|
||||
p 2 2 1 power=0
|
||||
p 3 1 1 power=0
|
||||
|
||||
t 6
|
||||
p -1 1 -1 lit=true
|
||||
p 2 1 0 lit=true
|
||||
p 0 1 -1 power=15
|
||||
p 1 2 -1 power=15
|
||||
p 2 1 -1 power=15
|
||||
p 3 1 -1 power=15
|
||||
|
||||
t 8
|
||||
p -1 1 1 lit=false
|
||||
|
||||
t 22
|
||||
p 5 1 -1 powered=false
|
||||
p 0 1 -1 power=15
|
||||
p 1 2 -1 power=15
|
||||
p 2 1 -1 power=15
|
||||
p 3 1 -1 power=15
|
||||
|
||||
t 32
|
||||
p 5 1 1 powered=true
|
||||
p 0 1 1 power=15
|
||||
p 1 1 1 power=15
|
||||
p 2 2 1 power=15
|
||||
p 3 1 1 power=15
|
||||
|
||||
t 34
|
||||
p 2 1 0 lit=false
|
||||
p 0 1 -1 power=0
|
||||
p 1 2 -1 power=0
|
||||
p 2 1 -1 power=0
|
||||
p 3 1 -1 power=0
|
||||
|
||||
t 36
|
||||
p -1 1 1 lit=true
|
||||
p 1 1 0 lit=true
|
||||
|
||||
t 38
|
||||
p -1 1 -1 lit=false
|
||||
|
||||
t 52
|
||||
p 5 1 1 powered=false
|
||||
|
||||
t 60
|
||||
p 0 0 0
|
||||
</mc-world>
|
||||
</mc-frame>
|
||||
|
||||
<div class="grid-2col">
|
||||
<mc-diorama frameid="nonsense" theta="180" phi="90"></mc-diorama>
|
||||
<mc-diorama frameid="nonsense">
|
||||
<mc-diorama frameid="d-latch" radius="6" theta="140" phi="25" target="2 1 0.5" style="border: 1px solid white">
|
||||
<mc-timeline autoplay controls="true" loop></mc-timeline>
|
||||
<mc-camera pan="false" zoom="false"></mc-camera>
|
||||
</mc-diorama>
|
||||
<mc-diorama frameid="nonsense" theta="180" phi="0"></mc-diorama>
|
||||
<mc-diorama frameid="nonsense" theta="270" phi="0"></mc-diorama>
|
||||
<mc-diorama frameid="d-latch" radius="6" theta="180" phi="90" target="2 1 0.5" style="border: 1px solid white"></mc-diorama>
|
||||
</div>
|
||||
|
||||
<p> Sed urna ante, aliquet et ex nec, posuere laoreet sem. Aenean non sem a massa vehicula faucibus. Donec varius
|
||||
@@ -217,8 +312,6 @@
|
||||
Ut id massa eget dui vestibulum pretium. Nunc tempus odio quis nisi ultrices, id volutpat dui tristique.
|
||||
Suspendisse lacus metus, pulvinar ac urna vitae, pellentesque volutpat eros. In cursus ipsum in enim ornare, sed
|
||||
tempus mi tincidunt. </p>
|
||||
|
||||
<div id="atlas-container"></div>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,7 +1,5 @@
|
||||
/* --- Component Structural Styles --- */
|
||||
mc-world, mc-timeline, mc-camera, mc-frame {
|
||||
display: none;
|
||||
}
|
||||
mc-world, mc-timeline, mc-camera, mc-frame { display: none; }
|
||||
|
||||
mc-diorama {
|
||||
display: block;
|
||||
|
||||
@@ -5,15 +5,8 @@ import {Diorama} from './diorama.js';
|
||||
import {SpacetimeController} from './controller.js';
|
||||
|
||||
// Global Engine Singleton
|
||||
let engineInstance = null;
|
||||
|
||||
function getEngine() {
|
||||
if (!engineInstance) {
|
||||
engineInstance = new Engine();
|
||||
document.body.appendChild(engineInstance.canvas)
|
||||
}
|
||||
return engineInstance;
|
||||
}
|
||||
window.mcEngine = new Engine();
|
||||
document.body.appendChild(window.mcEngine.canvas)
|
||||
|
||||
export class MCWorldElement extends HTMLElement {
|
||||
connectedCallback() {
|
||||
@@ -55,7 +48,7 @@ export class MCDioramaElement extends HTMLElement {
|
||||
}
|
||||
|
||||
init() {
|
||||
const engine = getEngine();
|
||||
const engine = window.mcEngine;
|
||||
let frame;
|
||||
|
||||
// 1. Resolve the Frame context
|
||||
@@ -90,7 +83,18 @@ export class MCDioramaElement extends HTMLElement {
|
||||
if (this.hasAttribute('theta')) diorama.theta = parseFloat(this.getAttribute('theta'));
|
||||
if (this.hasAttribute('phi')) diorama.phi = parseFloat(this.getAttribute('phi'));
|
||||
|
||||
diorama.centerView();
|
||||
if (this.hasAttribute('target')) {
|
||||
console.log('target:', this.getAttribute('target'));
|
||||
const [x, y, z] = this.getAttribute('target').trim().split(/\s+/);
|
||||
diorama.target = [
|
||||
parseFloat(x),
|
||||
parseFloat(y),
|
||||
parseFloat(z),
|
||||
];
|
||||
console.log('target:', diorama.target);
|
||||
} else {
|
||||
diorama.centerView();
|
||||
}
|
||||
diorama.saveState(); // Lock this in for the Reset button
|
||||
|
||||
// 3. Resolve the Controllers
|
||||
|
||||
@@ -15,7 +15,7 @@ export class SpacetimeController {
|
||||
subticks: true,
|
||||
autoplay: false,
|
||||
loop: false,
|
||||
speed: 0.5, ...options
|
||||
speed: 1.0, ...options
|
||||
};
|
||||
|
||||
this.hasSpatial = this.opts.orbit || this.opts.pan || this.opts.zoom;
|
||||
|
||||
100
src/engine.js
100
src/engine.js
@@ -74,7 +74,7 @@ export class Engine {
|
||||
this.canvas.style.display = 'none';
|
||||
document.body.appendChild(this.canvas);
|
||||
|
||||
this.gl = this.canvas.getContext('webgl2', {antialias: true, alpha: true});
|
||||
this.gl = this.canvas.getContext('webgl2', {antialias: false, alpha: true});
|
||||
const gl = this.gl;
|
||||
gl.enable(gl.DEPTH_TEST);
|
||||
gl.enable(gl.CULL_FACE);
|
||||
@@ -115,6 +115,8 @@ export class Engine {
|
||||
this.observedFrames = new Set();
|
||||
this.dirtyFrames = new Set(); // Tracks specific timelines that need mesh updates
|
||||
|
||||
this.debug_redraw = false;
|
||||
|
||||
window.addEventListener('resize', () => this.requestRender());
|
||||
window.addEventListener('scroll', () => this.requestRender());
|
||||
}
|
||||
@@ -367,50 +369,60 @@ export class Engine {
|
||||
// Lazy Render: Only draw if the diorama actually changed
|
||||
if (!diorama.dirty) continue;
|
||||
|
||||
if (!setupProgram) {
|
||||
gl.useProgram(this.program);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.atlasTexture);
|
||||
gl.uniform1i(this.uniforms.texture, 0);
|
||||
setupProgram = true;
|
||||
const draw = () => {
|
||||
if (!setupProgram) {
|
||||
gl.useProgram(this.program);
|
||||
gl.activeTexture(gl.TEXTURE0);
|
||||
gl.bindTexture(gl.TEXTURE_2D, this.atlasTexture);
|
||||
gl.uniform1i(this.uniforms.texture, 0);
|
||||
setupProgram = true;
|
||||
}
|
||||
|
||||
if (this.canvas.width < targetW || this.canvas.height < targetH) {
|
||||
this.canvas.width = Math.max(this.canvas.width, targetW);
|
||||
this.canvas.height = Math.max(this.canvas.height, targetH);
|
||||
}
|
||||
|
||||
const viewportY = this.canvas.height - targetH;
|
||||
gl.viewport(0, viewportY, targetW, targetH);
|
||||
gl.enable(gl.SCISSOR_TEST);
|
||||
gl.scissor(0, viewportY, targetW, targetH);
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.disable(gl.SCISSOR_TEST);
|
||||
|
||||
const aspect = rect.width / rect.height;
|
||||
const viewProj = diorama.updateMatrices(aspect);
|
||||
|
||||
gl.uniformMatrix4fv(this.uniforms.viewProj, false, viewProj);
|
||||
gl.uniform3f(this.uniforms.lightDir, 1.0, 3.0, 2.0);
|
||||
gl.uniform3f(this.uniforms.viewDir, diorama.viewDir[0], diorama.viewDir[1], diorama.viewDir[2]);
|
||||
gl.uniform3f(this.uniforms.upDir, diorama.upDir[0], diorama.upDir[1], diorama.upDir[2]);
|
||||
|
||||
const meshes = this.frameMeshes.get(diorama.frame) || [];
|
||||
for (const mesh of meshes) {
|
||||
gl.bindVertexArray(mesh.vao);
|
||||
gl.drawElementsInstanced(gl.TRIANGLES, mesh.indexCount, gl.UNSIGNED_SHORT, 0, mesh.instanceCount);
|
||||
}
|
||||
|
||||
diorama.ctx2d.clearRect(0, 0, targetW, targetH);
|
||||
diorama.ctx2d.drawImage(
|
||||
this.canvas,
|
||||
0, 0, targetW, targetH,
|
||||
0, 0, targetW, targetH
|
||||
);
|
||||
|
||||
// Clean state!
|
||||
diorama.dirty = false;
|
||||
};
|
||||
|
||||
if (this.debug_redraw) {
|
||||
diorama.ctx2d.fillStyle = '#FF00FF33';
|
||||
diorama.ctx2d.fillRect(0, 0, targetW, targetH);
|
||||
requestAnimationFrame(draw);
|
||||
} else {
|
||||
draw();
|
||||
}
|
||||
|
||||
if (this.canvas.width < targetW || this.canvas.height < targetH) {
|
||||
this.canvas.width = Math.max(this.canvas.width, targetW);
|
||||
this.canvas.height = Math.max(this.canvas.height, targetH);
|
||||
}
|
||||
|
||||
const viewportY = this.canvas.height - targetH;
|
||||
gl.viewport(0, viewportY, targetW, targetH);
|
||||
gl.enable(gl.SCISSOR_TEST);
|
||||
gl.scissor(0, viewportY, targetW, targetH);
|
||||
gl.clearColor(0.0, 0.0, 0.0, 0.0);
|
||||
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
||||
gl.disable(gl.SCISSOR_TEST);
|
||||
|
||||
const aspect = rect.width / rect.height;
|
||||
const viewProj = diorama.updateMatrices(aspect);
|
||||
|
||||
gl.uniformMatrix4fv(this.uniforms.viewProj, false, viewProj);
|
||||
gl.uniform3f(this.uniforms.lightDir, 1.0, 3.0, 2.0);
|
||||
gl.uniform3f(this.uniforms.viewDir, diorama.viewDir[0], diorama.viewDir[1], diorama.viewDir[2]);
|
||||
gl.uniform3f(this.uniforms.upDir, diorama.upDir[0], diorama.upDir[1], diorama.upDir[2]);
|
||||
|
||||
const meshes = this.frameMeshes.get(diorama.frame) || [];
|
||||
for (const mesh of meshes) {
|
||||
gl.bindVertexArray(mesh.vao);
|
||||
gl.drawElementsInstanced(gl.TRIANGLES, mesh.indexCount, gl.UNSIGNED_SHORT, 0, mesh.instanceCount);
|
||||
}
|
||||
|
||||
diorama.ctx2d.clearRect(0, 0, targetW, targetH);
|
||||
diorama.ctx2d.drawImage(
|
||||
this.canvas,
|
||||
0, 0, targetW, targetH,
|
||||
0, 0, targetW, targetH
|
||||
);
|
||||
|
||||
// Clean state!
|
||||
diorama.dirty = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
87
src/math.js
87
src/math.js
@@ -112,59 +112,50 @@ export function mat4Translate(out, a, v) {
|
||||
return out;
|
||||
}
|
||||
|
||||
export function mat4RotateY(out, a, rad) {
|
||||
export function mat4RotateX(m, rad) {
|
||||
let s = Math.sin(rad), c = Math.cos(rad);
|
||||
let a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3];
|
||||
let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
|
||||
let m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
|
||||
let m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
|
||||
|
||||
if (a !== out) {
|
||||
out[4] = a[4];
|
||||
out[5] = a[5];
|
||||
out[6] = a[6];
|
||||
out[7] = a[7];
|
||||
out[12] = a[12];
|
||||
out[13] = a[13];
|
||||
out[14] = a[14];
|
||||
out[15] = a[15];
|
||||
// TODO We only use this in-place for this prototype. Update the signature to enforce this
|
||||
}
|
||||
|
||||
out[0] = a00 * c - a20 * s;
|
||||
out[1] = a01 * c - a21 * s;
|
||||
out[2] = a02 * c - a22 * s;
|
||||
out[3] = a03 * c - a23 * s;
|
||||
out[8] = a00 * s + a20 * c;
|
||||
out[9] = a01 * s + a21 * c;
|
||||
out[10] = a02 * s + a22 * c;
|
||||
out[11] = a03 * s + a23 * c;
|
||||
return out;
|
||||
m[4] = m10 * c + m20 * s;
|
||||
m[5] = m11 * c + m21 * s;
|
||||
m[6] = m12 * c + m22 * s;
|
||||
m[7] = m13 * c + m23 * s;
|
||||
m[8] = m20 * c - m10 * s;
|
||||
m[9] = m21 * c - m11 * s;
|
||||
m[10] = m22 * c - m12 * s;
|
||||
m[11] = m23 * c - m13 * s;
|
||||
return m;
|
||||
}
|
||||
|
||||
export function mat4RotateX(out, a, rad) {
|
||||
export function mat4RotateY(m, rad) {
|
||||
let s = Math.sin(rad), c = Math.cos(rad);
|
||||
let a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7];
|
||||
let a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11];
|
||||
let m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
|
||||
let m20 = m[8], m21 = m[9], m22 = m[10], m23 = m[11];
|
||||
|
||||
if (a !== out) {
|
||||
out[0] = a[0];
|
||||
out[1] = a[1];
|
||||
out[2] = a[2];
|
||||
out[3] = a[3];
|
||||
out[12] = a[12];
|
||||
out[13] = a[13];
|
||||
out[14] = a[14];
|
||||
out[15] = a[15];
|
||||
// TODO We only use this in-place for this prototype.
|
||||
// update the signature to enforce this
|
||||
}
|
||||
m[0] = m00 * c - m20 * s;
|
||||
m[1] = m01 * c - m21 * s;
|
||||
m[2] = m02 * c - m22 * s;
|
||||
m[3] = m03 * c - m23 * s;
|
||||
m[8] = m00 * s + m20 * c;
|
||||
m[9] = m01 * s + m21 * c;
|
||||
m[10] = m02 * s + m22 * c;
|
||||
m[11] = m03 * s + m23 * c;
|
||||
return m;
|
||||
}
|
||||
|
||||
out[4] = a10 * c + a20 * s;
|
||||
out[5] = a11 * c + a21 * s;
|
||||
out[6] = a12 * c + a22 * s;
|
||||
out[7] = a13 * c + a23 * s;
|
||||
out[8] = a20 * c - a10 * s;
|
||||
out[9] = a21 * c - a11 * s;
|
||||
out[10] = a22 * c - a12 * s;
|
||||
out[11] = a23 * c - a13 * s;
|
||||
return out;
|
||||
export function mat4RotateZ(m, rad) {
|
||||
let s = Math.sin(rad), c = Math.cos(rad);
|
||||
let m00 = m[0], m01 = m[1], m02 = m[2], m03 = m[3];
|
||||
let m10 = m[4], m11 = m[5], m12 = m[6], m13 = m[7];
|
||||
|
||||
m[0] = m00 * c + m10 * s;
|
||||
m[1] = m01 * c + m11 * s;
|
||||
m[2] = m02 * c + m12 * s;
|
||||
m[3] = m03 * c + m13 * s;
|
||||
m[4] = m10 * c - m00 * s;
|
||||
m[5] = m11 * c - m01 * s;
|
||||
m[6] = m12 * c - m02 * s;
|
||||
m[7] = m13 * c - m03 * s;
|
||||
return m;
|
||||
}
|
||||
51
src/model.js
51
src/model.js
@@ -1,4 +1,4 @@
|
||||
import {mat4Identity, mat4RotateX, mat4RotateY, mat4Translate} from "./math.js";
|
||||
import {mat4Identity, mat4RotateX, mat4RotateY, mat4RotateZ, mat4Translate} from "./math.js";
|
||||
|
||||
const MISSING_MODEL_JSON = {
|
||||
"textures": {"missing": ":missing"},
|
||||
@@ -113,10 +113,11 @@ export class BlockModel {
|
||||
const pos = [], norm = [], uv = [], tint = [], shade = [], idx = [];
|
||||
let vOffset = 0;
|
||||
|
||||
// --- GLOBAL BLOCKSTATE MATRIX ---
|
||||
const blockMatrix = mat4Identity(new Float32Array(16));
|
||||
mat4Translate(blockMatrix, blockMatrix, [0.5, 0.5, 0.5]);
|
||||
if (variant.x) mat4RotateX(blockMatrix, blockMatrix, -variant.x * Math.PI / 180);
|
||||
if (variant.y) mat4RotateY(blockMatrix, blockMatrix, -variant.y * Math.PI / 180);
|
||||
if (variant.y) mat4RotateY(blockMatrix, -variant.y * Math.PI / 180);
|
||||
if (variant.x) mat4RotateX(blockMatrix, -variant.x * Math.PI / 180);
|
||||
mat4Translate(blockMatrix, blockMatrix, [-0.5, -0.5, -0.5]);
|
||||
|
||||
for (const el of this.elements) {
|
||||
@@ -125,6 +126,22 @@ export class BlockModel {
|
||||
const size = [tx - fx, ty - fy, tz - fz];
|
||||
const elementShade = el.shade === false ? 0.0 : 1.0;
|
||||
|
||||
// --- LOCAL ELEMENT MATRIX ---
|
||||
const elMatrix = mat4Identity(new Float32Array(16));
|
||||
if (el.rotation) {
|
||||
const [ox, oy, oz] = el.rotation.origin.map(x => x / 16);
|
||||
const angle = el.rotation.angle * Math.PI / 180;
|
||||
|
||||
mat4Translate(elMatrix, elMatrix, [ox, oy, oz]);
|
||||
if (el.rotation.axis === 'x') mat4RotateX(elMatrix, angle);
|
||||
else if (el.rotation.axis === 'y') mat4RotateY(elMatrix, angle);
|
||||
else if (el.rotation.axis === 'z') mat4RotateZ(elMatrix, angle);
|
||||
mat4Translate(elMatrix, elMatrix, [-ox, -oy, -oz]);
|
||||
|
||||
// Note: Minecraft specifies a "rescale" boolean on some rotations to fit faces to block bounds.
|
||||
// It is rarely used (the torch wall JSON doesn't use it), so it is omitted here to save math overhead.
|
||||
}
|
||||
|
||||
for (const [name, face] of Object.entries(el.faces || {})) {
|
||||
const tmpl = CUBOID_FACES[name];
|
||||
|
||||
@@ -154,16 +171,32 @@ export class BlockModel {
|
||||
|
||||
for (let i = 0; i < 4; i++) {
|
||||
const [cx, cy, cz] = tmpl.c[i];
|
||||
|
||||
// 1. Raw local position
|
||||
let vx = fx + cx * size[0], vy = fy + cy * size[1], vz = fz + cz * size[2];
|
||||
|
||||
let wx = blockMatrix[0] * vx + blockMatrix[4] * vy + blockMatrix[8] * vz + blockMatrix[12];
|
||||
let wy = blockMatrix[1] * vx + blockMatrix[5] * vy + blockMatrix[9] * vz + blockMatrix[13];
|
||||
let wz = blockMatrix[2] * vx + blockMatrix[6] * vy + blockMatrix[10] * vz + blockMatrix[14];
|
||||
// 2. Apply element rotation
|
||||
let evx = elMatrix[0] * vx + elMatrix[4] * vy + elMatrix[8] * vz + elMatrix[12];
|
||||
let evy = elMatrix[1] * vx + elMatrix[5] * vy + elMatrix[9] * vz + elMatrix[13];
|
||||
let evz = elMatrix[2] * vx + elMatrix[6] * vy + elMatrix[10] * vz + elMatrix[14];
|
||||
|
||||
// 3. Apply global blockstate rotation
|
||||
let wx = blockMatrix[0] * evx + blockMatrix[4] * evy + blockMatrix[8] * evz + blockMatrix[12];
|
||||
let wy = blockMatrix[1] * evx + blockMatrix[5] * evy + blockMatrix[9] * evz + blockMatrix[13];
|
||||
let wz = blockMatrix[2] * evx + blockMatrix[6] * evy + blockMatrix[10] * evz + blockMatrix[14];
|
||||
pos.push(wx, wy, wz);
|
||||
|
||||
let nx = blockMatrix[0] * tmpl.n[0] + blockMatrix[4] * tmpl.n[1] + blockMatrix[8] * tmpl.n[2];
|
||||
let ny = blockMatrix[1] * tmpl.n[0] + blockMatrix[5] * tmpl.n[1] + blockMatrix[9] * tmpl.n[2];
|
||||
let nz = blockMatrix[2] * tmpl.n[0] + blockMatrix[6] * tmpl.n[1] + blockMatrix[10] * tmpl.n[2];
|
||||
// --- Normals (Apply Rotations Only, No Translation) ---
|
||||
|
||||
// 1. Apply element rotation to normal
|
||||
let enx = elMatrix[0] * tmpl.n[0] + elMatrix[4] * tmpl.n[1] + elMatrix[8] * tmpl.n[2];
|
||||
let eny = elMatrix[1] * tmpl.n[0] + elMatrix[5] * tmpl.n[1] + elMatrix[9] * tmpl.n[2];
|
||||
let enz = elMatrix[2] * tmpl.n[0] + elMatrix[6] * tmpl.n[1] + elMatrix[10] * tmpl.n[2];
|
||||
|
||||
// 2. Apply global blockstate rotation to normal
|
||||
let nx = blockMatrix[0] * enx + blockMatrix[4] * eny + blockMatrix[8] * enz;
|
||||
let ny = blockMatrix[1] * enx + blockMatrix[5] * eny + blockMatrix[9] * enz;
|
||||
let nz = blockMatrix[2] * enx + blockMatrix[6] * eny + blockMatrix[10] * enz;
|
||||
norm.push(nx, ny, nz);
|
||||
|
||||
tint.push(tintable);
|
||||
|
||||
Reference in New Issue
Block a user