@woosh/meep-engine 2.35.8 → 2.35.12

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.
Files changed (28) hide show
  1. package/core/binary/BinaryBuffer.js +65 -18
  2. package/engine/graphics/ecs/camera/Camera.d.ts +3 -0
  3. package/engine/graphics/ecs/camera/Camera.js +39 -1
  4. package/engine/graphics/ecs/camera/CameraSystem.js +25 -6
  5. package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +8 -2
  6. package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +19 -4
  7. package/engine/graphics/ecs/path/PathDisplaySystem.js +1 -1
  8. package/engine/graphics/ecs/path/testPathDisplaySystem.js +13 -12
  9. package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +2 -0
  10. package/engine/graphics/ecs/path/tube/TubePathStyle.js +43 -1
  11. package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +138 -0
  12. package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +32 -0
  13. package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +16 -0
  14. package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +153 -0
  15. package/engine/graphics/ecs/path/tube/build/estimatePathLength.js +17 -0
  16. package/engine/graphics/ecs/path/tube/build/estimatePathViaIterativeIntegral.js +37 -0
  17. package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +156 -0
  18. package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -1
  19. package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +45 -76
  20. package/engine/graphics/micron/render/VirtualGeometryBuilder.js +19 -24
  21. package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -1
  22. package/engine/graphics/micron/render/v1/MaterialContext.js +1 -11
  23. package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +55 -2
  24. package/engine/graphics/micron/render/v1/MicronRenderContext.js +151 -0
  25. package/engine/graphics/micron/render/v1/VGThreeRenderer.js +24 -117
  26. package/engine/graphics/particles/particular/engine/utils/volume/ParticleVolume.js +1 -0
  27. package/package.json +1 -1
  28. package/engine/graphics/ecs/path/tube/TubePathBuilder.js +0 -522
@@ -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
+ }
@@ -0,0 +1,156 @@
1
+ import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
2
+
3
+ /**
4
+ * @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
5
+ * @param {Vector3[]} points
6
+ * @param {Vector3[]} _normals
7
+ * @param {Vector3[]} binormals
8
+ * @param {number} radius
9
+ * @param {number} radialSegments
10
+ * @param {boolean} [closed]
11
+ * @returns {BufferGeometry}
12
+ */
13
+ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegments, closed = false) {
14
+ const geometry = new BufferGeometry();
15
+ // helper variables
16
+
17
+ const vertex = new Vector3();
18
+ const normal = new Vector3();
19
+
20
+ // buffer
21
+
22
+ const vertices = [];
23
+ const normals = [];
24
+ const uvs = [];
25
+ const indices = [];
26
+
27
+ const tubularSegments = points.length - 1;
28
+
29
+ // create buffer data
30
+
31
+ generateBufferData();
32
+
33
+ // build geometry
34
+
35
+ geometry.setIndex(indices);
36
+ geometry.setAttribute('position', new Float32BufferAttribute(vertices, 3));
37
+ geometry.setAttribute('normal', new Float32BufferAttribute(normals, 3));
38
+ geometry.setAttribute('uv', new Float32BufferAttribute(uvs, 2));
39
+
40
+ // functions
41
+
42
+ function generateBufferData() {
43
+
44
+ for (let i = 0; i < tubularSegments; i++) {
45
+
46
+ generateSegment(i);
47
+
48
+ }
49
+
50
+ // if the geometry is not closed, generate the last row of vertices and normals
51
+ // at the regular position on the given path
52
+ //
53
+ // if the geometry is closed, duplicate the first row of vertices and normals (uvs will differ)
54
+
55
+ generateSegment((closed === false) ? tubularSegments : 0);
56
+
57
+ // uvs are generated in a separate function.
58
+ // this makes it easy compute correct values for closed geometries
59
+
60
+ generateUVs();
61
+
62
+ // finally create faces
63
+
64
+ generateIndices();
65
+
66
+ }
67
+
68
+ function generateSegment(i) {
69
+
70
+ // we use getPointAt to sample evenly distributed points from the given path
71
+
72
+ const P = points[i];
73
+
74
+ // retrieve corresponding normal and binormal
75
+
76
+ const N = _normals[i];
77
+ const B = binormals[i];
78
+
79
+ // generate normals and vertices for the current segment
80
+
81
+ const angular_step = (Math.PI * 2) / radialSegments;
82
+
83
+ for (let j = 0; j <= radialSegments; j++) {
84
+ const v = j * angular_step;
85
+
86
+ const sin = Math.sin(v);
87
+ const cos = -Math.cos(v);
88
+
89
+ // normal
90
+
91
+ normal.x = (cos * N.x + sin * B.x);
92
+ normal.y = (cos * N.y + sin * B.y);
93
+ normal.z = (cos * N.z + sin * B.z);
94
+ normal.normalize();
95
+
96
+ normals.push(normal.x, normal.y, normal.z);
97
+
98
+ // vertex
99
+
100
+ vertex.x = P.x + radius * normal.x;
101
+ vertex.y = P.y + radius * normal.y;
102
+ vertex.z = P.z + radius * normal.z;
103
+
104
+ vertices.push(vertex.x, vertex.y, vertex.z);
105
+
106
+ }
107
+
108
+ }
109
+
110
+ function generateIndices() {
111
+ let i = 0, j = 0;
112
+
113
+ for (j = 1; j <= tubularSegments; j++) {
114
+
115
+ for (i = 1; i <= radialSegments; i++) {
116
+
117
+ const s_1 = radialSegments + 1;
118
+
119
+ const a = s_1 * (j - 1) + (i - 1);
120
+ const b = s_1 * j + (i - 1);
121
+ const c = s_1 * j + i;
122
+ const d = s_1 * (j - 1) + i;
123
+
124
+ // faces
125
+
126
+ indices.push(a, b, d);
127
+ indices.push(b, c, d);
128
+
129
+ }
130
+
131
+ }
132
+
133
+ }
134
+
135
+ function generateUVs() {
136
+ let i = 0, j = 0;
137
+
138
+ for (i = 0; i <= tubularSegments; i++) {
139
+
140
+ const u = i / tubularSegments;
141
+
142
+ for (j = 0; j <= radialSegments; j++) {
143
+
144
+ const v = j / radialSegments;
145
+
146
+ uvs.push(u, v);
147
+
148
+ }
149
+
150
+ }
151
+
152
+ }
153
+
154
+
155
+ return geometry;
156
+ }
@@ -121,7 +121,7 @@ const PRINT_VERBOSE_STATS = false;
121
121
  /**
122
122
  *
123
123
  * @param {THREE.BufferGeometry} input
124
- * @returns {MicronGeometry}
124
+ * @returns {Promise<MicronGeometry>}
125
125
  */
