@adatechnology/game-vegetation-three 0.1.0 → 0.2.1
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/README.md +7 -0
- package/dist/index.global.js +1725 -198
- package/dist/index.global.js.map +1 -1
- package/dist/index.js +11 -7
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.global.js
CHANGED
|
@@ -41,6 +41,1523 @@ var GameVegetationThree = (() => {
|
|
|
41
41
|
scatterLot: () => scatterLot
|
|
42
42
|
});
|
|
43
43
|
|
|
44
|
+
// ../game-shapes/dist/chunk-R4RY23RH.js
|
|
45
|
+
function canonicalJson(value) {
|
|
46
|
+
if (value === null) return "null";
|
|
47
|
+
if (typeof value === "number") return Object.is(value, -0) ? "-0" : Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
|
|
48
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
49
|
+
if (typeof value === "boolean") return value ? "true" : "false";
|
|
50
|
+
if (typeof value === "function") throw new Error("game-shapes: a function cannot be part of a shape (register it as a generator)");
|
|
51
|
+
if (ArrayBuffer.isView(value)) return canonicalJson(Array.from(value));
|
|
52
|
+
if (Array.isArray(value)) return `[${value.map((item) => item === void 0 ? "null" : canonicalJson(item)).join(",")}]`;
|
|
53
|
+
if (typeof value === "object") {
|
|
54
|
+
const record = value;
|
|
55
|
+
const keys = Object.keys(record).filter((key) => record[key] !== void 0).sort();
|
|
56
|
+
return `{${keys.map((key) => `${JSON.stringify(key)}:${canonicalJson(record[key])}`).join(",")}}`;
|
|
57
|
+
}
|
|
58
|
+
return "null";
|
|
59
|
+
}
|
|
60
|
+
function needsUint32(array) {
|
|
61
|
+
for (let i = array.length - 1; i >= 0; --i) if (array[i] >= 65535) return true;
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
var indexArray = (indices) => needsUint32(indices) ? new Uint32Array(indices) : new Uint16Array(indices);
|
|
65
|
+
function pack(type, parameters, a) {
|
|
66
|
+
return {
|
|
67
|
+
type,
|
|
68
|
+
parameters,
|
|
69
|
+
positions: new Float32Array(a.vertices),
|
|
70
|
+
normals: new Float32Array(a.normals),
|
|
71
|
+
uvs: new Float32Array(a.uvs),
|
|
72
|
+
...a.indices ? { indices: indexArray(a.indices) } : {},
|
|
73
|
+
groups: a.groups
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
var normalize3 = (x, y, z) => {
|
|
77
|
+
const length = Math.sqrt(x * x + y * y + z * z);
|
|
78
|
+
const s = 1 / (length || 1);
|
|
79
|
+
return [x * s, y * s, z * s];
|
|
80
|
+
};
|
|
81
|
+
function box(width = 1, height = 1, depth = 1, widthSegments = 1, heightSegments = 1, depthSegments = 1) {
|
|
82
|
+
const parameters = { width, height, depth, widthSegments, heightSegments, depthSegments };
|
|
83
|
+
widthSegments = Math.floor(widthSegments);
|
|
84
|
+
heightSegments = Math.floor(heightSegments);
|
|
85
|
+
depthSegments = Math.floor(depthSegments);
|
|
86
|
+
const indices = [];
|
|
87
|
+
const vertices = [];
|
|
88
|
+
const normals = [];
|
|
89
|
+
const uvs = [];
|
|
90
|
+
const groups = [];
|
|
91
|
+
let numberOfVertices = 0;
|
|
92
|
+
let groupStart = 0;
|
|
93
|
+
const buildPlane = (u, v, w, udir, vdir, width2, height2, depth2, gridX, gridY, materialIndex) => {
|
|
94
|
+
const segmentWidth = width2 / gridX;
|
|
95
|
+
const segmentHeight = height2 / gridY;
|
|
96
|
+
const widthHalf = width2 / 2;
|
|
97
|
+
const heightHalf = height2 / 2;
|
|
98
|
+
const depthHalf = depth2 / 2;
|
|
99
|
+
const gridX1 = gridX + 1;
|
|
100
|
+
const gridY1 = gridY + 1;
|
|
101
|
+
let vertexCounter = 0;
|
|
102
|
+
let groupCount = 0;
|
|
103
|
+
const vector = { x: 0, y: 0, z: 0 };
|
|
104
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
105
|
+
const y = iy * segmentHeight - heightHalf;
|
|
106
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
107
|
+
const x = ix * segmentWidth - widthHalf;
|
|
108
|
+
vector[u] = x * udir;
|
|
109
|
+
vector[v] = y * vdir;
|
|
110
|
+
vector[w] = depthHalf;
|
|
111
|
+
vertices.push(vector.x, vector.y, vector.z);
|
|
112
|
+
vector[u] = 0;
|
|
113
|
+
vector[v] = 0;
|
|
114
|
+
vector[w] = depth2 > 0 ? 1 : -1;
|
|
115
|
+
normals.push(vector.x, vector.y, vector.z);
|
|
116
|
+
uvs.push(ix / gridX);
|
|
117
|
+
uvs.push(1 - iy / gridY);
|
|
118
|
+
vertexCounter += 1;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
122
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
123
|
+
const a = numberOfVertices + ix + gridX1 * iy;
|
|
124
|
+
const b = numberOfVertices + ix + gridX1 * (iy + 1);
|
|
125
|
+
const c = numberOfVertices + (ix + 1) + gridX1 * (iy + 1);
|
|
126
|
+
const d = numberOfVertices + (ix + 1) + gridX1 * iy;
|
|
127
|
+
indices.push(a, b, d);
|
|
128
|
+
indices.push(b, c, d);
|
|
129
|
+
groupCount += 6;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex });
|
|
133
|
+
groupStart += groupCount;
|
|
134
|
+
numberOfVertices += vertexCounter;
|
|
135
|
+
};
|
|
136
|
+
buildPlane("z", "y", "x", -1, -1, depth, height, width, depthSegments, heightSegments, 0);
|
|
137
|
+
buildPlane("z", "y", "x", 1, -1, depth, height, -width, depthSegments, heightSegments, 1);
|
|
138
|
+
buildPlane("x", "z", "y", 1, 1, width, depth, height, widthSegments, depthSegments, 2);
|
|
139
|
+
buildPlane("x", "z", "y", 1, -1, width, depth, -height, widthSegments, depthSegments, 3);
|
|
140
|
+
buildPlane("x", "y", "z", 1, -1, width, height, depth, widthSegments, heightSegments, 4);
|
|
141
|
+
buildPlane("x", "y", "z", -1, -1, width, height, -depth, widthSegments, heightSegments, 5);
|
|
142
|
+
return pack("BoxGeometry", parameters, { vertices, normals, uvs, indices, groups });
|
|
143
|
+
}
|
|
144
|
+
function cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) {
|
|
145
|
+
radialSegments = Math.floor(radialSegments);
|
|
146
|
+
heightSegments = Math.floor(heightSegments);
|
|
147
|
+
const indices = [];
|
|
148
|
+
const vertices = [];
|
|
149
|
+
const normals = [];
|
|
150
|
+
const uvs = [];
|
|
151
|
+
const groups = [];
|
|
152
|
+
let index = 0;
|
|
153
|
+
const indexArray2 = [];
|
|
154
|
+
const halfHeight = height / 2;
|
|
155
|
+
let groupStart = 0;
|
|
156
|
+
const generateTorso = () => {
|
|
157
|
+
let groupCount = 0;
|
|
158
|
+
const slope = (radiusBottom - radiusTop) / height;
|
|
159
|
+
for (let y = 0; y <= heightSegments; y++) {
|
|
160
|
+
const indexRow = [];
|
|
161
|
+
const v = y / heightSegments;
|
|
162
|
+
const radius = v * (radiusBottom - radiusTop) + radiusTop;
|
|
163
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
164
|
+
const u = x / radialSegments;
|
|
165
|
+
const theta = u * thetaLength + thetaStart;
|
|
166
|
+
const sinTheta = Math.sin(theta);
|
|
167
|
+
const cosTheta = Math.cos(theta);
|
|
168
|
+
vertices.push(radius * sinTheta, -v * height + halfHeight, radius * cosTheta);
|
|
169
|
+
const n = normalize3(sinTheta, slope, cosTheta);
|
|
170
|
+
normals.push(n[0], n[1], n[2]);
|
|
171
|
+
uvs.push(u, 1 - v);
|
|
172
|
+
indexRow.push(index++);
|
|
173
|
+
}
|
|
174
|
+
indexArray2.push(indexRow);
|
|
175
|
+
}
|
|
176
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
177
|
+
for (let y = 0; y < heightSegments; y++) {
|
|
178
|
+
const a = indexArray2[y][x];
|
|
179
|
+
const b = indexArray2[y + 1][x];
|
|
180
|
+
const c = indexArray2[y + 1][x + 1];
|
|
181
|
+
const d = indexArray2[y][x + 1];
|
|
182
|
+
indices.push(a, b, d);
|
|
183
|
+
indices.push(b, c, d);
|
|
184
|
+
groupCount += 6;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: 0 });
|
|
188
|
+
groupStart += groupCount;
|
|
189
|
+
};
|
|
190
|
+
const generateCap = (top) => {
|
|
191
|
+
const centerIndexStart = index;
|
|
192
|
+
let groupCount = 0;
|
|
193
|
+
const radius = top === true ? radiusTop : radiusBottom;
|
|
194
|
+
const sign2 = top === true ? 1 : -1;
|
|
195
|
+
for (let x = 1; x <= radialSegments; x++) {
|
|
196
|
+
vertices.push(0, halfHeight * sign2, 0);
|
|
197
|
+
normals.push(0, sign2, 0);
|
|
198
|
+
uvs.push(0.5, 0.5);
|
|
199
|
+
index++;
|
|
200
|
+
}
|
|
201
|
+
const centerIndexEnd = index;
|
|
202
|
+
for (let x = 0; x <= radialSegments; x++) {
|
|
203
|
+
const u = x / radialSegments;
|
|
204
|
+
const theta = u * thetaLength + thetaStart;
|
|
205
|
+
const cosTheta = Math.cos(theta);
|
|
206
|
+
const sinTheta = Math.sin(theta);
|
|
207
|
+
vertices.push(radius * sinTheta, halfHeight * sign2, radius * cosTheta);
|
|
208
|
+
normals.push(0, sign2, 0);
|
|
209
|
+
uvs.push(cosTheta * 0.5 + 0.5, sinTheta * 0.5 * sign2 + 0.5);
|
|
210
|
+
index++;
|
|
211
|
+
}
|
|
212
|
+
for (let x = 0; x < radialSegments; x++) {
|
|
213
|
+
const c = centerIndexStart + x;
|
|
214
|
+
const i = centerIndexEnd + x;
|
|
215
|
+
if (top === true) indices.push(i, i + 1, c);
|
|
216
|
+
else indices.push(i + 1, i, c);
|
|
217
|
+
groupCount += 3;
|
|
218
|
+
}
|
|
219
|
+
groups.push({ start: groupStart, count: groupCount, materialIndex: top === true ? 1 : 2 });
|
|
220
|
+
groupStart += groupCount;
|
|
221
|
+
};
|
|
222
|
+
generateTorso();
|
|
223
|
+
if (openEnded === false) {
|
|
224
|
+
if (radiusTop > 0) generateCap(true);
|
|
225
|
+
if (radiusBottom > 0) generateCap(false);
|
|
226
|
+
}
|
|
227
|
+
return { vertices, normals, uvs, indices, groups };
|
|
228
|
+
}
|
|
229
|
+
function cylinder(radiusTop = 1, radiusBottom = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
230
|
+
const parameters = { radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
231
|
+
return pack("CylinderGeometry", parameters, cylinderArrays(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
232
|
+
}
|
|
233
|
+
function cone(radius = 1, height = 1, radialSegments = 32, heightSegments = 1, openEnded = false, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
234
|
+
const parameters = { radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength };
|
|
235
|
+
return pack("ConeGeometry", parameters, cylinderArrays(0, radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength));
|
|
236
|
+
}
|
|
237
|
+
function sphere(radius = 1, widthSegments = 32, heightSegments = 16, phiStart = 0, phiLength = Math.PI * 2, thetaStart = 0, thetaLength = Math.PI) {
|
|
238
|
+
const parameters = { radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength };
|
|
239
|
+
widthSegments = Math.max(3, Math.floor(widthSegments));
|
|
240
|
+
heightSegments = Math.max(2, Math.floor(heightSegments));
|
|
241
|
+
const thetaEnd = Math.min(thetaStart + thetaLength, Math.PI);
|
|
242
|
+
let index = 0;
|
|
243
|
+
const grid = [];
|
|
244
|
+
const indices = [];
|
|
245
|
+
const vertices = [];
|
|
246
|
+
const normals = [];
|
|
247
|
+
const uvs = [];
|
|
248
|
+
for (let iy = 0; iy <= heightSegments; iy++) {
|
|
249
|
+
const verticesRow = [];
|
|
250
|
+
const v = iy / heightSegments;
|
|
251
|
+
let uOffset = 0;
|
|
252
|
+
if (iy === 0 && thetaStart === 0) uOffset = 0.5 / widthSegments;
|
|
253
|
+
else if (iy === heightSegments && thetaEnd === Math.PI) uOffset = -0.5 / widthSegments;
|
|
254
|
+
for (let ix = 0; ix <= widthSegments; ix++) {
|
|
255
|
+
const u = ix / widthSegments;
|
|
256
|
+
const x = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
257
|
+
const y = radius * Math.cos(thetaStart + v * thetaLength);
|
|
258
|
+
const z = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
259
|
+
vertices.push(x, y, z);
|
|
260
|
+
const n = normalize3(x, y, z);
|
|
261
|
+
normals.push(n[0], n[1], n[2]);
|
|
262
|
+
uvs.push(u + uOffset, 1 - v);
|
|
263
|
+
verticesRow.push(index++);
|
|
264
|
+
}
|
|
265
|
+
grid.push(verticesRow);
|
|
266
|
+
}
|
|
267
|
+
for (let iy = 0; iy < heightSegments; iy++) {
|
|
268
|
+
for (let ix = 0; ix < widthSegments; ix++) {
|
|
269
|
+
const a = grid[iy][ix + 1];
|
|
270
|
+
const b = grid[iy][ix];
|
|
271
|
+
const c = grid[iy + 1][ix];
|
|
272
|
+
const d = grid[iy + 1][ix + 1];
|
|
273
|
+
if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
|
|
274
|
+
if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return pack("SphereGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
278
|
+
}
|
|
279
|
+
function plane(width = 1, height = 1, widthSegments = 1, heightSegments = 1) {
|
|
280
|
+
const parameters = { width, height, widthSegments, heightSegments };
|
|
281
|
+
const widthHalf = width / 2;
|
|
282
|
+
const heightHalf = height / 2;
|
|
283
|
+
const gridX = Math.floor(widthSegments);
|
|
284
|
+
const gridY = Math.floor(heightSegments);
|
|
285
|
+
const gridX1 = gridX + 1;
|
|
286
|
+
const gridY1 = gridY + 1;
|
|
287
|
+
const segmentWidth = width / gridX;
|
|
288
|
+
const segmentHeight = height / gridY;
|
|
289
|
+
const indices = [];
|
|
290
|
+
const vertices = [];
|
|
291
|
+
const normals = [];
|
|
292
|
+
const uvs = [];
|
|
293
|
+
for (let iy = 0; iy < gridY1; iy++) {
|
|
294
|
+
const y = iy * segmentHeight - heightHalf;
|
|
295
|
+
for (let ix = 0; ix < gridX1; ix++) {
|
|
296
|
+
const x = ix * segmentWidth - widthHalf;
|
|
297
|
+
vertices.push(x, -y, 0);
|
|
298
|
+
normals.push(0, 0, 1);
|
|
299
|
+
uvs.push(ix / gridX);
|
|
300
|
+
uvs.push(1 - iy / gridY);
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
for (let iy = 0; iy < gridY; iy++) {
|
|
304
|
+
for (let ix = 0; ix < gridX; ix++) {
|
|
305
|
+
const a = ix + gridX1 * iy;
|
|
306
|
+
const b = ix + gridX1 * (iy + 1);
|
|
307
|
+
const c = ix + 1 + gridX1 * (iy + 1);
|
|
308
|
+
const d = ix + 1 + gridX1 * iy;
|
|
309
|
+
indices.push(a, b, d);
|
|
310
|
+
indices.push(b, c, d);
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return pack("PlaneGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
314
|
+
}
|
|
315
|
+
function circle(radius = 1, segments = 32, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
316
|
+
const parameters = { radius, segments, thetaStart, thetaLength };
|
|
317
|
+
segments = Math.max(3, segments);
|
|
318
|
+
const indices = [];
|
|
319
|
+
const vertices = [];
|
|
320
|
+
const normals = [];
|
|
321
|
+
const uvs = [];
|
|
322
|
+
vertices.push(0, 0, 0);
|
|
323
|
+
normals.push(0, 0, 1);
|
|
324
|
+
uvs.push(0.5, 0.5);
|
|
325
|
+
for (let s = 0, i = 3; s <= segments; s++, i += 3) {
|
|
326
|
+
const segment = thetaStart + s / segments * thetaLength;
|
|
327
|
+
vertices.push(radius * Math.cos(segment), radius * Math.sin(segment), 0);
|
|
328
|
+
normals.push(0, 0, 1);
|
|
329
|
+
uvs.push((vertices[i] / radius + 1) / 2, (vertices[i + 1] / radius + 1) / 2);
|
|
330
|
+
}
|
|
331
|
+
for (let i = 1; i <= segments; i++) indices.push(i, i + 1, 0);
|
|
332
|
+
return pack("CircleGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
333
|
+
}
|
|
334
|
+
function ring(innerRadius = 0.5, outerRadius = 1, thetaSegments = 32, phiSegments = 1, thetaStart = 0, thetaLength = Math.PI * 2) {
|
|
335
|
+
const parameters = { innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength };
|
|
336
|
+
thetaSegments = Math.max(3, thetaSegments);
|
|
337
|
+
phiSegments = Math.max(1, phiSegments);
|
|
338
|
+
const indices = [];
|
|
339
|
+
const vertices = [];
|
|
340
|
+
const normals = [];
|
|
341
|
+
const uvs = [];
|
|
342
|
+
let radius = innerRadius;
|
|
343
|
+
const radiusStep = (outerRadius - innerRadius) / phiSegments;
|
|
344
|
+
for (let j = 0; j <= phiSegments; j++) {
|
|
345
|
+
for (let i = 0; i <= thetaSegments; i++) {
|
|
346
|
+
const segment = thetaStart + i / thetaSegments * thetaLength;
|
|
347
|
+
const x = radius * Math.cos(segment);
|
|
348
|
+
const y = radius * Math.sin(segment);
|
|
349
|
+
vertices.push(x, y, 0);
|
|
350
|
+
normals.push(0, 0, 1);
|
|
351
|
+
uvs.push((x / outerRadius + 1) / 2, (y / outerRadius + 1) / 2);
|
|
352
|
+
}
|
|
353
|
+
radius += radiusStep;
|
|
354
|
+
}
|
|
355
|
+
for (let j = 0; j < phiSegments; j++) {
|
|
356
|
+
const thetaSegmentLevel = j * (thetaSegments + 1);
|
|
357
|
+
for (let i = 0; i < thetaSegments; i++) {
|
|
358
|
+
const segment = i + thetaSegmentLevel;
|
|
359
|
+
const a = segment;
|
|
360
|
+
const b = segment + thetaSegments + 1;
|
|
361
|
+
const c = segment + thetaSegments + 2;
|
|
362
|
+
const d = segment + 1;
|
|
363
|
+
indices.push(a, b, d);
|
|
364
|
+
indices.push(b, c, d);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
return pack("RingGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
368
|
+
}
|
|
369
|
+
function lathe(points, segments = 12, phiStart = 0, phiLength = Math.PI * 2) {
|
|
370
|
+
const parameters = { points: points.map(([x, y]) => ({ x, y })), segments, phiStart, phiLength };
|
|
371
|
+
segments = Math.floor(segments);
|
|
372
|
+
phiLength = Math.max(0, Math.min(Math.PI * 2, phiLength));
|
|
373
|
+
const indices = [];
|
|
374
|
+
const vertices = [];
|
|
375
|
+
const uvs = [];
|
|
376
|
+
const initNormals = [];
|
|
377
|
+
const normals = [];
|
|
378
|
+
const inverseSegments = 1 / segments;
|
|
379
|
+
const normal = { x: 0, y: 0, z: 0 };
|
|
380
|
+
const curNormal = { x: 0, y: 0, z: 0 };
|
|
381
|
+
const prevNormal = { x: 0, y: 0, z: 0 };
|
|
382
|
+
let dx = 0;
|
|
383
|
+
let dy = 0;
|
|
384
|
+
const last = points.length - 1;
|
|
385
|
+
const px = (j) => points[j][0];
|
|
386
|
+
const py = (j) => points[j][1];
|
|
387
|
+
const setNormalized = () => {
|
|
388
|
+
const n = normalize3(normal.x, normal.y, normal.z);
|
|
389
|
+
normal.x = n[0];
|
|
390
|
+
normal.y = n[1];
|
|
391
|
+
normal.z = n[2];
|
|
392
|
+
};
|
|
393
|
+
for (let j = 0; j <= last; j++) {
|
|
394
|
+
if (j === 0) {
|
|
395
|
+
dx = px(j + 1) - px(j);
|
|
396
|
+
dy = py(j + 1) - py(j);
|
|
397
|
+
normal.x = dy * 1;
|
|
398
|
+
normal.y = -dx;
|
|
399
|
+
normal.z = dy * 0;
|
|
400
|
+
prevNormal.x = normal.x;
|
|
401
|
+
prevNormal.y = normal.y;
|
|
402
|
+
prevNormal.z = normal.z;
|
|
403
|
+
setNormalized();
|
|
404
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
405
|
+
} else if (j === last) {
|
|
406
|
+
initNormals.push(prevNormal.x, prevNormal.y, prevNormal.z);
|
|
407
|
+
} else {
|
|
408
|
+
dx = px(j + 1) - px(j);
|
|
409
|
+
dy = py(j + 1) - py(j);
|
|
410
|
+
normal.x = dy * 1;
|
|
411
|
+
normal.y = -dx;
|
|
412
|
+
normal.z = dy * 0;
|
|
413
|
+
curNormal.x = normal.x;
|
|
414
|
+
curNormal.y = normal.y;
|
|
415
|
+
curNormal.z = normal.z;
|
|
416
|
+
normal.x += prevNormal.x;
|
|
417
|
+
normal.y += prevNormal.y;
|
|
418
|
+
normal.z += prevNormal.z;
|
|
419
|
+
setNormalized();
|
|
420
|
+
initNormals.push(normal.x, normal.y, normal.z);
|
|
421
|
+
prevNormal.x = curNormal.x;
|
|
422
|
+
prevNormal.y = curNormal.y;
|
|
423
|
+
prevNormal.z = curNormal.z;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
for (let i = 0; i <= segments; i++) {
|
|
427
|
+
const phi = phiStart + i * inverseSegments * phiLength;
|
|
428
|
+
const sin = Math.sin(phi);
|
|
429
|
+
const cos = Math.cos(phi);
|
|
430
|
+
for (let j = 0; j <= last; j++) {
|
|
431
|
+
vertices.push(px(j) * sin, py(j), px(j) * cos);
|
|
432
|
+
uvs.push(i / segments, j / (points.length - 1));
|
|
433
|
+
const x = initNormals[3 * j + 0] * sin;
|
|
434
|
+
const y = initNormals[3 * j + 1];
|
|
435
|
+
const z = initNormals[3 * j + 0] * cos;
|
|
436
|
+
normals.push(x, y, z);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
for (let i = 0; i < segments; i++) {
|
|
440
|
+
for (let j = 0; j < points.length - 1; j++) {
|
|
441
|
+
const base = j + i * points.length;
|
|
442
|
+
const a = base;
|
|
443
|
+
const b = base + points.length;
|
|
444
|
+
const c = base + points.length + 1;
|
|
445
|
+
const d = base + 1;
|
|
446
|
+
indices.push(a, b, d);
|
|
447
|
+
indices.push(c, d, b);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
return pack("LatheGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
451
|
+
}
|
|
452
|
+
function torus(radius = 1, tube2 = 0.4, radialSegments = 12, tubularSegments = 48, arc = Math.PI * 2) {
|
|
453
|
+
const parameters = { radius, tube: tube2, radialSegments, tubularSegments, arc };
|
|
454
|
+
radialSegments = Math.floor(radialSegments);
|
|
455
|
+
tubularSegments = Math.floor(tubularSegments);
|
|
456
|
+
const indices = [];
|
|
457
|
+
const vertices = [];
|
|
458
|
+
const normals = [];
|
|
459
|
+
const uvs = [];
|
|
460
|
+
for (let j = 0; j <= radialSegments; j++) {
|
|
461
|
+
for (let i = 0; i <= tubularSegments; i++) {
|
|
462
|
+
const u = i / tubularSegments * arc;
|
|
463
|
+
const v = j / radialSegments * Math.PI * 2;
|
|
464
|
+
const x = (radius + tube2 * Math.cos(v)) * Math.cos(u);
|
|
465
|
+
const y = (radius + tube2 * Math.cos(v)) * Math.sin(u);
|
|
466
|
+
const z = tube2 * Math.sin(v);
|
|
467
|
+
vertices.push(x, y, z);
|
|
468
|
+
const cx = radius * Math.cos(u);
|
|
469
|
+
const cy = radius * Math.sin(u);
|
|
470
|
+
const n = normalize3(x - cx, y - cy, z - 0);
|
|
471
|
+
normals.push(n[0], n[1], n[2]);
|
|
472
|
+
uvs.push(i / tubularSegments);
|
|
473
|
+
uvs.push(j / radialSegments);
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
for (let j = 1; j <= radialSegments; j++) {
|
|
477
|
+
for (let i = 1; i <= tubularSegments; i++) {
|
|
478
|
+
const a = (tubularSegments + 1) * j + i - 1;
|
|
479
|
+
const b = (tubularSegments + 1) * (j - 1) + i - 1;
|
|
480
|
+
const c = (tubularSegments + 1) * (j - 1) + i;
|
|
481
|
+
const d = (tubularSegments + 1) * j + i;
|
|
482
|
+
indices.push(a, b, d);
|
|
483
|
+
indices.push(b, c, d);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
return pack("TorusGeometry", parameters, { vertices, normals, uvs, indices, groups: [] });
|
|
487
|
+
}
|
|
488
|
+
function difference(size, subtract) {
|
|
489
|
+
const [w, h, d] = size;
|
|
490
|
+
const hw = w / 2;
|
|
491
|
+
const hh = h / 2;
|
|
492
|
+
const hd = d / 2;
|
|
493
|
+
const holes = subtract.map((hole) => ({ x0: Math.max(-hw, Math.min(hole.min[0], hole.max[0])), x1: Math.min(hw, Math.max(hole.min[0], hole.max[0])), y0: Math.max(-hh, Math.min(hole.min[1], hole.max[1])), y1: Math.min(hh, Math.max(hole.min[1], hole.max[1])) })).filter((hole) => hole.x1 > hole.x0 && hole.y1 > hole.y0);
|
|
494
|
+
const cuts = (lo, hi, values) => [.../* @__PURE__ */ new Set([lo, hi, ...values.filter((v) => v > lo && v < hi)])].sort((a, b) => a - b);
|
|
495
|
+
const xs = cuts(-hw, hw, holes.flatMap((hole) => [hole.x0, hole.x1]));
|
|
496
|
+
const ys = cuts(-hh, hh, holes.flatMap((hole) => [hole.y0, hole.y1]));
|
|
497
|
+
const nx = xs.length - 1;
|
|
498
|
+
const ny = ys.length - 1;
|
|
499
|
+
const solid = (i, j) => {
|
|
500
|
+
if (i < 0 || j < 0 || i >= nx || j >= ny) return false;
|
|
501
|
+
const cx = (xs[i] + xs[i + 1]) / 2;
|
|
502
|
+
const cy = (ys[j] + ys[j + 1]) / 2;
|
|
503
|
+
return !holes.some((hole) => cx > hole.x0 && cx < hole.x1 && cy > hole.y0 && cy < hole.y1);
|
|
504
|
+
};
|
|
505
|
+
const positions = [];
|
|
506
|
+
const normals = [];
|
|
507
|
+
const uvs = [];
|
|
508
|
+
const indices = [];
|
|
509
|
+
const quad = (corners, normal, uv) => {
|
|
510
|
+
const base = positions.length / 3;
|
|
511
|
+
for (let k = 0; k < 4; k++) {
|
|
512
|
+
positions.push(...corners[k]);
|
|
513
|
+
normals.push(...normal);
|
|
514
|
+
uvs.push(...uv[k]);
|
|
515
|
+
}
|
|
516
|
+
indices.push(base, base + 1, base + 2, base, base + 2, base + 3);
|
|
517
|
+
};
|
|
518
|
+
const fu = (x) => (x + hw) / w;
|
|
519
|
+
const fv = (y) => (y + hh) / h;
|
|
520
|
+
for (let j = 0; j < ny; j++) {
|
|
521
|
+
for (let i = 0; i < nx; i++) {
|
|
522
|
+
if (!solid(i, j)) continue;
|
|
523
|
+
const x0 = xs[i];
|
|
524
|
+
const x1 = xs[i + 1];
|
|
525
|
+
const y0 = ys[j];
|
|
526
|
+
const y1 = ys[j + 1];
|
|
527
|
+
quad([[x0, y0, hd], [x1, y0, hd], [x1, y1, hd], [x0, y1, hd]], [0, 0, 1], [[fu(x0), fv(y0)], [fu(x1), fv(y0)], [fu(x1), fv(y1)], [fu(x0), fv(y1)]]);
|
|
528
|
+
quad([[x1, y0, -hd], [x0, y0, -hd], [x0, y1, -hd], [x1, y1, -hd]], [0, 0, -1], [[1 - fu(x1), fv(y0)], [1 - fu(x0), fv(y0)], [1 - fu(x0), fv(y1)], [1 - fu(x1), fv(y1)]]);
|
|
529
|
+
const zu = (z) => (z + hd) / d;
|
|
530
|
+
if (!solid(i - 1, j)) quad([[x0, y0, -hd], [x0, y0, hd], [x0, y1, hd], [x0, y1, -hd]], [-1, 0, 0], [[zu(-hd), fv(y0)], [zu(hd), fv(y0)], [zu(hd), fv(y1)], [zu(-hd), fv(y1)]]);
|
|
531
|
+
if (!solid(i + 1, j)) quad([[x1, y0, hd], [x1, y0, -hd], [x1, y1, -hd], [x1, y1, hd]], [1, 0, 0], [[zu(hd), fv(y0)], [zu(-hd), fv(y0)], [zu(-hd), fv(y1)], [zu(hd), fv(y1)]]);
|
|
532
|
+
if (!solid(i, j - 1)) quad([[x0, y0, -hd], [x1, y0, -hd], [x1, y0, hd], [x0, y0, hd]], [0, -1, 0], [[fu(x0), zu(-hd)], [fu(x1), zu(-hd)], [fu(x1), zu(hd)], [fu(x0), zu(hd)]]);
|
|
533
|
+
if (!solid(i, j + 1)) quad([[x0, y1, hd], [x1, y1, hd], [x1, y1, -hd], [x0, y1, -hd]], [0, 1, 0], [[fu(x0), zu(hd)], [fu(x1), zu(hd)], [fu(x1), zu(-hd)], [fu(x0), zu(-hd)]]);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
return {
|
|
537
|
+
type: "BufferGeometry",
|
|
538
|
+
parameters: { size: [w, h, d], subtract: holes.map((hole) => ({ min: [hole.x0, hole.y0], max: [hole.x1, hole.y1] })) },
|
|
539
|
+
positions: new Float32Array(positions),
|
|
540
|
+
normals: new Float32Array(normals),
|
|
541
|
+
uvs: new Float32Array(uvs),
|
|
542
|
+
indices: indexArray(indices),
|
|
543
|
+
groups: []
|
|
544
|
+
};
|
|
545
|
+
}
|
|
546
|
+
var Earcut = {
|
|
547
|
+
triangulate: function(data, holeIndices, dim = 2) {
|
|
548
|
+
const hasHoles = holeIndices && holeIndices.length;
|
|
549
|
+
const outerLen = hasHoles ? holeIndices[0] * dim : data.length;
|
|
550
|
+
let outerNode = linkedList(data, 0, outerLen, dim, true);
|
|
551
|
+
const triangles = [];
|
|
552
|
+
if (!outerNode || outerNode.next === outerNode.prev) return triangles;
|
|
553
|
+
let minX, minY, maxX, maxY, x, y, invSize;
|
|
554
|
+
if (hasHoles) outerNode = eliminateHoles(data, holeIndices, outerNode, dim);
|
|
555
|
+
if (data.length > 80 * dim) {
|
|
556
|
+
minX = maxX = data[0];
|
|
557
|
+
minY = maxY = data[1];
|
|
558
|
+
for (let i = dim; i < outerLen; i += dim) {
|
|
559
|
+
x = data[i];
|
|
560
|
+
y = data[i + 1];
|
|
561
|
+
if (x < minX) minX = x;
|
|
562
|
+
if (y < minY) minY = y;
|
|
563
|
+
if (x > maxX) maxX = x;
|
|
564
|
+
if (y > maxY) maxY = y;
|
|
565
|
+
}
|
|
566
|
+
invSize = Math.max(maxX - minX, maxY - minY);
|
|
567
|
+
invSize = invSize !== 0 ? 32767 / invSize : 0;
|
|
568
|
+
}
|
|
569
|
+
earcutLinked(outerNode, triangles, dim, minX, minY, invSize, 0);
|
|
570
|
+
return triangles;
|
|
571
|
+
}
|
|
572
|
+
};
|
|
573
|
+
function linkedList(data, start, end, dim, clockwise) {
|
|
574
|
+
let i, last;
|
|
575
|
+
if (clockwise === signedArea(data, start, end, dim) > 0) {
|
|
576
|
+
for (i = start; i < end; i += dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
577
|
+
} else {
|
|
578
|
+
for (i = end - dim; i >= start; i -= dim) last = insertNode(i, data[i], data[i + 1], last);
|
|
579
|
+
}
|
|
580
|
+
if (last && equals(last, last.next)) {
|
|
581
|
+
removeNode(last);
|
|
582
|
+
last = last.next;
|
|
583
|
+
}
|
|
584
|
+
return last;
|
|
585
|
+
}
|
|
586
|
+
function filterPoints(start, end) {
|
|
587
|
+
if (!start) return start;
|
|
588
|
+
if (!end) end = start;
|
|
589
|
+
let p = start, again;
|
|
590
|
+
do {
|
|
591
|
+
again = false;
|
|
592
|
+
if (!p.steiner && (equals(p, p.next) || area(p.prev, p, p.next) === 0)) {
|
|
593
|
+
removeNode(p);
|
|
594
|
+
p = end = p.prev;
|
|
595
|
+
if (p === p.next) break;
|
|
596
|
+
again = true;
|
|
597
|
+
} else {
|
|
598
|
+
p = p.next;
|
|
599
|
+
}
|
|
600
|
+
} while (again || p !== end);
|
|
601
|
+
return end;
|
|
602
|
+
}
|
|
603
|
+
function earcutLinked(ear, triangles, dim, minX, minY, invSize, pass) {
|
|
604
|
+
if (!ear) return;
|
|
605
|
+
if (!pass && invSize) indexCurve(ear, minX, minY, invSize);
|
|
606
|
+
let stop = ear, prev, next;
|
|
607
|
+
while (ear.prev !== ear.next) {
|
|
608
|
+
prev = ear.prev;
|
|
609
|
+
next = ear.next;
|
|
610
|
+
if (invSize ? isEarHashed(ear, minX, minY, invSize) : isEar(ear)) {
|
|
611
|
+
triangles.push(prev.i / dim | 0);
|
|
612
|
+
triangles.push(ear.i / dim | 0);
|
|
613
|
+
triangles.push(next.i / dim | 0);
|
|
614
|
+
removeNode(ear);
|
|
615
|
+
ear = next.next;
|
|
616
|
+
stop = next.next;
|
|
617
|
+
continue;
|
|
618
|
+
}
|
|
619
|
+
ear = next;
|
|
620
|
+
if (ear === stop) {
|
|
621
|
+
if (!pass) {
|
|
622
|
+
earcutLinked(filterPoints(ear), triangles, dim, minX, minY, invSize, 1);
|
|
623
|
+
} else if (pass === 1) {
|
|
624
|
+
ear = cureLocalIntersections(filterPoints(ear), triangles, dim);
|
|
625
|
+
earcutLinked(ear, triangles, dim, minX, minY, invSize, 2);
|
|
626
|
+
} else if (pass === 2) {
|
|
627
|
+
splitEarcut(ear, triangles, dim, minX, minY, invSize);
|
|
628
|
+
}
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
function isEar(ear) {
|
|
634
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
635
|
+
if (area(a, b, c) >= 0) return false;
|
|
636
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
637
|
+
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
|
|
638
|
+
let p = c.next;
|
|
639
|
+
while (p !== a) {
|
|
640
|
+
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
641
|
+
p = p.next;
|
|
642
|
+
}
|
|
643
|
+
return true;
|
|
644
|
+
}
|
|
645
|
+
function isEarHashed(ear, minX, minY, invSize) {
|
|
646
|
+
const a = ear.prev, b = ear, c = ear.next;
|
|
647
|
+
if (area(a, b, c) >= 0) return false;
|
|
648
|
+
const ax = a.x, bx = b.x, cx = c.x, ay = a.y, by = b.y, cy = c.y;
|
|
649
|
+
const x0 = ax < bx ? ax < cx ? ax : cx : bx < cx ? bx : cx, y0 = ay < by ? ay < cy ? ay : cy : by < cy ? by : cy, x1 = ax > bx ? ax > cx ? ax : cx : bx > cx ? bx : cx, y1 = ay > by ? ay > cy ? ay : cy : by > cy ? by : cy;
|
|
650
|
+
const minZ = zOrder(x0, y0, minX, minY, invSize), maxZ = zOrder(x1, y1, minX, minY, invSize);
|
|
651
|
+
let p = ear.prevZ, n = ear.nextZ;
|
|
652
|
+
while (p && p.z >= minZ && n && n.z <= maxZ) {
|
|
653
|
+
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
654
|
+
p = p.prevZ;
|
|
655
|
+
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
656
|
+
n = n.nextZ;
|
|
657
|
+
}
|
|
658
|
+
while (p && p.z >= minZ) {
|
|
659
|
+
if (p.x >= x0 && p.x <= x1 && p.y >= y0 && p.y <= y1 && p !== a && p !== c && pointInTriangle(ax, ay, bx, by, cx, cy, p.x, p.y) && area(p.prev, p, p.next) >= 0) return false;
|
|
660
|
+
p = p.prevZ;
|
|
661
|
+
}
|
|
662
|
+
while (n && n.z <= maxZ) {
|
|
663
|
+
if (n.x >= x0 && n.x <= x1 && n.y >= y0 && n.y <= y1 && n !== a && n !== c && pointInTriangle(ax, ay, bx, by, cx, cy, n.x, n.y) && area(n.prev, n, n.next) >= 0) return false;
|
|
664
|
+
n = n.nextZ;
|
|
665
|
+
}
|
|
666
|
+
return true;
|
|
667
|
+
}
|
|
668
|
+
function cureLocalIntersections(start, triangles, dim) {
|
|
669
|
+
let p = start;
|
|
670
|
+
do {
|
|
671
|
+
const a = p.prev, b = p.next.next;
|
|
672
|
+
if (!equals(a, b) && intersects(a, p, p.next, b) && locallyInside(a, b) && locallyInside(b, a)) {
|
|
673
|
+
triangles.push(a.i / dim | 0);
|
|
674
|
+
triangles.push(p.i / dim | 0);
|
|
675
|
+
triangles.push(b.i / dim | 0);
|
|
676
|
+
removeNode(p);
|
|
677
|
+
removeNode(p.next);
|
|
678
|
+
p = start = b;
|
|
679
|
+
}
|
|
680
|
+
p = p.next;
|
|
681
|
+
} while (p !== start);
|
|
682
|
+
return filterPoints(p);
|
|
683
|
+
}
|
|
684
|
+
function splitEarcut(start, triangles, dim, minX, minY, invSize) {
|
|
685
|
+
let a = start;
|
|
686
|
+
do {
|
|
687
|
+
let b = a.next.next;
|
|
688
|
+
while (b !== a.prev) {
|
|
689
|
+
if (a.i !== b.i && isValidDiagonal(a, b)) {
|
|
690
|
+
let c = splitPolygon(a, b);
|
|
691
|
+
a = filterPoints(a, a.next);
|
|
692
|
+
c = filterPoints(c, c.next);
|
|
693
|
+
earcutLinked(a, triangles, dim, minX, minY, invSize, 0);
|
|
694
|
+
earcutLinked(c, triangles, dim, minX, minY, invSize, 0);
|
|
695
|
+
return;
|
|
696
|
+
}
|
|
697
|
+
b = b.next;
|
|
698
|
+
}
|
|
699
|
+
a = a.next;
|
|
700
|
+
} while (a !== start);
|
|
701
|
+
}
|
|
702
|
+
function eliminateHoles(data, holeIndices, outerNode, dim) {
|
|
703
|
+
const queue = [];
|
|
704
|
+
let i, len2, start, end, list2;
|
|
705
|
+
for (i = 0, len2 = holeIndices.length; i < len2; i++) {
|
|
706
|
+
start = holeIndices[i] * dim;
|
|
707
|
+
end = i < len2 - 1 ? holeIndices[i + 1] * dim : data.length;
|
|
708
|
+
list2 = linkedList(data, start, end, dim, false);
|
|
709
|
+
if (list2 === list2.next) list2.steiner = true;
|
|
710
|
+
queue.push(getLeftmost(list2));
|
|
711
|
+
}
|
|
712
|
+
queue.sort(compareX);
|
|
713
|
+
for (i = 0; i < queue.length; i++) {
|
|
714
|
+
outerNode = eliminateHole(queue[i], outerNode);
|
|
715
|
+
}
|
|
716
|
+
return outerNode;
|
|
717
|
+
}
|
|
718
|
+
function compareX(a, b) {
|
|
719
|
+
return a.x - b.x;
|
|
720
|
+
}
|
|
721
|
+
function eliminateHole(hole, outerNode) {
|
|
722
|
+
const bridge = findHoleBridge(hole, outerNode);
|
|
723
|
+
if (!bridge) {
|
|
724
|
+
return outerNode;
|
|
725
|
+
}
|
|
726
|
+
const bridgeReverse = splitPolygon(bridge, hole);
|
|
727
|
+
filterPoints(bridgeReverse, bridgeReverse.next);
|
|
728
|
+
return filterPoints(bridge, bridge.next);
|
|
729
|
+
}
|
|
730
|
+
function findHoleBridge(hole, outerNode) {
|
|
731
|
+
let p = outerNode, qx = -Infinity, m;
|
|
732
|
+
const hx = hole.x, hy = hole.y;
|
|
733
|
+
do {
|
|
734
|
+
if (hy <= p.y && hy >= p.next.y && p.next.y !== p.y) {
|
|
735
|
+
const x = p.x + (hy - p.y) * (p.next.x - p.x) / (p.next.y - p.y);
|
|
736
|
+
if (x <= hx && x > qx) {
|
|
737
|
+
qx = x;
|
|
738
|
+
m = p.x < p.next.x ? p : p.next;
|
|
739
|
+
if (x === hx) return m;
|
|
740
|
+
}
|
|
741
|
+
}
|
|
742
|
+
p = p.next;
|
|
743
|
+
} while (p !== outerNode);
|
|
744
|
+
if (!m) return null;
|
|
745
|
+
const stop = m, mx = m.x, my = m.y;
|
|
746
|
+
let tanMin = Infinity, tan;
|
|
747
|
+
p = m;
|
|
748
|
+
do {
|
|
749
|
+
if (hx >= p.x && p.x >= mx && hx !== p.x && pointInTriangle(hy < my ? hx : qx, hy, mx, my, hy < my ? qx : hx, hy, p.x, p.y)) {
|
|
750
|
+
tan = Math.abs(hy - p.y) / (hx - p.x);
|
|
751
|
+
if (locallyInside(p, hole) && (tan < tanMin || tan === tanMin && (p.x > m.x || p.x === m.x && sectorContainsSector(m, p)))) {
|
|
752
|
+
m = p;
|
|
753
|
+
tanMin = tan;
|
|
754
|
+
}
|
|
755
|
+
}
|
|
756
|
+
p = p.next;
|
|
757
|
+
} while (p !== stop);
|
|
758
|
+
return m;
|
|
759
|
+
}
|
|
760
|
+
function sectorContainsSector(m, p) {
|
|
761
|
+
return area(m.prev, m, p.prev) < 0 && area(p.next, m, m.next) < 0;
|
|
762
|
+
}
|
|
763
|
+
function indexCurve(start, minX, minY, invSize) {
|
|
764
|
+
let p = start;
|
|
765
|
+
do {
|
|
766
|
+
if (p.z === 0) p.z = zOrder(p.x, p.y, minX, minY, invSize);
|
|
767
|
+
p.prevZ = p.prev;
|
|
768
|
+
p.nextZ = p.next;
|
|
769
|
+
p = p.next;
|
|
770
|
+
} while (p !== start);
|
|
771
|
+
p.prevZ.nextZ = null;
|
|
772
|
+
p.prevZ = null;
|
|
773
|
+
sortLinked(p);
|
|
774
|
+
}
|
|
775
|
+
function sortLinked(list2) {
|
|
776
|
+
let i, p, q, e, tail, numMerges, pSize, qSize, inSize = 1;
|
|
777
|
+
do {
|
|
778
|
+
p = list2;
|
|
779
|
+
list2 = null;
|
|
780
|
+
tail = null;
|
|
781
|
+
numMerges = 0;
|
|
782
|
+
while (p) {
|
|
783
|
+
numMerges++;
|
|
784
|
+
q = p;
|
|
785
|
+
pSize = 0;
|
|
786
|
+
for (i = 0; i < inSize; i++) {
|
|
787
|
+
pSize++;
|
|
788
|
+
q = q.nextZ;
|
|
789
|
+
if (!q) break;
|
|
790
|
+
}
|
|
791
|
+
qSize = inSize;
|
|
792
|
+
while (pSize > 0 || qSize > 0 && q) {
|
|
793
|
+
if (pSize !== 0 && (qSize === 0 || !q || p.z <= q.z)) {
|
|
794
|
+
e = p;
|
|
795
|
+
p = p.nextZ;
|
|
796
|
+
pSize--;
|
|
797
|
+
} else {
|
|
798
|
+
e = q;
|
|
799
|
+
q = q.nextZ;
|
|
800
|
+
qSize--;
|
|
801
|
+
}
|
|
802
|
+
if (tail) tail.nextZ = e;
|
|
803
|
+
else list2 = e;
|
|
804
|
+
e.prevZ = tail;
|
|
805
|
+
tail = e;
|
|
806
|
+
}
|
|
807
|
+
p = q;
|
|
808
|
+
}
|
|
809
|
+
tail.nextZ = null;
|
|
810
|
+
inSize *= 2;
|
|
811
|
+
} while (numMerges > 1);
|
|
812
|
+
return list2;
|
|
813
|
+
}
|
|
814
|
+
function zOrder(x, y, minX, minY, invSize) {
|
|
815
|
+
x = (x - minX) * invSize | 0;
|
|
816
|
+
y = (y - minY) * invSize | 0;
|
|
817
|
+
x = (x | x << 8) & 16711935;
|
|
818
|
+
x = (x | x << 4) & 252645135;
|
|
819
|
+
x = (x | x << 2) & 858993459;
|
|
820
|
+
x = (x | x << 1) & 1431655765;
|
|
821
|
+
y = (y | y << 8) & 16711935;
|
|
822
|
+
y = (y | y << 4) & 252645135;
|
|
823
|
+
y = (y | y << 2) & 858993459;
|
|
824
|
+
y = (y | y << 1) & 1431655765;
|
|
825
|
+
return x | y << 1;
|
|
826
|
+
}
|
|
827
|
+
function getLeftmost(start) {
|
|
828
|
+
let p = start, leftmost = start;
|
|
829
|
+
do {
|
|
830
|
+
if (p.x < leftmost.x || p.x === leftmost.x && p.y < leftmost.y) leftmost = p;
|
|
831
|
+
p = p.next;
|
|
832
|
+
} while (p !== start);
|
|
833
|
+
return leftmost;
|
|
834
|
+
}
|
|
835
|
+
function pointInTriangle(ax, ay, bx, by, cx, cy, px, py) {
|
|
836
|
+
return (cx - px) * (ay - py) >= (ax - px) * (cy - py) && (ax - px) * (by - py) >= (bx - px) * (ay - py) && (bx - px) * (cy - py) >= (cx - px) * (by - py);
|
|
837
|
+
}
|
|
838
|
+
function isValidDiagonal(a, b) {
|
|
839
|
+
return a.next.i !== b.i && a.prev.i !== b.i && !intersectsPolygon(a, b) && // dones't intersect other edges
|
|
840
|
+
(locallyInside(a, b) && locallyInside(b, a) && middleInside(a, b) && // locally visible
|
|
841
|
+
(area(a.prev, a, b.prev) || area(a, b.prev, b)) || // does not create opposite-facing sectors
|
|
842
|
+
equals(a, b) && area(a.prev, a, a.next) > 0 && area(b.prev, b, b.next) > 0);
|
|
843
|
+
}
|
|
844
|
+
function area(p, q, r) {
|
|
845
|
+
return (q.y - p.y) * (r.x - q.x) - (q.x - p.x) * (r.y - q.y);
|
|
846
|
+
}
|
|
847
|
+
function equals(p1, p2) {
|
|
848
|
+
return p1.x === p2.x && p1.y === p2.y;
|
|
849
|
+
}
|
|
850
|
+
function intersects(p1, q1, p2, q2) {
|
|
851
|
+
const o1 = sign(area(p1, q1, p2));
|
|
852
|
+
const o2 = sign(area(p1, q1, q2));
|
|
853
|
+
const o3 = sign(area(p2, q2, p1));
|
|
854
|
+
const o4 = sign(area(p2, q2, q1));
|
|
855
|
+
if (o1 !== o2 && o3 !== o4) return true;
|
|
856
|
+
if (o1 === 0 && onSegment(p1, p2, q1)) return true;
|
|
857
|
+
if (o2 === 0 && onSegment(p1, q2, q1)) return true;
|
|
858
|
+
if (o3 === 0 && onSegment(p2, p1, q2)) return true;
|
|
859
|
+
if (o4 === 0 && onSegment(p2, q1, q2)) return true;
|
|
860
|
+
return false;
|
|
861
|
+
}
|
|
862
|
+
function onSegment(p, q, r) {
|
|
863
|
+
return q.x <= Math.max(p.x, r.x) && q.x >= Math.min(p.x, r.x) && q.y <= Math.max(p.y, r.y) && q.y >= Math.min(p.y, r.y);
|
|
864
|
+
}
|
|
865
|
+
function sign(num2) {
|
|
866
|
+
return num2 > 0 ? 1 : num2 < 0 ? -1 : 0;
|
|
867
|
+
}
|
|
868
|
+
function intersectsPolygon(a, b) {
|
|
869
|
+
let p = a;
|
|
870
|
+
do {
|
|
871
|
+
if (p.i !== a.i && p.next.i !== a.i && p.i !== b.i && p.next.i !== b.i && intersects(p, p.next, a, b)) return true;
|
|
872
|
+
p = p.next;
|
|
873
|
+
} while (p !== a);
|
|
874
|
+
return false;
|
|
875
|
+
}
|
|
876
|
+
function locallyInside(a, b) {
|
|
877
|
+
return area(a.prev, a, a.next) < 0 ? area(a, b, a.next) >= 0 && area(a, a.prev, b) >= 0 : area(a, b, a.prev) < 0 || area(a, a.next, b) < 0;
|
|
878
|
+
}
|
|
879
|
+
function middleInside(a, b) {
|
|
880
|
+
let p = a, inside = false;
|
|
881
|
+
const px = (a.x + b.x) / 2, py = (a.y + b.y) / 2;
|
|
882
|
+
do {
|
|
883
|
+
if (p.y > py !== p.next.y > py && p.next.y !== p.y && px < (p.next.x - p.x) * (py - p.y) / (p.next.y - p.y) + p.x)
|
|
884
|
+
inside = !inside;
|
|
885
|
+
p = p.next;
|
|
886
|
+
} while (p !== a);
|
|
887
|
+
return inside;
|
|
888
|
+
}
|
|
889
|
+
function splitPolygon(a, b) {
|
|
890
|
+
const a2 = new Node(a.i, a.x, a.y), b2 = new Node(b.i, b.x, b.y), an = a.next, bp = b.prev;
|
|
891
|
+
a.next = b;
|
|
892
|
+
b.prev = a;
|
|
893
|
+
a2.next = an;
|
|
894
|
+
an.prev = a2;
|
|
895
|
+
b2.next = a2;
|
|
896
|
+
a2.prev = b2;
|
|
897
|
+
bp.next = b2;
|
|
898
|
+
b2.prev = bp;
|
|
899
|
+
return b2;
|
|
900
|
+
}
|
|
901
|
+
function insertNode(i, x, y, last) {
|
|
902
|
+
const p = new Node(i, x, y);
|
|
903
|
+
if (!last) {
|
|
904
|
+
p.prev = p;
|
|
905
|
+
p.next = p;
|
|
906
|
+
} else {
|
|
907
|
+
p.next = last.next;
|
|
908
|
+
p.prev = last;
|
|
909
|
+
last.next.prev = p;
|
|
910
|
+
last.next = p;
|
|
911
|
+
}
|
|
912
|
+
return p;
|
|
913
|
+
}
|
|
914
|
+
function removeNode(p) {
|
|
915
|
+
p.next.prev = p.prev;
|
|
916
|
+
p.prev.next = p.next;
|
|
917
|
+
if (p.prevZ) p.prevZ.nextZ = p.nextZ;
|
|
918
|
+
if (p.nextZ) p.nextZ.prevZ = p.prevZ;
|
|
919
|
+
}
|
|
920
|
+
function Node(i, x, y) {
|
|
921
|
+
this.i = i;
|
|
922
|
+
this.x = x;
|
|
923
|
+
this.y = y;
|
|
924
|
+
this.prev = null;
|
|
925
|
+
this.next = null;
|
|
926
|
+
this.z = 0;
|
|
927
|
+
this.prevZ = null;
|
|
928
|
+
this.nextZ = null;
|
|
929
|
+
this.steiner = false;
|
|
930
|
+
}
|
|
931
|
+
function signedArea(data, start, end, dim) {
|
|
932
|
+
let sum = 0;
|
|
933
|
+
for (let i = start, j = end - dim; i < end; i += dim) {
|
|
934
|
+
sum += (data[j] - data[i]) * (data[i + 1] + data[j + 1]);
|
|
935
|
+
j = i;
|
|
936
|
+
}
|
|
937
|
+
return sum;
|
|
938
|
+
}
|
|
939
|
+
var equals2 = (a, b) => a.x === b.x && a.y === b.y;
|
|
940
|
+
var linePoint = (v1, v2, t) => t === 1 ? { x: v2.x, y: v2.y } : { x: (v2.x - v1.x) * t + v1.x, y: (v2.y - v1.y) * t + v1.y };
|
|
941
|
+
function pathPoints(points) {
|
|
942
|
+
const out = [];
|
|
943
|
+
let last;
|
|
944
|
+
for (let i = 1; i < points.length; i++) {
|
|
945
|
+
const v1 = { x: points[i - 1][0], y: points[i - 1][1] };
|
|
946
|
+
const v2 = { x: points[i][0], y: points[i][1] };
|
|
947
|
+
for (let d = 0; d <= 1; d++) {
|
|
948
|
+
const point = linePoint(v1, v2, d / 1);
|
|
949
|
+
if (last && equals2(last, point)) continue;
|
|
950
|
+
out.push(point);
|
|
951
|
+
last = point;
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return out;
|
|
955
|
+
}
|
|
956
|
+
function area2(contour) {
|
|
957
|
+
const n = contour.length;
|
|
958
|
+
let a = 0;
|
|
959
|
+
for (let p = n - 1, q = 0; q < n; p = q++) a += contour[p].x * contour[q].y - contour[q].x * contour[p].y;
|
|
960
|
+
return a * 0.5;
|
|
961
|
+
}
|
|
962
|
+
var isClockWise = (pts) => area2(pts) < 0;
|
|
963
|
+
function removeDupEndPts(points) {
|
|
964
|
+
const l = points.length;
|
|
965
|
+
if (l > 2 && equals2(points[l - 1], points[0])) points.pop();
|
|
966
|
+
}
|
|
967
|
+
function triangulateShape(contour, holes) {
|
|
968
|
+
const vertices = [];
|
|
969
|
+
const holeIndices = [];
|
|
970
|
+
const faces = [];
|
|
971
|
+
removeDupEndPts(contour);
|
|
972
|
+
for (const p of contour) vertices.push(p.x, p.y);
|
|
973
|
+
let holeIndex = contour.length;
|
|
974
|
+
holes.forEach(removeDupEndPts);
|
|
975
|
+
for (let i = 0; i < holes.length; i++) {
|
|
976
|
+
holeIndices.push(holeIndex);
|
|
977
|
+
holeIndex += holes[i].length;
|
|
978
|
+
for (const p of holes[i]) vertices.push(p.x, p.y);
|
|
979
|
+
}
|
|
980
|
+
const triangles = Earcut.triangulate(vertices, holeIndices);
|
|
981
|
+
for (let i = 0; i < triangles.length; i += 3) faces.push(triangles.slice(i, i + 3));
|
|
982
|
+
return faces;
|
|
983
|
+
}
|
|
984
|
+
function computeVertexNormals(positions, indices) {
|
|
985
|
+
const normals = new Float32Array(positions.length);
|
|
986
|
+
const get = (array, i) => [array[i * 3], array[i * 3 + 1], array[i * 3 + 2]];
|
|
987
|
+
const face = (vA, vB, vC) => {
|
|
988
|
+
const pA = get(positions, vA);
|
|
989
|
+
const pB = get(positions, vB);
|
|
990
|
+
const pC = get(positions, vC);
|
|
991
|
+
const cb = [pC[0] - pB[0], pC[1] - pB[1], pC[2] - pB[2]];
|
|
992
|
+
const ab = [pA[0] - pB[0], pA[1] - pB[1], pA[2] - pB[2]];
|
|
993
|
+
return [cb[1] * ab[2] - cb[2] * ab[1], cb[2] * ab[0] - cb[0] * ab[2], cb[0] * ab[1] - cb[1] * ab[0]];
|
|
994
|
+
};
|
|
995
|
+
const set = (i, x, y, z) => {
|
|
996
|
+
normals[i * 3] = x;
|
|
997
|
+
normals[i * 3 + 1] = y;
|
|
998
|
+
normals[i * 3 + 2] = z;
|
|
999
|
+
};
|
|
1000
|
+
if (indices) {
|
|
1001
|
+
for (let i = 0; i < indices.length; i += 3) {
|
|
1002
|
+
const vA = indices[i];
|
|
1003
|
+
const vB = indices[i + 1];
|
|
1004
|
+
const vC = indices[i + 2];
|
|
1005
|
+
const cb = face(vA, vB, vC);
|
|
1006
|
+
for (const v of [vA, vB, vC]) {
|
|
1007
|
+
const n = get(normals, v);
|
|
1008
|
+
set(v, n[0] + cb[0], n[1] + cb[1], n[2] + cb[2]);
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
} else {
|
|
1012
|
+
for (let i = 0; i < positions.length / 3; i += 3) {
|
|
1013
|
+
const cb = face(i, i + 1, i + 2);
|
|
1014
|
+
set(i, cb[0], cb[1], cb[2]);
|
|
1015
|
+
set(i + 1, cb[0], cb[1], cb[2]);
|
|
1016
|
+
set(i + 2, cb[0], cb[1], cb[2]);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
for (let i = 0; i < normals.length / 3; i++) {
|
|
1020
|
+
const [x, y, z] = get(normals, i);
|
|
1021
|
+
const s = 1 / (Math.sqrt(x * x + y * y + z * z) || 1);
|
|
1022
|
+
set(i, x * s, y * s, z * s);
|
|
1023
|
+
}
|
|
1024
|
+
return normals;
|
|
1025
|
+
}
|
|
1026
|
+
function extrude(outline, depth = 1, steps = 1, bevel = void 0) {
|
|
1027
|
+
const parameters = { shape: outline, depth, steps, bevel: bevel ?? {} };
|
|
1028
|
+
const verticesArray = [];
|
|
1029
|
+
const uvArray = [];
|
|
1030
|
+
const groups = [];
|
|
1031
|
+
const placeholder = [];
|
|
1032
|
+
const bevelEnabled = bevel !== false;
|
|
1033
|
+
const spec = bevel === false || bevel === void 0 ? {} : bevel;
|
|
1034
|
+
let bevelThickness = spec.thickness !== void 0 ? spec.thickness : 0.2;
|
|
1035
|
+
let bevelSize = spec.size !== void 0 ? spec.size : bevelThickness - 0.1;
|
|
1036
|
+
let bevelOffset = spec.offset !== void 0 ? spec.offset : 0;
|
|
1037
|
+
let bevelSegments = spec.segments !== void 0 ? spec.segments : 3;
|
|
1038
|
+
if (!bevelEnabled) {
|
|
1039
|
+
bevelSegments = 0;
|
|
1040
|
+
bevelThickness = 0;
|
|
1041
|
+
bevelSize = 0;
|
|
1042
|
+
bevelOffset = 0;
|
|
1043
|
+
}
|
|
1044
|
+
let vertices = pathPoints(outline.outer);
|
|
1045
|
+
const holes = (outline.holes ?? []).map(pathPoints);
|
|
1046
|
+
const reverse = !isClockWise(vertices);
|
|
1047
|
+
if (reverse) {
|
|
1048
|
+
vertices = vertices.reverse();
|
|
1049
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1050
|
+
const ahole = holes[h];
|
|
1051
|
+
if (isClockWise(ahole)) holes[h] = ahole.reverse();
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
const faces = triangulateShape(vertices, holes);
|
|
1055
|
+
const contour = vertices;
|
|
1056
|
+
for (const ahole of holes) vertices = vertices.concat(ahole);
|
|
1057
|
+
const scalePt2 = (pt, vec, size) => ({ x: pt.x + vec.x * size, y: pt.y + vec.y * size });
|
|
1058
|
+
const vlen = vertices.length;
|
|
1059
|
+
const flen = faces.length;
|
|
1060
|
+
const getBevelVec = (inPt, inPrev, inNext) => {
|
|
1061
|
+
let vTransX;
|
|
1062
|
+
let vTransY;
|
|
1063
|
+
let shrinkBy;
|
|
1064
|
+
const vPrevX = inPt.x - inPrev.x;
|
|
1065
|
+
const vPrevY = inPt.y - inPrev.y;
|
|
1066
|
+
const vNextX = inNext.x - inPt.x;
|
|
1067
|
+
const vNextY = inNext.y - inPt.y;
|
|
1068
|
+
const vPrevLensq = vPrevX * vPrevX + vPrevY * vPrevY;
|
|
1069
|
+
const collinear0 = vPrevX * vNextY - vPrevY * vNextX;
|
|
1070
|
+
if (Math.abs(collinear0) > Number.EPSILON) {
|
|
1071
|
+
const vPrevLen = Math.sqrt(vPrevLensq);
|
|
1072
|
+
const vNextLen = Math.sqrt(vNextX * vNextX + vNextY * vNextY);
|
|
1073
|
+
const ptPrevShiftX = inPrev.x - vPrevY / vPrevLen;
|
|
1074
|
+
const ptPrevShiftY = inPrev.y + vPrevX / vPrevLen;
|
|
1075
|
+
const ptNextShiftX = inNext.x - vNextY / vNextLen;
|
|
1076
|
+
const ptNextShiftY = inNext.y + vNextX / vNextLen;
|
|
1077
|
+
const sf = ((ptNextShiftX - ptPrevShiftX) * vNextY - (ptNextShiftY - ptPrevShiftY) * vNextX) / (vPrevX * vNextY - vPrevY * vNextX);
|
|
1078
|
+
vTransX = ptPrevShiftX + vPrevX * sf - inPt.x;
|
|
1079
|
+
vTransY = ptPrevShiftY + vPrevY * sf - inPt.y;
|
|
1080
|
+
const vTransLensq = vTransX * vTransX + vTransY * vTransY;
|
|
1081
|
+
if (vTransLensq <= 2) return { x: vTransX, y: vTransY };
|
|
1082
|
+
shrinkBy = Math.sqrt(vTransLensq / 2);
|
|
1083
|
+
} else {
|
|
1084
|
+
let directionEq = false;
|
|
1085
|
+
if (vPrevX > Number.EPSILON) {
|
|
1086
|
+
if (vNextX > Number.EPSILON) directionEq = true;
|
|
1087
|
+
} else if (vPrevX < -Number.EPSILON) {
|
|
1088
|
+
if (vNextX < -Number.EPSILON) directionEq = true;
|
|
1089
|
+
} else if (Math.sign(vPrevY) === Math.sign(vNextY)) {
|
|
1090
|
+
directionEq = true;
|
|
1091
|
+
}
|
|
1092
|
+
if (directionEq) {
|
|
1093
|
+
vTransX = -vPrevY;
|
|
1094
|
+
vTransY = vPrevX;
|
|
1095
|
+
shrinkBy = Math.sqrt(vPrevLensq);
|
|
1096
|
+
} else {
|
|
1097
|
+
vTransX = vPrevX;
|
|
1098
|
+
vTransY = vPrevY;
|
|
1099
|
+
shrinkBy = Math.sqrt(vPrevLensq / 2);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
return { x: vTransX / shrinkBy, y: vTransY / shrinkBy };
|
|
1103
|
+
};
|
|
1104
|
+
const movements = (ring2) => {
|
|
1105
|
+
const out = [];
|
|
1106
|
+
for (let i = 0, il = ring2.length, j = il - 1, k = i + 1; i < il; i++, j++, k++) {
|
|
1107
|
+
if (j === il) j = 0;
|
|
1108
|
+
if (k === il) k = 0;
|
|
1109
|
+
out[i] = getBevelVec(ring2[i], ring2[j], ring2[k]);
|
|
1110
|
+
}
|
|
1111
|
+
return out;
|
|
1112
|
+
};
|
|
1113
|
+
const contourMovements = movements(contour);
|
|
1114
|
+
const holesMovements = [];
|
|
1115
|
+
let verticesMovements = contourMovements.concat();
|
|
1116
|
+
for (const ahole of holes) {
|
|
1117
|
+
const oneHoleMovements = movements(ahole);
|
|
1118
|
+
holesMovements.push(oneHoleMovements);
|
|
1119
|
+
verticesMovements = verticesMovements.concat(oneHoleMovements);
|
|
1120
|
+
}
|
|
1121
|
+
const v = (x, y, z) => {
|
|
1122
|
+
placeholder.push(x);
|
|
1123
|
+
placeholder.push(y);
|
|
1124
|
+
placeholder.push(z);
|
|
1125
|
+
};
|
|
1126
|
+
const bevelLayer = (b, zOf) => {
|
|
1127
|
+
const t = b / bevelSegments;
|
|
1128
|
+
const z = bevelThickness * Math.cos(t * Math.PI / 2);
|
|
1129
|
+
const bs2 = bevelSize * Math.sin(t * Math.PI / 2) + bevelOffset;
|
|
1130
|
+
for (let i = 0; i < contour.length; i++) {
|
|
1131
|
+
const vert = scalePt2(contour[i], contourMovements[i], bs2);
|
|
1132
|
+
v(vert.x, vert.y, zOf(z));
|
|
1133
|
+
}
|
|
1134
|
+
for (let h = 0; h < holes.length; h++) {
|
|
1135
|
+
const ahole = holes[h];
|
|
1136
|
+
const oneHoleMovements = holesMovements[h];
|
|
1137
|
+
for (let i = 0; i < ahole.length; i++) {
|
|
1138
|
+
const vert = scalePt2(ahole[i], oneHoleMovements[i], bs2);
|
|
1139
|
+
v(vert.x, vert.y, zOf(z));
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1142
|
+
};
|
|
1143
|
+
for (let b = 0; b < bevelSegments; b++) bevelLayer(b, (z) => -z);
|
|
1144
|
+
const bs = bevelSize + bevelOffset;
|
|
1145
|
+
for (let i = 0; i < vlen; i++) {
|
|
1146
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1147
|
+
v(vert.x, vert.y, 0);
|
|
1148
|
+
}
|
|
1149
|
+
for (let s = 1; s <= steps; s++) {
|
|
1150
|
+
for (let i = 0; i < vlen; i++) {
|
|
1151
|
+
const vert = bevelEnabled ? scalePt2(vertices[i], verticesMovements[i], bs) : vertices[i];
|
|
1152
|
+
v(vert.x, vert.y, depth / steps * s);
|
|
1153
|
+
}
|
|
1154
|
+
}
|
|
1155
|
+
for (let b = bevelSegments - 1; b >= 0; b--) bevelLayer(b, (z) => depth + z);
|
|
1156
|
+
const addVertex = (index) => {
|
|
1157
|
+
verticesArray.push(placeholder[index * 3 + 0]);
|
|
1158
|
+
verticesArray.push(placeholder[index * 3 + 1]);
|
|
1159
|
+
verticesArray.push(placeholder[index * 3 + 2]);
|
|
1160
|
+
};
|
|
1161
|
+
const addUV = (x, y) => {
|
|
1162
|
+
uvArray.push(x);
|
|
1163
|
+
uvArray.push(y);
|
|
1164
|
+
};
|
|
1165
|
+
const at = (index, axis) => verticesArray[index * 3 + axis];
|
|
1166
|
+
const f3 = (a, b, c) => {
|
|
1167
|
+
addVertex(a);
|
|
1168
|
+
addVertex(b);
|
|
1169
|
+
addVertex(c);
|
|
1170
|
+
const nextIndex = verticesArray.length / 3;
|
|
1171
|
+
for (const index of [nextIndex - 3, nextIndex - 2, nextIndex - 1]) addUV(at(index, 0), at(index, 1));
|
|
1172
|
+
};
|
|
1173
|
+
const f4 = (a, b, c, d) => {
|
|
1174
|
+
addVertex(a);
|
|
1175
|
+
addVertex(b);
|
|
1176
|
+
addVertex(d);
|
|
1177
|
+
addVertex(b);
|
|
1178
|
+
addVertex(c);
|
|
1179
|
+
addVertex(d);
|
|
1180
|
+
const nextIndex = verticesArray.length / 3;
|
|
1181
|
+
const iA = nextIndex - 6;
|
|
1182
|
+
const iB = nextIndex - 3;
|
|
1183
|
+
const iC = nextIndex - 2;
|
|
1184
|
+
const iD = nextIndex - 1;
|
|
1185
|
+
const byX = Math.abs(at(iA, 1) - at(iB, 1)) < Math.abs(at(iA, 0) - at(iB, 0));
|
|
1186
|
+
const uv = (index) => [byX ? at(index, 0) : at(index, 1), 1 - at(index, 2)];
|
|
1187
|
+
const uvs = [uv(iA), uv(iB), uv(iC), uv(iD)];
|
|
1188
|
+
for (const k of [0, 1, 3, 1, 2, 3]) addUV(...uvs[k]);
|
|
1189
|
+
};
|
|
1190
|
+
let start = verticesArray.length / 3;
|
|
1191
|
+
if (bevelEnabled) {
|
|
1192
|
+
let offset = 0;
|
|
1193
|
+
for (let i = 0; i < flen; i++) {
|
|
1194
|
+
const face = faces[i];
|
|
1195
|
+
f3(face[2] + offset, face[1] + offset, face[0] + offset);
|
|
1196
|
+
}
|
|
1197
|
+
const layer = steps + bevelSegments * 2;
|
|
1198
|
+
offset = vlen * layer;
|
|
1199
|
+
for (let i = 0; i < flen; i++) {
|
|
1200
|
+
const face = faces[i];
|
|
1201
|
+
f3(face[0] + offset, face[1] + offset, face[2] + offset);
|
|
1202
|
+
}
|
|
1203
|
+
} else {
|
|
1204
|
+
for (let i = 0; i < flen; i++) {
|
|
1205
|
+
const face = faces[i];
|
|
1206
|
+
f3(face[2], face[1], face[0]);
|
|
1207
|
+
}
|
|
1208
|
+
for (let i = 0; i < flen; i++) {
|
|
1209
|
+
const face = faces[i];
|
|
1210
|
+
f3(face[0] + vlen * steps, face[1] + vlen * steps, face[2] + vlen * steps);
|
|
1211
|
+
}
|
|
1212
|
+
}
|
|
1213
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 0 });
|
|
1214
|
+
start = verticesArray.length / 3;
|
|
1215
|
+
const sidewalls = (ring2, layeroffset2) => {
|
|
1216
|
+
let i = ring2.length;
|
|
1217
|
+
while (--i >= 0) {
|
|
1218
|
+
const j = i;
|
|
1219
|
+
let k = i - 1;
|
|
1220
|
+
if (k < 0) k = ring2.length - 1;
|
|
1221
|
+
for (let s = 0, sl = steps + bevelSegments * 2; s < sl; s++) {
|
|
1222
|
+
const slen1 = vlen * s;
|
|
1223
|
+
const slen2 = vlen * (s + 1);
|
|
1224
|
+
f4(layeroffset2 + j + slen1, layeroffset2 + k + slen1, layeroffset2 + k + slen2, layeroffset2 + j + slen2);
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
let layeroffset = 0;
|
|
1229
|
+
sidewalls(contour, layeroffset);
|
|
1230
|
+
layeroffset += contour.length;
|
|
1231
|
+
for (const ahole of holes) {
|
|
1232
|
+
sidewalls(ahole, layeroffset);
|
|
1233
|
+
layeroffset += ahole.length;
|
|
1234
|
+
}
|
|
1235
|
+
groups.push({ start, count: verticesArray.length / 3 - start, materialIndex: 1 });
|
|
1236
|
+
const positions = new Float32Array(verticesArray);
|
|
1237
|
+
return { type: "ExtrudeGeometry", parameters, positions, normals: computeVertexNormals(positions), uvs: new Float32Array(uvArray), groups };
|
|
1238
|
+
}
|
|
1239
|
+
var PARAMETER_KEYS = {
|
|
1240
|
+
box: ["size", "segments"],
|
|
1241
|
+
cylinder: ["radiusTop", "radiusBottom", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1242
|
+
cone: ["radius", "height", "radialSegments", "heightSegments", "openEnded", "thetaStart", "thetaLength"],
|
|
1243
|
+
sphere: ["radius", "widthSegments", "heightSegments", "phiStart", "phiLength", "thetaStart", "thetaLength"],
|
|
1244
|
+
plane: ["width", "height", "widthSegments", "heightSegments"],
|
|
1245
|
+
circle: ["radius", "segments", "thetaStart", "thetaLength"],
|
|
1246
|
+
ring: ["innerRadius", "outerRadius", "thetaSegments", "phiSegments", "thetaStart", "thetaLength"],
|
|
1247
|
+
lathe: ["points", "segments", "phiStart", "phiLength"],
|
|
1248
|
+
torus: ["radius", "tube", "radialSegments", "tubularSegments", "arc"],
|
|
1249
|
+
extrude: ["shape", "depth", "steps", "bevel"],
|
|
1250
|
+
difference: ["size", "subtract"],
|
|
1251
|
+
mesh: ["positions", "normals", "uvs", "indices"]
|
|
1252
|
+
};
|
|
1253
|
+
var SEGMENT_KEYS = {
|
|
1254
|
+
cylinder: ["radialSegments"],
|
|
1255
|
+
cone: ["radialSegments"],
|
|
1256
|
+
sphere: ["widthSegments", "heightSegments"],
|
|
1257
|
+
circle: ["segments"],
|
|
1258
|
+
ring: ["thetaSegments"],
|
|
1259
|
+
lathe: ["segments"],
|
|
1260
|
+
torus: ["radialSegments", "tubularSegments"]
|
|
1261
|
+
};
|
|
1262
|
+
var SEGMENT_DEFAULTS = { radialSegments: 32, widthSegments: 32, heightSegments: 16, segments: 12, thetaSegments: 32, tubularSegments: 48 };
|
|
1263
|
+
var segmentDefault = (kind, key) => kind === "circle" && key === "segments" ? 32 : kind === "torus" && key === "radialSegments" ? 12 : SEGMENT_DEFAULTS[key];
|
|
1264
|
+
function primitiveParameters(node, options = {}) {
|
|
1265
|
+
const out = {};
|
|
1266
|
+
const source = node;
|
|
1267
|
+
for (const key of PARAMETER_KEYS[node.kind]) if (source[key] !== void 0) out[key] = source[key];
|
|
1268
|
+
const detail = options.detail ?? 1;
|
|
1269
|
+
if (detail !== 1) {
|
|
1270
|
+
const floor = options.minSegments ?? 3;
|
|
1271
|
+
for (const key of SEGMENT_KEYS[node.kind] ?? []) {
|
|
1272
|
+
const given = out[key] ?? segmentDefault(node.kind, key);
|
|
1273
|
+
out[key] = Math.max(floor, Math.round(given * detail));
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
return out;
|
|
1277
|
+
}
|
|
1278
|
+
function mesh(p) {
|
|
1279
|
+
const positions = new Float32Array(p.positions);
|
|
1280
|
+
const indices = p.indices ? indexArray(p.indices) : void 0;
|
|
1281
|
+
return {
|
|
1282
|
+
type: "BufferGeometry",
|
|
1283
|
+
parameters: {},
|
|
1284
|
+
positions,
|
|
1285
|
+
normals: p.normals ? new Float32Array(p.normals) : computeVertexNormals(positions, indices),
|
|
1286
|
+
uvs: p.uvs ? new Float32Array(p.uvs) : new Float32Array(positions.length / 3 * 2),
|
|
1287
|
+
...indices ? { indices } : {},
|
|
1288
|
+
groups: []
|
|
1289
|
+
};
|
|
1290
|
+
}
|
|
1291
|
+
function make(kind, p) {
|
|
1292
|
+
switch (kind) {
|
|
1293
|
+
case "box": {
|
|
1294
|
+
const size = p.size ?? [1, 1, 1];
|
|
1295
|
+
const segments = p.segments ?? [1, 1, 1];
|
|
1296
|
+
return box(size[0], size[1], size[2], segments[0], segments[1], segments[2]);
|
|
1297
|
+
}
|
|
1298
|
+
case "cylinder":
|
|
1299
|
+
return cylinder(p.radiusTop, p.radiusBottom, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
|
|
1300
|
+
case "cone":
|
|
1301
|
+
return cone(p.radius, p.height, p.radialSegments, p.heightSegments, p.openEnded, p.thetaStart, p.thetaLength);
|
|
1302
|
+
case "sphere":
|
|
1303
|
+
return sphere(p.radius, p.widthSegments, p.heightSegments, p.phiStart, p.phiLength, p.thetaStart, p.thetaLength);
|
|
1304
|
+
case "plane":
|
|
1305
|
+
return plane(p.width, p.height, p.widthSegments, p.heightSegments);
|
|
1306
|
+
case "circle":
|
|
1307
|
+
return circle(p.radius, p.segments, p.thetaStart, p.thetaLength);
|
|
1308
|
+
case "ring":
|
|
1309
|
+
return ring(p.innerRadius, p.outerRadius, p.thetaSegments, p.phiSegments, p.thetaStart, p.thetaLength);
|
|
1310
|
+
case "lathe":
|
|
1311
|
+
return lathe(p.points, p.segments, p.phiStart, p.phiLength);
|
|
1312
|
+
case "torus":
|
|
1313
|
+
return torus(p.radius, p.tube, p.radialSegments, p.tubularSegments, p.arc);
|
|
1314
|
+
case "extrude":
|
|
1315
|
+
return extrude(p.shape, p.depth, p.steps, p.bevel);
|
|
1316
|
+
case "difference":
|
|
1317
|
+
return difference(p.size, p.subtract ?? []);
|
|
1318
|
+
case "mesh":
|
|
1319
|
+
return mesh(p);
|
|
1320
|
+
default:
|
|
1321
|
+
throw new Error(`game-shapes: unknown primitive kind "${String(kind)}"`);
|
|
1322
|
+
}
|
|
1323
|
+
}
|
|
1324
|
+
var templates = /* @__PURE__ */ new Map();
|
|
1325
|
+
var templateLimit = 4096;
|
|
1326
|
+
var stats = { hits: 0, misses: 0 };
|
|
1327
|
+
function buildPrimitive(node, options = {}) {
|
|
1328
|
+
const parameters = primitiveParameters(node, options);
|
|
1329
|
+
if (node.kind === "mesh" || templateLimit === 0) return make(node.kind, parameters);
|
|
1330
|
+
const key = `${node.kind}:${canonicalJson(parameters)}`;
|
|
1331
|
+
const cached = templates.get(key);
|
|
1332
|
+
if (cached) {
|
|
1333
|
+
stats.hits++;
|
|
1334
|
+
templates.delete(key);
|
|
1335
|
+
templates.set(key, cached);
|
|
1336
|
+
return cached;
|
|
1337
|
+
}
|
|
1338
|
+
stats.misses++;
|
|
1339
|
+
const built = make(node.kind, parameters);
|
|
1340
|
+
templates.set(key, built);
|
|
1341
|
+
if (templates.size > templateLimit) templates.delete(templates.keys().next().value);
|
|
1342
|
+
return built;
|
|
1343
|
+
}
|
|
1344
|
+
var num = (v) => typeof v === "number" && Number.isFinite(v) ? null : "expected a finite number";
|
|
1345
|
+
var bool = (v) => typeof v === "boolean" ? null : "expected a boolean";
|
|
1346
|
+
var str = (v) => typeof v === "string" ? null : "expected a string";
|
|
1347
|
+
var int = (min) => (v) => typeof v === "number" && Number.isInteger(v) && v >= min ? null : `expected an integer >= ${min}`;
|
|
1348
|
+
var range = (lo, hi, loOpen = false) => (v) => typeof v === "number" && (loOpen ? v > lo : v >= lo) && v <= hi ? null : `expected a number in ${loOpen ? "(" : "["}${lo}, ${hi}]`;
|
|
1349
|
+
var tuple = (n, item = num) => (v) => {
|
|
1350
|
+
if (!Array.isArray(v) || v.length !== n) return `expected an array of ${n}`;
|
|
1351
|
+
for (const x of v) if (item(x)) return `expected an array of ${n} numbers`;
|
|
1352
|
+
return null;
|
|
1353
|
+
};
|
|
1354
|
+
var list = (item, min = 0) => (v) => {
|
|
1355
|
+
if (!Array.isArray(v)) return "expected an array";
|
|
1356
|
+
if (v.length < min) return `expected at least ${min} items`;
|
|
1357
|
+
for (let i = 0; i < v.length; i++) {
|
|
1358
|
+
const e = item(v[i]);
|
|
1359
|
+
if (e) return `[${i}]: ${e}`;
|
|
1360
|
+
}
|
|
1361
|
+
return null;
|
|
1362
|
+
};
|
|
1363
|
+
var color = (v) => typeof v === "number" && Number.isInteger(v) && v >= 0 && v <= 16777215 || typeof v === "string" && /^#[0-9a-fA-F]{6}$/.test(v) ? null : 'expected 0xRRGGBB or "#rrggbb"';
|
|
1364
|
+
var oneOf = (values) => (v) => values.includes(v) ? null : `expected one of ${values.map((x) => JSON.stringify(x)).join(", ")}`;
|
|
1365
|
+
var object = (fields, required = []) => (v) => {
|
|
1366
|
+
if (!v || typeof v !== "object" || Array.isArray(v)) return "expected an object";
|
|
1367
|
+
const record = v;
|
|
1368
|
+
for (const key of required) if (record[key] === void 0) return `${key}: required`;
|
|
1369
|
+
for (const key of Object.keys(record)) {
|
|
1370
|
+
if (record[key] === void 0) continue;
|
|
1371
|
+
const check = fields[key];
|
|
1372
|
+
if (!check) return `${key}: unknown field`;
|
|
1373
|
+
const e = check(record[key]);
|
|
1374
|
+
if (e) return `${key}: ${e}`;
|
|
1375
|
+
}
|
|
1376
|
+
return null;
|
|
1377
|
+
};
|
|
1378
|
+
var ORDERS = ["XYZ", "YXZ", "ZXY", "ZYX", "YZX", "XZY"];
|
|
1379
|
+
var TRANSFORM = {
|
|
1380
|
+
position: tuple(3),
|
|
1381
|
+
rotation: tuple(3),
|
|
1382
|
+
rotationOrder: oneOf(ORDERS),
|
|
1383
|
+
scale: (v) => typeof v === "number" ? num(v) : tuple(3)(v),
|
|
1384
|
+
heading: num,
|
|
1385
|
+
matrix: tuple(16)
|
|
1386
|
+
};
|
|
1387
|
+
var UV = object({ u0: num, v0: num, u1: num, v1: num }, ["u0", "v0", "u1", "v1"]);
|
|
1388
|
+
var INSTANCE = object({ ...TRANSFORM, color });
|
|
1389
|
+
var COMMON = {
|
|
1390
|
+
...TRANSFORM,
|
|
1391
|
+
kind: str,
|
|
1392
|
+
name: str,
|
|
1393
|
+
part: str,
|
|
1394
|
+
material: str,
|
|
1395
|
+
color,
|
|
1396
|
+
uv: UV,
|
|
1397
|
+
instances: list(INSTANCE),
|
|
1398
|
+
minDetail: range(0, 1, true),
|
|
1399
|
+
tags: list(str)
|
|
1400
|
+
};
|
|
1401
|
+
var vec2 = tuple(2);
|
|
1402
|
+
var BEVEL = (v) => v === false ? null : object({ thickness: num, size: num, offset: num, segments: int(1) })(v);
|
|
1403
|
+
var KINDS = {
|
|
1404
|
+
group: { fields: {} },
|
|
1405
|
+
box: { fields: { size: tuple(3), segments: tuple(3, int(1)) } },
|
|
1406
|
+
cylinder: { fields: { radiusTop: num, radiusBottom: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1407
|
+
cone: { fields: { radius: num, height: num, radialSegments: int(1), heightSegments: int(1), openEnded: bool, thetaStart: num, thetaLength: num } },
|
|
1408
|
+
sphere: { fields: { radius: num, widthSegments: int(3), heightSegments: int(2), phiStart: num, phiLength: num, thetaStart: num, thetaLength: num } },
|
|
1409
|
+
plane: { fields: { width: num, height: num, widthSegments: int(1), heightSegments: int(1) } },
|
|
1410
|
+
circle: { fields: { radius: num, segments: int(3), thetaStart: num, thetaLength: num } },
|
|
1411
|
+
ring: { fields: { innerRadius: num, outerRadius: num, thetaSegments: int(3), phiSegments: int(1), thetaStart: num, thetaLength: num } },
|
|
1412
|
+
lathe: { fields: { points: list(vec2, 2), segments: int(1), phiStart: num, phiLength: num }, required: ["points"] },
|
|
1413
|
+
torus: { fields: { radius: num, tube: num, radialSegments: int(1), tubularSegments: int(1), arc: num } },
|
|
1414
|
+
extrude: {
|
|
1415
|
+
fields: { shape: object({ outer: list(vec2, 3), holes: list(list(vec2, 3)) }, ["outer"]), depth: num, steps: int(1), bevel: BEVEL },
|
|
1416
|
+
required: ["shape"]
|
|
1417
|
+
},
|
|
1418
|
+
difference: { fields: { size: tuple(3), subtract: list(object({ min: vec2, max: vec2 }, ["min", "max"])) }, required: ["size", "subtract"] },
|
|
1419
|
+
mesh: {
|
|
1420
|
+
fields: {
|
|
1421
|
+
positions: (v) => list(num)(v) ?? (v.length % 3 === 0 ? null : "expected a multiple of 3 numbers"),
|
|
1422
|
+
normals: list(num),
|
|
1423
|
+
uvs: list(num),
|
|
1424
|
+
indices: list(int(0))
|
|
1425
|
+
},
|
|
1426
|
+
required: ["positions"]
|
|
1427
|
+
},
|
|
1428
|
+
point: { fields: { data: () => null } },
|
|
1429
|
+
custom: { fields: { generator: str, params: () => null }, required: ["generator"] }
|
|
1430
|
+
};
|
|
1431
|
+
|
|
1432
|
+
// ../game-shapes-three/dist/index.js
|
|
1433
|
+
var UV_BEFORE_NORMAL = /* @__PURE__ */ new Set(["LatheGeometry", "ExtrudeGeometry"]);
|
|
1434
|
+
var INDEX_FIRST = /* @__PURE__ */ new Set(["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry"]);
|
|
1435
|
+
var isPrimitive = (value) => typeof value.type === "string";
|
|
1436
|
+
function threeParameters(THREE, p) {
|
|
1437
|
+
if (p.type === "LatheGeometry") {
|
|
1438
|
+
const points = p.parameters.points.map(({ x, y }) => new THREE.Vector2(x, y));
|
|
1439
|
+
return { ...p.parameters, points };
|
|
1440
|
+
}
|
|
1441
|
+
return { ...p.parameters };
|
|
1442
|
+
}
|
|
1443
|
+
function toGeometry(THREE, source, options = {}) {
|
|
1444
|
+
const copy = options.copy !== false;
|
|
1445
|
+
const primitive = isPrimitive(source) ? source : null;
|
|
1446
|
+
const ThreeClass = primitive && primitive.type !== "BufferGeometry" ? THREE[primitive.type] : void 0;
|
|
1447
|
+
const geometry = ThreeClass ? Reflect.construct(THREE.BufferGeometry, [], ThreeClass) : new THREE.BufferGeometry();
|
|
1448
|
+
if (primitive && ThreeClass) {
|
|
1449
|
+
Object.assign(geometry, { type: primitive.type, parameters: threeParameters(THREE, primitive) });
|
|
1450
|
+
}
|
|
1451
|
+
const attribute = (array, size) => copy ? new THREE.Float32BufferAttribute(array, size) : new THREE.BufferAttribute(array, size);
|
|
1452
|
+
const index = source.indices;
|
|
1453
|
+
const setIndex = () => {
|
|
1454
|
+
if (!index) return;
|
|
1455
|
+
geometry.setIndex(copy ? index instanceof Uint32Array ? new THREE.Uint32BufferAttribute(index, 1) : new THREE.Uint16BufferAttribute(index, 1) : new THREE.BufferAttribute(index, 1));
|
|
1456
|
+
};
|
|
1457
|
+
const type = primitive?.type ?? "BufferGeometry";
|
|
1458
|
+
if (INDEX_FIRST.has(type)) setIndex();
|
|
1459
|
+
geometry.setAttribute("position", attribute(source.positions, 3));
|
|
1460
|
+
if (UV_BEFORE_NORMAL.has(type)) {
|
|
1461
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1462
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1463
|
+
} else {
|
|
1464
|
+
geometry.setAttribute("normal", attribute(source.normals, 3));
|
|
1465
|
+
geometry.setAttribute("uv", attribute(source.uvs, 2));
|
|
1466
|
+
}
|
|
1467
|
+
if (!primitive && source.colors) geometry.setAttribute("color", attribute(source.colors, 3));
|
|
1468
|
+
if (!INDEX_FIRST.has(type)) setIndex();
|
|
1469
|
+
if (primitive) for (const group of primitive.groups) geometry.addGroup(group.start, group.count, group.materialIndex);
|
|
1470
|
+
return geometry;
|
|
1471
|
+
}
|
|
1472
|
+
var SHAPE_GEOMETRY_TYPES = ["BoxGeometry", "CylinderGeometry", "ConeGeometry", "SphereGeometry", "PlaneGeometry", "CircleGeometry", "RingGeometry", "LatheGeometry", "TorusGeometry", "ExtrudeGeometry"];
|
|
1473
|
+
function linePoints(path) {
|
|
1474
|
+
if (path.autoClose || path.curves.length === 0) return null;
|
|
1475
|
+
const points = [];
|
|
1476
|
+
for (const [index, curve] of path.curves.entries()) {
|
|
1477
|
+
if (!curve.isLineCurve || !curve.v1 || !curve.v2) return null;
|
|
1478
|
+
if (index === 0) points.push([curve.v1.x, curve.v1.y]);
|
|
1479
|
+
else {
|
|
1480
|
+
const last = points[points.length - 1];
|
|
1481
|
+
if (last[0] !== curve.v1.x || last[1] !== curve.v1.y) return null;
|
|
1482
|
+
}
|
|
1483
|
+
points.push([curve.v2.x, curve.v2.y]);
|
|
1484
|
+
}
|
|
1485
|
+
return points;
|
|
1486
|
+
}
|
|
1487
|
+
function extrudeOf(shapes, options = {}) {
|
|
1488
|
+
if (Array.isArray(shapes) || !shapes || options.extrudePath !== void 0 || options.UVGenerator !== void 0) return null;
|
|
1489
|
+
const shape = shapes;
|
|
1490
|
+
const outer = linePoints(shape);
|
|
1491
|
+
if (!outer) return null;
|
|
1492
|
+
const holes = [];
|
|
1493
|
+
for (const hole of shape.holes ?? []) {
|
|
1494
|
+
const points = linePoints(hole);
|
|
1495
|
+
if (!points) return null;
|
|
1496
|
+
holes.push(points);
|
|
1497
|
+
}
|
|
1498
|
+
const outline = holes.length ? { outer, holes } : { outer };
|
|
1499
|
+
const bevel = options.bevelEnabled === false ? false : {
|
|
1500
|
+
...options.bevelThickness !== void 0 ? { thickness: options.bevelThickness } : {},
|
|
1501
|
+
...options.bevelSize !== void 0 ? { size: options.bevelSize } : {},
|
|
1502
|
+
...options.bevelOffset !== void 0 ? { offset: options.bevelOffset } : {},
|
|
1503
|
+
...options.bevelSegments !== void 0 ? { segments: options.bevelSegments } : {}
|
|
1504
|
+
};
|
|
1505
|
+
return extrude(outline, options.depth, options.steps, bevel);
|
|
1506
|
+
}
|
|
1507
|
+
var namespaces = /* @__PURE__ */ new WeakMap();
|
|
1508
|
+
var shims = /* @__PURE__ */ new WeakSet();
|
|
1509
|
+
function shapeGeometries(THREE, options = {}) {
|
|
1510
|
+
if (shims.has(THREE)) return THREE;
|
|
1511
|
+
const cache2 = options.cache !== false;
|
|
1512
|
+
const key = cache2 ? "cache" : "nocache";
|
|
1513
|
+
let byKey = namespaces.get(THREE);
|
|
1514
|
+
if (!byKey) {
|
|
1515
|
+
byKey = /* @__PURE__ */ new Map();
|
|
1516
|
+
namespaces.set(THREE, byKey);
|
|
1517
|
+
}
|
|
1518
|
+
const existing = byKey.get(key);
|
|
1519
|
+
if (existing) return existing;
|
|
1520
|
+
const made = (p) => toGeometry(THREE, p);
|
|
1521
|
+
const viaCache = (node, direct) => cache2 ? buildPrimitive(node) : direct();
|
|
1522
|
+
const defined = (o) => Object.fromEntries(Object.entries(o).filter(([, v]) => v !== void 0));
|
|
1523
|
+
const constructors = {
|
|
1524
|
+
BoxGeometry: (width, height, depth, ws = 1, hs = 1, ds = 1) => made(viaCache({ kind: "box", size: [width ?? 1, height ?? 1, depth ?? 1], segments: [ws, hs, ds] }, () => box(width, height, depth, ws, hs, ds))),
|
|
1525
|
+
CylinderGeometry: (radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cylinder", ...defined({ radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cylinder(radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
|
|
1526
|
+
ConeGeometry: (radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength) => made(viaCache({ kind: "cone", ...defined({ radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength }) }, () => cone(radius, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength))),
|
|
1527
|
+
SphereGeometry: (radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength) => made(viaCache({ kind: "sphere", ...defined({ radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength }) }, () => sphere(radius, widthSegments, heightSegments, phiStart, phiLength, thetaStart, thetaLength))),
|
|
1528
|
+
PlaneGeometry: (width, height, widthSegments, heightSegments) => made(viaCache({ kind: "plane", ...defined({ width, height, widthSegments, heightSegments }) }, () => plane(width, height, widthSegments, heightSegments))),
|
|
1529
|
+
CircleGeometry: (radius, segments, thetaStart, thetaLength) => made(viaCache({ kind: "circle", ...defined({ radius, segments, thetaStart, thetaLength }) }, () => circle(radius, segments, thetaStart, thetaLength))),
|
|
1530
|
+
RingGeometry: (innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength) => made(viaCache({ kind: "ring", ...defined({ innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength }) }, () => ring(innerRadius, outerRadius, thetaSegments, phiSegments, thetaStart, thetaLength))),
|
|
1531
|
+
LatheGeometry: (points, segments, phiStart, phiLength) => {
|
|
1532
|
+
if (!points) return new THREE.LatheGeometry(void 0, segments, phiStart, phiLength);
|
|
1533
|
+
const profile = points.map((p) => [p.x, p.y]);
|
|
1534
|
+
return made(viaCache({ kind: "lathe", points: profile, ...defined({ segments, phiStart, phiLength }) }, () => lathe(profile, segments, phiStart, phiLength)));
|
|
1535
|
+
},
|
|
1536
|
+
TorusGeometry: (radius, tube2, radialSegments, tubularSegments, arc) => made(viaCache({ kind: "torus", ...defined({ radius, tube: tube2, radialSegments, tubularSegments, arc }) }, () => torus(radius, tube2, radialSegments, tubularSegments, arc))),
|
|
1537
|
+
ExtrudeGeometry: (shapes, extrudeOptions) => {
|
|
1538
|
+
const buffers = shapes === void 0 ? null : extrudeOf(shapes, extrudeOptions);
|
|
1539
|
+
if (!buffers) return new THREE.ExtrudeGeometry(shapes, extrudeOptions);
|
|
1540
|
+
const geometry = toGeometry(THREE, buffers);
|
|
1541
|
+
geometry.parameters = { shapes, options: extrudeOptions ?? {} };
|
|
1542
|
+
return geometry;
|
|
1543
|
+
}
|
|
1544
|
+
};
|
|
1545
|
+
const namespace = { ...THREE };
|
|
1546
|
+
for (const type of SHAPE_GEOMETRY_TYPES) {
|
|
1547
|
+
const make2 = constructors[type];
|
|
1548
|
+
const Constructor = function(...args) {
|
|
1549
|
+
return make2(...args);
|
|
1550
|
+
};
|
|
1551
|
+
Constructor.prototype = THREE[type].prototype;
|
|
1552
|
+
Object.defineProperty(Constructor, "name", { value: type });
|
|
1553
|
+
namespace[type] = Constructor;
|
|
1554
|
+
}
|
|
1555
|
+
const result = namespace;
|
|
1556
|
+
byKey.set(key, result);
|
|
1557
|
+
shims.add(result);
|
|
1558
|
+
return result;
|
|
1559
|
+
}
|
|
1560
|
+
|
|
44
1561
|
// ../game-vegetation/dist/index.js
|
|
45
1562
|
var dist_exports = {};
|
|
46
1563
|
__export(dist_exports, {
|
|
@@ -50,7 +1567,7 @@ var GameVegetationThree = (() => {
|
|
|
50
1567
|
createRng: () => createRng,
|
|
51
1568
|
distanceToBoundary: () => distanceToBoundary,
|
|
52
1569
|
extractPlant: () => extractPlant,
|
|
53
|
-
generate: () =>
|
|
1570
|
+
generate: () => generate2,
|
|
54
1571
|
hashString: () => hashString,
|
|
55
1572
|
hideLotPlant: () => hideLotPlant,
|
|
56
1573
|
materialKeyForPart: () => materialKeyForPart,
|
|
@@ -285,7 +1802,7 @@ var GameVegetationThree = (() => {
|
|
|
285
1802
|
const [h, s, v] = toHsv(c);
|
|
286
1803
|
return fromHsv(h - amt * 0.03, clamp(s - amt * 0.12, 0, 1), clamp(v + amt * 0.35, 0, 1));
|
|
287
1804
|
}
|
|
288
|
-
function
|
|
1805
|
+
function srgbToLinear2(v) {
|
|
289
1806
|
return v <= 0.04045 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
|
|
290
1807
|
}
|
|
291
1808
|
function linearToSrgb(v) {
|
|
@@ -467,7 +1984,7 @@ var GameVegetationThree = (() => {
|
|
|
467
1984
|
this.groupTriangles();
|
|
468
1985
|
const n = this.pos.length / 3;
|
|
469
1986
|
const colors = new Float32Array(n * 3);
|
|
470
|
-
for (let i = 0; i < n * 3; i++) colors[i] =
|
|
1987
|
+
for (let i = 0; i < n * 3; i++) colors[i] = srgbToLinear2(this.col[i]);
|
|
471
1988
|
return {
|
|
472
1989
|
positions: new Float32Array(this.pos),
|
|
473
1990
|
normals: new Float32Array(this.nor),
|
|
@@ -668,16 +2185,16 @@ var GameVegetationThree = (() => {
|
|
|
668
2185
|
const f = frames[k];
|
|
669
2186
|
const r = o.radii[k];
|
|
670
2187
|
const t = k / (n - 1);
|
|
671
|
-
const
|
|
2188
|
+
const ring2 = [];
|
|
672
2189
|
for (let i = 0; i < S; i++) {
|
|
673
2190
|
const a = i / S * TAU;
|
|
674
2191
|
const ca = Math.cos(a);
|
|
675
2192
|
const sa = Math.sin(a);
|
|
676
2193
|
const dir = [f.n[0] * ca + f.b[0] * sa, f.n[1] * ca + f.b[1] * sa, f.n[2] * ca + f.b[2] * sa];
|
|
677
2194
|
const rr = r * (1 + (o.rough ? o.rough(k, i) : 0));
|
|
678
|
-
|
|
2195
|
+
ring2.push({ p: add(path[k], mul(dir, rr)), nr: dir, c: o.color(t, i / S) });
|
|
679
2196
|
}
|
|
680
|
-
rings.push(
|
|
2197
|
+
rings.push(ring2);
|
|
681
2198
|
}
|
|
682
2199
|
const sw = (k) => o.sway(k / (n - 1));
|
|
683
2200
|
if (smooth2) {
|
|
@@ -761,7 +2278,7 @@ var GameVegetationThree = (() => {
|
|
|
761
2278
|
const swayAt = (k) => o.sway(k / (N - 1));
|
|
762
2279
|
for (let k = 0; k < N; k++) {
|
|
763
2280
|
const f = fr[k];
|
|
764
|
-
const
|
|
2281
|
+
const ring2 = [];
|
|
765
2282
|
for (let i = 0; i < S; i++) {
|
|
766
2283
|
const ridge = i % 2 === 0;
|
|
767
2284
|
const rib = i >> 1;
|
|
@@ -771,10 +2288,10 @@ var GameVegetationThree = (() => {
|
|
|
771
2288
|
const rr = radii[k] * (ridge ? 1 + wob : o.valley);
|
|
772
2289
|
const p = add(pts[k], mul(dir, rr));
|
|
773
2290
|
const c = o.color(Math.min(1, k / (path.length - 1)), ridge, rib);
|
|
774
|
-
|
|
2291
|
+
ring2.push({ p, nr: dir, c });
|
|
775
2292
|
if (ridge && k < path.length) info.ridges.push({ p, n: dir, t: k / (path.length - 1), rib, ring: k });
|
|
776
2293
|
}
|
|
777
|
-
rings.push(
|
|
2294
|
+
rings.push(ring2);
|
|
778
2295
|
}
|
|
779
2296
|
for (let k = 0; k < N - 1; k++) {
|
|
780
2297
|
for (let i = 0; i < S; i++) {
|
|
@@ -1096,7 +2613,7 @@ var GameVegetationThree = (() => {
|
|
|
1096
2613
|
return { p, r, members: g.members };
|
|
1097
2614
|
});
|
|
1098
2615
|
}
|
|
1099
|
-
function octa(b, center, radii,
|
|
2616
|
+
function octa(b, center, radii, color2, sway) {
|
|
1100
2617
|
const pts = [
|
|
1101
2618
|
[radii[0], 0, 0],
|
|
1102
2619
|
[0, 0, radii[2]],
|
|
@@ -1109,11 +2626,11 @@ var GameVegetationThree = (() => {
|
|
|
1109
2626
|
const a = pts[i];
|
|
1110
2627
|
const c = pts[(i + 1) % 4];
|
|
1111
2628
|
const out = sub(add(a, c), mul(center, 2));
|
|
1112
|
-
b.flatTri(a, c, top,
|
|
1113
|
-
b.flatTri(c, a, bot,
|
|
2629
|
+
b.flatTri(a, c, top, color2(0.5), color2(0.5), color2(1), sway, sway, sway, add(out, [0, 1, 0]));
|
|
2630
|
+
b.flatTri(c, a, bot, color2(0.5), color2(0.5), color2(0), sway, sway, sway, add(out, [0, -1, 0]));
|
|
1114
2631
|
}
|
|
1115
2632
|
}
|
|
1116
|
-
function blossom(b, center, normal, size,
|
|
2633
|
+
function blossom(b, center, normal, size, color2, heart, sway, petals = 5, twist = 0) {
|
|
1117
2634
|
const f = frameFromAxis(center, normal, twist);
|
|
1118
2635
|
const peak = toWorld(f, [0, size * 0.3, 0]);
|
|
1119
2636
|
for (let i = 0; i < petals; i++) {
|
|
@@ -1123,7 +2640,7 @@ var GameVegetationThree = (() => {
|
|
|
1123
2640
|
const p0 = toWorld(f, [Math.cos(a0) * size * 0.75, 0, Math.sin(a0) * size * 0.75]);
|
|
1124
2641
|
const p1 = toWorld(f, [Math.cos(a1) * size * 0.75, 0, Math.sin(a1) * size * 0.75]);
|
|
1125
2642
|
const pm = toWorld(f, [Math.cos(am) * size * 1.1, size * 0.08, Math.sin(am) * size * 1.1]);
|
|
1126
|
-
const c = i % 2 ? shade(
|
|
2643
|
+
const c = i % 2 ? shade(color2, 0.93) : color2;
|
|
1127
2644
|
b.flatTri(peak, p0, pm, heart, c, lighten(c, 0.4), sway, sway, sway, normal);
|
|
1128
2645
|
b.flatTri(peak, pm, p1, heart, lighten(c, 0.4), c, sway, sway, sway, normal);
|
|
1129
2646
|
}
|
|
@@ -2013,8 +3530,8 @@ var GameVegetationThree = (() => {
|
|
|
2013
3530
|
const nb = ctx.pick(6, 5, 4);
|
|
2014
3531
|
for (let i = 0; i < nb; i++) {
|
|
2015
3532
|
const a = i * 2.2 + 0.4;
|
|
2016
|
-
const
|
|
2017
|
-
blobs.push({ c: [Math.cos(a) *
|
|
3533
|
+
const ring2 = i === 0 ? 0 : moundR * 0.5;
|
|
3534
|
+
blobs.push({ c: [Math.cos(a) * ring2, moundH * (i === 0 ? 0.55 : 0.28 + 0.07 * (i % 3)), Math.sin(a) * ring2], r: moundR * (i === 0 ? 0.62 : 0.52), sq: 0.95 });
|
|
2018
3535
|
}
|
|
2019
3536
|
}
|
|
2020
3537
|
for (const bl of blobs) clump(ctx.foliage, { center: bl.c, radius: bl.r, squash: bl.sq, detail: ctx.pick(1, 1, 0), seed: rng.seed(), base: dead ? mix(leafBase, [0.42, 0.32, 0.2], 0.7) : dry ? mix(leafBase, [0.55, 0.48, 0.22], 0.45) : mix(leafBase, [0.25, 0.55, 0.22], 0.25), swayK: 0.3, noise: 0.16, flatBottom: 0.8, ao: 0.5 });
|
|
@@ -2381,7 +3898,7 @@ var GameVegetationThree = (() => {
|
|
|
2381
3898
|
}
|
|
2382
3899
|
}
|
|
2383
3900
|
if (o.teeth && o.teeth.count > 0) {
|
|
2384
|
-
const { count, color, size } = o.teeth;
|
|
3901
|
+
const { count, color: color2, size } = o.teeth;
|
|
2385
3902
|
for (let k = 0; k < count; k++) {
|
|
2386
3903
|
const t0 = 0.12 + 0.7 * k / count;
|
|
2387
3904
|
const t1 = t0 + 0.05;
|
|
@@ -2392,7 +3909,7 @@ var GameVegetationThree = (() => {
|
|
|
2392
3909
|
const e1 = sd < 0 ? rows[i1].L : rows[i1].R;
|
|
2393
3910
|
const out = mul(right, sd * width * size);
|
|
2394
3911
|
const tip = add(add(mul(add(e0, e1), 0.5), out), mul(d, length * 0.02));
|
|
2395
|
-
b.flatTri(e0, tip, e1,
|
|
3912
|
+
b.flatTri(e0, tip, e1, color2, shade(color2, 1.3), color2, rows[i0].s, rows[i0].s, rows[i1].s, add(out, n2));
|
|
2396
3913
|
}
|
|
2397
3914
|
}
|
|
2398
3915
|
}
|
|
@@ -2421,8 +3938,8 @@ var GameVegetationThree = (() => {
|
|
|
2421
3938
|
const az = i / n * TAU * 1 + rng.range(-0.3, 0.3);
|
|
2422
3939
|
const el = rng.range(1.2, 1.5);
|
|
2423
3940
|
const len2 = h * rng.range(0.55, 1) * (ctx.condition === "dead" ? 0.6 : 1);
|
|
2424
|
-
const
|
|
2425
|
-
const base0 = [Math.cos(az) *
|
|
3941
|
+
const ring2 = rng.range(0, 0.08);
|
|
3942
|
+
const base0 = [Math.cos(az) * ring2, 0, Math.sin(az) * ring2];
|
|
2426
3943
|
const band = rng.range(0, TAU);
|
|
2427
3944
|
fleshyLeaf(ctx.foliage, base0, dirFromAngles(az, el), dirFromAngles(az + Math.PI, 0.2), len2, h * 0.16 * rng.range(0.85, 1.15), {
|
|
2428
3945
|
rows: ctx.pick(7, 4, 2),
|
|
@@ -2444,10 +3961,10 @@ var GameVegetationThree = (() => {
|
|
|
2444
3961
|
const base = tintFlesh(rgb(spec.color ?? 7315306), ctx.condition);
|
|
2445
3962
|
const edge = rgb(spec.edgeColor ?? 15261896);
|
|
2446
3963
|
for (let i = 0; i < n; i++) {
|
|
2447
|
-
const
|
|
3964
|
+
const ring2 = i < n * 0.4 ? 0 : i < n * 0.75 ? 1 : 2;
|
|
2448
3965
|
const az = i * 2.4 + rng.range(-0.2, 0.2);
|
|
2449
|
-
const el = [1.15, 0.85, 0.55][
|
|
2450
|
-
const len2 = h * [0.75, 0.95, 0.85][
|
|
3966
|
+
const el = [1.15, 0.85, 0.55][ring2] + rng.range(-0.1, 0.1);
|
|
3967
|
+
const len2 = h * [0.75, 0.95, 0.85][ring2] * rng.range(0.9, 1.1);
|
|
2451
3968
|
const c = jitter(base, rng, 0.01, 0.06, 0.04);
|
|
2452
3969
|
fleshyLeaf(ctx.foliage, [0, 0.02, 0], dirFromAngles(az, el), [0, 1, 0], len2, h * 0.26, {
|
|
2453
3970
|
rows: ctx.pick(3, 2, 2),
|
|
@@ -2534,7 +4051,7 @@ var GameVegetationThree = (() => {
|
|
|
2534
4051
|
}
|
|
2535
4052
|
return { collider: null, bendable: true };
|
|
2536
4053
|
}
|
|
2537
|
-
function spineTuft(b, p, n, count, length,
|
|
4054
|
+
function spineTuft(b, p, n, count, length, color2, rng, sway = 0) {
|
|
2538
4055
|
const nn = norm(n);
|
|
2539
4056
|
const t1 = perp(nn);
|
|
2540
4057
|
const t2 = norm(cross(nn, t1));
|
|
@@ -2547,15 +4064,15 @@ var GameVegetationThree = (() => {
|
|
|
2547
4064
|
const side = mul(norm(cross(dir, nn)), w);
|
|
2548
4065
|
const left = add(p, side);
|
|
2549
4066
|
const right = add(p, mul(side, -1));
|
|
2550
|
-
b.flatTri(left, right, tip, shade(
|
|
4067
|
+
b.flatTri(left, right, tip, shade(color2, 0.8), shade(color2, 0.8), color2, sway, sway, sway, nn);
|
|
2551
4068
|
}
|
|
2552
4069
|
}
|
|
2553
|
-
function areole(b, p, n, size,
|
|
4070
|
+
function areole(b, p, n, size, color2, sway = 0) {
|
|
2554
4071
|
const nn = norm(n);
|
|
2555
4072
|
const t1 = mul(perp(nn), size);
|
|
2556
4073
|
const t2 = mul(norm(cross(nn, perp(nn))), size);
|
|
2557
4074
|
const c = add(p, mul(nn, size * 0.1));
|
|
2558
|
-
b.flatTri(add(c, t1), add(c, add(mul(t1, -0.5), t2)), add(c, add(mul(t1, -0.5), mul(t2, -1))),
|
|
4075
|
+
b.flatTri(add(c, t1), add(c, add(mul(t1, -0.5), t2)), add(c, add(mul(t1, -0.5), mul(t2, -1))), color2, color2, color2, sway, sway, sway, nn);
|
|
2559
4076
|
}
|
|
2560
4077
|
function generateColumnarCactus(spec, ctx) {
|
|
2561
4078
|
const rng = ctx.rng;
|
|
@@ -2592,7 +4109,7 @@ var GameVegetationThree = (() => {
|
|
|
2592
4109
|
dome: domeSteps,
|
|
2593
4110
|
color: (t, ridge, rib) => bodyColor(t, ridge, rib + seedK),
|
|
2594
4111
|
sway: () => 0,
|
|
2595
|
-
rough: (
|
|
4112
|
+
rough: (ring2, rib) => Math.sin(ring2 * 1.7 + rib * 2.3 + seedK) * 0.03
|
|
2596
4113
|
});
|
|
2597
4114
|
const last = path[path.length - 1];
|
|
2598
4115
|
const prev = path[path.length - 2];
|
|
@@ -2804,7 +4321,7 @@ var GameVegetationThree = (() => {
|
|
|
2804
4321
|
return shade(base, k);
|
|
2805
4322
|
},
|
|
2806
4323
|
sway: () => 0,
|
|
2807
|
-
rough: (
|
|
4324
|
+
rough: (ring2, rib) => Math.sin(ring2 * 2.1 + rib * 1.3) * 0.02
|
|
2808
4325
|
});
|
|
2809
4326
|
if (ctx.lod === 0) {
|
|
2810
4327
|
for (const rd of res.ridges) {
|
|
@@ -2959,9 +4476,9 @@ var GameVegetationThree = (() => {
|
|
|
2959
4476
|
sides,
|
|
2960
4477
|
radii,
|
|
2961
4478
|
color: (t, a) => {
|
|
2962
|
-
const
|
|
4479
|
+
const ring2 = 0.84 + 0.16 * Math.cos(t * stemH * scars * TAU);
|
|
2963
4480
|
const stripe = 0.94 + 0.06 * Math.cos(a * TAU * 3 + t * 4);
|
|
2964
|
-
let c = shade(trunkC,
|
|
4481
|
+
let c = shade(trunkC, ring2 * stripe * (0.78 + 0.22 * Math.min(1, t * 6)));
|
|
2965
4482
|
if (variant === "royal") c = mix(c, [0.62, 0.62, 0.55], 0.25);
|
|
2966
4483
|
if (dead) c = mix(c, [0.4, 0.37, 0.32], 0.5);
|
|
2967
4484
|
return c;
|
|
@@ -2986,34 +4503,34 @@ var GameVegetationThree = (() => {
|
|
|
2986
4503
|
for (let i = 0; i < n; i++) {
|
|
2987
4504
|
if (ctx.lod === 2 && variant === "fan" && i % 2 === 1) continue;
|
|
2988
4505
|
const f = srng.fork(i);
|
|
2989
|
-
const
|
|
4506
|
+
const ring2 = i % 3;
|
|
2990
4507
|
const az2 = i / n * TAU * 1 + f.range(-0.2, 0.2) + si;
|
|
2991
4508
|
let elev;
|
|
2992
4509
|
let droop;
|
|
2993
4510
|
if (variant === "royal") {
|
|
2994
|
-
elev = [1.1, 0.65, 0.2][
|
|
2995
|
-
droop = [0.35, 0.65, 1][
|
|
4511
|
+
elev = [1.1, 0.65, 0.2][ring2] + f.range(-0.1, 0.1);
|
|
4512
|
+
droop = [0.35, 0.65, 1][ring2];
|
|
2996
4513
|
} else if (variant === "date") {
|
|
2997
|
-
elev = [1.15, 0.8, 0.45][
|
|
2998
|
-
droop = [0.3, 0.5, 0.75][
|
|
4514
|
+
elev = [1.15, 0.8, 0.45][ring2] + f.range(-0.08, 0.08);
|
|
4515
|
+
droop = [0.3, 0.5, 0.75][ring2];
|
|
2999
4516
|
} else if (variant === "areca") {
|
|
3000
|
-
elev = [1.1, 0.8, 0.5][
|
|
3001
|
-
droop = [0.45, 0.8, 1.1][
|
|
4517
|
+
elev = [1.1, 0.8, 0.5][ring2] + f.range(-0.1, 0.1);
|
|
4518
|
+
droop = [0.45, 0.8, 1.1][ring2];
|
|
3002
4519
|
} else if (variant === "fan") {
|
|
3003
|
-
elev = [1.3, 0.95, 0.6][
|
|
3004
|
-
droop = [0.25, 0.35, 0.5][
|
|
4520
|
+
elev = [1.3, 0.95, 0.6][ring2] + f.range(-0.1, 0.1);
|
|
4521
|
+
droop = [0.25, 0.35, 0.5][ring2];
|
|
3005
4522
|
} else {
|
|
3006
|
-
elev = [1.15, 0.75, 0.38][
|
|
3007
|
-
droop = [0.45, 0.78, 1.1][
|
|
4523
|
+
elev = [1.15, 0.75, 0.38][ring2] + f.range(-0.1, 0.1);
|
|
4524
|
+
droop = [0.45, 0.78, 1.1][ring2];
|
|
3008
4525
|
}
|
|
3009
4526
|
if (dead) {
|
|
3010
4527
|
elev = elev * 0.4 - 0.3;
|
|
3011
4528
|
droop *= 1.4;
|
|
3012
4529
|
}
|
|
3013
4530
|
if (dry) droop *= 1.15;
|
|
3014
|
-
const len2 = frondL * f.range(0.85, 1.1) * (
|
|
4531
|
+
const len2 = frondL * f.range(0.85, 1.1) * (ring2 === 2 ? 0.92 : 1);
|
|
3015
4532
|
const colA = dead ? mix(c0, deadC, 0.8) : c0;
|
|
3016
|
-
const colB = dead ? mix(c1, deadC, 0.7) :
|
|
4533
|
+
const colB = dead ? mix(c1, deadC, 0.7) : ring2 === 2 && dry ? mix(c1, deadC, 0.4) : c1;
|
|
3017
4534
|
if (variant === "fan") {
|
|
3018
4535
|
fanFrond(ctx, f, crownTop, az2, elev, len2, droop, colA, colB);
|
|
3019
4536
|
} else {
|
|
@@ -3124,10 +4641,10 @@ var GameVegetationThree = (() => {
|
|
|
3124
4641
|
const rows = ctx.pick(9, 6, 3);
|
|
3125
4642
|
for (let i = 0; i < nLeaves; i++) {
|
|
3126
4643
|
const f = rng.fork(i);
|
|
3127
|
-
const
|
|
4644
|
+
const ring2 = i % 3;
|
|
3128
4645
|
const az = i / nLeaves * TAU * 1 + f.range(-0.25, 0.25);
|
|
3129
|
-
const L = H * f.range(0.55, 0.72) * (
|
|
3130
|
-
const el = [1.15, 0.8, 0.45][
|
|
4646
|
+
const L = H * f.range(0.55, 0.72) * (ring2 === 2 ? 0.85 : 1);
|
|
4647
|
+
const el = [1.15, 0.8, 0.45][ring2] + f.range(-0.12, 0.12);
|
|
3131
4648
|
const horiz = dirFromAngles(az, 0);
|
|
3132
4649
|
const petL = L * 0.18;
|
|
3133
4650
|
const pEnd = [top[0] + horiz[0] * petL * Math.cos(el), top[1] + petL * Math.sin(el), top[2] + horiz[2] * petL * Math.cos(el)];
|
|
@@ -3138,7 +4655,7 @@ var GameVegetationThree = (() => {
|
|
|
3138
4655
|
const tearSeed = f.range(0, 10);
|
|
3139
4656
|
leaf(ctx.foliage, pEnd, norm([horiz[0] * Math.cos(el), Math.sin(el), horiz[2] * Math.cos(el)]), [0, 1, 0], L, L * 0.34, c0, c1, {
|
|
3140
4657
|
rows,
|
|
3141
|
-
curl: [0.55, 0.7, 0.8][
|
|
4658
|
+
curl: [0.55, 0.7, 0.8][ring2] * (dry ? 1.2 : 1),
|
|
3142
4659
|
fold: 0.35,
|
|
3143
4660
|
swayBase: 0.25,
|
|
3144
4661
|
swayTip: 1,
|
|
@@ -3238,8 +4755,8 @@ var GameVegetationThree = (() => {
|
|
|
3238
4755
|
if (hit) return hit;
|
|
3239
4756
|
const m = Math.max(Math.abs(c[0]), Math.abs(c[1]), Math.abs(c[2]));
|
|
3240
4757
|
const s = norm(c);
|
|
3241
|
-
const
|
|
3242
|
-
const q = [lerp(c[0],
|
|
4758
|
+
const sphere2 = [s[0] * m, s[1] * m, s[2] * m];
|
|
4759
|
+
const q = [lerp(c[0], sphere2[0], o.round), lerp(c[1], sphere2[1], o.round), lerp(c[2], sphere2[2], o.round)];
|
|
3243
4760
|
const base = [o.center[0] + q[0] * hx, o.center[1] + q[1] * hy, o.center[2] + q[2] * hz];
|
|
3244
4761
|
const nz = valueNoise3(base[0] * freq, base[1] * freq, base[2] * freq, seed) * 0.75 + valueNoise3(base[0] * freq * 2.3, base[1] * freq * 2.3, base[2] * freq * 2.3, seed + 7) * 0.25;
|
|
3245
4762
|
const out = norm([q[0] / Math.max(hx, 1e-6), q[1] / Math.max(hy, 1e-6), q[2] / Math.max(hz, 1e-6)]);
|
|
@@ -3445,14 +4962,14 @@ var GameVegetationThree = (() => {
|
|
|
3445
4962
|
const dark = shade(col, 0.62);
|
|
3446
4963
|
const apex = [lean * (y / H) ** 2, y + tierH * 0.55, 0];
|
|
3447
4964
|
const innerR = r * 0.32;
|
|
3448
|
-
const
|
|
4965
|
+
const ring2 = (rad, yy, k, ph) => [apex[0] + Math.cos(k / K * TAU + ph) * rad, yy, Math.sin(k / K * TAU + ph) * rad];
|
|
3449
4966
|
const tphase = i * 0.7;
|
|
3450
4967
|
for (let k = 0; k < K; k++) {
|
|
3451
4968
|
const jag = k % 2 === 0 ? 1 : 0.78;
|
|
3452
|
-
const a0 =
|
|
3453
|
-
const a1 =
|
|
3454
|
-
const o0 =
|
|
3455
|
-
const o1 =
|
|
4969
|
+
const a0 = ring2(innerR, y + tierH * 0.12, k, tphase);
|
|
4970
|
+
const a1 = ring2(innerR, y + tierH * 0.12, k + 1, tphase);
|
|
4971
|
+
const o0 = ring2(r * jag, y - tierH * 0.42, k, tphase);
|
|
4972
|
+
const o1 = ring2(r * (k % 2 === 0 ? 0.78 : 1), y - tierH * 0.42, k + 1, tphase);
|
|
3456
4973
|
const sw = 0.25 + 0.35 * (1 - f);
|
|
3457
4974
|
foliage.flat(apex, a1, a0, shade(lightTop, 0.85), 0.2, [0, 1, 0]);
|
|
3458
4975
|
foliage.flatTri(a0, a1, o1, mix(col, lightTop, 0.2), mix(col, lightTop, 0.2), lightTop, 0.2, 0.2, sw, [0, 1, 0]);
|
|
@@ -3641,12 +5158,12 @@ var GameVegetationThree = (() => {
|
|
|
3641
5158
|
const a1 = (i + 1) / sides * TAU;
|
|
3642
5159
|
ctx.wood.flatTri([0, soilY, 0], [Math.cos(a1) * rim, soilY, Math.sin(a1) * rim], [Math.cos(a0) * rim, soilY, Math.sin(a0) * rim], shade(soil, 0.9), soil, soil, 0, 0, 0, [0, 1, 0]);
|
|
3643
5160
|
}
|
|
3644
|
-
const inner =
|
|
5161
|
+
const inner = generate2(spec.plant, { seed: ctx.rng.fork("plant").seed(), lod: ctx.lod, condition: ctx.condition, season: ctx.season });
|
|
3645
5162
|
const yOff = soilY - 0.02;
|
|
3646
5163
|
const tagsOf = (part) => {
|
|
3647
|
-
const
|
|
3648
|
-
if (!
|
|
3649
|
-
const tags = new Array(
|
|
5164
|
+
const mesh2 = inner.parts[part];
|
|
5165
|
+
if (!mesh2) return void 0;
|
|
5166
|
+
const tags = new Array(mesh2.triangleCount).fill("");
|
|
3650
5167
|
for (const pc of inner.pieces) {
|
|
3651
5168
|
if (pc.part !== part) continue;
|
|
3652
5169
|
if (!ctx.pieceMeta.has(pc.id)) ctx.pieceMeta.set(pc.id, { kind: pc.kind, parent: pc.parent, pivot: [pc.pivot[0], pc.pivot[1] + yOff, pc.pivot[2]], ...pc.radius === void 0 || pc.length === void 0 || !pc.axis || !pc.base ? {} : { geom: { radius: pc.radius, length: pc.length, axis: pc.axis, base: [pc.base[0], pc.base[1] + yOff, pc.base[2]] } } });
|
|
@@ -3686,7 +5203,7 @@ var GameVegetationThree = (() => {
|
|
|
3686
5203
|
function archetypes() {
|
|
3687
5204
|
return Object.keys(GENERATORS);
|
|
3688
5205
|
}
|
|
3689
|
-
function
|
|
5206
|
+
function generate2(spec, opts = {}) {
|
|
3690
5207
|
const gen = GENERATORS[spec.archetype];
|
|
3691
5208
|
if (!gen) throw new Error(`game-vegetation: unknown archetype "${spec.archetype}"`);
|
|
3692
5209
|
const seed = toSeed(opts.seed);
|
|
@@ -3895,29 +5412,29 @@ var GameVegetationThree = (() => {
|
|
|
3895
5412
|
}
|
|
3896
5413
|
const tagBox = /* @__PURE__ */ new Map();
|
|
3897
5414
|
for (const part of PART_ORDER) {
|
|
3898
|
-
const
|
|
3899
|
-
if (!
|
|
5415
|
+
const mesh2 = parts[part];
|
|
5416
|
+
if (!mesh2) continue;
|
|
3900
5417
|
for (const r of builders[part].ranges) {
|
|
3901
5418
|
const tag = r.tag || rootId;
|
|
3902
|
-
let
|
|
3903
|
-
if (!
|
|
5419
|
+
let box2 = tagBox.get(tag);
|
|
5420
|
+
if (!box2) tagBox.set(tag, box2 = { min: [Infinity, Infinity, Infinity], max: [-Infinity, -Infinity, -Infinity] });
|
|
3904
5421
|
for (let i = r.indexStart; i < r.indexStart + r.indexCount; i++) {
|
|
3905
|
-
const v =
|
|
5422
|
+
const v = mesh2.indices[i] * 3;
|
|
3906
5423
|
for (let a = 0; a < 3; a++) {
|
|
3907
|
-
const c =
|
|
3908
|
-
if (c <
|
|
3909
|
-
if (c >
|
|
5424
|
+
const c = mesh2.positions[v + a];
|
|
5425
|
+
if (c < box2.min[a]) box2.min[a] = c;
|
|
5426
|
+
if (c > box2.max[a]) box2.max[a] = c;
|
|
3910
5427
|
}
|
|
3911
5428
|
}
|
|
3912
5429
|
}
|
|
3913
5430
|
}
|
|
3914
5431
|
const fruitHang = /* @__PURE__ */ new Map();
|
|
3915
5432
|
const PRIORITY = { branch: 0, pad: 0, arm: 0, stem: 1, trunk: 1, crown: 2 };
|
|
3916
|
-
for (const [tag,
|
|
5433
|
+
for (const [tag, box2] of tagBox) {
|
|
3917
5434
|
if (meta.get(tag)?.kind !== "fruit") continue;
|
|
3918
|
-
const cx = (
|
|
3919
|
-
const cy = (
|
|
3920
|
-
const cz = (
|
|
5435
|
+
const cx = (box2.min[0] + box2.max[0]) / 2;
|
|
5436
|
+
const cy = (box2.min[1] + box2.max[1]) / 2;
|
|
5437
|
+
const cz = (box2.min[2] + box2.max[2]) / 2;
|
|
3921
5438
|
let parent;
|
|
3922
5439
|
let pbox;
|
|
3923
5440
|
let bd = Infinity;
|
|
@@ -3939,24 +5456,24 @@ var GameVegetationThree = (() => {
|
|
|
3939
5456
|
pbox = pb;
|
|
3940
5457
|
}
|
|
3941
5458
|
}
|
|
3942
|
-
const pc = pbox ? [(pbox.min[0] + pbox.max[0]) / 2, (pbox.min[1] + pbox.max[1]) / 2, (pbox.min[2] + pbox.max[2]) / 2] : [cx,
|
|
3943
|
-
const pivot = [Math.min(
|
|
5459
|
+
const pc = pbox ? [(pbox.min[0] + pbox.max[0]) / 2, (pbox.min[1] + pbox.max[1]) / 2, (pbox.min[2] + pbox.max[2]) / 2] : [cx, box2.max[1], cz];
|
|
5460
|
+
const pivot = [Math.min(box2.max[0], Math.max(box2.min[0], pc[0])), Math.min(box2.max[1], Math.max(box2.min[1], pc[1])), Math.min(box2.max[2], Math.max(box2.min[2], pc[2]))];
|
|
3944
5461
|
fruitHang.set(tag, { parent, pivot });
|
|
3945
5462
|
}
|
|
3946
5463
|
const idOf = (tag, part) => primary.get(tag) === part ? tag : `${tag}:${part}`;
|
|
3947
5464
|
const out = [];
|
|
3948
5465
|
for (const part of PART_ORDER) {
|
|
3949
|
-
const
|
|
3950
|
-
if (!
|
|
5466
|
+
const mesh2 = parts[part];
|
|
5467
|
+
if (!mesh2) continue;
|
|
3951
5468
|
for (const r of builders[part].ranges) {
|
|
3952
5469
|
const tag = r.tag || rootId;
|
|
3953
5470
|
const m = meta.get(tag) ?? { kind: "stem" };
|
|
3954
5471
|
const min = [Infinity, Infinity, Infinity];
|
|
3955
5472
|
const max = [-Infinity, -Infinity, -Infinity];
|
|
3956
5473
|
for (let i = r.indexStart; i < r.indexStart + r.indexCount; i++) {
|
|
3957
|
-
const v =
|
|
5474
|
+
const v = mesh2.indices[i] * 3;
|
|
3958
5475
|
for (let a = 0; a < 3; a++) {
|
|
3959
|
-
const c =
|
|
5476
|
+
const c = mesh2.positions[v + a];
|
|
3960
5477
|
if (c < min[a]) min[a] = c;
|
|
3961
5478
|
if (c > max[a]) max[a] = c;
|
|
3962
5479
|
}
|
|
@@ -3968,7 +5485,7 @@ var GameVegetationThree = (() => {
|
|
|
3968
5485
|
else if (m.kind === "crown" || m.kind === "fruit") pivot = [(min[0] + max[0]) / 2, (min[1] + max[1]) / 2, (min[2] + max[2]) / 2];
|
|
3969
5486
|
else pivot = [(min[0] + max[0]) / 2, min[1], (min[2] + max[2]) / 2];
|
|
3970
5487
|
if (m.kind === "trunk" || m.kind === "stem" || m.kind === "pot" || m.kind === "soil") pivot = [0, min[1], 0];
|
|
3971
|
-
let material = materialKeyForPart(part,
|
|
5488
|
+
let material = materialKeyForPart(part, mesh2.material, condition);
|
|
3972
5489
|
if (m.kind === "pot") material = "pot";
|
|
3973
5490
|
else if (m.kind === "soil") material = "soil";
|
|
3974
5491
|
const parentTag = hang ? hang.parent : m.parent;
|
|
@@ -4012,10 +5529,10 @@ var GameVegetationThree = (() => {
|
|
|
4012
5529
|
const go = { seed: hashMix(base, p.seed), lod, condition: opts.condition, season: opts.season };
|
|
4013
5530
|
let plant;
|
|
4014
5531
|
if (smallLod !== lod) {
|
|
4015
|
-
const cheap =
|
|
5532
|
+
const cheap = generate2(spec, { ...go, lod: smallLod });
|
|
4016
5533
|
if (cheap.bounds.height * p.scale <= smallMax) plant = cheap;
|
|
4017
5534
|
}
|
|
4018
|
-
plant ?? (plant =
|
|
5535
|
+
plant ?? (plant = generate2(spec, go));
|
|
4019
5536
|
total += plant.triangleCount;
|
|
4020
5537
|
entries.push({ p, plant, go, spec });
|
|
4021
5538
|
}
|
|
@@ -4039,7 +5556,7 @@ var GameVegetationThree = (() => {
|
|
|
4039
5556
|
const group = entries.filter((e) => !e.dropped && kindOf(e) === kind && e.plant.lod < 2).sort((a, b) => a.plant.bounds.height * a.p.scale - b.plant.bounds.height * b.p.scale);
|
|
4040
5557
|
for (const e of group) {
|
|
4041
5558
|
if (total <= budget) break;
|
|
4042
|
-
const cheap =
|
|
5559
|
+
const cheap = generate2(e.spec, { ...e.go, lod: 2 });
|
|
4043
5560
|
total += cheap.triangleCount - e.plant.triangleCount;
|
|
4044
5561
|
e.plant = cheap;
|
|
4045
5562
|
}
|
|
@@ -4055,9 +5572,9 @@ var GameVegetationThree = (() => {
|
|
|
4055
5572
|
lodCounts[plant.lod]++;
|
|
4056
5573
|
const { parts } = plant;
|
|
4057
5574
|
const tagsOf = (part) => {
|
|
4058
|
-
const
|
|
4059
|
-
if (!
|
|
4060
|
-
const tags = new Array(
|
|
5575
|
+
const mesh2 = parts[part];
|
|
5576
|
+
if (!mesh2) return void 0;
|
|
5577
|
+
const tags = new Array(mesh2.triangleCount).fill("");
|
|
4061
5578
|
for (const pc of plant.pieces) {
|
|
4062
5579
|
if (pc.part !== part) continue;
|
|
4063
5580
|
for (let t = pc.indexStart / 3; t < (pc.indexStart + pc.indexCount) / 3; t++) tags[t] = `${pi}|${pc.id}`;
|
|
@@ -4082,8 +5599,8 @@ var GameVegetationThree = (() => {
|
|
|
4082
5599
|
};
|
|
4083
5600
|
for (const k of Object.keys(meshes)) if (!meshes[k]) delete meshes[k];
|
|
4084
5601
|
const rangeOf = /* @__PURE__ */ new Map();
|
|
4085
|
-
for (const [
|
|
4086
|
-
for (const r of b.ranges) if (r.tag) rangeOf.set(r.tag, { mesh, indexStart: r.indexStart, indexCount: r.indexCount });
|
|
5602
|
+
for (const [mesh2, b] of [["wood", wood], ["foliage", foliage], ["flowers", flowers]]) {
|
|
5603
|
+
for (const r of b.ranges) if (r.tag) rangeOf.set(r.tag, { mesh: mesh2, indexStart: r.indexStart, indexCount: r.indexCount });
|
|
4087
5604
|
}
|
|
4088
5605
|
const pieceTable = kept.map(({ plantIndex, e }) => ({
|
|
4089
5606
|
plantIndex,
|
|
@@ -4111,7 +5628,7 @@ var GameVegetationThree = (() => {
|
|
|
4111
5628
|
function extractPlant(lot, plantIndex) {
|
|
4112
5629
|
const e = entryOf(lot, plantIndex);
|
|
4113
5630
|
return {
|
|
4114
|
-
plant:
|
|
5631
|
+
plant: generate2(e.spec, { ...e.generateOptions, species: e.species }),
|
|
4115
5632
|
placement: { species: e.species, x: e.x, z: e.z, ry: e.ry, scale: e.scale },
|
|
4116
5633
|
ranges: e.pieces
|
|
4117
5634
|
};
|
|
@@ -4173,10 +5690,10 @@ var GameVegetationThree = (() => {
|
|
|
4173
5690
|
}
|
|
4174
5691
|
return w[w.length - 1].id;
|
|
4175
5692
|
}
|
|
4176
|
-
function scatter(
|
|
4177
|
-
const poly = regionPolygon(
|
|
5693
|
+
function scatter(area3, rules, opts) {
|
|
5694
|
+
const poly = regionPolygon(area3);
|
|
4178
5695
|
const seed = toSeed(opts.seed);
|
|
4179
|
-
const
|
|
5696
|
+
const area22 = polygonArea(poly);
|
|
4180
5697
|
let minX = Infinity;
|
|
4181
5698
|
let minZ = Infinity;
|
|
4182
5699
|
let maxX = -Infinity;
|
|
@@ -4192,11 +5709,11 @@ var GameVegetationThree = (() => {
|
|
|
4192
5709
|
const grid = /* @__PURE__ */ new Map();
|
|
4193
5710
|
const key = (cx, cz) => cx + "," + cz;
|
|
4194
5711
|
const out = [];
|
|
4195
|
-
const inradius = Math.max(0.5, Math.sqrt(
|
|
5712
|
+
const inradius = Math.max(0.5, Math.sqrt(area22) * 0.5);
|
|
4196
5713
|
rules.forEach((rule, ri) => {
|
|
4197
5714
|
const rng = new Rng(hashMix(seed, ri + 1));
|
|
4198
5715
|
const ex = [...globalEx, ...(rule.exclude ?? []).map(regionPolygon)];
|
|
4199
|
-
let target = Math.round(
|
|
5716
|
+
let target = Math.round(area22 * rule.density);
|
|
4200
5717
|
if (rule.maxCount !== void 0) target = Math.min(target, rule.maxCount);
|
|
4201
5718
|
const prefer = rule.prefer ?? "any";
|
|
4202
5719
|
const edgeW = rule.edgeWidth ?? Math.max(0.4, rule.minDistance * 0.8);
|
|
@@ -4243,8 +5760,8 @@ var GameVegetationThree = (() => {
|
|
|
4243
5760
|
}
|
|
4244
5761
|
if (!ok) continue;
|
|
4245
5762
|
const k = key(cx, cz);
|
|
4246
|
-
const
|
|
4247
|
-
if (
|
|
5763
|
+
const list2 = grid.get(k);
|
|
5764
|
+
if (list2) list2.push({ x, z, md: rule.minDistance });
|
|
4248
5765
|
else grid.set(k, [{ x, z, md: rule.minDistance }]);
|
|
4249
5766
|
out.push({ species: spec, x, z, ry, scale, seed: sd });
|
|
4250
5767
|
placed++;
|
|
@@ -4348,7 +5865,7 @@ var GameVegetationThree = (() => {
|
|
|
4348
5865
|
toBufferGeometry: () => toBufferGeometry
|
|
4349
5866
|
});
|
|
4350
5867
|
var MAX_ACTORS = 4;
|
|
4351
|
-
var
|
|
5868
|
+
var COMMON2 = (
|
|
4352
5869
|
/* glsl */
|
|
4353
5870
|
`
|
|
4354
5871
|
#include <common>
|
|
@@ -4409,13 +5926,13 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4409
5926
|
const patch = (m) => {
|
|
4410
5927
|
m.onBeforeCompile = (shader) => {
|
|
4411
5928
|
Object.assign(shader.uniforms, uniforms);
|
|
4412
|
-
shader.vertexShader = shader.vertexShader.replace("#include <common>",
|
|
5929
|
+
shader.vertexShader = shader.vertexShader.replace("#include <common>", COMMON2).replace("#include <project_vertex>", PROJECT);
|
|
4413
5930
|
};
|
|
4414
5931
|
m.customProgramCacheKey = () => "vegetation-sway-v1";
|
|
4415
5932
|
};
|
|
4416
5933
|
const flat = opts.flatShading !== false;
|
|
4417
5934
|
const rough = typeof opts.roughness === "number" ? opts.roughness : 0.85;
|
|
4418
|
-
const
|
|
5935
|
+
const make2 = (flatShading, double, roughness) => {
|
|
4419
5936
|
const m = new THREE.MeshStandardMaterial({
|
|
4420
5937
|
vertexColors: true,
|
|
4421
5938
|
flatShading,
|
|
@@ -4426,11 +5943,11 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4426
5943
|
patch(m);
|
|
4427
5944
|
return m;
|
|
4428
5945
|
};
|
|
4429
|
-
const wood =
|
|
4430
|
-
const foliage =
|
|
4431
|
-
const flowers =
|
|
4432
|
-
const fruit =
|
|
4433
|
-
const spines =
|
|
5946
|
+
const wood = make2(false, false, 0.95);
|
|
5947
|
+
const foliage = make2(flat, true, rough);
|
|
5948
|
+
const flowers = make2(flat, true, 0.7);
|
|
5949
|
+
const fruit = make2(flat, false, 0.55);
|
|
5950
|
+
const spines = make2(true, true, 0.8);
|
|
4434
5951
|
const dir = uniforms.uWindDir.value;
|
|
4435
5952
|
return {
|
|
4436
5953
|
materialIds: resolveMaterialIds(opts.materialIds),
|
|
@@ -4468,13 +5985,13 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4468
5985
|
};
|
|
4469
5986
|
}
|
|
4470
5987
|
var PART_ORDER2 = ["wood", "foliage", "flowers", "fruit", "spines"];
|
|
4471
|
-
function toBufferGeometry(THREE,
|
|
5988
|
+
function toBufferGeometry(THREE, mesh2) {
|
|
4472
5989
|
const g = new THREE.BufferGeometry();
|
|
4473
|
-
g.setAttribute("position", new THREE.BufferAttribute(
|
|
4474
|
-
g.setAttribute("normal", new THREE.BufferAttribute(
|
|
4475
|
-
g.setAttribute("color", new THREE.BufferAttribute(
|
|
4476
|
-
g.setAttribute("sway", new THREE.BufferAttribute(
|
|
4477
|
-
g.setIndex(new THREE.BufferAttribute(
|
|
5990
|
+
g.setAttribute("position", new THREE.BufferAttribute(mesh2.positions, 3));
|
|
5991
|
+
g.setAttribute("normal", new THREE.BufferAttribute(mesh2.normals, 3));
|
|
5992
|
+
g.setAttribute("color", new THREE.BufferAttribute(mesh2.colors, 3));
|
|
5993
|
+
g.setAttribute("sway", new THREE.BufferAttribute(mesh2.sway, 1));
|
|
5994
|
+
g.setIndex(new THREE.BufferAttribute(mesh2.indices, 1));
|
|
4478
5995
|
g.computeBoundingSphere();
|
|
4479
5996
|
g.computeBoundingBox();
|
|
4480
5997
|
return g;
|
|
@@ -4488,18 +6005,18 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4488
6005
|
function meshFor(THREE, data, material, ids, condition) {
|
|
4489
6006
|
const geo = toBufferGeometry(THREE, data);
|
|
4490
6007
|
inflate(geo, 0.4);
|
|
4491
|
-
const
|
|
4492
|
-
|
|
4493
|
-
|
|
4494
|
-
if (ids)
|
|
4495
|
-
return
|
|
6008
|
+
const mesh2 = new THREE.Mesh(geo, material);
|
|
6009
|
+
mesh2.name = data.kind;
|
|
6010
|
+
mesh2.userData.damageable = { material: data.material };
|
|
6011
|
+
if (ids) mesh2.userData.destructionMaterial = ids[materialKeyForPart(data.kind, data.material, condition)];
|
|
6012
|
+
return mesh2;
|
|
4496
6013
|
}
|
|
4497
|
-
function slicePiece(
|
|
6014
|
+
function slicePiece(mesh2, indexStart, indexCount, origin) {
|
|
4498
6015
|
const remap = /* @__PURE__ */ new Map();
|
|
4499
6016
|
const order = [];
|
|
4500
6017
|
const indices = new Uint32Array(indexCount);
|
|
4501
6018
|
for (let i = 0; i < indexCount; i++) {
|
|
4502
|
-
const v =
|
|
6019
|
+
const v = mesh2.indices[indexStart + i];
|
|
4503
6020
|
let n = remap.get(v);
|
|
4504
6021
|
if (n === void 0) {
|
|
4505
6022
|
n = order.length;
|
|
@@ -4514,14 +6031,14 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4514
6031
|
const sway = new Float32Array(order.length);
|
|
4515
6032
|
for (let i = 0; i < order.length; i++) {
|
|
4516
6033
|
const v = order[i];
|
|
4517
|
-
positions[i * 3] =
|
|
4518
|
-
positions[i * 3 + 1] =
|
|
4519
|
-
positions[i * 3 + 2] =
|
|
4520
|
-
normals.set(
|
|
4521
|
-
colors.set(
|
|
4522
|
-
sway[i] =
|
|
6034
|
+
positions[i * 3] = mesh2.positions[v * 3] - origin[0];
|
|
6035
|
+
positions[i * 3 + 1] = mesh2.positions[v * 3 + 1] - origin[1];
|
|
6036
|
+
positions[i * 3 + 2] = mesh2.positions[v * 3 + 2] - origin[2];
|
|
6037
|
+
normals.set(mesh2.normals.subarray(v * 3, v * 3 + 3), i * 3);
|
|
6038
|
+
colors.set(mesh2.colors.subarray(v * 3, v * 3 + 3), i * 3);
|
|
6039
|
+
sway[i] = mesh2.sway[v];
|
|
4523
6040
|
}
|
|
4524
|
-
return { positions, normals, colors, sway, indices, triangleCount: indexCount / 3, kind:
|
|
6041
|
+
return { positions, normals, colors, sway, indices, triangleCount: indexCount / 3, kind: mesh2.kind, material: mesh2.material };
|
|
4525
6042
|
}
|
|
4526
6043
|
function createPlantObject(THREE, plant, materials, options = {}) {
|
|
4527
6044
|
const mats = materials ?? createVegetationMaterials(THREE);
|
|
@@ -4533,13 +6050,13 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4533
6050
|
const src = plant.parts[pc.part];
|
|
4534
6051
|
if (!src) continue;
|
|
4535
6052
|
const data = slicePiece(src, pc.indexStart, pc.indexCount, pc.pivot);
|
|
4536
|
-
const
|
|
4537
|
-
|
|
4538
|
-
|
|
6053
|
+
const mesh2 = meshFor(THREE, data, mats[pc.part]);
|
|
6054
|
+
mesh2.name = pc.id;
|
|
6055
|
+
mesh2.position.set(pc.pivot[0], pc.pivot[1], pc.pivot[2]);
|
|
4539
6056
|
const id = ids[pc.material];
|
|
4540
|
-
|
|
4541
|
-
|
|
4542
|
-
g.add(
|
|
6057
|
+
mesh2.userData.destructionMaterial = id;
|
|
6058
|
+
mesh2.userData.destructionPart = { id: pc.id, kind: pc.kind, parent: pc.parent, material: id, materialKey: pc.material, ...pc.radius === void 0 ? {} : { radius: pc.radius, length: pc.length, axis: pc.axis, base: pc.base } };
|
|
6059
|
+
g.add(mesh2);
|
|
4543
6060
|
}
|
|
4544
6061
|
g.userData = { collider: plant.collider, bendable: plant.bendable, thorny: plant.thorny, species: plant.species, bounds: plant.bounds, pieces: plant.pieces, separable: true };
|
|
4545
6062
|
return g;
|
|
@@ -4618,10 +6135,10 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
4618
6135
|
const d = opts.distances ?? [0, 25, 60];
|
|
4619
6136
|
const lod = new THREE.LOD();
|
|
4620
6137
|
for (const l of [0, 1, 2]) {
|
|
4621
|
-
const plant =
|
|
6138
|
+
const plant = generate2(spec, { ...opts, lod: l });
|
|
4622
6139
|
lod.addLevel(createPlantObject(THREE, plant, mats), d[l]);
|
|
4623
6140
|
}
|
|
4624
|
-
lod.userData = { collider:
|
|
6141
|
+
lod.userData = { collider: generate2(spec, { ...opts, lod: 2 }).collider };
|
|
4625
6142
|
return lod;
|
|
4626
6143
|
}
|
|
4627
6144
|
|
|
@@ -7830,16 +9347,16 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7830
9347
|
};
|
|
7831
9348
|
}
|
|
7832
9349
|
function pickForContext(region, context, rng) {
|
|
7833
|
-
const
|
|
7834
|
-
if (!
|
|
9350
|
+
const list2 = region.contexts[context];
|
|
9351
|
+
if (!list2 || list2.length === 0) throw new Error(`game-vegetation-species: region "${region.id}" has no context "${context}"`);
|
|
7835
9352
|
const next = toNext(rng);
|
|
7836
|
-
const total =
|
|
9353
|
+
const total = list2.reduce((a, b) => a + b.weight, 0);
|
|
7837
9354
|
let r = next() * total;
|
|
7838
|
-
for (const it of
|
|
9355
|
+
for (const it of list2) {
|
|
7839
9356
|
r -= it.weight;
|
|
7840
9357
|
if (r <= 0) return it.species;
|
|
7841
9358
|
}
|
|
7842
|
-
return
|
|
9359
|
+
return list2[list2.length - 1].species;
|
|
7843
9360
|
}
|
|
7844
9361
|
|
|
7845
9362
|
// src/test-area/nursery.ts
|
|
@@ -7867,10 +9384,10 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7867
9384
|
for (const planting of plantings) {
|
|
7868
9385
|
const { rules, picks } = species2.scatterRulesFor(species2.regions.br, planting.context, seed, planting.options);
|
|
7869
9386
|
for (const p of picks) used.add(p.id);
|
|
7870
|
-
planting.areas.forEach((
|
|
9387
|
+
planting.areas.forEach((area3, i) => {
|
|
7871
9388
|
const exclude = [EX_HOUSE, EX_PATH];
|
|
7872
9389
|
const perArea = rules.map((rule) => ({ ...rule, maxCount: Math.max(1, Math.ceil(rule.maxCount / planting.areas.length)) }));
|
|
7873
|
-
placements.push(...sdk.scatter(
|
|
9390
|
+
placements.push(...sdk.scatter(area3, perArea, { seed: `${seed}:${planting.tag}:${i}`, exclude }));
|
|
7874
9391
|
});
|
|
7875
9392
|
}
|
|
7876
9393
|
return { placements, used };
|
|
@@ -7878,45 +9395,46 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7878
9395
|
function buildScenery(THREE) {
|
|
7879
9396
|
const g = new THREE.Group();
|
|
7880
9397
|
g.name = "nursery-scenery";
|
|
7881
|
-
const material = (
|
|
9398
|
+
const material = (color2, roughness = 0.9) => new THREE.MeshStandardMaterial({ color: color2, roughness, metalness: 0 });
|
|
7882
9399
|
const mats = { ground: material(6257989), asphalt: material(3816770), concrete: material(12039080), house: material(14931645), roof: material(10112312), door: material(7031343), glass: material(10471389, 0.2), wall: material(13222578) };
|
|
7883
|
-
const
|
|
7884
|
-
const
|
|
7885
|
-
|
|
7886
|
-
|
|
7887
|
-
g.add(
|
|
7888
|
-
return
|
|
9400
|
+
const box2 = (w, h, d, m, x, y, z) => {
|
|
9401
|
+
const mesh2 = new THREE.Mesh(new THREE.BoxGeometry(w, h, d), m);
|
|
9402
|
+
mesh2.position.set(x, y, z);
|
|
9403
|
+
mesh2.castShadow = mesh2.receiveShadow = true;
|
|
9404
|
+
g.add(mesh2);
|
|
9405
|
+
return mesh2;
|
|
7889
9406
|
};
|
|
7890
9407
|
const yard = new THREE.Mesh(new THREE.PlaneGeometry(LOT.W, LOT.D), mats.ground);
|
|
7891
9408
|
yard.rotation.x = -Math.PI / 2;
|
|
7892
9409
|
yard.position.set(6, 0.01, 10);
|
|
7893
9410
|
yard.receiveShadow = true;
|
|
7894
9411
|
g.add(yard);
|
|
7895
|
-
|
|
7896
|
-
|
|
7897
|
-
|
|
9412
|
+
box2(30, 0.04, 4.6, mats.asphalt, 6, 0, -4.6);
|
|
9413
|
+
box2(30, 0.1, 2.2, mats.concrete, 6, 0.04, -1.1);
|
|
9414
|
+
box2(1.6, 0.03, 6.6, mats.concrete, 6, 0.02, 3.2);
|
|
7898
9415
|
const hw = HOUSE.x1 - HOUSE.x0;
|
|
7899
9416
|
const hd = HOUSE.z1 - HOUSE.z0;
|
|
7900
9417
|
const hcx = (HOUSE.x0 + HOUSE.x1) / 2;
|
|
7901
9418
|
const hcz = (HOUSE.z0 + HOUSE.z1) / 2;
|
|
7902
|
-
|
|
9419
|
+
box2(hw, 2.8, hd, mats.house, hcx, 1.4, hcz);
|
|
7903
9420
|
const roof = new THREE.Mesh(new THREE.ConeGeometry(1, 1.5, 4), mats.roof);
|
|
7904
9421
|
roof.rotation.y = Math.PI / 4;
|
|
7905
9422
|
roof.scale.set((hw + 1.2) / Math.SQRT2, 1, (hd + 1.2) / Math.SQRT2);
|
|
7906
9423
|
roof.position.set(hcx, 2.8 + 0.75, hcz);
|
|
7907
9424
|
roof.castShadow = roof.receiveShadow = true;
|
|
7908
9425
|
g.add(roof);
|
|
7909
|
-
|
|
7910
|
-
|
|
7911
|
-
|
|
7912
|
-
|
|
7913
|
-
|
|
7914
|
-
|
|
7915
|
-
|
|
7916
|
-
|
|
9426
|
+
box2(1.1, 2.1, 0.08, mats.door, 6, 1.05, HOUSE.z0 - 0.04);
|
|
9427
|
+
box2(1.6, 1.1, 0.08, mats.glass, 3.8, 1.5, HOUSE.z0 - 0.04);
|
|
9428
|
+
box2(1.6, 1.1, 0.08, mats.glass, 8.2, 1.5, HOUSE.z0 - 0.04);
|
|
9429
|
+
box2(5, 1.4, 0.2, mats.wall, 2.5, 0.7, 0);
|
|
9430
|
+
box2(5, 1.4, 0.2, mats.wall, 9.5, 0.7, 0);
|
|
9431
|
+
box2(0.2, 1.8, LOT.D, mats.wall, 0, 0.9, 10);
|
|
9432
|
+
box2(0.2, 1.8, LOT.D, mats.wall, LOT.W, 0.9, 10);
|
|
9433
|
+
box2(LOT.W, 1.8, 0.2, mats.wall, 6, 0.9, LOT.D);
|
|
7917
9434
|
return g;
|
|
7918
9435
|
}
|
|
7919
9436
|
function createNursery(sdk, species2, THREE, materials, parent, origin) {
|
|
9437
|
+
const T = shapeGeometries(THREE);
|
|
7920
9438
|
const group = new THREE.Group();
|
|
7921
9439
|
group.name = "nursery";
|
|
7922
9440
|
group.position.set(origin[0], 0, origin[1]);
|
|
@@ -7926,13 +9444,13 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7926
9444
|
let scenery = null;
|
|
7927
9445
|
let lotObject = null;
|
|
7928
9446
|
let data = null;
|
|
7929
|
-
let
|
|
9447
|
+
let stats2 = null;
|
|
7930
9448
|
let actor = null;
|
|
7931
9449
|
const walker = { x: WAY[0][0], z: WAY[0][1], waypoint: 0, on: false };
|
|
7932
9450
|
const actors = [{ x: 0, y: 0.8, z: 0, radius: 1.5 }];
|
|
7933
9451
|
let attached = false;
|
|
7934
|
-
const disposeObject = (
|
|
7935
|
-
|
|
9452
|
+
const disposeObject = (object2) => {
|
|
9453
|
+
object2.traverse((child) => {
|
|
7936
9454
|
child.geometry?.dispose?.();
|
|
7937
9455
|
});
|
|
7938
9456
|
};
|
|
@@ -7946,8 +9464,8 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7946
9464
|
const ensure = () => {
|
|
7947
9465
|
if (attached) return;
|
|
7948
9466
|
attached = true;
|
|
7949
|
-
scenery = buildScenery(
|
|
7950
|
-
const body =
|
|
9467
|
+
scenery = buildScenery(T);
|
|
9468
|
+
const body = T.CapsuleGeometry ? new T.CapsuleGeometry(0.28, 1, 4, 10) : new T.CylinderGeometry(0.28, 0.28, 1.56, 10);
|
|
7951
9469
|
actor = new THREE.Mesh(body, new THREE.MeshStandardMaterial({ color: 14254635, roughness: 0.7 }));
|
|
7952
9470
|
actor.castShadow = true;
|
|
7953
9471
|
actor.position.set(walker.x, 0.78, walker.z);
|
|
@@ -7971,7 +9489,7 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
7971
9489
|
return {
|
|
7972
9490
|
group,
|
|
7973
9491
|
get stats() {
|
|
7974
|
-
return
|
|
9492
|
+
return stats2;
|
|
7975
9493
|
},
|
|
7976
9494
|
get planted() {
|
|
7977
9495
|
return lotObject !== null;
|
|
@@ -8004,12 +9522,12 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8004
9522
|
});
|
|
8005
9523
|
dynamic.add(lotObject);
|
|
8006
9524
|
for (const c of data.colliders) {
|
|
8007
|
-
const m = new THREE.Mesh(new
|
|
9525
|
+
const m = new THREE.Mesh(new T.CylinderGeometry(c.r, c.r, c.h, 14, 1, true), new THREE.MeshBasicMaterial({ color: c.damage ? 16742938 : 15017245, wireframe: true }));
|
|
8008
9526
|
m.position.set(c.x, c.h / 2, c.z);
|
|
8009
9527
|
colliders.add(m);
|
|
8010
9528
|
}
|
|
8011
|
-
|
|
8012
|
-
return
|
|
9529
|
+
stats2 = { triangles: data.triangleCount, plants: data.plantCount, colliders: data.colliders.length, meshes: Object.keys(data.meshes).length, lod: data.lodCounts, buildMs, species: used.size };
|
|
9530
|
+
return stats2;
|
|
8013
9531
|
},
|
|
8014
9532
|
/** The colliders of the lot as wire cylinders. */
|
|
8015
9533
|
showColliders(on) {
|
|
@@ -8071,7 +9589,7 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8071
9589
|
actor = null;
|
|
8072
9590
|
}
|
|
8073
9591
|
data = null;
|
|
8074
|
-
|
|
9592
|
+
stats2 = null;
|
|
8075
9593
|
walker.x = WAY[0][0];
|
|
8076
9594
|
walker.z = WAY[0][1];
|
|
8077
9595
|
walker.waypoint = 0;
|
|
@@ -8079,6 +9597,15 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8079
9597
|
};
|
|
8080
9598
|
}
|
|
8081
9599
|
|
|
9600
|
+
// ../game-contracts/dist/index.js
|
|
9601
|
+
var KIT_ORIGIN_KEY = "kitOrigin";
|
|
9602
|
+
function markKitOrigin(object2, origin) {
|
|
9603
|
+
const node = object2;
|
|
9604
|
+
node.userData = node.userData ?? {};
|
|
9605
|
+
node.userData[KIT_ORIGIN_KEY] = { ...origin };
|
|
9606
|
+
return object2;
|
|
9607
|
+
}
|
|
9608
|
+
|
|
8082
9609
|
// src/test-area/plants.ts
|
|
8083
9610
|
var PLANT_SEED = 3;
|
|
8084
9611
|
var PLANT_DEFS = [
|
|
@@ -8122,25 +9649,25 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8122
9649
|
return plant;
|
|
8123
9650
|
};
|
|
8124
9651
|
function buildObject(THREE, def) {
|
|
8125
|
-
const root = new THREE.Group();
|
|
9652
|
+
const root = markKitOrigin(new THREE.Group(), { owner: "game-vegetation-three", kind: "plant", id: def.base ?? def.id });
|
|
8126
9653
|
root.name = def.id;
|
|
8127
9654
|
const segments = def.segments ?? 1;
|
|
8128
9655
|
let spacing = 0;
|
|
8129
9656
|
for (let s = 0; s < segments; s++) {
|
|
8130
9657
|
const plant = plantFor(def, s);
|
|
8131
|
-
const
|
|
9658
|
+
const object2 = sdk.createPlantObject(THREE, plant, materials(THREE), { separable: true });
|
|
8132
9659
|
const prefix = segments > 1 ? `s${s}_` : "";
|
|
8133
|
-
const children =
|
|
9660
|
+
const children = object2.children.slice();
|
|
8134
9661
|
if (s === 0 && segments > 1) {
|
|
8135
9662
|
const g0 = children[0].geometry;
|
|
8136
9663
|
g0.computeBoundingBox();
|
|
8137
9664
|
spacing = (g0.boundingBox.max.x - g0.boundingBox.min.x) * 0.9;
|
|
8138
9665
|
}
|
|
8139
|
-
for (const
|
|
8140
|
-
const piece =
|
|
9666
|
+
for (const mesh2 of children) {
|
|
9667
|
+
const piece = mesh2.userData.destructionPart;
|
|
8141
9668
|
const bits = String(piece.id).split(":");
|
|
8142
9669
|
const id = prefix + sanitize(piece.id);
|
|
8143
|
-
|
|
9670
|
+
mesh2.name = id;
|
|
8144
9671
|
const dx = (s - (segments - 1) / 2) * spacing;
|
|
8145
9672
|
const meta = {
|
|
8146
9673
|
id,
|
|
@@ -8156,10 +9683,10 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8156
9683
|
axis: piece.axis,
|
|
8157
9684
|
dx
|
|
8158
9685
|
};
|
|
8159
|
-
|
|
8160
|
-
|
|
8161
|
-
|
|
8162
|
-
root.add(
|
|
9686
|
+
mesh2.userData.veg = meta;
|
|
9687
|
+
mesh2.position.x += dx;
|
|
9688
|
+
mesh2.castShadow = true;
|
|
9689
|
+
root.add(mesh2);
|
|
8163
9690
|
}
|
|
8164
9691
|
if (s === 0) {
|
|
8165
9692
|
root.userData.collider = plant.collider || null;
|
|
@@ -8381,7 +9908,7 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8381
9908
|
const j = specOf(so).joints.filter((x) => x.id === jid)[0];
|
|
8382
9909
|
const a = partSpec(so, j.a);
|
|
8383
9910
|
const b = partSpec(so, j.b);
|
|
8384
|
-
const
|
|
9911
|
+
const box2 = (p) => {
|
|
8385
9912
|
const h = p.shape.kind === "box" ? p.shape.size.map((s) => s / 2) : [p.shape.radius, p.shape.height / 2, p.shape.radius];
|
|
8386
9913
|
const r = p.rotation || [0, 0, 0];
|
|
8387
9914
|
const cx = Math.cos(r[0]), sx = Math.sin(r[0]), cz = Math.cos(r[2]), sz = Math.sin(r[2]);
|
|
@@ -8398,8 +9925,8 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8398
9925
|
}
|
|
8399
9926
|
return { min: mn, max: mx };
|
|
8400
9927
|
};
|
|
8401
|
-
const A_ =
|
|
8402
|
-
const B_ =
|
|
9928
|
+
const A_ = box2(a);
|
|
9929
|
+
const B_ = box2(b);
|
|
8403
9930
|
return world(so, [0, 1, 2].map((k) => (Math.max(A_.min[k], B_.min[k]) + Math.min(A_.max[k], B_.max[k])) / 2));
|
|
8404
9931
|
};
|
|
8405
9932
|
const lost = (p) => !p || p.status === "broken" || p.status === "ashes" || p.status === "melted";
|
|
@@ -8828,17 +10355,17 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8828
10355
|
};
|
|
8829
10356
|
if (host.bench.layer("colliders") && root.userData.collider && root.userData.collider.kind) {
|
|
8830
10357
|
const c = root.userData.collider;
|
|
8831
|
-
const wire = new three.Mesh(new three.CylinderGeometry(c.radius, c.radius, c.height, 14, 1, true), new three.MeshBasicMaterial({ color: root.userData.thorny ? 16742938 : 15017245, wireframe: true }));
|
|
10358
|
+
const wire = new three.Mesh(new (shapeGeometries(three)).CylinderGeometry(c.radius, c.radius, c.height, 14, 1, true), new three.MeshBasicMaterial({ color: root.userData.thorny ? 16742938 : 15017245, wireframe: true }));
|
|
8832
10359
|
wire.position.set(0, c.height / 2, 0);
|
|
8833
10360
|
mark(wire);
|
|
8834
10361
|
}
|
|
8835
|
-
for (const
|
|
8836
|
-
const v =
|
|
8837
|
-
if (!v || !
|
|
10362
|
+
for (const mesh2 of root.children) {
|
|
10363
|
+
const v = mesh2.userData?.veg;
|
|
10364
|
+
if (!v || !mesh2.geometry) continue;
|
|
8838
10365
|
if (host.bench.layer("piece_boxes")) {
|
|
8839
|
-
|
|
8840
|
-
const
|
|
8841
|
-
mark(new three.Box3Helper(
|
|
10366
|
+
mesh2.geometry.computeBoundingBox();
|
|
10367
|
+
const box2 = mesh2.geometry.boundingBox.clone().translate(mesh2.position);
|
|
10368
|
+
mark(new three.Box3Helper(box2, 6737151));
|
|
8842
10369
|
}
|
|
8843
10370
|
if (host.bench.layer("wood_axes") && v.radius !== void 0 && v.length !== void 0 && v.axis && !v.sub) {
|
|
8844
10371
|
const base = pieceBase(root, v);
|
|
@@ -8854,8 +10381,8 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8854
10381
|
helpersShown = boxKey();
|
|
8855
10382
|
};
|
|
8856
10383
|
const pieceBase = (root, v) => {
|
|
8857
|
-
const
|
|
8858
|
-
const base =
|
|
10384
|
+
const mesh2 = root.children.find((c) => c.userData?.veg?.id === v.id);
|
|
10385
|
+
const base = mesh2?.userData?.destructionPart?.base;
|
|
8859
10386
|
return Array.isArray(base) ? base : null;
|
|
8860
10387
|
};
|
|
8861
10388
|
const nurseryOf = () => {
|
|
@@ -8866,9 +10393,9 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8866
10393
|
const plantLot = () => {
|
|
8867
10394
|
const n = nurseryOf();
|
|
8868
10395
|
if (!n || !host) return;
|
|
8869
|
-
const
|
|
10396
|
+
const stats2 = n.plant(settings);
|
|
8870
10397
|
n.showColliders(Boolean(host.bench?.layer("colliders")));
|
|
8871
|
-
host.log(fill(text.lot.planted,
|
|
10398
|
+
host.log(fill(text.lot.planted, stats2.plants, stats2.triangles, stats2.colliders), { plants: stats2.plants, triangles: stats2.triangles });
|
|
8872
10399
|
};
|
|
8873
10400
|
const replant = () => {
|
|
8874
10401
|
if (nursery?.planted) plantLot();
|
|
@@ -8937,12 +10464,12 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8937
10464
|
let triangles = 0;
|
|
8938
10465
|
let pieces = 0;
|
|
8939
10466
|
for (const entry of placed.values()) {
|
|
8940
|
-
entry.root.traverse((
|
|
8941
|
-
if (!
|
|
8942
|
-
for (let p =
|
|
10467
|
+
entry.root.traverse((mesh2) => {
|
|
10468
|
+
if (!mesh2.isMesh || mesh2.userData.helper) return;
|
|
10469
|
+
for (let p = mesh2; p; p = p.parent) if (p.visible === false) return;
|
|
8943
10470
|
drawCalls += 1;
|
|
8944
|
-
if (
|
|
8945
|
-
const g =
|
|
10471
|
+
if (mesh2.userData.veg) pieces += 1;
|
|
10472
|
+
const g = mesh2.geometry;
|
|
8946
10473
|
triangles += (g.index ? g.index.count : g.attributes.position.count) / 3;
|
|
8947
10474
|
});
|
|
8948
10475
|
}
|
|
@@ -8997,9 +10524,9 @@ gl_Position = projectionMatrix * mvPosition;
|
|
|
8997
10524
|
{ kind: "choice", id: "season", name: text.controls.season, hint: text.hints.season, options: ["spring", "summer", "autumn", "winter"].map((id) => ({ id, name: text.options.season[id] })), default: "spring" },
|
|
8998
10525
|
{ kind: "number", id: "wind", name: text.controls.wind, hint: text.hints.wind, min: 0, max: 1, step: 0.05, default: 0.35 },
|
|
8999
10526
|
{ kind: "number", id: "gust", name: text.controls.gust, hint: text.hints.gust, min: 0, max: 1, step: 0.05, default: 0.15 },
|
|
9000
|
-
{ kind: "action", id: "plant_lot", name: text.actions.plant_lot },
|
|
10527
|
+
{ kind: "action", id: "plant_lot", name: text.actions.plant_lot, slow: true },
|
|
9001
10528
|
{ kind: "action", id: "clear_lot", name: text.actions.clear_lot },
|
|
9002
|
-
{ kind: "action", id: "toggle_walker", name: text.actions.toggle_walker }
|
|
10529
|
+
{ kind: "action", id: "toggle_walker", name: text.actions.toggle_walker, slow: true }
|
|
9003
10530
|
],
|
|
9004
10531
|
layers: () => ["colliders", "piece_boxes", "wood_axes"].map((id) => ({ id, name: text.layers[id], default: false })),
|
|
9005
10532
|
metrics: () => [
|