@woosh/meep-engine 2.35.10 → 2.35.14
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/core/binary/BinaryBuffer.js +65 -18
- package/engine/graphics/ecs/camera/Camera.js +1 -1
- package/engine/graphics/ecs/camera/CameraSystem.js +2 -2
- package/engine/graphics/ecs/mesh-v2/ShadedGeometrySystem.js +38 -3
- package/engine/graphics/ecs/mesh-v2/sample/prototypeShadedGeometry.js +19 -4
- package/engine/graphics/ecs/path/testPathDisplaySystem.js +6 -0
- package/engine/graphics/ecs/path/tube/TubePathStyle.d.ts +1 -0
- package/engine/graphics/ecs/path/tube/TubePathStyle.js +29 -2
- package/engine/graphics/ecs/path/tube/build/TubePathBuilder.js +42 -21
- package/engine/graphics/ecs/path/tube/build/build_geometry_catmullrom.js +41 -12
- package/engine/graphics/ecs/path/tube/build/build_geometry_linear.js +43 -3
- package/engine/graphics/ecs/path/tube/build/computeFrenetFrames.js +28 -16
- package/engine/graphics/ecs/path/tube/build/makeTubeGeometry.js +11 -6
- package/engine/graphics/micron/build/buildMicronGeometryFromBufferGeometry.js +1 -1
- package/engine/graphics/micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js +45 -3
- package/engine/graphics/micron/render/instanced/ThreeInstancedAdapter.js +1 -1
- package/engine/graphics/micron/render/v1/MaterialContext.js +1 -11
- package/engine/graphics/micron/render/v1/MaterialVertexSpec.js +55 -2
- package/engine/graphics/micron/render/v1/MicronRenderContext.js +25 -13
- package/engine/graphics/micron/render/v1/VGThreeRenderer.js +13 -1
- package/engine/navigation/ecs/components/Path.js +5 -6
- package/package.json +1 -1
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
* @enum {boolean}
|
|
4
4
|
*/
|
|
5
5
|
import { assert } from "../assert.js";
|
|
6
|
+
import { array_copy } from "../collection/array/copyArray.js";
|
|
6
7
|
|
|
7
8
|
/**
|
|
8
9
|
*
|
|
@@ -30,17 +31,50 @@ function BinaryBuffer() {
|
|
|
30
31
|
*/
|
|
31
32
|
this.endianness = EndianType.BigEndian;
|
|
32
33
|
|
|
34
|
+
/**
|
|
35
|
+
*
|
|
36
|
+
* @type {number}
|
|
37
|
+
*/
|
|
33
38
|
this.position = 0;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
*
|
|
42
|
+
* @type {number}
|
|
43
|
+
*/
|
|
34
44
|
this.length = 0;
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
*
|
|
48
|
+
* @type {number}
|
|
49
|
+
*/
|
|
35
50
|
this.capacity = 0;
|
|
36
51
|
|
|
37
52
|
/**
|
|
38
53
|
*
|
|
39
54
|
* @type {ArrayBuffer}
|
|
55
|
+
* @private
|
|
40
56
|
*/
|
|
41
57
|
this.data = new ArrayBuffer(0);
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
*
|
|
61
|
+
* @type {DataView}
|
|
62
|
+
* @private
|
|
63
|
+
*/
|
|
42
64
|
this.dataView = new DataView(this.data);
|
|
43
65
|
|
|
66
|
+
/**
|
|
67
|
+
*
|
|
68
|
+
* @type {Uint8Array}
|
|
69
|
+
* @private
|
|
70
|
+
*/
|
|
71
|
+
this.__data_uint8 = new Uint8Array(0);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
*
|
|
75
|
+
* @type {number}
|
|
76
|
+
* @private
|
|
77
|
+
*/
|
|
44
78
|
this.__growFactor = 1.1;
|
|
45
79
|
}
|
|
46
80
|
|
|
@@ -50,6 +84,7 @@ function BinaryBuffer() {
|
|
|
50
84
|
BinaryBuffer.prototype.fromArrayBuffer = function (arrayBuffer) {
|
|
51
85
|
this.data = arrayBuffer;
|
|
52
86
|
this.dataView = new DataView(arrayBuffer);
|
|
87
|
+
this.__data_uint8 = new Uint8Array(arrayBuffer);
|
|
53
88
|
|
|
54
89
|
this.capacity = arrayBuffer.byteLength;
|
|
55
90
|
//assume that all the data is useful
|
|
@@ -85,7 +120,7 @@ BinaryBuffer.prototype.trim = function () {
|
|
|
85
120
|
* @param {number} capacity
|
|
86
121
|
*/
|
|
87
122
|
BinaryBuffer.prototype.setCapacity = function (capacity) {
|
|
88
|
-
const oldData =
|
|
123
|
+
const oldData = this.__data_uint8;
|
|
89
124
|
const newData = new Uint8Array(capacity);
|
|
90
125
|
|
|
91
126
|
//copy old data
|
|
@@ -93,7 +128,7 @@ BinaryBuffer.prototype.setCapacity = function (capacity) {
|
|
|
93
128
|
newData.set(oldData.subarray(0, copyLength), 0);
|
|
94
129
|
|
|
95
130
|
this.data = newData.buffer;
|
|
96
|
-
|
|
131
|
+
this.__data_uint8 = newData;
|
|
97
132
|
this.dataView = new DataView(this.data);
|
|
98
133
|
|
|
99
134
|
this.capacity = capacity;
|
|
@@ -680,11 +715,11 @@ BinaryBuffer.prototype.writeUint32LE = function (value) {
|
|
|
680
715
|
/**
|
|
681
716
|
*
|
|
682
717
|
* @param {Uint8Array|Uint8ClampedArray} array
|
|
683
|
-
* @param {number}
|
|
718
|
+
* @param {number} source_offset
|
|
684
719
|
* @param {number} length
|
|
685
720
|
*/
|
|
686
|
-
BinaryBuffer.prototype.writeBytes = function (array,
|
|
687
|
-
const source_end =
|
|
721
|
+
BinaryBuffer.prototype.writeBytes = function (array, source_offset, length) {
|
|
722
|
+
const source_end = source_offset + length;
|
|
688
723
|
assert.greaterThanOrEqual(array.length, source_end, 'source array underflow');
|
|
689
724
|
|
|
690
725
|
const targetAddress = this.position;
|
|
@@ -693,18 +728,21 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
|
|
|
693
728
|
|
|
694
729
|
this.ensureCapacity(end);
|
|
695
730
|
|
|
696
|
-
|
|
731
|
+
if (source_offset === 0 && array.length === length) {
|
|
732
|
+
// copying entire source array
|
|
697
733
|
|
|
698
|
-
|
|
734
|
+
this.__data_uint8.set(array, targetAddress);
|
|
735
|
+
|
|
736
|
+
} else if (typeof array.subarray === "function") {
|
|
699
737
|
// typed array, use "subarray" method
|
|
700
738
|
|
|
701
|
-
|
|
739
|
+
this.__data_uint8.set(array.subarray(source_offset, source_end), targetAddress);
|
|
702
740
|
|
|
703
741
|
} else {
|
|
704
742
|
// not a typed array, copy byte by byte manually
|
|
705
743
|
|
|
706
744
|
for (let i = 0; i < length; i++) {
|
|
707
|
-
|
|
745
|
+
this.__data_uint8[targetAddress + i] = array[source_offset + i];
|
|
708
746
|
}
|
|
709
747
|
|
|
710
748
|
}
|
|
@@ -714,14 +752,23 @@ BinaryBuffer.prototype.writeBytes = function (array, sourceOffset, length) {
|
|
|
714
752
|
|
|
715
753
|
/**
|
|
716
754
|
*
|
|
717
|
-
* @param {Uint8Array}
|
|
718
|
-
* @param {number}
|
|
755
|
+
* @param {Uint8Array} destination
|
|
756
|
+
* @param {number} destination_offset
|
|
719
757
|
* @param {number} length
|
|
720
758
|
*/
|
|
721
|
-
BinaryBuffer.prototype.readBytes = function (
|
|
722
|
-
const
|
|
723
|
-
|
|
724
|
-
|
|
759
|
+
BinaryBuffer.prototype.readBytes = function (destination, destination_offset, length) {
|
|
760
|
+
const source_position = this.position;
|
|
761
|
+
|
|
762
|
+
const end = source_position + length;
|
|
763
|
+
|
|
764
|
+
const uint8 = this.__data_uint8;
|
|
765
|
+
|
|
766
|
+
if (length < 128) {
|
|
767
|
+
// small copy
|
|
768
|
+
array_copy(uint8, source_position, destination, destination_offset, length);
|
|
769
|
+
} else {
|
|
770
|
+
destination.set(uint8.subarray(source_position, end), destination_offset);
|
|
771
|
+
}
|
|
725
772
|
|
|
726
773
|
this.position = end;
|
|
727
774
|
};
|
|
@@ -764,7 +811,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
|
|
|
764
811
|
|
|
765
812
|
this.ensureCapacity(tlen + at);
|
|
766
813
|
|
|
767
|
-
let target =
|
|
814
|
+
let target = this.__data_uint8; // ... but at 8 byte offset
|
|
768
815
|
|
|
769
816
|
|
|
770
817
|
while (pos < len) {
|
|
@@ -791,7 +838,7 @@ BinaryBuffer.prototype.writeUTF8String = function (string) {
|
|
|
791
838
|
|
|
792
839
|
this.ensureCapacity(tlen + startPosition);
|
|
793
840
|
|
|
794
|
-
target =
|
|
841
|
+
target = this.__data_uint8;
|
|
795
842
|
}
|
|
796
843
|
|
|
797
844
|
if ((value & 0xffffff80) === 0) { // 1-byte
|
|
@@ -851,7 +898,7 @@ BinaryBuffer.prototype.readUTF8String = function () {
|
|
|
851
898
|
return undefined;
|
|
852
899
|
}
|
|
853
900
|
|
|
854
|
-
const bytes =
|
|
901
|
+
const bytes = this.__data_uint8;
|
|
855
902
|
|
|
856
903
|
|
|
857
904
|
let result = "";
|
|
@@ -297,7 +297,7 @@ export class CameraSystem extends System {
|
|
|
297
297
|
* @param {Camera} c
|
|
298
298
|
*/
|
|
299
299
|
function visitCameraEntity(c) {
|
|
300
|
-
if (c.object
|
|
300
|
+
if (c.object === null) {
|
|
301
301
|
return;
|
|
302
302
|
}
|
|
303
303
|
|
|
@@ -305,7 +305,7 @@ export class CameraSystem extends System {
|
|
|
305
305
|
return;
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
if (c.autoClip) {
|
|
308
|
+
if (c.autoClip === true) {
|
|
309
309
|
autoSetClippingPlanes(c, layers);
|
|
310
310
|
} else {
|
|
311
311
|
c.object.far = c.clip_far;
|
|
@@ -74,6 +74,13 @@ export class ShadedGeometrySystem extends System {
|
|
|
74
74
|
* @private
|
|
75
75
|
*/
|
|
76
76
|
this.__adapter_suppliers = new Map();
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Keeps count of usage for each geometry by ID
|
|
80
|
+
* @type {Map<number, number>}
|
|
81
|
+
* @private
|
|
82
|
+
*/
|
|
83
|
+
this.__geometry_usage_counters = new Map();
|
|
77
84
|
}
|
|
78
85
|
|
|
79
86
|
/**
|
|
@@ -166,7 +173,7 @@ export class ShadedGeometrySystem extends System {
|
|
|
166
173
|
this.__render_layer.buildVisibleSet = ((destination, destination_offset, view) => {
|
|
167
174
|
const ctx = this.__view_contexts.get(view);
|
|
168
175
|
|
|
169
|
-
return ctx.collect(destination, destination_offset,graphics.renderer, view, this.__bvh);
|
|
176
|
+
return ctx.collect(destination, destination_offset, graphics.renderer, view, this.__bvh);
|
|
170
177
|
});
|
|
171
178
|
|
|
172
179
|
readyCallback();
|
|
@@ -210,6 +217,13 @@ export class ShadedGeometrySystem extends System {
|
|
|
210
217
|
sg.__entity = entity;
|
|
211
218
|
|
|
212
219
|
this.__bvh.insertNode(leaf_node);
|
|
220
|
+
|
|
221
|
+
// update usage count
|
|
222
|
+
const geometry_id = sg.geometry.id;
|
|
223
|
+
const count_existing = this.__geometry_usage_counters.get(geometry_id);
|
|
224
|
+
const count_new = count_existing !== undefined ? count_existing + 1 : 1;
|
|
225
|
+
|
|
226
|
+
this.__geometry_usage_counters.set(geometry_id, count_new);
|
|
213
227
|
}
|
|
214
228
|
|
|
215
229
|
/**
|
|
@@ -224,6 +238,21 @@ export class ShadedGeometrySystem extends System {
|
|
|
224
238
|
t.scale.onChanged.remove(sg.updateTransform, sg);
|
|
225
239
|
|
|
226
240
|
sg.bvh.disconnect();
|
|
241
|
+
|
|
242
|
+
const geometry_id = sg.geometry.id;
|
|
243
|
+
const count_existing = this.__geometry_usage_counters.get(geometry_id);
|
|
244
|
+
|
|
245
|
+
if (count_existing === 1) {
|
|
246
|
+
|
|
247
|
+
// last instance
|
|
248
|
+
this.__geometry_usage_counters.delete(geometry_id);
|
|
249
|
+
|
|
250
|
+
// release GPU memory
|
|
251
|
+
sg.geometry.dispose();
|
|
252
|
+
|
|
253
|
+
} else {
|
|
254
|
+
this.__geometry_usage_counters.set(geometry_id, count_existing - 1);
|
|
255
|
+
}
|
|
227
256
|
}
|
|
228
257
|
|
|
229
258
|
/**
|
|
@@ -313,13 +342,19 @@ export class ShadedGeometrySystem extends System {
|
|
|
313
342
|
const local_direction_x = scratch_m4[0] * direction_x + scratch_m4[4] * direction_y + scratch_m4[8] * direction_z;
|
|
314
343
|
const local_direction_y = scratch_m4[1] * direction_x + scratch_m4[5] * direction_y + scratch_m4[9] * direction_z;
|
|
315
344
|
const local_direction_z = scratch_m4[2] * direction_x + scratch_m4[6] * direction_y + scratch_m4[10] * direction_z;
|
|
316
|
-
|
|
345
|
+
|
|
346
|
+
const local_direction_magnitude = Math.hypot(local_direction_x, local_direction_y, local_direction_z);
|
|
347
|
+
const local_direction_magnitude_inverse = 1 / local_direction_magnitude;
|
|
348
|
+
|
|
349
|
+
const local_direction_normalized_x = local_direction_x * local_direction_magnitude_inverse;
|
|
350
|
+
const local_direction_normalized_y = local_direction_y * local_direction_magnitude_inverse;
|
|
351
|
+
const local_direction_normalized_z = local_direction_z * local_direction_magnitude_inverse;
|
|
317
352
|
|
|
318
353
|
const geometry_hit_found = GeometrySpatialQueryAccelerator.INSTANCE.queryRaycastNearest_primitive(
|
|
319
354
|
scratch_point,
|
|
320
355
|
sg.geometry,
|
|
321
356
|
local_origin_x, local_origin_y, local_origin_z,
|
|
322
|
-
|
|
357
|
+
local_direction_normalized_x, local_direction_normalized_y, local_direction_normalized_z
|
|
323
358
|
);
|
|
324
359
|
|
|
325
360
|
if (!geometry_hit_found) {
|
|
@@ -20,6 +20,9 @@ import { randomFromArray } from "../../../../../core/math/random/randomFromArray
|
|
|
20
20
|
import { BehaviorComponent } from "../../../../intelligence/behavior/ecs/BehaviorComponent.js";
|
|
21
21
|
import { OrbitingBehavior } from "../../../../../../model/game/util/behavior/OrbitingBehavior.js";
|
|
22
22
|
import { BehaviorSystem } from "../../../../intelligence/behavior/ecs/BehaviorSystem.js";
|
|
23
|
+
import { MicronShadedGeometryRenderAdapter } from "../../../micron/plugin/shaded_geometry/MicronShadedGeometryRenderAdapter.js";
|
|
24
|
+
import { MicronRenderContext } from "../../../micron/render/v1/MicronRenderContext.js";
|
|
25
|
+
import { randomIntegerBetween } from "../../../../../core/math/random/randomIntegerBetween.js";
|
|
23
26
|
|
|
24
27
|
const eh = new EngineHarness();
|
|
25
28
|
|
|
@@ -50,7 +53,17 @@ async function main(engine) {
|
|
|
50
53
|
|
|
51
54
|
const em = engine.entityManager;
|
|
52
55
|
|
|
53
|
-
|
|
56
|
+
const sgs = new ShadedGeometrySystem(engine);
|
|
57
|
+
|
|
58
|
+
const mrc = new MicronRenderContext();
|
|
59
|
+
sgs.register_render_adapter(2, () => {
|
|
60
|
+
const adapter = new MicronShadedGeometryRenderAdapter();
|
|
61
|
+
adapter.ctx = mrc;
|
|
62
|
+
|
|
63
|
+
return adapter;
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
await em.addSystem(sgs);
|
|
54
67
|
await em.addSystem(new BehaviorSystem(engine));
|
|
55
68
|
|
|
56
69
|
EngineHarness.buildBasics({
|
|
@@ -74,7 +87,7 @@ async function main(engine) {
|
|
|
74
87
|
|
|
75
88
|
const random = seededRandom(0);
|
|
76
89
|
|
|
77
|
-
const INSTANCE_COUNT =
|
|
90
|
+
const INSTANCE_COUNT = 10000;
|
|
78
91
|
|
|
79
92
|
const geometries = [];
|
|
80
93
|
|
|
@@ -87,7 +100,9 @@ async function main(engine) {
|
|
|
87
100
|
const sg = ShadedGeometry.from(geometry, material);
|
|
88
101
|
|
|
89
102
|
// sg.draw_method = DRAW_METHOD_PLAIN;
|
|
90
|
-
sg.draw_method = DRAW_METHOD_INSTANCED;
|
|
103
|
+
// sg.draw_method = DRAW_METHOD_INSTANCED;
|
|
104
|
+
// sg.draw_method = 2;
|
|
105
|
+
sg.draw_method = randomIntegerBetween(random, 0, 2);
|
|
91
106
|
// sg.draw_method = random() > 0.01 ? DRAW_METHOD_INSTANCED : DRAW_METHOD_PLAIN;
|
|
92
107
|
sg.clearFlag(ShadedGeometryFlags.CastShadow | ShadedGeometryFlags.ReceiveShadow);
|
|
93
108
|
|
|
@@ -105,7 +120,7 @@ async function main(engine) {
|
|
|
105
120
|
.build(ecd);
|
|
106
121
|
}
|
|
107
122
|
|
|
108
|
-
makeMovingShadedGeometry().build(ecd);
|
|
123
|
+
// makeMovingShadedGeometry().build(ecd);
|
|
109
124
|
|
|
110
125
|
debug_button(engine, () => {
|
|
111
126
|
const dot = convert_bvh_to_dot_format_string(engine.graphics.layers.getLayerByName('shaded-geometry').bvh);
|
|
@@ -3,6 +3,8 @@ import { TubeMaterialType } from "./TubeMaterialType.js";
|
|
|
3
3
|
import { assert } from "../../../../../core/assert.js";
|
|
4
4
|
import { MatcapMaterialDefinition } from "./MatcapMaterialDefinition.js";
|
|
5
5
|
|
|
6
|
+
const DEFAULT_COLOR = Object.freeze({ r: 1, g: 1, b: 1 });
|
|
7
|
+
|
|
6
8
|
export class TubePathStyle {
|
|
7
9
|
constructor() {
|
|
8
10
|
/**
|
|
@@ -59,6 +61,16 @@ export class TubePathStyle {
|
|
|
59
61
|
* @type {boolean}
|
|
60
62
|
*/
|
|
61
63
|
this.receive_shadow = false;
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Which parts of the path to visualise, in normalized offsets
|
|
67
|
+
* @example [ 0, 1 ] = whole path
|
|
68
|
+
* @example [ 0, 0.5 ] = first half of the path
|
|
69
|
+
* @example [ 0.5, 0.6 ] = segment from 50% along the path, to 60% along the path
|
|
70
|
+
* @example [ 0, 0.1, 0.9, 1] = first 10% of the path, and last 10% of the path
|
|
71
|
+
* @type {number[]}
|
|
72
|
+
*/
|
|
73
|
+
this.path_mask = [0, 1];
|
|
62
74
|
}
|
|
63
75
|
|
|
64
76
|
static fromJSON(j) {
|
|
@@ -70,7 +82,7 @@ export class TubePathStyle {
|
|
|
70
82
|
}
|
|
71
83
|
|
|
72
84
|
fromJSON({
|
|
73
|
-
color,
|
|
85
|
+
color = DEFAULT_COLOR,
|
|
74
86
|
material_type,
|
|
75
87
|
material,
|
|
76
88
|
opacity = 1,
|
|
@@ -78,9 +90,22 @@ export class TubePathStyle {
|
|
|
78
90
|
resolution = 10,
|
|
79
91
|
radial_resolution = 16,
|
|
80
92
|
cast_shadow = false,
|
|
81
|
-
receive_shadow = false
|
|
93
|
+
receive_shadow = false,
|
|
94
|
+
path_mask = [0, 1]
|
|
82
95
|
}) {
|
|
83
96
|
assert.enum(material_type, TubeMaterialType, 'material_type');
|
|
97
|
+
assert.isNumber(opacity, 'opacity');
|
|
98
|
+
assert.isNumber(width, 'width');
|
|
99
|
+
assert.isNumber(resolution, 'resolution');
|
|
100
|
+
assert.isNumber(radial_resolution, 'radial_resolution');
|
|
101
|
+
assert.isBoolean(cast_shadow, 'cast_shadow');
|
|
102
|
+
assert.isBoolean(receive_shadow, 'receive_shadow');
|
|
103
|
+
|
|
104
|
+
assert.defined(path_mask, 'path_mask');
|
|
105
|
+
assert.notNull(path_mask, 'path_mask');
|
|
106
|
+
|
|
107
|
+
assert.greaterThanOrEqual(path_mask.length, 2, 'path_mask length bust be at least 2');
|
|
108
|
+
assert.ok(path_mask.length % 2 === 0, 'path_mask length bust be multiple of 2');
|
|
84
109
|
|
|
85
110
|
if (typeof color === 'string') {
|
|
86
111
|
this.color.parse(color);
|
|
@@ -97,6 +122,8 @@ export class TubePathStyle {
|
|
|
97
122
|
this.cast_shadow = cast_shadow;
|
|
98
123
|
this.receive_shadow = receive_shadow;
|
|
99
124
|
|
|
125
|
+
this.path_mask = path_mask;
|
|
126
|
+
|
|
100
127
|
switch (material_type) {
|
|
101
128
|
case TubeMaterialType.Matcap:
|
|
102
129
|
this.material = new MatcapMaterialDefinition();
|
|
@@ -9,6 +9,28 @@ import { build_geometry_catmullrom } from "./build_geometry_catmullrom.js";
|
|
|
9
9
|
import { ShadedGeometry } from "../../../mesh-v2/ShadedGeometry.js";
|
|
10
10
|
import { ShadedGeometryFlags } from "../../../mesh-v2/ShadedGeometryFlags.js";
|
|
11
11
|
|
|
12
|
+
/**
|
|
13
|
+
*
|
|
14
|
+
* @param {number} segment_start
|
|
15
|
+
* @param {number} segment_end
|
|
16
|
+
* @returns {BufferGeometry}
|
|
17
|
+
* @param {Path} path_component
|
|
18
|
+
* @param {TubePathStyle} style
|
|
19
|
+
*/
|
|
20
|
+
function make_geometry_segment(path_component, style, segment_start, segment_end) {
|
|
21
|
+
|
|
22
|
+
const interpolation = path_component.interpolation;
|
|
23
|
+
|
|
24
|
+
if (interpolation === InterpolationType.Linear) {
|
|
25
|
+
return build_geometry_linear(path_component, style, segment_start, segment_end);
|
|
26
|
+
} else if (interpolation === InterpolationType.CatmullRom) {
|
|
27
|
+
return build_geometry_catmullrom(path_component, style, segment_start, segment_end);
|
|
28
|
+
} else {
|
|
29
|
+
throw new Error(`Unsupported interpolation type '${interpolation}'`);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
}
|
|
33
|
+
|
|
12
34
|
export class TubePathBuilder {
|
|
13
35
|
constructor() {
|
|
14
36
|
/**
|
|
@@ -80,20 +102,6 @@ export class TubePathBuilder {
|
|
|
80
102
|
}
|
|
81
103
|
|
|
82
104
|
|
|
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
105
|
let material_def;
|
|
98
106
|
|
|
99
107
|
if (style.material_type === TubeMaterialType.Standard) {
|
|
@@ -122,17 +130,30 @@ export class TubePathBuilder {
|
|
|
122
130
|
material_def.opacity = style.opacity;
|
|
123
131
|
}
|
|
124
132
|
|
|
125
|
-
const
|
|
133
|
+
const segment_count = style.path_mask.length / 2;
|
|
134
|
+
|
|
135
|
+
for (let i = 0; i < segment_count; i++) {
|
|
136
|
+
const i2 = i * 2;
|
|
137
|
+
|
|
138
|
+
const segment_start = style.path_mask[i2];
|
|
139
|
+
const segment_end = style.path_mask[i2 + 1];
|
|
140
|
+
|
|
141
|
+
const geometry = make_geometry_segment(path_component, style, segment_start, segment_end);
|
|
126
142
|
|
|
127
|
-
|
|
143
|
+
const entityBuilder = new EntityBuilder();
|
|
128
144
|
|
|
129
|
-
|
|
145
|
+
entityBuilder.add(new Transform());
|
|
130
146
|
|
|
131
|
-
|
|
132
|
-
shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow);
|
|
147
|
+
const shaded_geometry = ShadedGeometry.from(geometry, material_def);
|
|
133
148
|
|
|
134
|
-
|
|
149
|
+
shaded_geometry.writeFlag(ShadedGeometryFlags.CastShadow, style.cast_shadow);
|
|
150
|
+
shaded_geometry.writeFlag(ShadedGeometryFlags.ReceiveShadow, style.receive_shadow);
|
|
151
|
+
|
|
152
|
+
entityBuilder.add(shaded_geometry);
|
|
153
|
+
|
|
154
|
+
destination.push(entityBuilder);
|
|
155
|
+
|
|
156
|
+
}
|
|
135
157
|
|
|
136
|
-
destination.push(entityBuilder);
|
|
137
158
|
}
|
|
138
159
|
}
|
|
@@ -1,32 +1,61 @@
|
|
|
1
|
-
import { CatmullRomCurve3 } from "three";
|
|
2
1
|
import { computeFrenetFrames } from "./computeFrenetFrames.js";
|
|
3
2
|
import { makeTubeGeometry } from "./makeTubeGeometry.js";
|
|
3
|
+
import Vector3 from "../../../../../../core/geom/Vector3.js";
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
*
|
|
7
|
-
* @param {
|
|
8
|
-
* @param {number} point_count
|
|
7
|
+
* @param {Path} path
|
|
9
8
|
* @param {TubePathStyle} style
|
|
9
|
+
* @param {number} segment_start
|
|
10
|
+
* @param {number} segment_end
|
|
10
11
|
* @return {THREE.BufferGeometry}
|
|
11
12
|
*/
|
|
12
|
-
export function build_geometry_catmullrom(
|
|
13
|
+
export function build_geometry_catmullrom(path, style, segment_start, segment_end) {
|
|
13
14
|
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
const sample_points = [];
|
|
15
|
+
const point_count = path.getPointCount();
|
|
17
16
|
|
|
18
17
|
// resample curve
|
|
19
18
|
const total_points = Math.ceil(point_count * style.resolution);
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
const path_length = path.length;
|
|
21
|
+
|
|
22
|
+
const point = new Vector3();
|
|
23
|
+
|
|
24
|
+
const points_f32 = [];
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
let added_points = 0;
|
|
28
|
+
let i;
|
|
29
|
+
|
|
30
|
+
// initial segment
|
|
31
|
+
path.sample_catmull_rom(point, segment_start * path_length);
|
|
32
|
+
|
|
33
|
+
point.writeToArray(points_f32, added_points * 3);
|
|
34
|
+
|
|
35
|
+
added_points++;
|
|
36
|
+
|
|
37
|
+
const max_i = segment_end * (total_points - 1);
|
|
38
|
+
for (i = Math.ceil(segment_start * total_points + 0.0001); i < max_i; i++) {
|
|
22
39
|
const t = i / (total_points - 1);
|
|
23
40
|
|
|
24
|
-
|
|
41
|
+
path.sample_catmull_rom(point, path_length * t);
|
|
42
|
+
|
|
43
|
+
point.writeToArray(points_f32, added_points * 3);
|
|
44
|
+
|
|
45
|
+
added_points++
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
if (((i - 1) / (total_points - 1)) - segment_end > 0.0001) {
|
|
49
|
+
// end
|
|
50
|
+
|
|
51
|
+
path.sample_catmull_rom(point, segment_end * path_length);
|
|
52
|
+
|
|
53
|
+
point.writeToArray(points_f32, added_points * 3);
|
|
25
54
|
|
|
26
|
-
|
|
55
|
+
added_points++;
|
|
27
56
|
}
|
|
28
57
|
|
|
29
|
-
const frames = computeFrenetFrames(
|
|
58
|
+
const frames = computeFrenetFrames(points_f32, false);
|
|
30
59
|
|
|
31
|
-
return makeTubeGeometry(
|
|
60
|
+
return makeTubeGeometry(points_f32, frames.normals, frames.binormals, style.width, style.radial_resolution, false);
|
|
32
61
|
}
|
|
@@ -1,14 +1,54 @@
|
|
|
1
1
|
import { computeFrenetFrames } from "./computeFrenetFrames.js";
|
|
2
2
|
import { makeTubeGeometry } from "./makeTubeGeometry.js";
|
|
3
|
+
import Vector3 from "../../../../../../core/geom/Vector3.js";
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
const scratch_array_0 = [];
|
|
7
|
+
const scratch_array_1 = [];
|
|
8
|
+
|
|
9
|
+
const scratch_v3 = new Vector3();
|
|
3
10
|
|
|
4
11
|
/**
|
|
5
12
|
*
|
|
6
|
-
* @param {
|
|
7
|
-
* @param {number} point_count
|
|
13
|
+
* @param {Path} path
|
|
8
14
|
* @param {TubePathStyle} style
|
|
15
|
+
* @param {number} segment_start
|
|
16
|
+
* @param {number} segment_end
|
|
9
17
|
* @return {THREE.BufferGeometry}
|
|
10
18
|
*/
|
|
11
|
-
export function build_geometry_linear(
|
|
19
|
+
export function build_geometry_linear(path, style, segment_start, segment_end) {
|
|
20
|
+
const points = [];
|
|
21
|
+
|
|
22
|
+
const path_length = path.length;
|
|
23
|
+
|
|
24
|
+
path.find_index_and_normalized_distance(scratch_array_0, segment_start * path_length);
|
|
25
|
+
path.find_index_and_normalized_distance(scratch_array_1, segment_end * path_length);
|
|
26
|
+
|
|
27
|
+
let points_added = 0;
|
|
28
|
+
|
|
29
|
+
if (scratch_array_0[1] !== 0) {
|
|
30
|
+
path.sample_linear(scratch_v3, segment_start * path_length);
|
|
31
|
+
|
|
32
|
+
scratch_v3.writeToArray(points, points_added * 3);
|
|
33
|
+
points_added++;
|
|
34
|
+
|
|
35
|
+
// offset first sampled point, we're already past it
|
|
36
|
+
scratch_array_0[0]++;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const max_i = scratch_array_1[0];
|
|
40
|
+
for (let i = scratch_array_0[0]; i <= max_i; i++) {
|
|
41
|
+
path.readPositionToArray(i, points, points_added * 3);
|
|
42
|
+
points_added++;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (scratch_array_1[1] !== 0) {
|
|
46
|
+
//add end
|
|
47
|
+
path.sample_linear(scratch_v3, segment_end * path_length);
|
|
48
|
+
|
|
49
|
+
scratch_v3.writeToArray(points, points_added * 3);
|
|
50
|
+
points_added++;
|
|
51
|
+
}
|
|
12
52
|
|
|
13
53
|
const frames = computeFrenetFrames(points, false);
|
|
14
54
|
|
|
@@ -2,15 +2,17 @@ import { Vector3 } from "three";
|
|
|
2
2
|
import { Matrix4 } from "three/src/math/Matrix4.js";
|
|
3
3
|
import { max2 } from "../../../../../../core/math/max2.js";
|
|
4
4
|
import { min2 } from "../../../../../../core/math/min2.js";
|
|
5
|
-
import
|
|
5
|
+
import { clamp } from "../../../../../../core/math/clamp.js";
|
|
6
|
+
import { vec3 } from "gl-matrix";
|
|
7
|
+
import { array_copy } from "../../../../../../core/collection/array/copyArray.js";
|
|
6
8
|
|
|
7
9
|
/**
|
|
8
10
|
* @see https://github.com/mrdoob/three.js/blob/c12c9a166a1369cdd58622fff2aff7e3a84305d7/src/extras/core/Curve.js#L260
|
|
9
|
-
* @param {
|
|
11
|
+
* @param {Float32Array|number[]} points
|
|
10
12
|
* @param {boolean} [closed]
|
|
11
13
|
*/
|
|
12
14
|
export function computeFrenetFrames(points, closed = false) {
|
|
13
|
-
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
15
|
+
// see http://www.cs.indiana.edu/pub/techreports/TR425.pdf
|
|
14
16
|
|
|
15
17
|
const normal = new Vector3();
|
|
16
18
|
|
|
@@ -21,10 +23,15 @@ export function computeFrenetFrames(points, closed = false) {
|
|
|
21
23
|
const vec = new Vector3();
|
|
22
24
|
const mat = new Matrix4();
|
|
23
25
|
|
|
24
|
-
const
|
|
26
|
+
const point_count = points.length / 3;
|
|
27
|
+
const segments = point_count - 1;
|
|
25
28
|
|
|
26
|
-
const v3_0 =
|
|
27
|
-
const v3_1 =
|
|
29
|
+
const v3_0 = [];
|
|
30
|
+
const v3_1 = [];
|
|
31
|
+
|
|
32
|
+
const p0_a = [];
|
|
33
|
+
const pR_a = [];
|
|
34
|
+
const p1_a = [];
|
|
28
35
|
|
|
29
36
|
/**
|
|
30
37
|
*
|
|
@@ -36,18 +43,23 @@ export function computeFrenetFrames(points, closed = false) {
|
|
|
36
43
|
const i0 = max2(i - 1, 0);
|
|
37
44
|
const i1 = min2(i + 1, segments);
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
46
|
+
array_copy(points, i0 * 3, p0_a, 0, 3);
|
|
47
|
+
array_copy(points, i * 3, pR_a, 0, 3);
|
|
48
|
+
array_copy(points, i1 * 3, p1_a, 0, 3);
|
|
42
49
|
|
|
43
50
|
// get points along the lines close to the reference
|
|
44
|
-
v3_0.subVectors(p0, pR);
|
|
45
|
-
v3_0.normalize();
|
|
46
51
|
|
|
47
|
-
|
|
48
|
-
|
|
52
|
+
vec3.sub(v3_0, p0_a, pR_a);
|
|
53
|
+
vec3.normalize(v3_0, v3_0);
|
|
54
|
+
|
|
55
|
+
vec3.sub(v3_1, p1_a, pR_a);
|
|
56
|
+
vec3.normalize(v3_1, v3_1);
|
|
49
57
|
|
|
50
|
-
destination.
|
|
58
|
+
destination.set(
|
|
59
|
+
v3_1[0] - v3_0[0],
|
|
60
|
+
v3_1[1] - v3_0[1],
|
|
61
|
+
v3_1[2] - v3_0[2],
|
|
62
|
+
);
|
|
51
63
|
destination.normalize();
|
|
52
64
|
}
|
|
53
65
|
|
|
@@ -112,7 +124,7 @@ export function computeFrenetFrames(points, closed = false) {
|
|
|
112
124
|
|
|
113
125
|
vec.normalize();
|
|
114
126
|
|
|
115
|
-
const theta = Math.acos(
|
|
127
|
+
const theta = Math.acos(clamp(tangents[i - 1].dot(tangents[i]), -1, 1)); // clamp for floating pt errors
|
|
116
128
|
|
|
117
129
|
normals[i].applyMatrix4(mat.makeRotationAxis(vec, theta));
|
|
118
130
|
|
|
@@ -126,7 +138,7 @@ export function computeFrenetFrames(points, closed = false) {
|
|
|
126
138
|
|
|
127
139
|
if (closed === true) {
|
|
128
140
|
|
|
129
|
-
let theta = Math.acos(
|
|
141
|
+
let theta = Math.acos(clamp(normals[0].dot(normals[segments]), -1, 1));
|
|
130
142
|
theta /= segments;
|
|
131
143
|
|
|
132
144
|
if (tangents[0].dot(vec.crossVectors(normals[0], normals[segments])) > 0) {
|
|
@@ -2,7 +2,7 @@ import { BufferGeometry, Float32BufferAttribute, Vector3 } from "three";
|
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
4
|
* @see https://github.com/mrdoob/three.js/blob/master/src/geometries/TubeGeometry.js
|
|
5
|
-
* @param {
|
|
5
|
+
* @param {Float32Array|number[]} points
|
|
6
6
|
* @param {Vector3[]} _normals
|
|
7
7
|
* @param {Vector3[]} binormals
|
|
8
8
|
* @param {number} radius
|
|
@@ -24,7 +24,8 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
|
|
|
24
24
|
const uvs = [];
|
|
25
25
|
const indices = [];
|
|
26
26
|
|
|
27
|
-
const
|
|
27
|
+
const point_count = points.length / 3;
|
|
28
|
+
const tubularSegments = point_count - 1;
|
|
28
29
|
|
|
29
30
|
// create buffer data
|
|
30
31
|
|
|
@@ -69,7 +70,11 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
|
|
|
69
70
|
|
|
70
71
|
// we use getPointAt to sample evenly distributed points from the given path
|
|
71
72
|
|
|
72
|
-
const
|
|
73
|
+
const i3 = i * 3;
|
|
74
|
+
|
|
75
|
+
const Px = points[i3];
|
|
76
|
+
const Py = points[i3 + 1];
|
|
77
|
+
const Pz = points[i3 + 2];
|
|
73
78
|
|
|
74
79
|
// retrieve corresponding normal and binormal
|
|
75
80
|
|
|
@@ -97,9 +102,9 @@ export function makeTubeGeometry(points, _normals, binormals, radius, radialSegm
|
|
|
97
102
|
|
|
98
103
|
// vertex
|
|
99
104
|
|
|
100
|
-
vertex.x =
|
|
101
|
-
vertex.y =
|
|
102
|
-
vertex.z =
|
|
105
|
+
vertex.x = Px + radius * normal.x;
|
|
106
|
+
vertex.y = Py + radius * normal.y;
|
|
107
|
+
vertex.z = Pz + radius * normal.z;
|
|
103
108
|
|
|
104
109
|
vertices.push(vertex.x, vertex.y, vertex.z);
|
|
105
110
|
|
|
@@ -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,5 +1,14 @@
|
|
|
1
1
|
import { AbstractRenderAdapter } from "../../../ecs/mesh-v2/render/adapters/AbstractRenderAdapter.js";
|
|
2
2
|
import Vector2 from "../../../../../core/geom/Vector2.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";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @type {MaterialVertexSpec}
|
|
10
|
+
*/
|
|
11
|
+
const scratch_mvc = new MaterialVertexSpec();
|
|
3
12
|
|
|
4
13
|
export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
|
|
5
14
|
|
|
@@ -21,19 +30,42 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
|
|
|
21
30
|
|
|
22
31
|
/**
|
|
23
32
|
*
|
|
24
|
-
* @type {
|
|
33
|
+
* @type {Map<number, Promise>}
|
|
25
34
|
* @private
|
|
26
35
|
*/
|
|
27
|
-
this.
|
|
36
|
+
this.__geometry_building_promises = new Map();
|
|
28
37
|
}
|
|
29
38
|
|
|
30
39
|
add(sg) {
|
|
31
40
|
|
|
32
|
-
const
|
|
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
|
+
}
|
|
33
56
|
|
|
34
57
|
// populate virtual geometry for this instance
|
|
35
58
|
this.ctx.builder.buildForMesh(sg.entity, micron_geometry, sg.transform);
|
|
36
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);
|
|
68
|
+
|
|
37
69
|
}
|
|
38
70
|
|
|
39
71
|
/**
|
|
@@ -69,6 +101,16 @@ export class MicronShadedGeometryRenderAdapter extends AbstractRenderAdapter {
|
|
|
69
101
|
|
|
70
102
|
build_end() {
|
|
71
103
|
this.ctx.render();
|
|
104
|
+
|
|
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
|
+
|
|
110
|
+
if (mc.instances.size > 0) {
|
|
111
|
+
this.__objects[this.__object_count++] = mc.mesh;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
72
114
|
}
|
|
73
115
|
|
|
74
116
|
dispose() {
|
|
@@ -52,7 +52,7 @@ export class MaterialContext {
|
|
|
52
52
|
|
|
53
53
|
/**
|
|
54
54
|
*
|
|
55
|
-
* @type {
|
|
55
|
+
* @type {Uint32Array|number[]}
|
|
56
56
|
*/
|
|
57
57
|
this.__patches = [];
|
|
58
58
|
|
|
@@ -63,16 +63,6 @@ export class MaterialContext {
|
|
|
63
63
|
this.__patch_count = 0;
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
*
|
|
69
|
-
* @param {MicronGeometryPatchOccurrence} occurrence
|
|
70
|
-
* @returns {boolean}
|
|
71
|
-
*/
|
|
72
|
-
filter(occurrence) {
|
|
73
|
-
return this.instances.has(occurrence.instance_id);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
66
|
init() {
|
|
77
67
|
|
|
78
68
|
const material = this.material.clone();
|
|
@@ -1,3 +1,8 @@
|
|
|
1
|
+
export const MaterialVertexSpecFlags = {
|
|
2
|
+
CastShadow: 1,
|
|
3
|
+
ReceiveShadow: 2
|
|
4
|
+
};
|
|
5
|
+
|
|
1
6
|
export class MaterialVertexSpec {
|
|
2
7
|
constructor() {
|
|
3
8
|
/**
|
|
@@ -10,6 +15,52 @@ export class MaterialVertexSpec {
|
|
|
10
15
|
* @type {THREE.Material|null}
|
|
11
16
|
*/
|
|
12
17
|
this.material = null;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
*
|
|
21
|
+
* @type {number}
|
|
22
|
+
*/
|
|
23
|
+
this.flags = 0;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
*
|
|
28
|
+
* @param {number|MaterialVertexSpecFlags} flag
|
|
29
|
+
* @returns {void}
|
|
30
|
+
*/
|
|
31
|
+
setFlag(flag) {
|
|
32
|
+
this.flags |= flag;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
*
|
|
37
|
+
* @param {number|MaterialVertexSpecFlags} flag
|
|
38
|
+
* @returns {void}
|
|
39
|
+
*/
|
|
40
|
+
clearFlag(flag) {
|
|
41
|
+
this.flags &= ~flag;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
*
|
|
46
|
+
* @param {number|MaterialVertexSpecFlags} flag
|
|
47
|
+
* @param {boolean} value
|
|
48
|
+
*/
|
|
49
|
+
writeFlag(flag, value) {
|
|
50
|
+
if (value) {
|
|
51
|
+
this.setFlag(flag);
|
|
52
|
+
} else {
|
|
53
|
+
this.clearFlag(flag);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
*
|
|
59
|
+
* @param {number|MaterialVertexSpecFlags} flag
|
|
60
|
+
* @returns {boolean}
|
|
61
|
+
*/
|
|
62
|
+
getFlag(flag) {
|
|
63
|
+
return (this.flags & flag) === flag;
|
|
13
64
|
}
|
|
14
65
|
|
|
15
66
|
/**
|
|
@@ -19,6 +70,7 @@ export class MaterialVertexSpec {
|
|
|
19
70
|
copy(other) {
|
|
20
71
|
this.spec = other.spec;
|
|
21
72
|
this.material = other.material;
|
|
73
|
+
this.flags = other.flags;
|
|
22
74
|
}
|
|
23
75
|
|
|
24
76
|
/**
|
|
@@ -47,7 +99,7 @@ export class MaterialVertexSpec {
|
|
|
47
99
|
}
|
|
48
100
|
|
|
49
101
|
hash() {
|
|
50
|
-
return this.spec.hash() ^ this.material.id;
|
|
102
|
+
return this.spec.hash() ^ this.material.id ^ this.flags;
|
|
51
103
|
}
|
|
52
104
|
|
|
53
105
|
equals(other) {
|
|
@@ -55,8 +107,9 @@ export class MaterialVertexSpec {
|
|
|
55
107
|
return true;
|
|
56
108
|
}
|
|
57
109
|
|
|
58
|
-
return this.
|
|
110
|
+
return this.flags === other.flags
|
|
59
111
|
&& this.material === other.material
|
|
112
|
+
&& this.spec.equals(other.spec)
|
|
60
113
|
;
|
|
61
114
|
}
|
|
62
115
|
}
|
|
@@ -3,7 +3,7 @@ import { VirtualGeometryBuilder } from "../VirtualGeometryBuilder.js";
|
|
|
3
3
|
import { HashMap } from "../../../../../core/collection/HashMap.js";
|
|
4
4
|
import Signal from "../../../../../core/events/signal/Signal.js";
|
|
5
5
|
import { MaterialContext } from "./MaterialContext.js";
|
|
6
|
-
import { MaterialVertexSpec } from "./MaterialVertexSpec.js";
|
|
6
|
+
import { MaterialVertexSpec, MaterialVertexSpecFlags } from "./MaterialVertexSpec.js";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
*
|
|
@@ -39,6 +39,14 @@ export class MicronRenderContext {
|
|
|
39
39
|
};
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
+
/**
|
|
43
|
+
*
|
|
44
|
+
* @returns {MaterialContext[]}
|
|
45
|
+
*/
|
|
46
|
+
get contexts_array() {
|
|
47
|
+
return this.__contexts_array;
|
|
48
|
+
}
|
|
49
|
+
|
|
42
50
|
/**
|
|
43
51
|
* Enable/disable wireframe rendering
|
|
44
52
|
* @param {boolean} x
|
|
@@ -55,27 +63,27 @@ export class MicronRenderContext {
|
|
|
55
63
|
|
|
56
64
|
/**
|
|
57
65
|
*
|
|
58
|
-
* @param {
|
|
59
|
-
* @param {Material} m
|
|
66
|
+
* @param {MaterialVertexSpec} descriptor
|
|
60
67
|
* @returns {MaterialContext}
|
|
61
68
|
*/
|
|
62
|
-
obtain_material(
|
|
63
|
-
scratch_mvc.material = m;
|
|
64
|
-
scratch_mvc.spec = spec;
|
|
69
|
+
obtain_material(descriptor) {
|
|
65
70
|
|
|
66
|
-
const existing = this.material_contexts.get(
|
|
71
|
+
const existing = this.material_contexts.get(descriptor);
|
|
67
72
|
|
|
68
73
|
if (existing !== undefined) {
|
|
69
74
|
return existing;
|
|
70
75
|
}
|
|
71
76
|
|
|
72
77
|
// create a separate instance to be stored alongside the context
|
|
73
|
-
const key =
|
|
78
|
+
const key = descriptor.clone();
|
|
74
79
|
|
|
75
|
-
const ctx = new MaterialContext(spec,
|
|
80
|
+
const ctx = new MaterialContext(descriptor.spec, descriptor.material);
|
|
76
81
|
|
|
77
82
|
ctx.init();
|
|
78
83
|
|
|
84
|
+
ctx.mesh.castShadow = descriptor.getFlag(MaterialVertexSpecFlags.CastShadow);
|
|
85
|
+
ctx.mesh.receiveShadow = descriptor.getFlag(MaterialVertexSpecFlags.ReceiveShadow);
|
|
86
|
+
|
|
79
87
|
this.material_contexts.set(key, ctx);
|
|
80
88
|
this.__contexts_array.push(ctx);
|
|
81
89
|
|
|
@@ -95,7 +103,9 @@ export class MicronRenderContext {
|
|
|
95
103
|
*/
|
|
96
104
|
const instance_to_ctx = new Map();
|
|
97
105
|
|
|
98
|
-
|
|
106
|
+
let i = 0;
|
|
107
|
+
|
|
108
|
+
for (i = 0; i < context_count; i++) {
|
|
99
109
|
const ctx = contexts[i];
|
|
100
110
|
ctx.__patch_count = 0;
|
|
101
111
|
|
|
@@ -109,7 +119,7 @@ export class MicronRenderContext {
|
|
|
109
119
|
const patch_count = geometry.patch_count;
|
|
110
120
|
const patches = geometry.patches;
|
|
111
121
|
|
|
112
|
-
for (
|
|
122
|
+
for (i = 0; i < patch_count; i++) {
|
|
113
123
|
const i3 = i * 3;
|
|
114
124
|
|
|
115
125
|
const instance_id = patches[i3];
|
|
@@ -118,7 +128,9 @@ export class MicronRenderContext {
|
|
|
118
128
|
|
|
119
129
|
const ctx = instance_to_ctx.get(instance_id);
|
|
120
130
|
|
|
121
|
-
const
|
|
131
|
+
const target_index = ctx.__patch_count;
|
|
132
|
+
const target_address = target_index * 3;
|
|
133
|
+
|
|
122
134
|
ctx.__patch_count++;
|
|
123
135
|
|
|
124
136
|
const ctx_patches = ctx.__patches;
|
|
@@ -129,7 +141,7 @@ export class MicronRenderContext {
|
|
|
129
141
|
}
|
|
130
142
|
|
|
131
143
|
// build each context
|
|
132
|
-
for (
|
|
144
|
+
for (i = 0; i < context_count; i++) {
|
|
133
145
|
const ctx = contexts[i];
|
|
134
146
|
|
|
135
147
|
ctx.build(geometry);
|
|
@@ -2,6 +2,13 @@ import Vector2 from "../../../../../core/geom/Vector2.js";
|
|
|
2
2
|
import { MaterialContext } from "./MaterialContext.js";
|
|
3
3
|
import { array_copy } from "../../../../../core/collection/array/copyArray.js";
|
|
4
4
|
import { MicronRenderContext } from "./MicronRenderContext.js";
|
|
5
|
+
import { MaterialVertexSpec, MaterialVertexSpecFlags } from "./MaterialVertexSpec.js";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @type {MaterialVertexSpec}
|
|
10
|
+
*/
|
|
11
|
+
const scratch_mvc = new MaterialVertexSpec();
|
|
5
12
|
|
|
6
13
|
export class VGThreeRenderer {
|
|
7
14
|
constructor() {
|
|
@@ -73,7 +80,12 @@ export class VGThreeRenderer {
|
|
|
73
80
|
*/
|
|
74
81
|
const m = o;
|
|
75
82
|
|
|
76
|
-
|
|
83
|
+
scratch_mvc.material = m.material;
|
|
84
|
+
scratch_mvc.spec = m.geometry.vertex_spec;
|
|
85
|
+
scratch_mvc.writeFlag(MaterialVertexSpecFlags.CastShadow, m.castShadow);
|
|
86
|
+
scratch_mvc.writeFlag(MaterialVertexSpecFlags.ReceiveShadow, m.receiveShadow);
|
|
87
|
+
|
|
88
|
+
const ctx = render_context.obtain_material(scratch_mvc);
|
|
77
89
|
|
|
78
90
|
ctx.instances.add(o.instance_id);
|
|
79
91
|
}
|
|
@@ -436,7 +436,7 @@ class Path {
|
|
|
436
436
|
*/
|
|
437
437
|
sample_linear(result, offset) {
|
|
438
438
|
|
|
439
|
-
const found = this.
|
|
439
|
+
const found = this.find_index_and_normalized_distance(scratch_array_0, offset);
|
|
440
440
|
|
|
441
441
|
if (!found) {
|
|
442
442
|
return false;
|
|
@@ -466,9 +466,8 @@ class Path {
|
|
|
466
466
|
* @param {number[]} result
|
|
467
467
|
* @param {number} absolute_distance absolute distance along the path
|
|
468
468
|
* @returns {boolean}
|
|
469
|
-
* @private
|
|
470
469
|
*/
|
|
471
|
-
|
|
470
|
+
find_index_and_normalized_distance(result, absolute_distance) {
|
|
472
471
|
const pointCount = this.getPointCount();
|
|
473
472
|
|
|
474
473
|
if (pointCount === 0) {
|
|
@@ -495,10 +494,10 @@ class Path {
|
|
|
495
494
|
// we found v1
|
|
496
495
|
const local_offset = absolute_distance - cursor;
|
|
497
496
|
|
|
498
|
-
const
|
|
497
|
+
const local_normalized_offset = local_offset / distance;
|
|
499
498
|
|
|
500
499
|
result[0] = i - 1;
|
|
501
|
-
result[1] =
|
|
500
|
+
result[1] = local_normalized_offset;
|
|
502
501
|
return true;
|
|
503
502
|
}
|
|
504
503
|
|
|
@@ -525,7 +524,7 @@ class Path {
|
|
|
525
524
|
* @returns {boolean}
|
|
526
525
|
*/
|
|
527
526
|
sample_catmull_rom(result, offset) {
|
|
528
|
-
const found = this.
|
|
527
|
+
const found = this.find_index_and_normalized_distance(scratch_array_0, offset);
|
|
529
528
|
|
|
530
529
|
if (!found) {
|
|
531
530
|
return false;
|
package/package.json
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
"productName": "Meep",
|
|
6
6
|
"description": "production-ready JavaScript game engine based on Entity Component System Architecture",
|
|
7
7
|
"author": "Alexander Goldring",
|
|
8
|
-
"version": "2.35.
|
|
8
|
+
"version": "2.35.14",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|