126
126
  export async function buildMicronGeometryFromBufferGeometry(input) {
127
127
  const source_geometry = deinterleaveBufferGeometry(input);
@@ -1,6 +1,8 @@
1
1
  import { AbstractRenderAdapter } from "../../../ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js";
2
2
  import Vector2 from "../../../../../core/geom/Vector2.js";
3
- import { MaterialVertexSpec } from "../../render/v1/MaterialVertexSpec.js";
3
+ import { buildMicronGeometryFromBufferGeometry } from "../../build/buildMicronGeometryFromBufferGeometry.js";
4
+ import { ShadedGeometryFlags } from "../../../ecs/mesh-v2/ShadedGeometryFlags.js";
5
+ import { MaterialVertexSpec, MaterialVertexSpecFlags } from "../../render/v1/MaterialVertexSpec.js";
4
6
 
5
7
  /**
6
8
  *
@@ -8,35 +10,16 @@ import { MaterialVertexSpec } from "../../render/v1/MaterialVertexSpec.js";
8
10
  */
9
11
  const scratch_mvc = new MaterialVertexSpec();
10
12
 
11
- class MicronRenderContext {
12
-
13
- }
14
-
15
13
  export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
16
- constructor({
17
- vgtr
18
- }) {
19
- super();
20
-
21
- /**
22
- * @type {VGThreeRenderer}
23
- * @private
24
- */
25
- this.__vgtr = vgtr;
26
14
 
27
- /**
28
- *
29
- * @type {VirtualGeometry|null}
30
- * @private
31
- */
32
- this.__vg = null;
15
+ constructor() {
16
+ super();
33
17
 
34
18
  /**
35
19
  *
36
- * @type {VirtualGeometryBuilder|null}
37
- * @private
20
+ * @type {MicronRenderContext|null}
38
21
  */
39
- this.__vgb = null;
22
+ this.ctx = null;
40
23
 
41
24
  /**
42
25
  *
@@ -47,18 +30,41 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
47
30
 
48
31
  /**
49
32
  *
50
- * @type {Set<MaterialContext>}
33
+ * @type {Map<number, Promise>}
51
34
  * @private
52
35
  */
53
- this.__active_contexts = new Set();
36
+ this.__geometry_building_promises = new Map();
54
37
  }
55
38
 
56
39
  add(sg) {
57
40
 
58
- const micron_geometry = this.__geometry_map.get(sg.geometry.id);
41
+ const geometry_id = sg.geometry.id;
42
+
43
+ const micron_geometry = this.__geometry_map.get(geometry_id);
44
+
45
+ if (micron_geometry === undefined) {
46
+ // no micron geometry
47
+ if (!this.__geometry_building_promises.has(geometry_id)) {
48
+ const p = buildMicronGeometryFromBufferGeometry(sg.geometry);
49
+
50
+ this.__geometry_building_promises.set(geometry_id, p);
51
+
52
+ p.then(mg => this.__geometry_map.set(geometry_id, mg));
53
+ }
54
+ return;
55
+ }
59
56
 
60
57
  // populate virtual geometry for this instance
61
- this.__vgb.buildForMesh(sg.entity, micron_geometry, sg.transform);
58
+ this.ctx.builder.buildForMesh(sg.entity, micron_geometry, sg.transform);
59
+
60
+ scratch_mvc.material = sg.material;
61
+ scratch_mvc.spec = micron_geometry.vertex_spec;
62
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.CastShadow, sg.getFlag(ShadedGeometryFlags.CastShadow));
63
+ scratch_mvc.writeFlag(MaterialVertexSpecFlags.ReceiveShadow, sg.getFlag(ShadedGeometryFlags.ReceiveShadow));
64
+
65
+ const material_context = this.ctx.obtain_material(scratch_mvc);
66
+
67
+ material_context.instances.add(sg.entity);
62
68
 
63
69
  }
64
70
 
@@ -84,67 +90,30 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
84
90
  pixelRatio = renderer.getPixelRatio();
85
91
  }
