minerender 2.1.0-alpha.141 → 2.1.0-alpha.142
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/browser/esm/world/SectionMesh.mjs +65 -9
- package/dist/browser/esm/world/SectionMesh.mjs.map +1 -1
- package/dist/browser/index.js +64 -8
- package/dist/browser/index.js.map +1 -1
- package/dist/bundle.js +66 -9
- package/dist/bundle.js.map +1 -1
- package/dist/node/index.js +64 -8
- package/dist/node/index.js.map +1 -1
- package/dist/node/index.mjs +80 -24
- package/dist/node/index.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
// src/world/SectionMesh.ts
|
|
2
|
-
import { BufferGeometry, Color, DoubleSide, Float32BufferAttribute, FrontSide, Group, Mesh, Uint32BufferAttribute } from "three";
|
|
2
|
+
import { BufferGeometry, Color, DoubleSide, DynamicDrawUsage, Float32BufferAttribute, FrontSide, Group, Matrix4, Mesh, Uint32BufferAttribute, Vector3 } from "three";
|
|
3
3
|
import { createCanvas } from "../canvas/CanvasCompat.mjs";
|
|
4
4
|
import { Materials } from "../Materials.mjs";
|
|
5
5
|
import { buildSectionGeometry } from "./SectionGeometry.mjs";
|
|
6
6
|
import { SectionWorker } from "./SectionWorker.mjs";
|
|
7
|
+
var BLOCK_SIZE = 16;
|
|
8
|
+
var QUAD_VERTICES = 4;
|
|
9
|
+
var QUAD_INDICES = 6;
|
|
7
10
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
8
11
|
var WATER_TINT = new Color(4159204).toArray();
|
|
9
12
|
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
@@ -40,8 +43,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
40
43
|
if (!data) {
|
|
41
44
|
const indices = template.geometry.getIndex();
|
|
42
45
|
const position = template.geometry.getAttribute("position");
|
|
43
|
-
const quads = indices.count /
|
|
44
|
-
if (indices.count %
|
|
46
|
+
const quads = indices.count / QUAD_INDICES;
|
|
47
|
+
if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
|
|
45
48
|
throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
|
|
46
49
|
}
|
|
47
50
|
data = {
|
|
@@ -51,8 +54,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
51
54
|
uvs: new Float32Array(quads * 8),
|
|
52
55
|
uvBounds: new Float32Array(quads * 16),
|
|
53
56
|
colors: new Float32Array(quads * 12),
|
|
54
|
-
shade: new Float32Array(quads *
|
|
55
|
-
indices: new Uint16Array(quads *
|
|
57
|
+
shade: new Float32Array(quads * QUAD_VERTICES),
|
|
58
|
+
indices: new Uint16Array(quads * QUAD_INDICES),
|
|
56
59
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
57
60
|
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
58
61
|
atlas: 0,
|
|
@@ -64,7 +67,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
64
67
|
const bounds = template.geometry.getAttribute("uvBounds");
|
|
65
68
|
const color = template.geometry.getAttribute("color");
|
|
66
69
|
const shade = template.geometry.getAttribute("shade");
|
|
67
|
-
for (let vertex = 0; vertex < quads *
|
|
70
|
+
for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
|
|
68
71
|
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
69
72
|
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
70
73
|
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
@@ -77,7 +80,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
77
80
|
data.colors.set([color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1], vertex * 3);
|
|
78
81
|
data.shade[vertex] = shade?.getX(vertex) ?? 1;
|
|
79
82
|
}
|
|
80
|
-
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) -
|
|
83
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
|
|
81
84
|
templateCache.set(template, data);
|
|
82
85
|
}
|
|
83
86
|
id = input.templateData.length;
|
|
@@ -110,6 +113,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
110
113
|
}
|
|
111
114
|
return { input, atlases };
|
|
112
115
|
}
|
|
116
|
+
function sortTransparentQuads(mesh) {
|
|
117
|
+
const index = mesh.geometry.getIndex();
|
|
118
|
+
if (index.count <= QUAD_INDICES) return;
|
|
119
|
+
index.setUsage(DynamicDrawUsage);
|
|
120
|
+
const count = index.count / QUAD_INDICES;
|
|
121
|
+
let source, centers, depths, order;
|
|
122
|
+
const view = new Matrix4(), localCamera = new Vector3(), key = new Vector3(), previous = new Vector3();
|
|
123
|
+
let previousOrthographic;
|
|
124
|
+
mesh.onBeforeRender = (_renderer, _scene, camera) => {
|
|
125
|
+
localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
|
|
126
|
+
const orthographic = !!camera.isOrthographicCamera;
|
|
127
|
+
if (orthographic) {
|
|
128
|
+
view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
|
|
129
|
+
key.set(view.elements[2], view.elements[6], view.elements[10]);
|
|
130
|
+
} else {
|
|
131
|
+
key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
|
|
132
|
+
}
|
|
133
|
+
if (orthographic === previousOrthographic && key.equals(previous)) return;
|
|
134
|
+
previous.copy(key);
|
|
135
|
+
previousOrthographic = orthographic;
|
|
136
|
+
if (!centers) {
|
|
137
|
+
source = new Uint32Array(index.array);
|
|
138
|
+
centers = new Float32Array(count * 3);
|
|
139
|
+
depths = new Float32Array(count);
|
|
140
|
+
order = Uint32Array.from({ length: count }, (_, quad) => quad);
|
|
141
|
+
const positions = mesh.geometry.getAttribute("position");
|
|
142
|
+
for (let quad = 0; quad < count; quad++) {
|
|
143
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
144
|
+
let center = 0;
|
|
145
|
+
for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
|
|
146
|
+
centers[quad * 3 + axis] = center / QUAD_VERTICES;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
for (let quad = 0; quad < count; quad++) {
|
|
151
|
+
const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
|
|
152
|
+
depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
|
|
153
|
+
}
|
|
154
|
+
order.sort((a, b) => depths[a] - depths[b] || a - b);
|
|
155
|
+
if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
|
|
156
|
+
for (let offset = 0; offset < count; offset++) {
|
|
157
|
+
for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
|
|
158
|
+
index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
index.needsUpdate = true;
|
|
162
|
+
};
|
|
163
|
+
}
|
|
113
164
|
var SectionMesh = class _SectionMesh extends Group {
|
|
114
165
|
constructor() {
|
|
115
166
|
super(...arguments);
|
|
@@ -119,7 +170,11 @@ var SectionMesh = class _SectionMesh extends Group {
|
|
|
119
170
|
/** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
|
|
120
171
|
getBlockIndex(hit) {
|
|
121
172
|
if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
|
|
122
|
-
|
|
173
|
+
const mesh = hit.object;
|
|
174
|
+
const owners = this.blockIndices.get(mesh);
|
|
175
|
+
if (!owners) return void 0;
|
|
176
|
+
const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
|
|
177
|
+
return owners[Math.floor(vertex / QUAD_VERTICES)];
|
|
123
178
|
}
|
|
124
179
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
125
180
|
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
@@ -155,13 +210,14 @@ var SectionMesh = class _SectionMesh extends Group {
|
|
|
155
210
|
const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
|
|
156
211
|
material.side = FrontSide;
|
|
157
212
|
if (page.layer === 2) {
|
|
213
|
+
material.depthWrite = !material.transparent;
|
|
158
214
|
material.side = DoubleSide;
|
|
159
215
|
material.forceSinglePass = true;
|
|
160
|
-
material.depthWrite = !material.transparent;
|
|
161
216
|
}
|
|
162
217
|
material.vertexColors = true;
|
|
163
218
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
164
219
|
const mesh = new Mesh(geometry, material);
|
|
220
|
+
if (transparent) sortTransparentQuads(mesh);
|
|
165
221
|
section.add(mesh);
|
|
166
222
|
section.blockIndices.set(mesh, page.blockIndices);
|
|
167
223
|
const unsubscribe = [];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/world/SectionMesh.ts"],"sourcesContent":["import { BufferGeometry, Color, DoubleSide, Float32BufferAttribute, FrontSide, Group, Material, Mesh, MeshBasicMaterial, ShaderMaterial, Texture, Uint32BufferAttribute } from \"three\";\nimport { createCanvas } from \"../canvas/CanvasCompat\";\nimport { Materials } from \"../Materials\";\nimport type { MineRenderScene } from \"../renderer/MineRenderScene\";\nimport { TextureAtlas } from \"../texture/TextureAtlas\";\nimport { buildSectionGeometry, SectionGeometryInput, SectionGeometryPage, SectionTemplateData } from \"./SectionGeometry\";\nimport { SectionWorker } from \"./SectionWorker\";\nimport type { Intersection } from \"three\";\n\n/** Shared quad geometry and atlas data prepared for section merging. */\nexport interface SectionMeshTemplate {\n geometry: BufferGeometry;\n atlas: TextureAtlas;\n cullFaces: Uint8Array;\n /** Quads that accept a placement's biome tint, stored as zero or one. */\n biomeTint?: Uint8Array;\n occludes: boolean;\n /** Whether the model permits corner shading from neighboring full cubes. */\n ambientOcclusion?: boolean;\n layer: 0 | 1;\n}\n\n/** One block placement in a section, with its storage index and hidden-face mask. */\nexport interface SectionMeshEntry {\n index: number;\n template: SectionMeshTemplate;\n cullMask: number;\n /** Linear RGB replacing automatic biome colors on eligible quads. */\n biomeTint?: readonly [number, number, number];\n}\n\n/** Fluid cells for one section and the shared atlases of the kinds present. */\nexport interface SectionFluids {\n cells: Uint8Array;\n /** Linear RGB per section cell, indexed by (x + 16z + 256y) * 3. */\n waterTints?: Float32Array;\n water?: TextureAtlas;\n lava?: TextureAtlas;\n}\n\nconst templateCache = new WeakMap<SectionMeshTemplate, SectionTemplateData>();\nconst WATER_TINT = new Color(0x3f76e4).toArray() as [number, number, number];\n\nfunction toInput(entries: readonly SectionMeshEntry[], maxAtlasSize: number, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): { input: SectionGeometryInput; atlases: TextureAtlas[] } {\n if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError(\"Section atlas size must be a positive integer\");\n const atlases: TextureAtlas[] = [];\n const atlasIds = new Map<TextureAtlas, number>();\n const templateIds = new Map<SectionMeshTemplate, number>();\n const input: SectionGeometryInput = {\n count: entries.length, indices: new Uint16Array(entries.length), templates: new Uint16Array(entries.length),\n cullMasks: new Uint8Array(entries.length), templateData: [], atlases: [], maxAtlasSize, ...lighting\n };\n for (let i = 0; i < entries.length; i++) {\n const { index, template, cullMask, biomeTint } = entries[i];\n if (biomeTint) {\n input.biomeTints ??= new Float32Array(entries.length * 3).fill(NaN);\n input.biomeTints.set(biomeTint, i * 3);\n }\n let id = templateIds.get(template);\n if (id === undefined) {\n let atlas = atlasIds.get(template.atlas);\n if (atlas === undefined) {\n atlas = atlases.length;\n atlasIds.set(template.atlas, atlas);\n atlases.push(template.atlas);\n input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });\n }\n let data = templateCache.get(template);\n if (!data) {\n const indices = template.geometry.getIndex()!;\n const position = template.geometry.getAttribute(\"position\");\n const quads = indices.count / 6;\n if (indices.count % 6 !== 0 || position.count !== quads * 4) {\n throw new RangeError(\"Section template geometry must hold four vertices and six indices per quad\");\n }\n data = {\n quads, positions: new Float32Array(quads * 12), normals: new Float32Array(quads * 12), uvs: new Float32Array(quads * 8),\n uvBounds: new Float32Array(quads * 16), colors: new Float32Array(quads * 12), shade: new Float32Array(quads * 4), indices: new Uint16Array(quads * 6),\n cullFaces: new Uint8Array(template.cullFaces), biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : undefined, atlas: 0, layer: template.layer, ambientOcclusion: template.ambientOcclusion\n };\n const normal = template.geometry.getAttribute(\"normal\");\n const uv = template.geometry.getAttribute(\"uv\");\n const bounds = template.geometry.getAttribute(\"uvBounds\");\n const color = template.geometry.getAttribute(\"color\");\n const shade = template.geometry.getAttribute(\"shade\");\n for (let vertex = 0; vertex < quads * 4; vertex++) {\n data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);\n data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);\n data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);\n data.uvBounds.set([bounds?.getX(vertex) ?? 0, bounds?.getY(vertex) ?? 0,\n bounds?.getZ(vertex) ?? 1, bounds?.getW(vertex) ?? 1], vertex * 4);\n data.colors.set([color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1], vertex * 3);\n data.shade![vertex] = shade?.getX(vertex) ?? 1;\n }\n for (let i = 0; i < data.indices.length; i++) data.indices[i] = indices.getX(i) - 4 * Math.floor(i / 6);\n templateCache.set(template, data);\n }\n id = input.templateData.length;\n templateIds.set(template, id);\n // Atlas IDs belong to each build; cached buffers can be shared across concurrent builds.\n input.templateData.push({ ...data, atlas });\n }\n input.indices[i] = index;\n input.templates[i] = id;\n input.cullMasks[i] = cullMask;\n }\n if (fluids) {\n input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };\n for (const kind of [\"water\", \"lava\"] as const) {\n const atlas = fluids[kind];\n if (!atlas) continue;\n let id = atlasIds.get(atlas);\n if (id === undefined) {\n id = atlases.length;\n atlasIds.set(atlas, id);\n atlases.push(atlas);\n input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });\n }\n input.fluids[kind] = {\n atlas: id, still: [...atlas.positions.still, ...atlas.sizes.still],\n flow: [...atlas.positions.flow, ...atlas.sizes.flow], tint: kind === \"water\" ? WATER_TINT : [1, 1, 1]\n };\n }\n }\n return { input, atlases };\n}\n\n/** Merged terrain geometry and atlas pages for one 16×16×16 section. Owns its generated render resources. */\nexport class SectionMesh extends Group {\n\n private readonly ownedMeshes: { mesh: Mesh<BufferGeometry, Material>; texture?: Texture; unsubscribe: (() => void)[] }[] = [];\n private readonly blockIndices = new Map<Mesh, Uint16Array>();\n\n /** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */\n public getBlockIndex(hit: Intersection): number | undefined {\n if (hit.faceIndex === undefined || hit.faceIndex === null) return undefined;\n return this.blockIndices.get(hit.object as Mesh)?.[Math.floor(hit.faceIndex / 2)];\n }\n\n /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */\n public static build(entries: readonly SectionMeshEntry[], maxAtlasSize = 2048, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): SectionMesh {\n const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);\n return this.fromPages(buildSectionGeometry(input), atlases);\n }\n\n /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */\n public static async buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize = 2048, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): Promise<SectionMesh> {\n const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);\n const worker = SectionWorker.shared();\n const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);\n return this.fromPages(pages, atlases);\n }\n\n private static fromPages(pages: SectionGeometryPage[], atlases: TextureAtlas[]): SectionMesh {\n const section = new SectionMesh();\n for (const page of pages) {\n const canvas = createCanvas(page.width, page.height);\n const context = canvas.getContext(\"2d\") as CanvasRenderingContext2D;\n for (const { atlas, x, y } of page.placements) {\n context.drawImage(atlases[atlas].image.canvas as CanvasImageSource, x, y);\n }\n const geometry = new BufferGeometry();\n // Passing buffers keeps Three's typed attributes from copying the builder's arrays.\n geometry.setAttribute(\"position\", new Float32BufferAttribute(page.positions.buffer, 3));\n geometry.setAttribute(\"normal\", new Float32BufferAttribute(page.normals.buffer, 3));\n geometry.setAttribute(\"uv\", new Float32BufferAttribute(page.uvs.buffer, 2));\n geometry.setAttribute(\"uvBounds\", new Float32BufferAttribute(page.uvBounds.buffer, 4));\n geometry.setAttribute(\"color\", new Float32BufferAttribute(page.colors.buffer, 3));\n geometry.setAttribute(\"shade\", new Float32BufferAttribute(page.shade.buffer, 1));\n geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));\n geometry.computeBoundingBox();\n geometry.computeBoundingSphere();\n const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;\n const material = Materials.createShadedCanvasMaterial(canvas as HTMLCanvasElement, transparent, false, true);\n material.side = FrontSide;\n if (page.layer === 2) {\n material.side = DoubleSide;\n material.forceSinglePass = true;\n material.depthWrite = !material.transparent;\n }\n material.vertexColors = true;\n const texture = (material as ShaderMaterial).uniforms?.map?.value ?? (material as MeshBasicMaterial).map;\n const mesh = new Mesh(geometry, material);\n section.add(mesh);\n section.blockIndices.set(mesh, page.blockIndices);\n const unsubscribe: (() => void)[] = [];\n for (const { atlas: id, x, y } of page.placements) {\n const atlas = atlases[id];\n if (!atlas.hasAnimation) continue;\n unsubscribe.push(atlas.subscribe(() => {\n context.clearRect(x, y, atlas.image.width, atlas.image.height);\n context.drawImage(atlas.image.canvas as CanvasImageSource, x, y);\n texture.needsUpdate = true;\n section.traverseAncestors(parent => {\n if ((parent as MineRenderScene).isMineRenderScene) (parent as MineRenderScene).dirty = true;\n });\n }));\n }\n section.ownedMeshes.push({ mesh, texture, unsubscribe });\n }\n return section;\n }\n\n public dispose(): void {\n for (const { mesh, texture, unsubscribe } of this.ownedMeshes.splice(0)) {\n for (const stop of unsubscribe) stop();\n mesh.removeFromParent();\n mesh.geometry.dispose();\n mesh.material.dispose();\n texture?.dispose();\n }\n this.blockIndices.clear();\n this.removeFromParent();\n super.dispose();\n }\n\n}\n"],"mappings":"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","names":["i"]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/world/SectionMesh.ts"],"sourcesContent":["import { BufferGeometry, Color, DoubleSide, DynamicDrawUsage, Float32BufferAttribute, FrontSide, Group, Material, Matrix4, Mesh, MeshBasicMaterial, ShaderMaterial, Texture, Uint32BufferAttribute, Vector3 } from \"three\";\nimport { createCanvas } from \"../canvas/CanvasCompat\";\nimport { Materials } from \"../Materials\";\nimport type { MineRenderScene } from \"../renderer/MineRenderScene\";\nimport { TextureAtlas } from \"../texture/TextureAtlas\";\nimport { buildSectionGeometry, SectionGeometryInput, SectionGeometryPage, SectionTemplateData } from \"./SectionGeometry\";\nimport { SectionWorker } from \"./SectionWorker\";\nimport type { Camera, Intersection } from \"three\";\n\n/** Shared quad geometry and atlas data prepared for section merging. */\nexport interface SectionMeshTemplate {\n geometry: BufferGeometry;\n atlas: TextureAtlas;\n cullFaces: Uint8Array;\n /** Quads that accept a placement's biome tint, stored as zero or one. */\n biomeTint?: Uint8Array;\n occludes: boolean;\n /** Whether the model permits corner shading from neighboring full cubes. */\n ambientOcclusion?: boolean;\n layer: 0 | 1;\n}\n\n/** One block placement in a section, with its storage index and hidden-face mask. */\nexport interface SectionMeshEntry {\n index: number;\n template: SectionMeshTemplate;\n cullMask: number;\n /** Linear RGB replacing automatic biome colors on eligible quads. */\n biomeTint?: readonly [number, number, number];\n}\n\n/** Fluid cells for one section and the shared atlases of the kinds present. */\nexport interface SectionFluids {\n cells: Uint8Array;\n /** Linear RGB per section cell, indexed by (x + 16z + 256y) * 3. */\n waterTints?: Float32Array;\n water?: TextureAtlas;\n lava?: TextureAtlas;\n}\n\nconst BLOCK_SIZE = 16;\nconst QUAD_VERTICES = 4;\nconst QUAD_INDICES = 6;\n\nconst templateCache = new WeakMap<SectionMeshTemplate, SectionTemplateData>();\nconst WATER_TINT = new Color(0x3f76e4).toArray() as [number, number, number];\n\nfunction toInput(entries: readonly SectionMeshEntry[], maxAtlasSize: number, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): { input: SectionGeometryInput; atlases: TextureAtlas[] } {\n if (!Number.isInteger(maxAtlasSize) || maxAtlasSize < 1) throw new RangeError(\"Section atlas size must be a positive integer\");\n const atlases: TextureAtlas[] = [];\n const atlasIds = new Map<TextureAtlas, number>();\n const templateIds = new Map<SectionMeshTemplate, number>();\n const input: SectionGeometryInput = {\n count: entries.length, indices: new Uint16Array(entries.length), templates: new Uint16Array(entries.length),\n cullMasks: new Uint8Array(entries.length), templateData: [], atlases: [], maxAtlasSize, ...lighting\n };\n for (let i = 0; i < entries.length; i++) {\n const { index, template, cullMask, biomeTint } = entries[i];\n if (biomeTint) {\n input.biomeTints ??= new Float32Array(entries.length * 3).fill(NaN);\n input.biomeTints.set(biomeTint, i * 3);\n }\n let id = templateIds.get(template);\n if (id === undefined) {\n let atlas = atlasIds.get(template.atlas);\n if (atlas === undefined) {\n atlas = atlases.length;\n atlasIds.set(template.atlas, atlas);\n atlases.push(template.atlas);\n input.atlases.push({ width: template.atlas.image.width, height: template.atlas.image.height, animated: template.atlas.hasAnimation });\n }\n let data = templateCache.get(template);\n if (!data) {\n const indices = template.geometry.getIndex()!;\n const position = template.geometry.getAttribute(\"position\");\n const quads = indices.count / QUAD_INDICES;\n if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {\n throw new RangeError(\"Section template geometry must hold four vertices and six indices per quad\");\n }\n data = {\n quads, positions: new Float32Array(quads * 12), normals: new Float32Array(quads * 12), uvs: new Float32Array(quads * 8),\n uvBounds: new Float32Array(quads * 16), colors: new Float32Array(quads * 12), shade: new Float32Array(quads * QUAD_VERTICES), indices: new Uint16Array(quads * QUAD_INDICES),\n cullFaces: new Uint8Array(template.cullFaces), biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : undefined, atlas: 0, layer: template.layer, ambientOcclusion: template.ambientOcclusion\n };\n const normal = template.geometry.getAttribute(\"normal\");\n const uv = template.geometry.getAttribute(\"uv\");\n const bounds = template.geometry.getAttribute(\"uvBounds\");\n const color = template.geometry.getAttribute(\"color\");\n const shade = template.geometry.getAttribute(\"shade\");\n for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {\n data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);\n data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);\n data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);\n data.uvBounds.set([bounds?.getX(vertex) ?? 0, bounds?.getY(vertex) ?? 0,\n bounds?.getZ(vertex) ?? 1, bounds?.getW(vertex) ?? 1], vertex * 4);\n data.colors.set([color?.getX(vertex) ?? 1, color?.getY(vertex) ?? 1, color?.getZ(vertex) ?? 1], vertex * 3);\n data.shade![vertex] = shade?.getX(vertex) ?? 1;\n }\n for (let i = 0; i < data.indices.length; i++) data.indices[i] = indices.getX(i) - QUAD_VERTICES * Math.floor(i / QUAD_INDICES);\n templateCache.set(template, data);\n }\n id = input.templateData.length;\n templateIds.set(template, id);\n // Atlas IDs belong to each build; cached buffers can be shared across concurrent builds.\n input.templateData.push({ ...data, atlas });\n }\n input.indices[i] = index;\n input.templates[i] = id;\n input.cullMasks[i] = cullMask;\n }\n if (fluids) {\n input.fluids = { cells: fluids.cells, waterTints: fluids.waterTints };\n for (const kind of [\"water\", \"lava\"] as const) {\n const atlas = fluids[kind];\n if (!atlas) continue;\n let id = atlasIds.get(atlas);\n if (id === undefined) {\n id = atlases.length;\n atlasIds.set(atlas, id);\n atlases.push(atlas);\n input.atlases.push({ width: atlas.image.width, height: atlas.image.height, animated: atlas.hasAnimation });\n }\n input.fluids[kind] = {\n atlas: id, still: [...atlas.positions.still, ...atlas.sizes.still],\n flow: [...atlas.positions.flow, ...atlas.sizes.flow], tint: kind === \"water\" ? WATER_TINT : [1, 1, 1]\n };\n }\n }\n return { input, atlases };\n}\n\nfunction sortTransparentQuads(mesh: Mesh<BufferGeometry, Material>): void {\n const index = mesh.geometry.getIndex()!;\n if (index.count <= QUAD_INDICES) return;\n index.setUsage(DynamicDrawUsage);\n const count = index.count / QUAD_INDICES;\n let source: Uint32Array, centers: Float32Array, depths: Float32Array, order: Uint32Array;\n const view = new Matrix4(), localCamera = new Vector3(), key = new Vector3(), previous = new Vector3();\n let previousOrthographic: boolean | undefined;\n mesh.onBeforeRender = (_renderer, _scene, camera) => {\n localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());\n const orthographic = !!(camera as Camera & { isOrthographicCamera?: boolean }).isOrthographicCamera;\n if (orthographic) {\n // Three updates mesh.modelViewMatrix after this callback.\n view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);\n key.set(view.elements[2], view.elements[6], view.elements[10]);\n } else {\n key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();\n }\n if (orthographic === previousOrthographic && key.equals(previous)) return;\n previous.copy(key);\n previousOrthographic = orthographic;\n if (!centers) {\n source = new Uint32Array(index.array);\n centers = new Float32Array(count * 3);\n depths = new Float32Array(count);\n order = Uint32Array.from({ length: count }, (_, quad) => quad);\n const positions = mesh.geometry.getAttribute(\"position\");\n for (let quad = 0; quad < count; quad++) {\n for (let axis = 0; axis < 3; axis++) {\n let center = 0;\n for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);\n centers[quad * 3 + axis] = center / QUAD_VERTICES;\n }\n }\n }\n for (let quad = 0; quad < count; quad++) {\n const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];\n depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z\n : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);\n }\n order.sort((a, b) => depths[a] - depths[b] || a - b);\n if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;\n for (let offset = 0; offset < count; offset++) {\n for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {\n index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];\n }\n }\n index.needsUpdate = true;\n };\n}\n\n/** Merged terrain geometry and atlas pages for one 16×16×16 section. Owns its generated render resources. */\nexport class SectionMesh extends Group {\n\n private readonly ownedMeshes: { mesh: Mesh<BufferGeometry, Material>; texture?: Texture; unsubscribe: (() => void)[] }[] = [];\n private readonly blockIndices = new Map<Mesh, Uint16Array>();\n\n /** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */\n public getBlockIndex(hit: Intersection): number | undefined {\n if (hit.faceIndex === undefined || hit.faceIndex === null) return undefined;\n const mesh = hit.object as Mesh;\n const owners = this.blockIndices.get(mesh);\n if (!owners) return undefined;\n // Sorted triangles still refer to each quad's original four consecutive vertices.\n const vertex = mesh.geometry.getIndex()!.getX(hit.faceIndex * 3);\n return owners[Math.floor(vertex / QUAD_VERTICES)];\n }\n\n /** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */\n public static build(entries: readonly SectionMeshEntry[], maxAtlasSize = 2048, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): SectionMesh {\n const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);\n return this.fromPages(buildSectionGeometry(input), atlases);\n }\n\n /** Builds section-local meshes in a browser worker when available, with synchronous fallback. */\n public static async buildAsync(entries: readonly SectionMeshEntry[], maxAtlasSize = 2048, fluids?: SectionFluids,\n lighting?: Pick<SectionGeometryInput, \"light\" | \"ambientLight\" | \"ambientOcclusion\">): Promise<SectionMesh> {\n const { input, atlases } = toInput(entries, maxAtlasSize, fluids, lighting);\n const worker = SectionWorker.shared();\n const pages = worker ? await worker.build(input).catch(() => buildSectionGeometry(input)) : buildSectionGeometry(input);\n return this.fromPages(pages, atlases);\n }\n\n private static fromPages(pages: SectionGeometryPage[], atlases: TextureAtlas[]): SectionMesh {\n const section = new SectionMesh();\n for (const page of pages) {\n const canvas = createCanvas(page.width, page.height);\n const context = canvas.getContext(\"2d\") as CanvasRenderingContext2D;\n for (const { atlas, x, y } of page.placements) {\n context.drawImage(atlases[atlas].image.canvas as CanvasImageSource, x, y);\n }\n const geometry = new BufferGeometry();\n // Passing buffers keeps Three's typed attributes from copying the builder's arrays.\n geometry.setAttribute(\"position\", new Float32BufferAttribute(page.positions.buffer, 3));\n geometry.setAttribute(\"normal\", new Float32BufferAttribute(page.normals.buffer, 3));\n geometry.setAttribute(\"uv\", new Float32BufferAttribute(page.uvs.buffer, 2));\n geometry.setAttribute(\"uvBounds\", new Float32BufferAttribute(page.uvBounds.buffer, 4));\n geometry.setAttribute(\"color\", new Float32BufferAttribute(page.colors.buffer, 3));\n geometry.setAttribute(\"shade\", new Float32BufferAttribute(page.shade.buffer, 1));\n geometry.setIndex(new Uint32BufferAttribute(page.indices.buffer, 1));\n geometry.computeBoundingBox();\n geometry.computeBoundingSphere();\n const transparent = page.layer === 2 ? atlases[page.placements[0].atlas].hasTranslucency : page.layer === 1;\n const material = Materials.createShadedCanvasMaterial(canvas as HTMLCanvasElement, transparent, false, true);\n material.side = FrontSide;\n if (page.layer === 2) {\n material.depthWrite = !material.transparent;\n material.side = DoubleSide;\n material.forceSinglePass = true;\n }\n material.vertexColors = true;\n const texture = (material as ShaderMaterial).uniforms?.map?.value ?? (material as MeshBasicMaterial).map;\n const mesh = new Mesh(geometry, material);\n if (transparent) sortTransparentQuads(mesh);\n section.add(mesh);\n section.blockIndices.set(mesh, page.blockIndices);\n const unsubscribe: (() => void)[] = [];\n for (const { atlas: id, x, y } of page.placements) {\n const atlas = atlases[id];\n if (!atlas.hasAnimation) continue;\n unsubscribe.push(atlas.subscribe(() => {\n context.clearRect(x, y, atlas.image.width, atlas.image.height);\n context.drawImage(atlas.image.canvas as CanvasImageSource, x, y);\n texture.needsUpdate = true;\n section.traverseAncestors(parent => {\n if ((parent as MineRenderScene).isMineRenderScene) (parent as MineRenderScene).dirty = true;\n });\n }));\n }\n section.ownedMeshes.push({ mesh, texture, unsubscribe });\n }\n return section;\n }\n\n public dispose(): void {\n for (const { mesh, texture, unsubscribe } of this.ownedMeshes.splice(0)) {\n for (const stop of unsubscribe) stop();\n mesh.removeFromParent();\n mesh.geometry.dispose();\n mesh.material.dispose();\n texture?.dispose();\n }\n this.blockIndices.clear();\n this.removeFromParent();\n super.dispose();\n }\n\n}\n"],"mappings":"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","names":["i"]}
|
package/dist/browser/index.js
CHANGED
|
@@ -19651,6 +19651,9 @@ _SectionWorker.initialized = false;
|
|
|
19651
19651
|
var SectionWorker = _SectionWorker;
|
|
19652
19652
|
|
|
19653
19653
|
// src/world/SectionMesh.ts
|
|
19654
|
+
var BLOCK_SIZE = 16;
|
|
19655
|
+
var QUAD_VERTICES = 4;
|
|
19656
|
+
var QUAD_INDICES = 6;
|
|
19654
19657
|
var templateCache = /* @__PURE__ */ new WeakMap();
|
|
19655
19658
|
var WATER_TINT = new import_three46.Color(4159204).toArray();
|
|
19656
19659
|
function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
@@ -19687,8 +19690,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19687
19690
|
if (!data) {
|
|
19688
19691
|
const indices = template.geometry.getIndex();
|
|
19689
19692
|
const position = template.geometry.getAttribute("position");
|
|
19690
|
-
const quads = indices.count /
|
|
19691
|
-
if (indices.count %
|
|
19693
|
+
const quads = indices.count / QUAD_INDICES;
|
|
19694
|
+
if (indices.count % QUAD_INDICES !== 0 || position.count !== quads * QUAD_VERTICES) {
|
|
19692
19695
|
throw new RangeError("Section template geometry must hold four vertices and six indices per quad");
|
|
19693
19696
|
}
|
|
19694
19697
|
data = {
|
|
@@ -19698,8 +19701,8 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19698
19701
|
uvs: new Float32Array(quads * 8),
|
|
19699
19702
|
uvBounds: new Float32Array(quads * 16),
|
|
19700
19703
|
colors: new Float32Array(quads * 12),
|
|
19701
|
-
shade: new Float32Array(quads *
|
|
19702
|
-
indices: new Uint16Array(quads *
|
|
19704
|
+
shade: new Float32Array(quads * QUAD_VERTICES),
|
|
19705
|
+
indices: new Uint16Array(quads * QUAD_INDICES),
|
|
19703
19706
|
cullFaces: new Uint8Array(template.cullFaces),
|
|
19704
19707
|
biomeTint: template.biomeTint ? new Uint8Array(template.biomeTint) : void 0,
|
|
19705
19708
|
atlas: 0,
|
|
@@ -19711,7 +19714,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19711
19714
|
const bounds = template.geometry.getAttribute("uvBounds");
|
|
19712
19715
|
const color2 = template.geometry.getAttribute("color");
|
|
19713
19716
|
const shade = template.geometry.getAttribute("shade");
|
|
19714
|
-
for (let vertex = 0; vertex < quads *
|
|
19717
|
+
for (let vertex = 0; vertex < quads * QUAD_VERTICES; vertex++) {
|
|
19715
19718
|
data.positions.set([position.getX(vertex), position.getY(vertex), position.getZ(vertex)], vertex * 3);
|
|
19716
19719
|
data.normals.set([normal.getX(vertex), normal.getY(vertex), normal.getZ(vertex)], vertex * 3);
|
|
19717
19720
|
data.uvs.set([uv.getX(vertex), uv.getY(vertex)], vertex * 2);
|
|
@@ -19724,7 +19727,7 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19724
19727
|
data.colors.set([color2?.getX(vertex) ?? 1, color2?.getY(vertex) ?? 1, color2?.getZ(vertex) ?? 1], vertex * 3);
|
|
19725
19728
|
data.shade[vertex] = shade?.getX(vertex) ?? 1;
|
|
19726
19729
|
}
|
|
19727
|
-
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) -
|
|
19730
|
+
for (let i2 = 0; i2 < data.indices.length; i2++) data.indices[i2] = indices.getX(i2) - QUAD_VERTICES * Math.floor(i2 / QUAD_INDICES);
|
|
19728
19731
|
templateCache.set(template, data);
|
|
19729
19732
|
}
|
|
19730
19733
|
id = input.templateData.length;
|
|
@@ -19757,6 +19760,54 @@ function toInput(entries, maxAtlasSize, fluids, lighting) {
|
|
|
19757
19760
|
}
|
|
19758
19761
|
return { input, atlases: atlases2 };
|
|
19759
19762
|
}
|
|
19763
|
+
function sortTransparentQuads(mesh) {
|
|
19764
|
+
const index = mesh.geometry.getIndex();
|
|
19765
|
+
if (index.count <= QUAD_INDICES) return;
|
|
19766
|
+
index.setUsage(import_three46.DynamicDrawUsage);
|
|
19767
|
+
const count = index.count / QUAD_INDICES;
|
|
19768
|
+
let source, centers, depths, order;
|
|
19769
|
+
const view = new import_three46.Matrix4(), localCamera = new import_three46.Vector3(), key = new import_three46.Vector3(), previous = new import_three46.Vector3();
|
|
19770
|
+
let previousOrthographic;
|
|
19771
|
+
mesh.onBeforeRender = (_renderer, _scene, camera) => {
|
|
19772
|
+
localCamera.setFromMatrixPosition(camera.matrixWorld).applyMatrix4(view.copy(mesh.matrixWorld).invert());
|
|
19773
|
+
const orthographic = !!camera.isOrthographicCamera;
|
|
19774
|
+
if (orthographic) {
|
|
19775
|
+
view.multiplyMatrices(camera.matrixWorldInverse, mesh.matrixWorld);
|
|
19776
|
+
key.set(view.elements[2], view.elements[6], view.elements[10]);
|
|
19777
|
+
} else {
|
|
19778
|
+
key.copy(localCamera).divideScalar(BLOCK_SIZE).floor();
|
|
19779
|
+
}
|
|
19780
|
+
if (orthographic === previousOrthographic && key.equals(previous)) return;
|
|
19781
|
+
previous.copy(key);
|
|
19782
|
+
previousOrthographic = orthographic;
|
|
19783
|
+
if (!centers) {
|
|
19784
|
+
source = new Uint32Array(index.array);
|
|
19785
|
+
centers = new Float32Array(count * 3);
|
|
19786
|
+
depths = new Float32Array(count);
|
|
19787
|
+
order = Uint32Array.from({ length: count }, (_, quad) => quad);
|
|
19788
|
+
const positions = mesh.geometry.getAttribute("position");
|
|
19789
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19790
|
+
for (let axis = 0; axis < 3; axis++) {
|
|
19791
|
+
let center = 0;
|
|
19792
|
+
for (let corner = 0; corner < QUAD_VERTICES; corner++) center += positions.getComponent(quad * QUAD_VERTICES + corner, axis);
|
|
19793
|
+
centers[quad * 3 + axis] = center / QUAD_VERTICES;
|
|
19794
|
+
}
|
|
19795
|
+
}
|
|
19796
|
+
}
|
|
19797
|
+
for (let quad = 0; quad < count; quad++) {
|
|
19798
|
+
const x = centers[quad * 3], y = centers[quad * 3 + 1], z = centers[quad * 3 + 2];
|
|
19799
|
+
depths[quad] = orthographic ? key.x * x + key.y * y + key.z * z : -((x - localCamera.x) ** 2 + (y - localCamera.y) ** 2 + (z - localCamera.z) ** 2);
|
|
19800
|
+
}
|
|
19801
|
+
order.sort((a, b) => depths[a] - depths[b] || a - b);
|
|
19802
|
+
if (!order.some((quad, offset) => index.array[offset * QUAD_INDICES] !== source[quad * QUAD_INDICES])) return;
|
|
19803
|
+
for (let offset = 0; offset < count; offset++) {
|
|
19804
|
+
for (let indexOffset = 0; indexOffset < QUAD_INDICES; indexOffset++) {
|
|
19805
|
+
index.array[offset * QUAD_INDICES + indexOffset] = source[order[offset] * QUAD_INDICES + indexOffset];
|
|
19806
|
+
}
|
|
19807
|
+
}
|
|
19808
|
+
index.needsUpdate = true;
|
|
19809
|
+
};
|
|
19810
|
+
}
|
|
19760
19811
|
var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
19761
19812
|
constructor() {
|
|
19762
19813
|
super(...arguments);
|
|
@@ -19766,7 +19817,11 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
|
19766
19817
|
/** Returns the owning section cell for a triangle hit on this mesh's merged geometry. */
|
|
19767
19818
|
getBlockIndex(hit) {
|
|
19768
19819
|
if (hit.faceIndex === void 0 || hit.faceIndex === null) return void 0;
|
|
19769
|
-
|
|
19820
|
+
const mesh = hit.object;
|
|
19821
|
+
const owners = this.blockIndices.get(mesh);
|
|
19822
|
+
if (!owners) return void 0;
|
|
19823
|
+
const vertex = mesh.geometry.getIndex().getX(hit.faceIndex * 3);
|
|
19824
|
+
return owners[Math.floor(vertex / QUAD_VERTICES)];
|
|
19770
19825
|
}
|
|
19771
19826
|
/** Builds section-local meshes from visible quads. `maxAtlasSize` limits each atlas dimension in pixels. */
|
|
19772
19827
|
static build(entries, maxAtlasSize = 2048, fluids, lighting) {
|
|
@@ -19802,13 +19857,14 @@ var SectionMesh = class _SectionMesh extends import_three46.Group {
|
|
|
19802
19857
|
const material = Materials.createShadedCanvasMaterial(canvas, transparent, false, true);
|
|
19803
19858
|
material.side = import_three46.FrontSide;
|
|
19804
19859
|
if (page.layer === 2) {
|
|
19860
|
+
material.depthWrite = !material.transparent;
|
|
19805
19861
|
material.side = import_three46.DoubleSide;
|
|
19806
19862
|
material.forceSinglePass = true;
|
|
19807
|
-
material.depthWrite = !material.transparent;
|
|
19808
19863
|
}
|
|
19809
19864
|
material.vertexColors = true;
|
|
19810
19865
|
const texture = material.uniforms?.map?.value ?? material.map;
|
|
19811
19866
|
const mesh = new import_three46.Mesh(geometry, material);
|
|
19867
|
+
if (transparent) sortTransparentQuads(mesh);
|
|
19812
19868
|
section.add(mesh);
|
|
19813
19869
|
section.blockIndices.set(mesh, page.blockIndices);
|
|
19814
19870
|
const unsubscribe = [];
|