@directivegames/genesys.js 3.1.28 → 3.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/games/index.d.ts +1 -0
- package/dist/games/index.js +2 -1
- package/dist/games/spline-mesh-demo.d.ts +17 -0
- package/dist/games/spline-mesh-demo.js +118 -0
- package/dist/genesys.min.mjs +246 -246
- package/dist/launcher.js +5 -1
- package/dist/src/actors/Actor.d.ts +2 -2
- package/dist/src/actors/Actor.js +9 -3
- package/dist/src/components/visual/SplineMeshComponent.d.ts +107 -0
- package/dist/src/components/visual/SplineMeshComponent.js +373 -0
- package/dist/src/components/visual/index.d.ts +1 -0
- package/dist/src/components/visual/index.js +2 -1
- package/dist/test/prefab.test.js +1 -1
- package/games/index.ts +1 -0
- package/games/spline-mesh-demo.ts +119 -0
- package/package.json +1 -1
- package/src/actors/Actor.ts +10 -2
- package/src/components/visual/SplineMeshComponent.ts +421 -0
- package/src/components/visual/index.ts +1 -0
|
@@ -0,0 +1,373 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* SplineMeshComponent generates a flat ribbon mesh along a Catmull-Rom spline.
|
|
12
|
+
*
|
|
13
|
+
* Use cases include roads, paths, rivers, and any geometry that follows a curved path.
|
|
14
|
+
* The component samples points along the spline and creates a ribbon with proper
|
|
15
|
+
* UV mapping for texture tiling based on world distance.
|
|
16
|
+
*
|
|
17
|
+
* Recommended usage (for humans and AI assistants):
|
|
18
|
+
* - Provide an array of control points defining the path
|
|
19
|
+
* - Adjust width, segments, and tension for desired appearance
|
|
20
|
+
* - Use uvScale to control texture tiling density
|
|
21
|
+
* - Enable showDebug to visualize control points and spline curve
|
|
22
|
+
*/
|
|
23
|
+
import * as THREE from 'three';
|
|
24
|
+
import { MaterialUtils } from '../../materials.js';
|
|
25
|
+
import { EngineClass } from '../../systems/ClassRegistry.js';
|
|
26
|
+
import { resourceManager } from '../../utils/ResourceManager.js';
|
|
27
|
+
import { isNode } from '../../utils/Utils.js';
|
|
28
|
+
import { PrimitiveComponent } from '../PrimitiveComponent.js';
|
|
29
|
+
let SplineMeshComponent = class SplineMeshComponent extends PrimitiveComponent {
|
|
30
|
+
static get DEFAULT_OPTIONS() {
|
|
31
|
+
return {
|
|
32
|
+
...PrimitiveComponent.DEFAULT_OPTIONS,
|
|
33
|
+
// Provide default starter points so the spline is visible when created
|
|
34
|
+
points: [
|
|
35
|
+
new THREE.Vector3(-2, 0, 0),
|
|
36
|
+
new THREE.Vector3(0, 0, 1),
|
|
37
|
+
new THREE.Vector3(2, 0, 0),
|
|
38
|
+
],
|
|
39
|
+
width: 1,
|
|
40
|
+
segments: 50,
|
|
41
|
+
closed: false,
|
|
42
|
+
tension: 0.5,
|
|
43
|
+
uvScale: 1,
|
|
44
|
+
material: '', // Asset path to material JSON file
|
|
45
|
+
showDebug: false,
|
|
46
|
+
physicsOptions: {
|
|
47
|
+
...PrimitiveComponent.DEFAULT_PHYSICS_OPTIONS,
|
|
48
|
+
enabled: false,
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
constructor(options = {}) {
|
|
53
|
+
super(options);
|
|
54
|
+
Object.defineProperty(this, "mesh", {
|
|
55
|
+
enumerable: true,
|
|
56
|
+
configurable: true,
|
|
57
|
+
writable: true,
|
|
58
|
+
value: null
|
|
59
|
+
});
|
|
60
|
+
Object.defineProperty(this, "curve", {
|
|
61
|
+
enumerable: true,
|
|
62
|
+
configurable: true,
|
|
63
|
+
writable: true,
|
|
64
|
+
value: null
|
|
65
|
+
});
|
|
66
|
+
Object.defineProperty(this, "debugGroup", {
|
|
67
|
+
enumerable: true,
|
|
68
|
+
configurable: true,
|
|
69
|
+
writable: true,
|
|
70
|
+
value: null
|
|
71
|
+
});
|
|
72
|
+
this.rebuildMesh();
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Returns the THREE.Mesh object associated with this component
|
|
76
|
+
*/
|
|
77
|
+
getMesh() {
|
|
78
|
+
return this.mesh;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Returns the underlying Catmull-Rom curve
|
|
82
|
+
*/
|
|
83
|
+
getCurve() {
|
|
84
|
+
return this.curve;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Sets new control points and rebuilds the mesh
|
|
88
|
+
*/
|
|
89
|
+
setPoints(points) {
|
|
90
|
+
this.options.points = points;
|
|
91
|
+
this.rebuildMesh();
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Sets the ribbon width and rebuilds the mesh
|
|
95
|
+
*/
|
|
96
|
+
setWidth(width) {
|
|
97
|
+
this.options.width = width;
|
|
98
|
+
this.rebuildMesh();
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Sets the segment count and rebuilds the mesh
|
|
102
|
+
*/
|
|
103
|
+
setSegments(segments) {
|
|
104
|
+
this.options.segments = segments;
|
|
105
|
+
this.rebuildMesh();
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Sets the Catmull-Rom tension and rebuilds the mesh
|
|
109
|
+
*/
|
|
110
|
+
setTension(tension) {
|
|
111
|
+
this.options.tension = tension;
|
|
112
|
+
this.rebuildMesh();
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Sets the UV scale and rebuilds the mesh
|
|
116
|
+
*/
|
|
117
|
+
setUvScale(uvScale) {
|
|
118
|
+
this.options.uvScale = uvScale;
|
|
119
|
+
this.rebuildMesh();
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Sets whether the spline is closed and rebuilds the mesh
|
|
123
|
+
*/
|
|
124
|
+
setClosed(closed) {
|
|
125
|
+
this.options.closed = closed;
|
|
126
|
+
this.rebuildMesh();
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Sets the material for the mesh
|
|
130
|
+
*/
|
|
131
|
+
setMaterial(material) {
|
|
132
|
+
this.options.material = material;
|
|
133
|
+
if (this.mesh) {
|
|
134
|
+
resourceManager.loadGenericMaterial(material).then((loadedMaterial) => {
|
|
135
|
+
if (loadedMaterial && this.mesh) {
|
|
136
|
+
this.mesh.material = loadedMaterial;
|
|
137
|
+
}
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
// Mesh doesn't exist yet - rebuild to create it with the new material
|
|
142
|
+
this.rebuildMesh();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/**
|
|
146
|
+
* Toggles debug visualization
|
|
147
|
+
*/
|
|
148
|
+
setShowDebug(show) {
|
|
149
|
+
this.options.showDebug = show;
|
|
150
|
+
this.updateDebugVisualization();
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Rebuilds the ribbon mesh based on current options
|
|
154
|
+
*/
|
|
155
|
+
rebuildMesh() {
|
|
156
|
+
// Filter out null/undefined points and ensure they are Vector3 instances
|
|
157
|
+
// (points may be plain {x,y,z} objects after deserialization)
|
|
158
|
+
const rawPoints = this.options.points ?? [];
|
|
159
|
+
const points = rawPoints
|
|
160
|
+
.filter((p) => p != null)
|
|
161
|
+
.map(p => p instanceof THREE.Vector3 ? p : new THREE.Vector3(p.x, p.y, p.z));
|
|
162
|
+
if (points.length < 2) {
|
|
163
|
+
this.clearMesh();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
// Create the Catmull-Rom curve
|
|
167
|
+
this.curve = new THREE.CatmullRomCurve3(points, this.options.closed ?? false, 'catmullrom', this.options.tension ?? 0.5);
|
|
168
|
+
const segments = this.options.segments ?? 50;
|
|
169
|
+
const width = this.options.width ?? 1;
|
|
170
|
+
const uvScale = this.options.uvScale ?? 1;
|
|
171
|
+
const halfWidth = width / 2;
|
|
172
|
+
// Generate geometry
|
|
173
|
+
const geometry = this.generateRibbonGeometry(this.curve, segments, halfWidth, uvScale);
|
|
174
|
+
// Remove old mesh if exists
|
|
175
|
+
if (this.mesh) {
|
|
176
|
+
this.mesh.removeFromParent();
|
|
177
|
+
this.mesh.geometry.dispose();
|
|
178
|
+
}
|
|
179
|
+
// Create new mesh with default material initially
|
|
180
|
+
this.mesh = new THREE.Mesh(geometry, MaterialUtils.DefaultMaterial);
|
|
181
|
+
this.mesh.setTransient(true);
|
|
182
|
+
this.bindObject3DProperties(this.mesh, ['castShadow', 'receiveShadow']);
|
|
183
|
+
this.add(this.mesh);
|
|
184
|
+
// Load material if specified
|
|
185
|
+
if (!isNode() && this.options.material) {
|
|
186
|
+
resourceManager.loadGenericMaterial(this.options.material).then((loadedMaterial) => {
|
|
187
|
+
if (loadedMaterial && this.mesh) {
|
|
188
|
+
this.mesh.material = loadedMaterial;
|
|
189
|
+
}
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
// Update debug visualization
|
|
193
|
+
this.updateDebugVisualization();
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Generates a ribbon geometry along the curve
|
|
197
|
+
*/
|
|
198
|
+
generateRibbonGeometry(curve, segments, halfWidth, uvScale) {
|
|
199
|
+
const vertices = [];
|
|
200
|
+
const normals = [];
|
|
201
|
+
const uvs = [];
|
|
202
|
+
const indices = [];
|
|
203
|
+
const totalLength = curve.getLength();
|
|
204
|
+
const up = new THREE.Vector3(0, 1, 0);
|
|
205
|
+
const tempTangent = new THREE.Vector3();
|
|
206
|
+
const tempNormal = new THREE.Vector3();
|
|
207
|
+
const tempBinormal = new THREE.Vector3();
|
|
208
|
+
// Sample points along the curve
|
|
209
|
+
for (let i = 0; i <= segments; i++) {
|
|
210
|
+
const t = i / segments;
|
|
211
|
+
const point = curve.getPointAt(t);
|
|
212
|
+
const tangent = curve.getTangentAt(t).normalize();
|
|
213
|
+
// Calculate binormal (perpendicular to tangent and up)
|
|
214
|
+
tempBinormal.crossVectors(up, tangent).normalize();
|
|
215
|
+
// Handle degenerate case when tangent is parallel to up
|
|
216
|
+
if (tempBinormal.lengthSq() < 0.001) {
|
|
217
|
+
tempBinormal.set(1, 0, 0);
|
|
218
|
+
}
|
|
219
|
+
// Calculate normal (perpendicular to tangent and binormal)
|
|
220
|
+
tempNormal.crossVectors(tangent, tempBinormal).normalize();
|
|
221
|
+
// Create two vertices at this point (left and right edges)
|
|
222
|
+
const leftPoint = point.clone().addScaledVector(tempBinormal, -halfWidth);
|
|
223
|
+
const rightPoint = point.clone().addScaledVector(tempBinormal, halfWidth);
|
|
224
|
+
vertices.push(leftPoint.x, leftPoint.y, leftPoint.z);
|
|
225
|
+
vertices.push(rightPoint.x, rightPoint.y, rightPoint.z);
|
|
226
|
+
// Normals point up
|
|
227
|
+
normals.push(tempNormal.x, tempNormal.y, tempNormal.z);
|
|
228
|
+
normals.push(tempNormal.x, tempNormal.y, tempNormal.z);
|
|
229
|
+
// UVs: U tiles along length based on world distance, V is 0-1 across width
|
|
230
|
+
const distanceAlongCurve = t * totalLength;
|
|
231
|
+
const u = distanceAlongCurve * uvScale;
|
|
232
|
+
uvs.push(u, 0); // Left edge
|
|
233
|
+
uvs.push(u, 1); // Right edge
|
|
234
|
+
}
|
|
235
|
+
// Generate indices for triangle strip (CCW winding for upward-facing normals)
|
|
236
|
+
for (let i = 0; i < segments; i++) {
|
|
237
|
+
const baseIndex = i * 2;
|
|
238
|
+
// First triangle: left[i] -> left[i+1] -> right[i]
|
|
239
|
+
indices.push(baseIndex, baseIndex + 2, baseIndex + 1);
|
|
240
|
+
// Second triangle: right[i] -> left[i+1] -> right[i+1]
|
|
241
|
+
indices.push(baseIndex + 1, baseIndex + 2, baseIndex + 3);
|
|
242
|
+
}
|
|
243
|
+
const geometry = new THREE.BufferGeometry();
|
|
244
|
+
geometry.setAttribute('position', new THREE.Float32BufferAttribute(vertices, 3));
|
|
245
|
+
geometry.setAttribute('normal', new THREE.Float32BufferAttribute(normals, 3));
|
|
246
|
+
geometry.setAttribute('uv', new THREE.Float32BufferAttribute(uvs, 2));
|
|
247
|
+
geometry.setIndex(indices);
|
|
248
|
+
return geometry;
|
|
249
|
+
}
|
|
250
|
+
/**
|
|
251
|
+
* Clears the current mesh
|
|
252
|
+
*/
|
|
253
|
+
clearMesh() {
|
|
254
|
+
if (this.mesh) {
|
|
255
|
+
this.mesh.removeFromParent();
|
|
256
|
+
this.mesh.geometry.dispose();
|
|
257
|
+
this.mesh = null;
|
|
258
|
+
}
|
|
259
|
+
this.curve = null;
|
|
260
|
+
this.clearDebugVisualization();
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Updates debug visualization showing control points and spline
|
|
264
|
+
*/
|
|
265
|
+
updateDebugVisualization() {
|
|
266
|
+
this.clearDebugVisualization();
|
|
267
|
+
if (!this.options.showDebug || !this.curve) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
this.debugGroup = new THREE.Group();
|
|
271
|
+
this.debugGroup.setTransient(true);
|
|
272
|
+
// Filter out null/undefined points and ensure they are Vector3 instances
|
|
273
|
+
const rawPoints = this.options.points ?? [];
|
|
274
|
+
const points = rawPoints
|
|
275
|
+
.filter((p) => p != null)
|
|
276
|
+
.map(p => p instanceof THREE.Vector3 ? p : new THREE.Vector3(p.x, p.y, p.z));
|
|
277
|
+
// Draw control point spheres
|
|
278
|
+
const sphereGeometry = new THREE.SphereGeometry(0.15);
|
|
279
|
+
const sphereMaterial = new THREE.MeshBasicMaterial({ color: 0xff0000 });
|
|
280
|
+
for (let i = 0; i < points.length; i++) {
|
|
281
|
+
const sphere = new THREE.Mesh(sphereGeometry, sphereMaterial);
|
|
282
|
+
sphere.position.copy(points[i]);
|
|
283
|
+
this.debugGroup.add(sphere);
|
|
284
|
+
}
|
|
285
|
+
// Draw spline curve line
|
|
286
|
+
const curvePoints = this.curve.getPoints(100);
|
|
287
|
+
const lineGeometry = new THREE.BufferGeometry().setFromPoints(curvePoints);
|
|
288
|
+
const lineMaterial = new THREE.LineBasicMaterial({ color: 0x00ff00 });
|
|
289
|
+
const line = new THREE.Line(lineGeometry, lineMaterial);
|
|
290
|
+
this.debugGroup.add(line);
|
|
291
|
+
// Draw tangent indicators at control points
|
|
292
|
+
const arrowMaterial = new THREE.LineBasicMaterial({ color: 0x0000ff });
|
|
293
|
+
for (let i = 0; i < points.length; i++) {
|
|
294
|
+
const t = this.options.closed ? i / points.length : i / (points.length - 1);
|
|
295
|
+
const tangent = this.curve.getTangentAt(Math.min(t, 1)).normalize();
|
|
296
|
+
const arrowPoints = [
|
|
297
|
+
points[i].clone(),
|
|
298
|
+
points[i].clone().addScaledVector(tangent, 0.5)
|
|
299
|
+
];
|
|
300
|
+
const arrowGeometry = new THREE.BufferGeometry().setFromPoints(arrowPoints);
|
|
301
|
+
const arrow = new THREE.Line(arrowGeometry, arrowMaterial);
|
|
302
|
+
this.debugGroup.add(arrow);
|
|
303
|
+
}
|
|
304
|
+
this.add(this.debugGroup);
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* Clears debug visualization
|
|
308
|
+
*/
|
|
309
|
+
clearDebugVisualization() {
|
|
310
|
+
if (this.debugGroup) {
|
|
311
|
+
this.debugGroup.removeFromParent();
|
|
312
|
+
this.debugGroup.traverse((child) => {
|
|
313
|
+
if (child instanceof THREE.Mesh || child instanceof THREE.Line) {
|
|
314
|
+
child.geometry.dispose();
|
|
315
|
+
if (Array.isArray(child.material)) {
|
|
316
|
+
child.material.forEach(m => m.dispose());
|
|
317
|
+
}
|
|
318
|
+
else {
|
|
319
|
+
child.material.dispose();
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
});
|
|
323
|
+
this.debugGroup = null;
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
calcBoundingBox() {
|
|
327
|
+
if (this.mesh) {
|
|
328
|
+
this.mesh.geometry.computeBoundingBox();
|
|
329
|
+
this._boundingBox.copy(this.mesh.geometry.boundingBox).applyMatrix4(this.mesh.matrixWorld);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
getEditorClassIcon() {
|
|
333
|
+
return 'Icon_Path';
|
|
334
|
+
}
|
|
335
|
+
onEditorPropertyChanged(path, value, result) {
|
|
336
|
+
super.onEditorPropertyChanged(path, value, result);
|
|
337
|
+
// Rebuild mesh when any spline-related property changes
|
|
338
|
+
if (result.isOptions) {
|
|
339
|
+
if (path === 'material') {
|
|
340
|
+
this.setMaterial(value);
|
|
341
|
+
}
|
|
342
|
+
else if (path === 'points' || path.startsWith('points[') || path.startsWith('points.')) {
|
|
343
|
+
// Handle array changes: full replacement, element changes, or property changes
|
|
344
|
+
this.rebuildMesh();
|
|
345
|
+
}
|
|
346
|
+
else if (['width', 'segments', 'tension', 'uvScale', 'closed', 'showDebug'].includes(path)) {
|
|
347
|
+
this.rebuildMesh();
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
};
|
|
352
|
+
Object.defineProperty(SplineMeshComponent, "EDITOR_CLASS_META", {
|
|
353
|
+
enumerable: true,
|
|
354
|
+
configurable: true,
|
|
355
|
+
writable: true,
|
|
356
|
+
value: {
|
|
357
|
+
...PrimitiveComponent.EDITOR_CLASS_META,
|
|
358
|
+
points: { array: { child: { type: THREE.Vector3 } } },
|
|
359
|
+
width: { number: { min: 0.1, max: 100, step: 0.1, decimals: 2 } },
|
|
360
|
+
segments: { integer: { min: 2, max: 500, step: 1 } },
|
|
361
|
+
tension: { number: { min: 0, max: 1, step: 0.05, decimals: 2 } },
|
|
362
|
+
uvScale: { number: { min: 0.01, max: 10, step: 0.1, decimals: 2 } },
|
|
363
|
+
closed: { boolean: {} },
|
|
364
|
+
showDebug: { boolean: {} },
|
|
365
|
+
material: { type: ['material'] },
|
|
366
|
+
}
|
|
367
|
+
});
|
|
368
|
+
SplineMeshComponent = __decorate([
|
|
369
|
+
EngineClass('SplineMeshComponent'),
|
|
370
|
+
__metadata("design:paramtypes", [Object])
|
|
371
|
+
], SplineMeshComponent);
|
|
372
|
+
export { SplineMeshComponent };
|
|
373
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU3BsaW5lTWVzaENvbXBvbmVudC5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uLy4uL3NyYy9jb21wb25lbnRzL3Zpc3VhbC9TcGxpbmVNZXNoQ29tcG9uZW50LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7OztBQUFBOzs7Ozs7Ozs7Ozs7R0FZRztBQUNILE9BQU8sS0FBSyxLQUFLLE1BQU0sT0FBTyxDQUFDO0FBRS9CLE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxvQkFBb0IsQ0FBQztBQUNuRCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDN0QsT0FBTyxFQUFFLGVBQWUsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBQ2pFLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxzQkFBc0IsQ0FBQztBQUM5QyxPQUFPLEVBQUUsa0JBQWtCLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQTBCdkQsSUFBTSxtQkFBbUIsR0FBekIsTUFBTSxtQkFBb0IsU0FBUSxrQkFBOEM7SUFpQnJGLE1BQU0sS0FBYyxlQUFlO1FBQ2pDLE9BQU87WUFDTCxHQUFHLGtCQUFrQixDQUFDLGVBQWU7WUFDckMsdUVBQXVFO1lBQ3ZFLE1BQU0sRUFBRTtnQkFDTixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztnQkFDM0IsSUFBSSxLQUFLLENBQUMsT0FBTyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDO2dCQUMxQixJQUFJLEtBQUssQ0FBQyxPQUFPLENBQUMsQ0FBQyxFQUFFLENBQUMsRUFBRSxDQUFDLENBQUM7YUFDM0I7WUFDRCxLQUFLLEVBQUUsQ0FBQztZQUNSLFFBQVEsRUFBRSxFQUFFO1lBQ1osTUFBTSxFQUFFLEtBQUs7WUFDYixPQUFPLEVBQUUsR0FBRztZQUNaLE9BQU8sRUFBRSxDQUFDO1lBQ1YsUUFBUSxFQUFFLEVBQUUsRUFBRyxtQ0FB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
|
|
@@ -21,4 +21,5 @@ export * from './TerrainMeshComponent.js';
|
|
|
21
21
|
export * from './SkyboxComponent.js';
|
|
22
22
|
export * from './FogComponent.js';
|
|
23
23
|
export * from './UI3DComponent.js';
|
|
24
|
-
|
|
24
|
+
export * from './SplineMeshComponent.js';
|
|
25
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aXN1YWwvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFDSCxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsaUNBQWlDLENBQUM7QUFDaEQsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYyxxQ0FBcUMsQ0FBQztBQUNwRCxjQUFjLGlDQUFpQyxDQUFDO0FBQ2hELGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYywwQkFBMEIsQ0FBQyJ9
|
package/dist/test/prefab.test.js
CHANGED
|
@@ -53,7 +53,7 @@ QUnit.module('Prefab', hooks => {
|
|
|
53
53
|
for (let index = 0; index < 4; index++) {
|
|
54
54
|
const instanceRootData = [index === 0 ? -123 : undefined, index === 1 ? -456 : undefined];
|
|
55
55
|
const instanceComponentData = [index === 2 ? -789 : undefined, index === 3 ? -101 : undefined];
|
|
56
|
-
//
|
|
56
|
+
// load an instance actor then modify its properties
|
|
57
57
|
const instanceActor = await ENGINE.Actor.spawnPrefabActor(prefabName, {
|
|
58
58
|
position: instancePosition,
|
|
59
59
|
rotation: instanceRotation,
|
package/games/index.ts
CHANGED
|
@@ -12,6 +12,7 @@ export * from './performance-test-demo.js';
|
|
|
12
12
|
export * from './postprocess-demo.js';
|
|
13
13
|
export * from './ragdoll-demo-simple.js';
|
|
14
14
|
export * from './ragdoll-demo.js';
|
|
15
|
+
export * from './spline-mesh-demo.js';
|
|
15
16
|
export * from './swipe-tap-demo.js';
|
|
16
17
|
export * from './tween-demo.js';
|
|
17
18
|
export * from './ui-demo.js';
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import * as ENGINE from '@directivegames/genesys.js';
|
|
2
|
+
import * as THREE from 'three';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* SplineMeshDemo showcases the SplineMeshComponent.
|
|
6
|
+
*
|
|
7
|
+
* Demonstrates:
|
|
8
|
+
* - Creating a road-like path using control points
|
|
9
|
+
* - Debug visualization of control points and spline curve
|
|
10
|
+
* - UV tiling for texture mapping
|
|
11
|
+
*/
|
|
12
|
+
export class SplineMeshDemo extends ENGINE.BaseGameLoop {
|
|
13
|
+
private splineActor: ENGINE.Actor | null = null;
|
|
14
|
+
private splineComponent: ENGINE.SplineMeshComponent | null = null;
|
|
15
|
+
|
|
16
|
+
protected override async preStart(): Promise<void> {
|
|
17
|
+
// Setup camera
|
|
18
|
+
const { pawn } = ENGINE.GameBuilder.createDefaultPawn(this.world, new THREE.Vector3(0, 15, 20));
|
|
19
|
+
const camera = pawn.getComponent(THREE.Camera)!;
|
|
20
|
+
camera.position.set(0, 15, 20);
|
|
21
|
+
camera.lookAt(0, 0, 0);
|
|
22
|
+
|
|
23
|
+
// Setup lighting
|
|
24
|
+
ENGINE.GameBuilder.createDefaultLighting(this.world);
|
|
25
|
+
|
|
26
|
+
// Create ground plane with grass color
|
|
27
|
+
const groundMaterial = new THREE.MeshStandardMaterial({ color: 0x228833 });
|
|
28
|
+
const groundGeometry = new THREE.BoxGeometry(50, 0.1, 50);
|
|
29
|
+
const ground = new ENGINE.Actor({
|
|
30
|
+
rootComponent: new ENGINE.MeshComponent({
|
|
31
|
+
position: new THREE.Vector3(0, -0.1, 0),
|
|
32
|
+
geometry: groundGeometry,
|
|
33
|
+
material: groundMaterial,
|
|
34
|
+
})
|
|
35
|
+
});
|
|
36
|
+
this.world.addActor(ground);
|
|
37
|
+
|
|
38
|
+
// Create a winding road path
|
|
39
|
+
this.createRoadPath();
|
|
40
|
+
|
|
41
|
+
// Create a river path
|
|
42
|
+
this.createRiverPath();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private createRoadPath(): void {
|
|
46
|
+
// Define control points for a winding road
|
|
47
|
+
const roadPoints = [
|
|
48
|
+
new THREE.Vector3(-15, 0, -10),
|
|
49
|
+
new THREE.Vector3(-8, 0, -5),
|
|
50
|
+
new THREE.Vector3(-3, 0, 0),
|
|
51
|
+
new THREE.Vector3(0, 0, 5),
|
|
52
|
+
new THREE.Vector3(5, 0, 3),
|
|
53
|
+
new THREE.Vector3(10, 0, -2),
|
|
54
|
+
new THREE.Vector3(15, 0, 0),
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
// Create the spline mesh component with a texture
|
|
58
|
+
this.splineComponent = new ENGINE.SplineMeshComponent({
|
|
59
|
+
points: roadPoints,
|
|
60
|
+
width: 3,
|
|
61
|
+
segments: 80,
|
|
62
|
+
tension: 0.3,
|
|
63
|
+
uvScale: 0.5,
|
|
64
|
+
material: '@engine/assets/dev/materials/RoadAsphalt.json',
|
|
65
|
+
showDebug: true, // Enable debug visualization
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Create actor and add component
|
|
69
|
+
this.splineActor = new ENGINE.Actor();
|
|
70
|
+
this.splineActor.rootComponent.add(this.splineComponent);
|
|
71
|
+
this.world.addActor(this.splineActor);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
private createRiverPath(): void {
|
|
75
|
+
// Define control points for a river
|
|
76
|
+
const riverPoints = [
|
|
77
|
+
new THREE.Vector3(-12, 0.01, 8),
|
|
78
|
+
new THREE.Vector3(-6, 0.01, 12),
|
|
79
|
+
new THREE.Vector3(0, 0.01, 10),
|
|
80
|
+
new THREE.Vector3(6, 0.01, 14),
|
|
81
|
+
new THREE.Vector3(12, 0.01, 12),
|
|
82
|
+
];
|
|
83
|
+
|
|
84
|
+
// Create river material with blue color
|
|
85
|
+
const riverMaterial = new THREE.MeshStandardMaterial({
|
|
86
|
+
color: 0x2266aa,
|
|
87
|
+
roughness: 0.2,
|
|
88
|
+
metalness: 0.3,
|
|
89
|
+
transparent: true,
|
|
90
|
+
opacity: 0.8,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Create the spline mesh component for river
|
|
94
|
+
const riverComponent = new ENGINE.SplineMeshComponent({
|
|
95
|
+
points: riverPoints,
|
|
96
|
+
width: 2,
|
|
97
|
+
segments: 60,
|
|
98
|
+
tension: 0.5,
|
|
99
|
+
uvScale: 1,
|
|
100
|
+
material: riverMaterial,
|
|
101
|
+
showDebug: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
// Create actor and add component
|
|
105
|
+
const riverActor = new ENGINE.Actor();
|
|
106
|
+
riverActor.rootComponent.add(riverComponent);
|
|
107
|
+
this.world.addActor(riverActor);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
public override destroy(): void {
|
|
111
|
+
if (this.splineActor) {
|
|
112
|
+
this.splineActor.destroy();
|
|
113
|
+
this.splineActor = null;
|
|
114
|
+
}
|
|
115
|
+
this.splineComponent = null;
|
|
116
|
+
super.destroy();
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
package/package.json
CHANGED
package/src/actors/Actor.ts
CHANGED
|
@@ -196,6 +196,7 @@ export class Actor<TOptions extends ActorOptions = ActorOptions> extends Playabl
|
|
|
196
196
|
constructor(readonly options: TOptions = {} as TOptions) {
|
|
197
197
|
super();
|
|
198
198
|
this.ctorArgOptions = options;
|
|
199
|
+
|
|
199
200
|
options = { ...Actor.DEFAULT_OPTIONS, ...options, };
|
|
200
201
|
this.name = ObjectNamingSystem.generateName(this);
|
|
201
202
|
this.actorTags = options.actorTags ? [...options.actorTags] : [];
|
|
@@ -236,6 +237,13 @@ export class Actor<TOptions extends ActorOptions = ActorOptions> extends Playabl
|
|
|
236
237
|
if (legacyPrefabName) {
|
|
237
238
|
this.prefabName = legacyPrefabName;
|
|
238
239
|
}
|
|
240
|
+
|
|
241
|
+
// HACK copy the ctorArgOptions and delete the transform data, so they get populated by the data stored in root component when loaded
|
|
242
|
+
// and also this won't mutate the original `options` passed in
|
|
243
|
+
this.ctorArgOptions = {...this.ctorArgOptions};
|
|
244
|
+
delete this.ctorArgOptions.position;
|
|
245
|
+
delete this.ctorArgOptions.rotation;
|
|
246
|
+
delete this.ctorArgOptions.scale;
|
|
239
247
|
}
|
|
240
248
|
|
|
241
249
|
/**
|
|
@@ -263,7 +271,7 @@ export class Actor<TOptions extends ActorOptions = ActorOptions> extends Playabl
|
|
|
263
271
|
}
|
|
264
272
|
|
|
265
273
|
/**
|
|
266
|
-
*
|
|
274
|
+
* @deprecated deprecated, use $$prefab, setAsPrefabInstance instead
|
|
267
275
|
* @param name The prefab name
|
|
268
276
|
* @param data The prefab data
|
|
269
277
|
* @param inheritanceChain The full prefab inheritance chain (optional)
|
|
@@ -277,7 +285,7 @@ export class Actor<TOptions extends ActorOptions = ActorOptions> extends Playabl
|
|
|
277
285
|
}
|
|
278
286
|
|
|
279
287
|
/**
|
|
280
|
-
* Clears the prefab name and data of this actor
|
|
288
|
+
* @deprecated Clears the prefab name and data of this actor. Use setAsPrefabInstance(null) instead
|
|
281
289
|
* @returns This actor instance for method chaining
|
|
282
290
|
*/
|
|
283
291
|
public clearPrefab(): this {
|