86
92
 
87
- const vg_builder = this.__vgb;
93
+ const vg_builder = this.ctx.builder;
88
94
 
89
95
  vg_builder.resolution.set(resolution.x * pixelRatio, resolution.y * pixelRatio);
90
96
  vg_builder.setMatricesFromCamera(view.camera);
91
- vg_builder.setOutput(this.__vg);
97
+ vg_builder.setOutput(this.ctx.geometry);
92
98
 
93
- vg_builder.build();
99
+ vg_builder.initialize_build();
94
100
  }
95
101
 
96
102
  build_end() {
97
- const contexts = this.__contexts_array;
98
- const context_count = contexts.length;
99
-
100
- /**
101
- *
102
- * @type {Map<number, MaterialContext>}
103
- */
104
- const instance_to_ctx = new Map();
103
+ this.ctx.render();
105
104
 
106
- for (let i = 0; i < context_count; i++) {
107
- const ctx = contexts[i];
108
- ctx.__patch_count = 0;
105
+ const material_contexts = this.ctx.contexts_array;
106
+ const contexts_count = material_contexts.length;
107
+ for (let i = 0; i < contexts_count; i++) {
108
+ const mc = material_contexts[i];
109
109
 
110
- for (const instance of ctx.instances) {
111
- instance_to_ctx.set(instance, ctx);
110
+ if (mc.instances.size > 0) {
111
+ this.__objects[this.__object_count++] = mc.mesh;
112
112
  }
113
113
  }
114
-
115
- // sort patches into relevant contexts
116
- const geometry = this.geometry;
117
- const patch_count = geometry.patch_count;
118
- const patches = geometry.patches;
119
-
120
- for (let i = 0; i < patch_count; i++) {
121
- const i3 = i * 3;
122
-
123
- const instance_id = patches[i3];
124
- const geometry_id = patches[i3 + 1];
125
- const patch_id = patches[i3 + 2];
126
-
127
- const ctx = instance_to_ctx.get(instance_id);
128
-
129
- const target_address = ctx.__patch_count * 3;
130
- ctx.__patch_count++;
131
-
132
- const ctx_patches = ctx.__patches;
133
-
134
- ctx_patches[target_address] = instance_id;
135
- ctx_patches[target_address + 1] = geometry_id;
136
- ctx_patches[target_address + 2] = patch_id;
137
- }
138
-
139
- // build each context
140
- for (let i = 0; i < context_count; i++) {
141
- const ctx = contexts[i];
142
-
143
- ctx.build(this.geometry);
144
- }
145
114
  }
146
115
 
147
116
  dispose() {
148
-
117
+ // rendering context is not owned, do nothing
149
118
  }
150
119
  }