diff --git a/src/mson/mson.type.ts b/src/mson/mson.type.ts index f2e3a97..e216af3 100644 --- a/src/mson/mson.type.ts +++ b/src/mson/mson.type.ts @@ -17,6 +17,7 @@ export type MsonOperand = export type MsonVariable = string | number; +export type Mirror3 = [boolean, boolean, boolean]; export type Vec3 = [number, number, number]; export type Vec2 = [number, number]; export type MsonVec3 = Vec3 | [MsonVariable, MsonVariable, MsonVariable]; @@ -92,7 +93,7 @@ export interface MsonCompound extends MsonBaseComponent { * Whether to flip this part's textures. * @default false */ - mirror?: boolean | [boolean, boolean, boolean]; + mirror?: boolean | Mirror3; /** * default type for components (if omitted): mson:box * allowed types: diff --git a/src/three/builder.ts b/src/three/builder.ts index 34dea72..7c05f5c 100644 --- a/src/three/builder.ts +++ b/src/three/builder.ts @@ -10,6 +10,7 @@ import { Vector3, } from 'three'; import { + Mirror3, MsonBaseComponent, MsonBox, MsonComponent, @@ -28,6 +29,7 @@ import { } from '../mson'; import { isArrayOfArrays } from '../util/array-of-array'; import { UVMapper } from './uv-mapper'; +import { MirrorHelper } from './mirror-helper'; export class ThreeBuilder { /** @@ -195,6 +197,13 @@ export class ThreeBuilder { texture?: MsonTexture, ) { const size = new Vector3().fromArray(component.size as Vec3); + const mirror = (parentComponent as MsonCompound)?.mirror; + const effectiveMirroring: Mirror3 = + mirror !== undefined + ? Array.isArray(mirror) + ? mirror + : [mirror, mirror, mirror] + : [false, false, false]; const dilate = new Vector3(); if (component.dilate) { @@ -207,8 +216,19 @@ export class ThreeBuilder { ); const halfSize = size.clone().divideScalar(2); + // Invert Y axis in the offset offset.setY(offset.y * -1); + const adjustedTranslate = halfSize.clone().add(offset); + // Y axis is reversed in the mesh format. + adjustedTranslate.setY(adjustedTranslate.y - size.y); + // Apply mirrored translations, if applicable + // X axis is reversed as well. + adjustedTranslate.set( + adjustedTranslate.x * (effectiveMirroring[0] ? 1 : -1), + adjustedTranslate.y * (effectiveMirroring[1] ? -1 : 1), + adjustedTranslate.z * (effectiveMirroring[2] ? -1 : 1), + ); const geometry = new BoxGeometry( size.x + dilate.x, @@ -220,8 +240,8 @@ export class ThreeBuilder { ); geometry.translate( - -adjustedTranslate.x, - adjustedTranslate.y - size.y, + adjustedTranslate.x, + adjustedTranslate.y, adjustedTranslate.z, ); @@ -230,12 +250,11 @@ export class ThreeBuilder { (geometry as any).type = 'BufferGeometry'; delete (geometry as any).parameters; - UVMapper.mapBoxUVs( - size, - geometry, - texture, - (parentComponent as MsonCompound)?.mirror, - ); + // Map UVs to box + UVMapper.mapBoxUVs(size, geometry, texture); + + // Mirror geometry, if applicable + MirrorHelper.mirrorGeometry(effectiveMirroring, geometry); const mesh = new Mesh(geometry, this.material); mesh.name = `${name}__mesh`; diff --git a/src/three/mirror-helper.ts b/src/three/mirror-helper.ts new file mode 100644 index 0000000..3e09875 --- /dev/null +++ b/src/three/mirror-helper.ts @@ -0,0 +1,38 @@ +import { BufferGeometry, Matrix4 } from 'three'; +import { Mirror3 } from '../mson/mson.type'; + +/** + * Utilities to mirror a THREE BufferGeometry in set axes. + */ +export class MirrorHelper { + static mirrorGeometry(axes: Mirror3, geometry: BufferGeometry) { + // No axes are mirrored, ignore + if (!axes.some((entry) => !!entry)) return; + + // Invert geometry + const inversionMatrix = new Matrix4().makeScale( + axes[0] ? -1 : 1, + axes[1] ? -1 : 1, + axes[2] ? -1 : 1, + ); + geometry.applyMatrix4(inversionMatrix); + + // Reverse faces winding direction + MirrorHelper.reverseIndexBuffer(geometry); + + // Recalculate normals to face correctly + geometry.computeVertexNormals(); + } + + static reverseIndexBuffer(geometry: BufferGeometry) { + if (!geometry.index) return; + + const index = geometry.index.array; + for (let i = 0, il = index.length / 3; i < il; i++) { + let x = index[i * 3]; + index[i * 3] = index[i * 3 + 2]; + index[i * 3 + 2] = x; + } + geometry.index.needsUpdate = true; + } +} diff --git a/src/three/uv-mapper.ts b/src/three/uv-mapper.ts index 0c7206d..38b0d95 100644 --- a/src/three/uv-mapper.ts +++ b/src/three/uv-mapper.ts @@ -148,7 +148,6 @@ export class UVMapper { size: Vector3, geometry: BoxGeometry, texture?: MsonTexture, - mirror?: boolean | [boolean, boolean, boolean], ) { const uvAttribute = geometry.getAttribute('uv') as BufferAttribute; const index = geometry.getIndex() as BufferAttribute; @@ -161,22 +160,6 @@ export class UVMapper { let textureOffsetU = texture?.u ?? 0; let textureOffsetV = texture?.v ?? 0; - const invU: string[] = ['up']; - const invV: string[] = ['up']; - - const mirroring = - mirror !== undefined - ? Array.isArray(mirror) - ? mirror - : [mirror, mirror, mirror] - : [false, false, false]; - - // TODO: figure out what rest of the components mirror - const [flipX, flipY, flipZ] = mirroring; - if (flipX) { - invU.push('south', 'north'); - } - // px nx py ny pz nz // clockwise count for (let i = 0; i < index!.count; i += 3) { @@ -195,8 +178,8 @@ export class UVMapper { textureHeight, finalU, finalV, - invU.includes(faceName), - invV.includes(faceName), + faceName === 'up' || faceName === 'down', + faceName === 'up', ); for (let attrib = 0; attrib < 3; attrib++) {