@woosh/meep-engine 2.35.9 → 2.35.10

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.
@@ -9,6 +9,9 @@ export class Camera {
9
9
 
10
10
  autoClip: boolean
11
11
 
12
+ clip_near: number
13
+ clip_far: number
14
+
12
15
  computeProjectionMatrix(result: Matrix4): boolean
13
16
 
14
17
  projectWorldPointOntoNearPlane(x: number, y: number, z: number, result: Vector3): void
@@ -26,7 +26,7 @@ export const ProjectionType = {
26
26
  export class Camera {
27
27
  constructor() {
28
28
  /**
29
- *
29
+ * Whether clipping planes should be automatically calculated or not
30
30
  * @type {boolean}
31
31
  */
32
32
  this.autoClip = true;
@@ -56,6 +56,44 @@ export class Camera {
56
56
  * @type {ObservedBoolean}
57
57
  */
58
58
  this.active = new ObservedBoolean(false);
59
+
60
+ /**
61
+ * Near clipping plane
62
+ * @type {number}
63
+ * @private
64
+ */
65
+ this.__clip_far = 0;
66
+
67
+ /**
68
+ * Far clipping plane
69
+ * @type {number}
70
+ * @private
71
+ */
72
+ this.__clip_near = 0.1;
73
+ }
74
+
75
+ get clip_far() {
76
+ return this.__clip_far;
77
+ }
78
+
79
+ set clip_far(v) {
80
+ this.__clip_far = v;
81
+
82
+ if (this.object !== null) {
83
+ this.object.far = v;
84
+ }
85
+ }
86
+
87
+ get clip_near() {
88
+ return this.__clip_near;
89
+ }
90
+
91
+ set clip_near(v) {
92
+ this.__clip_near = v;
93
+
94
+ if (this.object !== null) {
95
+ this.object.near = v;
96
+ }
59
97
  }
60
98
 
61
99
  updateMatrices() {
@@ -9,7 +9,6 @@ import { Transform } from '../../../ecs/transform/Transform.js';
9
9
  import {
10
10
  Frustum as ThreeFrustum,
11
11
  Matrix4,
12
- Matrix4 as ThreeMatrix4,
13
12
  OrthographicCamera as ThreeOrthographicCamera,
14
13
  PerspectiveCamera as ThreePerspectiveCamera,
15
14
  Vector3 as ThreeVector3,
@@ -76,16 +75,25 @@ function setAspectRatio(camera, width, height) {
76
75
  * @returns {PerspectiveCamera| OrthographicCamera}
77
76
  */
78
77
  function buildThreeCamera(component) {
78
+
79
+ let result;
79
80
  switch (component.projectionType.getValue()) {
80
81
  default:
81
82
  console.error(`Unsupported camera projection type "${component.projectionType.getValue()}", defaulting to perspective projection`);
82
83
  //fallthrough
83
84
  case Camera.ProjectionType.Perspective:
84
- return new ThreePerspectiveCamera(component.fov.getValue(), 1, 1, 50);
85
-
85
+ result = new ThreePerspectiveCamera(component.fov.getValue(), 1, 1, 50);
86
+ break;
86
87
  case Camera.ProjectionType.Orthographic:
87
- return new ThreeOrthographicCamera(-10, 10, 20, -20, 1, 1);
88
+ result = new ThreeOrthographicCamera(-10, 10, 20, -20, 1, 1);
89
+ break;
88
90
  }
91
+
92
+
93
+ result.far = component.clip_far;
94
+ result.near = component.clip_near;
95
+
96
+ return result;
89
97
  }
90
98
 
91
99
  export class CameraSystem extends System {
@@ -289,8 +297,19 @@ export class CameraSystem extends System {
289
297
  * @param {Camera} c
290
298
  */
291
299
  function visitCameraEntity(c) {
292
- if (c.active.getValue() && c.autoClip && c.object !== null) {
300
+ if (c.object !== null) {
301
+ return;
302
+ }
303
+
304
+ if (!c.active.getValue()) {
305
+ return;
306
+ }
307
+
308
+ if (c.autoClip) {
293
309
  autoSetClippingPlanes(c, layers);
310
+ } else {
311
+ c.object.far = c.clip_far;
312
+ c.object.near = c.clip_near;
294
313
  }
295
314
  }
296
315
 
@@ -376,7 +395,7 @@ export class CameraSystem extends System {
376
395
  }
377
396
  }
378
397
 
379
- const matrix4 = new ThreeMatrix4();
398
+ const matrix4 = new Matrix4();
380
399
 
381
400
  /**
382
401
  *
@@ -10,7 +10,7 @@ import { RibbonXPlugin } from "../../trail/x/RibbonXPlugin.js";
10
10
  import { RibbonPathBuilder } from "./ribbon/RibbonPathBuilder.js";
11
11
  import { PathEvents } from "../../../navigation/ecs/components/PathEvents.js";
12
12
  import { assert } from "../../../../core/assert.js";
13
- import { TubePathBuilder } from "./tube/TubePathBuilder.js";
13
+ import { TubePathBuilder } from "./tube/build/TubePathBuilder.js";
14
14
 
15
15
  const builders = {
16
16
  [PathDisplayType.None]: function (style, path, result) {
@@ -1,5 +1,4 @@
1
1
  import { EngineHarness } from "../../../EngineHarness.js";
2
- import RenderSystem from "../../../ecs/systems/RenderSystem.js";
3
2
  import { makeEngineOptionsModel } from "../../../../../model/game/options/makeEngineOptionsModel.js";
4
3
  import Path from "../../../navigation/ecs/components/Path.js";
5
4
  import Vector3 from "../../../../core/geom/Vector3.js";
@@ -68,6 +67,7 @@ import { TubePathStyle } from "./tube/TubePathStyle.js";
68
67
  import { TubeMaterialType } from "./tube/TubeMaterialType.js";
69
68
  import { MatcapMaterialDefinition } from "./tube/MatcapMaterialDefinition.js";
70
69
  import { TextureAssetLoader } from "../../../asset/loaders/texture/TextureAssetLoader.js";
70
+ import { ShadedGeometrySystem } from "../mesh-v2/ShadedGeometrySystem.js";
71
71
 
72
72
  const engineHarness = new EngineHarness();
73
73
 
@@ -156,7 +156,8 @@ function makeConfig(engine) {
156
156
  async function init(harness) {
157
157
  const engine = harness.engine;
158
158
 
159
- engine.entityManager.addSystem(new RenderSystem(engine.graphics));
159
+ // engine.entityManager.addSystem(new RenderSystem(engine.graphics));
160
+ engine.entityManager.addSystem(new ShadedGeometrySystem(engine));
160
161
 
161
162
  makeEngineOptionsModel(engine.options, engine);
162
163
 
@@ -323,8 +324,8 @@ function main(engine) {
323
324
  EngineHarness.buildBasics({ engine });
324
325
 
325
326
  const _p = new Path();
326
- // _p.interpolation = InterpolationType.CatmullRom;
327
- _p.interpolation = InterpolationType.Linear;
327
+ _p.interpolation = InterpolationType.CatmullRom;
328
+ // _p.interpolation = InterpolationType.Linear;
328
329
 
329
330
  // _p.setPositionsFromVectorArray(Path.smoothPath([
330
331
  // new Vector3(3, 1, 1),
@@ -352,14 +353,14 @@ function main(engine) {
352
353
 
353
354
  const pathDisplay = new PathDisplay();
354
355
  pathDisplay.specs.push(
355
- // PathDisplaySpec.from(
356
- // PathDisplayType.Ribbon,
357
- // trailPathStyle
358
- // ),
359
- // PathDisplaySpec.from(
360
- // PathDisplayType.Entity,
361
- // sample_style_entity()
362
- // ),
356
+ PathDisplaySpec.from(
357
+ PathDisplayType.Ribbon,
358
+ trailPathStyle
359
+ ),
360
+ PathDisplaySpec.from(
361
+ PathDisplayType.Entity,
362
+ sample_style_entity()
363
+ ),
363
364
  PathDisplaySpec.from(
364
365
  PathDisplayType.Tube,
365
366
  sample_style_tube()
@@ -11,4 +11,6 @@ export class TubePathStyle {
11
11
  public width: number
12
12
  public resolution: number
13
13
  public radial_resolution: number
14
+ public cast_shadow: boolean
15
+ public receive_shadow: boolean
14
16
  }
@@ -5,8 +5,15 @@ import { MatcapMaterialDefinition } from "./MatcapMaterialDefinition.js";
5
5
 
6
6
  export class TubePathStyle {
7
7
  constructor() {
8
+ /**
9
+ *
10
+ * @type {Color}
11
+ */
8
12
  this.color = new Color(1, 1, 1);
9
13
 
14
+ /**
15
+ * @type {TubeMaterialType}
16
+ */
10
17
  this.material_type = TubeMaterialType.Unknown;
11
18
 
12
19
  /**
@@ -15,13 +22,43 @@ export class TubePathStyle {
15
22
  */
16
23
  this.material = null;
17
24
 
25
+ /**
26
+ *
27
+ * @type {number}
28
+ */
18
29
  this.opacity = 1;
30
+
31
+ /**
32
+ *
33
+ * @type {number}
34
+ */
19
35
  this.width = 1;
20
36
 
21
37
  // TODO expose shading style "flat/smooth"
22
38
 
39
+ /**
40
+ *
41
+ * @type {number}
42
+ */
23
43
  this.resolution = 10;
44
+
45
+ /**
46
+ *
47
+ * @type {number}
48
+ */
24
49
  this.radial_resolution = 16;
50
+
51
+ /**
52
+ *
53
+ * @type {boolean}
54
+ */
55
+ this.cast_shadow = false;
56
+
57
+ /**
58
+ *
59
+ * @type {boolean}
60
+ */
61
+ this.receive_shadow = false;
25
62
  }
26
63
 
27
64
  static fromJSON(j) {
@@ -39,7 +76,9 @@ export class TubePathStyle {
39
76
  opacity = 1,
40
77
  width = 1,
41
78
  resolution = 10,
42
- radial_resolution = 16
79
+ radial_resolution = 16,
80
+ cast_shadow = false,
81
+ receive_shadow = false
43
82
  }) {
44
83
  assert.enum(material_type, TubeMaterialType, 'material_type');
45
84
 
@@ -55,6 +94,9 @@ export class TubePathStyle {
55
94
  this.resolution = resolution;
56
95
  this.radial_resolution = radial_resolution;
57
96
 
97
+ this.cast_shadow = cast_shadow;
98
+ this.receive_shadow = receive_shadow;
99
+
58
100
  switch (material_type) {
59
101
  case TubeMaterialType.Matcap:
60
102
  this.material = new MatcapMaterialDefinition();
@@ -0,0 +1,138 @@
1
+ import { BufferGeometry, MeshMatcapMaterial, MeshStandardMaterial, Vector3 } from "three";
2
+ import { InterpolationType } from "../../../../../navigation/ecs/components/InterpolationType.js";
3
+ import EntityBuilder from "../../../../../ecs/EntityBuilder.js";
4
+ import { Transform } from "../../../../../ecs/transform/Transform.js";
5
+ import { TubeMaterialType } from "../TubeMaterialType.js";
6
+ import { GameAssetType } from "../../../../../asset/GameAssetType.js";
7
+ import { build_geometry_linear } from "./build_geometry_linear.js";
8
+ import { build_geometry_catmullrom } from "./build_geometry_catmullrom.js";
9
+ import { ShadedGeometry } from "../../../mesh-v2/ShadedGeometry.js";
10
+ import { ShadedGeometryFlags } from "../../../mesh-v2/ShadedGeometryFlags.js";
11
+
12
+ export class TubePathBuilder {
13
+ constructor() {
14
+ /**
15
+ *
16
+ * @type {TubePathStyle|null}
17
+ */
18
+ this.style = null;
19
+
20
+ /**
21
+ *
22
+ * @type {Path|null}
23
+ */
24
+ this.path = null;
25
+
26
+ /**
27
+ *
28
+ * @type {MaterialManager|null}
29
+ */
30
+ this.materials = null;
31
+
32
+ /**
33
+ *
34
+ * @type {AssetManager|null}
35
+ */
36
+ this.assetManager = null;
37
+ }
38
+
39
+ setPath(path) {
40
+ this.path = path;
41
+ }
42
+
43
+ setStyle(style) {
44
+ this.style = style;
45
+ }
46
+
47
+ setMaterials(m) {
48
+ this.materials = m;
49
+ }
50
+
51
+ setAssetManager(assetManager) {
52
+ this.assetManager = assetManager;
53
+ }
54
+
55
+ /**
56
+ *
57
+ * @param {EntityBuilder[]} destination
58
+ */
59
+ build(destination) {
60
+ const style = this.style;
61
+
62
+ // generate three.js curve from path
63
+ const path_component = this.path;
64
+
65
+ const three_points = [];
66
+
67
+ const pointCount = path_component.getPointCount();
68
+
69
+ if (pointCount < 2) {
70
+ // no curve possible without at least 2 points
71
+ return;
72
+ }
73
+
74
+ for (let i = 0; i < pointCount; i++) {
75
+ const v3 = new Vector3();
76
+
77
+ path_component.getPosition(i, v3);
78
+
79
+ three_points.push(v3);
80
+ }
81
+
82
+
83
+ /**
84
+ * @type {THREE.BufferGeometry}
85
+ */
86
+ let geometry;
87
+
88
+ if (path_component.interpolation === InterpolationType.Linear) {
89
+ geometry = build_geometry_linear(three_points, pointCount, style);
90
+ } else if (path_component.interpolation === InterpolationType.CatmullRom) {
91
+ geometry = build_geometry_catmullrom(three_points, pointCount, style);
92
+ } else {
93
+ throw new Error(`Unsupported interpolation type '${path_component.interpolation}'`);
94
+ }
95
+
96
+
97
+ let material_def;
98
+
99
+ if (style.material_type === TubeMaterialType.Standard) {
100
+ material_def = new MeshStandardMaterial({
101
+ metalness: style.material.metalness,
102
+ roughness: style.material.roughness
103
+ });
104
+ } else if (style.material_type === TubeMaterialType.Matcap) {
105
+
106
+ material_def = new MeshMatcapMaterial();
107
+
108
+ this.assetManager.get(style.material.texture, GameAssetType.Texture, asset => {
109
+ material_def.matcap = asset.create();
110
+
111
+ material_def.needsUpdate = true;
112
+ }, console.error);
113
+
114
+ } else {
115
+ throw new Error(`Unsupported material type '${style.material_type}'`);
116
+ }
117
+
118
+ material_def.color.set(style.color.toUint());
119
+
120
+ if (style.opacity < 1) {
121
+ material_def.transparent = true;
122
+ material_def.opacity = style.opacity;
123
+ }
124
+
125
+ const entityBuilder = new EntityBuilder();
126
+
127
+ entityBuilder.add(new Transform());
128
+
129
+ const shaded_geometry = ShadedGeometry.from(geometry, material_def);
130
+
131
+ shaded_geometry.writeFlag(ShadedGeometryFlags.CastShadow, style.cast_shadow);
132
+ shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow);
133
+
134
+ entityBuilder.add(shaded_geometry);
135
+
136
+ destination.push(entityBuilder);
137
+ }
138
+ }
@@ -0,0 +1,32 @@
1
+ import { CatmullRomCurve3 } from "three";
2
+ import { computeFrenetFrames } from "./computeFrenetFrames.js";
3
+ import { makeTubeGeometry } from "./makeTubeGeometry.js";
4
+
5
+ /**
6
+ *
7
+ * @param {THREE.Vector3[]} points
8
+ * @param {number} point_count
9
+ * @param {TubePathStyle} style
10
+ * @return {THREE.BufferGeometry}
11
+ */
12
+ export function build_geometry_catmullrom(points, point_count, style) {
13
+
14
+ const path = new CatmullRomCurve3(points, false, "centripetal");
15
+
16
+ const sample_points = [];
17
+
18
+ // resample curve
19
+ const total_points = Math.ceil(point_count * style.resolution);
20
+
21
+ for (let i = 0; i < total_points; i++) {
22
+ const t = i / (total_points - 1);
23
+
24
+ const point = path.getPoint(t);
25
+
26
+ sample_points.push(point);
27
+ }
28
+
29
+ const frames = computeFrenetFrames(sample_points, false);
30
+
31
+ return makeTubeGeometry(sample_points, frames.normals, frames.binormals, style.width, style.radial_resolution, false);
32
+ }
@@ -0,0 +1,16 @@
1
+ import { computeFrenetFrames } from "./computeFrenetFrames.js";
2
+ import { makeTubeGeometry } from "./makeTubeGeometry.js";
3
+
4
+ /**
5
+ *
6
+ * @param {THREE.Vector3[]} points
7
+ * @param {number} point_count
8
+ * @param {TubePathStyle} style
9
+ * @return {THREE.BufferGeometry}
10
+ */
11
+ export function build_geometry_linear(points, point_count, style) {
12
+
13
+ const frames = computeFrenetFrames(points, false);
14
+
15
+ return makeTubeGeometry(points, frames.normals, frames.binormals, style.width, style.radial_resolution, false);
16
+ }
@@ -0,0 +1,153 @@
1
+ import { Vector3 } from "three";
2
+ import { Matrix4 } from "three/src/math/Matrix4.js";
3
+ import { max2 } from "../../../../../../core/math/max2.js";
4
+ import { min2 } from "../../../../../../core/math/min2.js";
5
+ import * as MathUtils from "three/src/math/MathUtils.js";
6
+
7
+ /**
8
+ * @see https://github.com/mrdoob/three.js/blob/c12c9a166a1369cdd58622fff2aff7e3a84305d7/src/extras/core/Curve.js#L260
9
+ * @param {Vector3[]} points
10
+ * @param {boolean} [closed]
11
+ */
12
+ export function computeFrenetFrames(points, closed = false) {
13
+ // see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
14
+
15
+ const normal = new Vector3();
16
+
17
+ const tangents = [];
18
+ const normals = [];
19
+ const binormals = [];
20
+
21
+ const vec = new Vector3();
22
+ const mat = new Matrix4();
23
+
24
+ const segments = points.length - 1;
25
+
26
+ const v3_0 = new Vector3();
27
+ const v3_1 = new Vector3();
28
+
29
+ /**
30
+ *
31
+ * @param {number} i
32
+ * @param {Vector3} destination
33
+ */
34
+ function getTangent(i, destination) {
35
+ // get points on either side
36
+ const i0 = max2(i - 1, 0);
37
+ const i1 = min2(i + 1, segments);
38
+
39
+ const p0 = points[i0];
40
+ const pR = points[i];
41
+ const p1 = points[i1];
42
+
43
+ // get points along the lines close to the reference
44
+ v3_0.subVectors(p0, pR);
45
+ v3_0.normalize();
46
+
47
+ v3_1.subVectors(p1, pR);
48
+ v3_1.normalize();
49
+
50
+ destination.subVectors(v3_1, v3_0);
51
+ destination.normalize();
52
+ }
53
+
54
+ // compute the tangent vectors for each segment on the curve
55
+
56
+ for (let i = 0; i <= segments; i++) {
57
+
58
+ const tangent = new Vector3();
59
+ getTangent(i, tangent);
60
+
61
+ tangents[i] = tangent;
62
+
63
+ }
64
+
65
+ // select an initial normal vector perpendicular to the first tangent vector,
66
+ // and in the direction of the minimum tangent xyz component
67
+
68
+ normals[0] = new Vector3();
69
+ binormals[0] = new Vector3();
70
+ let min = Number.MAX_VALUE;
71
+ const tx = Math.abs(tangents[0].x);
72
+ const ty = Math.abs(tangents[0].y);
73
+ const tz = Math.abs(tangents[0].z);
74
+
75
+ if (tx <= min) {
76
+
77
+ min = tx;
78
+ normal.set(1, 0, 0);
79
+
80
+ }
81
+
82
+ if (ty <= min) {
83
+
84
+ min = ty;
85
+ normal.set(0, 1, 0);
86
+
87
+ }
88
+
89
+ if (tz <= min) {
90
+
91
+ normal.set(0, 0, 1);
92
+
93
+ }
94
+
95
+ vec.crossVectors(tangents[0], normal).normalize();
96
+
97
+ normals[0].crossVectors(tangents[0], vec);
98
+ binormals[0].crossVectors(tangents[0], normals[0]);
99
+
100
+
101
+ // compute the slowly-varying normal and binormal vectors for each segment on the curve
102
+
103
+ for (let i = 1; i <= segments; i++) {
104
+
105
+ normals[i] = normals[i - 1].clone();
106
+
107
+ binormals[i] = binormals[i - 1].clone();
108
+
109
+ vec.crossVectors(tangents[i - 1], tangents[i]);
110
+
111
+ if (vec.length() > Number.EPSILON) {
112
+
113
+ vec.normalize();
114
+
115
+ const theta = Math.acos(MathUtils.clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
116
+
117
+ normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
118
+
119
+ }
120
+
121
+ binormals[i].crossVectors(tangents[i], normals[i]);
122
+
123
+ }
124
+
125
+ // if the curve is closed, postprocess the vectors so the first and last normal vectors are the same
126
+
127
+ if (closed === true) {
128
+
129
+ let theta = Math.acos(MathUtils.clamp(normals[0].dot(normals[segments]), -1, 1));
130
+ theta /= segments;
131
+
132
+ if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
133
+
134
+ theta = -theta;
135
+
136
+ }
137
+
138
+ for (let i = 1; i <= segments; i++) {
139
+
140
+ // twist a little...
141
+ normals[i].applyMatrix4(mat.makeRotationAxis(tangents[i], theta * i));
142
+ binormals[i].crossVectors(tangents[i], normals[i]);
143
+
144
+ }
145
+
146
+ }
147
+
148
+ return {
149
+ tangents: tangents,
150
+ normals: normals,
151
+ binormals: binormals
152
+ };
153
+ }
@@ -0,0 +1,17 @@
1
+ import { InterpolationType } from "../../../../../navigation/ecs/components/InterpolationType.js";
2
+ import { estimatePathViaIterativeIntegral } from "./estimatePathViaIterativeIntegral.js";
3
+
4
+ /**
5
+ *
6
+ * @param {Path} path
7
+ * @returns {number}
8
+ */
9
+ function estimatePathLength(path) {
10
+
11
+ if (path.interpolation === InterpolationType.Linear) {
12
+ return path.getLength();
13
+ } else {
14
+ return estimatePathViaIterativeIntegral(path, 0.01);
15
+ }
16
+
17
+ }
@@ -0,0 +1,37 @@
1
+ import { Vector3 } from "three";
2
+
3
+ /**
4
+ * some useful references:
5
+ * https://stackoverflow.com/questions/29438398/cheap-way-of-calculating-cubic-bezier-length
6
+ * https://stackoverflow.com/questions/1147249/connecting-catmull-rom-splines-together-and-calculating-its-length
7
+ * @param {Path} path
8
+ * @param {number} epsilon
9
+ */
10
+ export function estimatePathViaIterativeIntegral(path, epsilon) {
11
+ let sum = 0;
12
+
13
+ let p0 = new Vector3();
14
+ let p1 = new Vector3();
15
+
16
+ if (!path.sample(p0, 0)) {
17
+ // path has 0 length
18
+ return 0;
19
+ }
20
+
21
+ for (let p = epsilon; ; i += epsilon) {
22
+ const ended = path.sample(p1, p);
23
+
24
+ if (ended) {
25
+ break;
26
+ }
27
+
28
+ sum += p0.distanceTo(p1);
29
+
30
+ const swap = p1;
31
+
32
+ p1 = p0;
33
+ p0 = swap;
34
+ }
35
+
36
+ return sum;
37
+ }