@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.
@@ -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,3 +21,4 @@ export * from './TerrainMeshComponent.js';
21
21
  export * from './SkyboxComponent.js';
22
22
  export * from './FogComponent.js';
23
23
  export * from './UI3DComponent.js';
24
+ export * from './SplineMeshComponent.js';
@@ -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
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aXN1YWwvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFDSCxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsaUNBQWlDLENBQUM7QUFDaEQsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYyxxQ0FBcUMsQ0FBQztBQUNwRCxjQUFjLGlDQUFpQyxDQUFDO0FBQ2hELGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsb0JBQW9CLENBQUMifQ==
24
+ export * from './SplineMeshComponent.js';
25
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi8uLi9zcmMvY29tcG9uZW50cy92aXN1YWwvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7Ozs7Ozs7O0dBUUc7QUFDSCxjQUFjLHlCQUF5QixDQUFDO0FBQ3hDLGNBQWMsaUNBQWlDLENBQUM7QUFDaEQsY0FBYyw4QkFBOEIsQ0FBQztBQUM3QyxjQUFjLHdCQUF3QixDQUFDO0FBQ3ZDLGNBQWMseUJBQXlCLENBQUM7QUFDeEMsY0FBYyxxQ0FBcUMsQ0FBQztBQUNwRCxjQUFjLGlDQUFpQyxDQUFDO0FBQ2hELGNBQWMsNkJBQTZCLENBQUM7QUFDNUMsY0FBYyxvQkFBb0IsQ0FBQztBQUNuQyxjQUFjLCtCQUErQixDQUFDO0FBQzlDLGNBQWMsMkJBQTJCLENBQUM7QUFDMUMsY0FBYyxzQkFBc0IsQ0FBQztBQUNyQyxjQUFjLG1CQUFtQixDQUFDO0FBQ2xDLGNBQWMsb0JBQW9CLENBQUM7QUFDbkMsY0FBYywwQkFBMEIsQ0FBQyJ9
@@ -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
- // laod an instance actor them modify its properties
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@directivegames/genesys.js",
3
- "version": "3.1.28",
3
+ "version": "3.1.30",
4
4
  "description": "Mini game engine built on top of three.js",
5
5
  "license": "UNLICENSED",
6
6
  "main": "./dist/src/index.js",
@@ -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
- * Sets the prefab name and data of this actor
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 {