@woosh/meep-engine 2.43.22 → 2.43.23
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/geom/3d/tetrahedra/delaunay/compute_delaunay_tetrahedral_mesh.js +2 -2
- package/core/geom/3d/topology/struct/{BinaryElementPool.js → binary/BinaryElementPool.js} +7 -11
- package/core/geom/3d/topology/struct/{BinaryElementPool.spec.js → binary/BinaryElementPool.spec.js} +0 -0
- package/core/geom/3d/topology/struct/{BinaryTopology.js → binary/BinaryTopology.js} +123 -6
- package/core/geom/3d/topology/struct/{BinaryTopology.spec.js → binary/BinaryTopology.spec.js} +0 -0
- package/core/geom/3d/topology/struct/binary/io/OrderedEdge.js +66 -0
- package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.js +188 -0
- package/core/geom/3d/topology/struct/binary/io/bt_index_geometry_to_topology.spec.js +84 -0
- package/core/geom/3d/topology/struct/binary/io/bt_mesh_calc_edges.js +51 -0
- package/core/geom/3d/topology/struct/binary/io/create_edge.js +106 -0
- package/core/geom/3d/topology/struct/binary/io/get_or_create_edge_map.js +26 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.js +16 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_has_vertex.spec.js +15 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.js +21 -0
- package/core/geom/3d/topology/struct/binary/query/bt_mesh_edge_other_vertex.spec.js +16 -0
- package/core/geom/3d/topology/struct/prototypeBinaryTopology.js +14 -14
- package/core/path/PATH_SEPARATOR.js +1 -0
- package/core/path/computeFileExtension.js +24 -0
- package/core/path/computeFileExtension.spec.js +13 -0
- package/core/path/computePathBase.js +21 -0
- package/core/path/computePathBase.spec.js +13 -0
- package/core/path/computePathDirectory.js +25 -0
- package/editor/view/library/MeshLibraryView.js +1 -1
- package/engine/asset/guessAssetType.js +1 -1
- package/engine/asset/loaders/image/ImageRGBADataLoader.js +1 -1
- package/engine/asset/loaders/texture/TextureAssetLoader.js +1 -1
- package/engine/ecs/storage/BinaryBufferSerializer.js +7 -0
- package/engine/graphics/material/composeCompile.js +3 -5
- package/engine/graphics/micron/plugin/GLTFAssetTransformer.js +1 -1
- package/engine/graphics/sh3/README.md +2 -0
- package/engine/graphics/sh3/path_tracer/PathTracer.js +0 -14
- package/engine/graphics/sh3/path_tracer/prototypePathTracer.js +11 -7
- package/engine/graphics/texture/cubemap/load_environment_map.js +1 -1
- package/engine/graphics/util/makeMeshPreviewScene.js +38 -26
- package/engine/network/RemoteController.js +98 -0
- package/engine/network/remoteEditor.js +33 -0
- package/package.json +1 -1
- package/view/elements/MeshPreview.js +5 -1
- package/core/FilePath.js +0 -73
- package/core/FilePath.spec.js +0 -25
- package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.js +0 -329
- package/core/geom/3d/topology/struct/bt_index_geometry_to_topology.spec.js +0 -26
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BinaryBuffer } from "../../core/binary/BinaryBuffer.js";
|
|
2
|
+
import BinaryBufferDeSerializer from "../ecs/storage/BinaryBufferDeSerializer.js";
|
|
3
|
+
|
|
4
|
+
const scratch_buffer = new BinaryBuffer();
|
|
5
|
+
|
|
6
|
+
class LocalAPI {
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @type {Engine|null}
|
|
10
|
+
*/
|
|
11
|
+
engine = null;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
*
|
|
15
|
+
* @param {BinaryBuffer} buffer
|
|
16
|
+
*/
|
|
17
|
+
async writeCurrentSceneDataset(buffer) {
|
|
18
|
+
const deSerializer = new BinaryBufferDeSerializer();
|
|
19
|
+
|
|
20
|
+
const engine = this.engine;
|
|
21
|
+
|
|
22
|
+
const ecd = engine.entityManager.dataset;
|
|
23
|
+
|
|
24
|
+
ecd.clear();
|
|
25
|
+
|
|
26
|
+
const task = deSerializer.process(buffer, engine, ecd);
|
|
27
|
+
|
|
28
|
+
const p = task.promise();
|
|
29
|
+
|
|
30
|
+
engine.executor.run(task);
|
|
31
|
+
|
|
32
|
+
await p;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export class RemoteController {
|
|
37
|
+
/**
|
|
38
|
+
*
|
|
39
|
+
* @type {WebSocket|null}
|
|
40
|
+
*/
|
|
41
|
+
socket = null;
|
|
42
|
+
|
|
43
|
+
localAPI = new LocalAPI();
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
*
|
|
47
|
+
* @param {Engine} engine
|
|
48
|
+
*/
|
|
49
|
+
attach(engine) {
|
|
50
|
+
this.localAPI.engine = engine;
|
|
51
|
+
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
*
|
|
56
|
+
* @param {ArrayBuffer} data
|
|
57
|
+
* @private
|
|
58
|
+
*/
|
|
59
|
+
async __handle_message(data) {
|
|
60
|
+
// console.log(`received message `, data);
|
|
61
|
+
|
|
62
|
+
scratch_buffer.fromArrayBuffer(data);
|
|
63
|
+
|
|
64
|
+
// read packet ID
|
|
65
|
+
const packet_id = scratch_buffer.readUTF8String();
|
|
66
|
+
|
|
67
|
+
console.log(`packet ID: ${packet_id}`);
|
|
68
|
+
|
|
69
|
+
if (this.localAPI[packet_id] === undefined) {
|
|
70
|
+
console.error(`Local API does not have method '${packet_id}'`);
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
try {
|
|
75
|
+
await this.localAPI[packet_id](scratch_buffer);
|
|
76
|
+
} catch (e) {
|
|
77
|
+
//
|
|
78
|
+
console.error(`Failed to process remote call to '${packet_id}':`, e);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
startup() {
|
|
84
|
+
this.socket = new WebSocket('ws://localhost:9020');
|
|
85
|
+
|
|
86
|
+
this.socket.binaryType = "arraybuffer";
|
|
87
|
+
|
|
88
|
+
this.socket.addEventListener('open', () => {
|
|
89
|
+
console.log('socket connection open');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
this.socket.addEventListener('message', (event) => {
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
this.__handle_message(event.data);
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { EngineHarness } from "../EngineHarness.js";
|
|
2
|
+
import { RemoteController } from "./RemoteController.js";
|
|
3
|
+
import { ShadedGeometrySystem } from "../graphics/ecs/mesh-v2/ShadedGeometrySystem.js";
|
|
4
|
+
|
|
5
|
+
const harness = new EngineHarness();
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
*
|
|
9
|
+
* @param {Engine} engine
|
|
10
|
+
* @return {Promise<void>}
|
|
11
|
+
*/
|
|
12
|
+
async function main(engine) {
|
|
13
|
+
|
|
14
|
+
await EngineHarness.buildBasics({
|
|
15
|
+
engine
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const remote = new RemoteController();
|
|
19
|
+
|
|
20
|
+
remote.attach(engine);
|
|
21
|
+
remote.startup();
|
|
22
|
+
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
harness.initialize({
|
|
26
|
+
configuration(config, engine) {
|
|
27
|
+
|
|
28
|
+
config.addSystem(new ShadedGeometrySystem(engine));
|
|
29
|
+
|
|
30
|
+
}
|
|
31
|
+
}).then(main);
|
|
32
|
+
|
|
33
|
+
|
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.43.
|
|
8
|
+
"version": "2.43.23",
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"gl-matrix": "3.4.3",
|
|
11
11
|
"fast-levenshtein": "2.0.6",
|
|
@@ -11,10 +11,14 @@ import { PointerDevice } from '../../engine/input/devices/PointerDevice.js';
|
|
|
11
11
|
import dom from "../DOM.js";
|
|
12
12
|
import { WebGLRendererPool } from "../../engine/graphics/render/RendererPool.js";
|
|
13
13
|
import { advanceAnimation } from "../../engine/ecs/systems/AnimationSystem.js";
|
|
14
|
+
import { computeFileExtension } from "../../core/path/computeFileExtension.js";
|
|
14
15
|
|
|
15
16
|
const mapExtension2Mime = {
|
|
16
17
|
gltf: "model/gltf+json",
|
|
17
18
|
glb: "model/gltf",
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated no longer supported, please don't use
|
|
21
|
+
*/
|
|
18
22
|
json: "three.js"
|
|
19
23
|
};
|
|
20
24
|
|
|
@@ -56,7 +60,7 @@ class MeshPreview extends View {
|
|
|
56
60
|
throw new Error(`Mesh URL was expected to be a string, instead was ${typeof url}: ${s}`);
|
|
57
61
|
}
|
|
58
62
|
|
|
59
|
-
const extension =
|
|
63
|
+
const extension = computeFileExtension(url);
|
|
60
64
|
|
|
61
65
|
if (extension.length === 0) {
|
|
62
66
|
throw new Error(`No file extension on url '${url}'`);
|
package/core/FilePath.js
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
const PATH_SEPARATOR = '/';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Strips all directories from the path.
|
|
5
|
-
* @example 'a/b/c' -> 'c'
|
|
6
|
-
* @param {string} path
|
|
7
|
-
* @returns {string}
|
|
8
|
-
*/
|
|
9
|
-
function computePathBase(path) {
|
|
10
|
-
if (typeof path !== "string") {
|
|
11
|
-
throw new Error('path is not a string');
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const lastSlashIndex = path.lastIndexOf(PATH_SEPARATOR);
|
|
15
|
-
|
|
16
|
-
if (lastSlashIndex !== -1) {
|
|
17
|
-
return path.substring(lastSlashIndex + 1);
|
|
18
|
-
} else {
|
|
19
|
-
return path;
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Returns the directory name of a path, similar to the Unix dirname command.
|
|
25
|
-
* @param {string} path
|
|
26
|
-
* @return {string}
|
|
27
|
-
*/
|
|
28
|
-
export function computePathDirectory(path) {
|
|
29
|
-
if (typeof path !== "string") {
|
|
30
|
-
throw new Error('path is not a string');
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
let lastSlashIndex = path.lastIndexOf(PATH_SEPARATOR);
|
|
34
|
-
|
|
35
|
-
//rewind in case of trailing slashes
|
|
36
|
-
while (lastSlashIndex > 0 && path.charAt(lastSlashIndex - 1) === PATH_SEPARATOR) {
|
|
37
|
-
lastSlashIndex--;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
if (lastSlashIndex !== -1) {
|
|
41
|
-
return path.substring(0, lastSlashIndex);
|
|
42
|
-
} else {
|
|
43
|
-
return path;
|
|
44
|
-
}
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
*
|
|
49
|
-
* @param {string} path
|
|
50
|
-
* @returns {String|null}
|
|
51
|
-
*/
|
|
52
|
-
function computeFileExtension(path) {
|
|
53
|
-
if (typeof path !== "string") {
|
|
54
|
-
throw new Error('path is not a string');
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
//get base
|
|
58
|
-
const pathBase = computePathBase(path);
|
|
59
|
-
|
|
60
|
-
const lastDotIndex = pathBase.lastIndexOf('.');
|
|
61
|
-
|
|
62
|
-
if (lastDotIndex !== -1) {
|
|
63
|
-
return pathBase.substring(lastDotIndex + 1);
|
|
64
|
-
} else {
|
|
65
|
-
//no extension
|
|
66
|
-
return null;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
export {
|
|
71
|
-
computePathBase,
|
|
72
|
-
computeFileExtension
|
|
73
|
-
};
|
package/core/FilePath.spec.js
DELETED
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
import { computeFileExtension, computePathBase } from "./FilePath.js";
|
|
2
|
-
|
|
3
|
-
test('extract extension of cat.txt', () => {
|
|
4
|
-
expect(computeFileExtension('cat.txt')).toEqual('txt');
|
|
5
|
-
});
|
|
6
|
-
|
|
7
|
-
test('extract extension of ../hmm/cat.txt', () => {
|
|
8
|
-
expect(computeFileExtension('../hmm/cat.txt')).toEqual('txt');
|
|
9
|
-
});
|
|
10
|
-
|
|
11
|
-
test('extract extension of empty string', () => {
|
|
12
|
-
expect(computeFileExtension('')).toBeNull();
|
|
13
|
-
});
|
|
14
|
-
|
|
15
|
-
test('extract base path of empty string', () => {
|
|
16
|
-
expect(computePathBase('')).toBe('');
|
|
17
|
-
});
|
|
18
|
-
|
|
19
|
-
test('extract base path of hello', () => {
|
|
20
|
-
expect(computePathBase('hello')).toBe('hello');
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('extract base path of hello/there/world', () => {
|
|
24
|
-
expect(computePathBase('hello/there/world')).toBe('world');
|
|
25
|
-
});
|
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
import { HashMap } from "../../../../collection/HashMap.js";
|
|
2
|
-
import { split_by_2 } from "../../morton/mortonEncode_magicbits.js";
|
|
3
|
-
import { NULL_POINTER } from "./BinaryTopology.js";
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Populates supplied topology with data from supplied indexed geometry
|
|
8
|
-
* @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc
|
|
9
|
-
* @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/io/stl/importer/stl_import_mesh.cc#L63
|
|
10
|
-
* @param {BinaryTopology} out
|
|
11
|
-
* @param {number[]} indices
|
|
12
|
-
* @param {number[]} vertex_positions
|
|
13
|
-
* @param {number[]} [vertex_normals]
|
|
14
|
-
*/
|
|
15
|
-
export function bt_index_geometry_to_topology(
|
|
16
|
-
out,
|
|
17
|
-
indices,
|
|
18
|
-
vertex_positions,
|
|
19
|
-
vertex_normals
|
|
20
|
-
) {
|
|
21
|
-
|
|
22
|
-
// remove any existing data
|
|
23
|
-
out.clear();
|
|
24
|
-
|
|
25
|
-
/*
|
|
26
|
-
NOTES:
|
|
27
|
-
- we use {@link BinaryElementPool#allocate_continuous} to avoid having to allocate additional mapping structure
|
|
28
|
-
*/
|
|
29
|
-
|
|
30
|
-
// ingest all vertices
|
|
31
|
-
const vertex_count = vertex_positions.length / 3;
|
|
32
|
-
|
|
33
|
-
const vertex_allocation_offset = out.vertices.allocate_continuous(vertex_count);
|
|
34
|
-
|
|
35
|
-
for (let i = 0; i < vertex_count; i++) {
|
|
36
|
-
const i3 = i * 3;
|
|
37
|
-
|
|
38
|
-
const vertex_id = i + vertex_allocation_offset;
|
|
39
|
-
|
|
40
|
-
out.vertex_write_coordinate(vertex_id, vertex_positions, i3);
|
|
41
|
-
|
|
42
|
-
if (vertex_normals !== undefined) {
|
|
43
|
-
out.vertex_write_normal(vertex_id, vertex_normals, i3);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
// initialize edge to NULL
|
|
47
|
-
out.vertex_write_edge(vertex_id, NULL_POINTER);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const face_count = indices.length / 3;
|
|
51
|
-
|
|
52
|
-
// reserve some memory to avoid extra allocations
|
|
53
|
-
out.loops.ensure_capacity(face_count * 3);
|
|
54
|
-
out.edges.ensure_capacity(face_count * 1.6);
|
|
55
|
-
|
|
56
|
-
// create faces
|
|
57
|
-
const face_allocation_offset = out.faces.allocate_continuous(face_count);
|
|
58
|
-
|
|
59
|
-
/**
|
|
60
|
-
*
|
|
61
|
-
* @type {HashMap<OrderedEdge, number>}
|
|
62
|
-
*/
|
|
63
|
-
const edge_hash_map = new HashMap({
|
|
64
|
-
capacity: Math.ceil(1 + 2.1 * face_count)
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
for (let i = 0; i < face_count; i++) {
|
|
68
|
-
const i3 = i * 3;
|
|
69
|
-
|
|
70
|
-
const a = indices[i3];
|
|
71
|
-
const b = indices[i3 + 1];
|
|
72
|
-
const c = indices[i3 + 2];
|
|
73
|
-
|
|
74
|
-
const vertex_a = a + vertex_allocation_offset;
|
|
75
|
-
const vertex_b = b + vertex_allocation_offset;
|
|
76
|
-
const vertex_c = c + vertex_allocation_offset;
|
|
77
|
-
|
|
78
|
-
const face_id = i + face_allocation_offset;
|
|
79
|
-
|
|
80
|
-
// create edges
|
|
81
|
-
const edge_ab = get_or_create_edge(vertex_a, vertex_b, edge_hash_map, out);
|
|
82
|
-
const edge_bc = get_or_create_edge(vertex_b, vertex_c, edge_hash_map, out);
|
|
83
|
-
const edge_ca = get_or_create_edge(vertex_c, vertex_a, edge_hash_map, out);
|
|
84
|
-
|
|
85
|
-
// allocate loops
|
|
86
|
-
const loop_a = out.loop_create();
|
|
87
|
-
const loop_b = out.loop_create();
|
|
88
|
-
const loop_c = out.loop_create();
|
|
89
|
-
|
|
90
|
-
// write loops
|
|
91
|
-
out.loop_write_vertex(loop_a, vertex_a);
|
|
92
|
-
out.loop_write_vertex(loop_b, vertex_b);
|
|
93
|
-
out.loop_write_vertex(loop_c, vertex_c);
|
|
94
|
-
|
|
95
|
-
// link loops to edges
|
|
96
|
-
link_loop_to_edge(loop_a, edge_ab, out);
|
|
97
|
-
link_loop_to_edge(loop_b, edge_bc, out);
|
|
98
|
-
link_loop_to_edge(loop_c, edge_ca, out);
|
|
99
|
-
|
|
100
|
-
out.loop_write_face(loop_a, face_id);
|
|
101
|
-
out.loop_write_face(loop_b, face_id);
|
|
102
|
-
out.loop_write_face(loop_c, face_id);
|
|
103
|
-
|
|
104
|
-
/*
|
|
105
|
-
NOTE: Vertices (#BMLoop.v & #BMLoop.next.v) always contain vertices from (#BMEdge.v1 & #BMEdge.v2).
|
|
106
|
-
*/
|
|
107
|
-
// link up loops around the face
|
|
108
|
-
out.loop_write_next(loop_a, loop_b);
|
|
109
|
-
out.loop_write_prev(loop_a, loop_c);
|
|
110
|
-
|
|
111
|
-
out.loop_write_next(loop_b, loop_c);
|
|
112
|
-
out.loop_write_prev(loop_b, loop_a);
|
|
113
|
-
|
|
114
|
-
out.loop_write_next(loop_c, loop_a);
|
|
115
|
-
out.loop_write_prev(loop_c, loop_b);
|
|
116
|
-
|
|
117
|
-
// update face
|
|
118
|
-
out.face_write_loop(face_id, loop_a);
|
|
119
|
-
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
/**
|
|
124
|
-
* follow the pointer list to the end along radial list
|
|
125
|
-
* @param {number} start_loop
|
|
126
|
-
* @param {BinaryTopology} mesh
|
|
127
|
-
* @returns {number}
|
|
128
|
-
*/
|
|
129
|
-
function loop_radial_get_last(start_loop, mesh) {
|
|
130
|
-
let prev_loop = start_loop;
|
|
131
|
-
let next_loop;
|
|
132
|
-
|
|
133
|
-
while ((next_loop = mesh.loop_read_radial_next(prev_loop)) !== NULL_POINTER) {
|
|
134
|
-
prev_loop = next_loop;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return prev_loop;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
/**
|
|
141
|
-
*
|
|
142
|
-
* @param {number} loop_id
|
|
143
|
-
* @param {number} edge_id
|
|
144
|
-
* @param {BinaryTopology} mesh
|
|
145
|
-
*/
|
|
146
|
-
function link_loop_to_edge(loop_id, edge_id, mesh) {
|
|
147
|
-
mesh.loop_write_edge(loop_id, edge_id);
|
|
148
|
-
|
|
149
|
-
/**
|
|
150
|
-
* First loop attached to the edge
|
|
151
|
-
* @type {number}
|
|
152
|
-
*/
|
|
153
|
-
const edge_loop_start = mesh.edge_read_loop(edge_id);
|
|
154
|
-
|
|
155
|
-
if (edge_loop_start === NULL_POINTER) {
|
|
156
|
-
// no loops connected yet
|
|
157
|
-
mesh.edge_write_loop(edge_id, loop_id);
|
|
158
|
-
} else {
|
|
159
|
-
// connect loop to the end of the list
|
|
160
|
-
const last_loop_in_list = loop_radial_get_last(edge_loop_start, mesh);
|
|
161
|
-
|
|
162
|
-
mesh.loop_write_radial_next(last_loop_in_list, loop_id);
|
|
163
|
-
mesh.loop_write_radial_prev(loop_id, last_loop_in_list);
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc#L209
|
|
170
|
-
* @param {BinaryTopology} mesh
|
|
171
|
-
*/
|
|
172
|
-
function bt_mesh_calc_edges(mesh) {
|
|
173
|
-
/**
|
|
174
|
-
*
|
|
175
|
-
* @type {HashMap<OrderedEdge, unknown>}
|
|
176
|
-
*/
|
|
177
|
-
const map = new HashMap();
|
|
178
|
-
|
|
179
|
-
add_polygon_edges_to_map(mesh, map);
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
class OrderedEdge {
|
|
183
|
-
/**
|
|
184
|
-
*
|
|
185
|
-
* @param {number} v1
|
|
186
|
-
* @param {number} v2
|
|
187
|
-
*/
|
|
188
|
-
constructor(v1, v2) {
|
|
189
|
-
if (v1 < v2) {
|
|
190
|
-
this.v_low = v1;
|
|
191
|
-
this.v_high = v2;
|
|
192
|
-
} else {
|
|
193
|
-
this.v_low = v2;
|
|
194
|
-
this.v_high = v1;
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
/**
|
|
199
|
-
*
|
|
200
|
-
* @param {number} v1
|
|
201
|
-
* @param {number} v2
|
|
202
|
-
*/
|
|
203
|
-
set(v1, v2) {
|
|
204
|
-
if (v1 < v2) {
|
|
205
|
-
this.v_low = v1;
|
|
206
|
-
this.v_high = v2;
|
|
207
|
-
} else {
|
|
208
|
-
this.v_low = v2;
|
|
209
|
-
this.v_high = v1;
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
/**
|
|
214
|
-
*
|
|
215
|
-
* @param {OrderedEdge} other
|
|
216
|
-
*/
|
|
217
|
-
copy(other) {
|
|
218
|
-
this.v_low = other.v_low;
|
|
219
|
-
this.v_high = other.v_high;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
clone() {
|
|
223
|
-
return new OrderedEdge(this.v_low, this.v_high);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
*
|
|
228
|
-
* @return {number}
|
|
229
|
-
*/
|
|
230
|
-
hash() {
|
|
231
|
-
return split_by_2(this.v_low) | (split_by_2(this.v_high) << 1);
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
*
|
|
236
|
-
* @param {OrderedEdge} other
|
|
237
|
-
* @returns {boolean}
|
|
238
|
-
*/
|
|
239
|
-
equals(other) {
|
|
240
|
-
return this.v_low === other.v_low
|
|
241
|
-
&& this.v_high === other.v_high
|
|
242
|
-
;
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/**
|
|
247
|
-
* @see https://github.com/blender/blender/blob/9cb061f4f0119e647173e7d354e1457e97632333/source/blender/blenkernel/intern/mesh_calc_edges.cc#L96
|
|
248
|
-
* @param {BinaryTopology} mesh
|
|
249
|
-
* @param {HashMap<OrderedEdge,*>} edge_map
|
|
250
|
-
*/
|
|
251
|
-
function add_polygon_edges_to_map(mesh, edge_map) {
|
|
252
|
-
|
|
253
|
-
const faces = mesh.faces;
|
|
254
|
-
const face_count = faces.size;
|
|
255
|
-
|
|
256
|
-
for (let i = 0; i < face_count; i++) {
|
|
257
|
-
if (!faces.is_allocated(i)) {
|
|
258
|
-
continue;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
const first_loop = mesh.face_read_loop(i);
|
|
262
|
-
let prev_loop = first_loop + j;
|
|
263
|
-
|
|
264
|
-
for (let j = 0; j < 3; j++) {
|
|
265
|
-
const loop = first_loop + j;
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
const prev_v = mesh.loop_read_vertex(prev_loop);
|
|
269
|
-
const next_v = mesh.loop_read_vertex(loop);
|
|
270
|
-
|
|
271
|
-
const edge = new OrderedEdge(prev_v, next_v);
|
|
272
|
-
|
|
273
|
-
edge_map.getOrSet(edge, null);
|
|
274
|
-
|
|
275
|
-
prev_loop = loop;
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
/**
|
|
282
|
-
*
|
|
283
|
-
* @param {number} v0
|
|
284
|
-
* @param {number} v1
|
|
285
|
-
* @param {HashMap<OrderedEdge,number>} map
|
|
286
|
-
* @param {BinaryTopology} mesh
|
|
287
|
-
*/
|
|
288
|
-
function get_or_create_edge(v0, v1, map, mesh) {
|
|
289
|
-
const scratch_ordered_edge = new OrderedEdge(v0, v1);
|
|
290
|
-
|
|
291
|
-
const existing = map.get(scratch_ordered_edge);
|
|
292
|
-
|
|
293
|
-
if (existing !== undefined) {
|
|
294
|
-
return existing;
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
// doesn't exist - create
|
|
298
|
-
const edge_id = mesh.edges.allocate();
|
|
299
|
-
|
|
300
|
-
mesh.edge_write_vertex1(edge_id, v0);
|
|
301
|
-
mesh.edge_write_vertex2(edge_id, v1);
|
|
302
|
-
|
|
303
|
-
// initialize rest of the edge
|
|
304
|
-
mesh.edge_write_loop(edge_id, NULL_POINTER);
|
|
305
|
-
|
|
306
|
-
// TODO process disk_link pointers for the edge
|
|
307
|
-
|
|
308
|
-
map.set(scratch_ordered_edge, edge_id);
|
|
309
|
-
|
|
310
|
-
return edge_id;
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
/**
|
|
315
|
-
* Ensure an edge between two vertices
|
|
316
|
-
* NOTE: works without having to use a separate hash map, resulting in faster execution and smaller RAM requirement
|
|
317
|
-
* @param {number} v0
|
|
318
|
-
* @param {number} v1
|
|
319
|
-
* @param {BinaryTopology} mesh
|
|
320
|
-
*/
|
|
321
|
-
function get_or_create_edge_2(v0, v1, mesh) {
|
|
322
|
-
const first_edge = mesh.vertex_read_edge(v0);
|
|
323
|
-
|
|
324
|
-
// iterate over all edges around the vertex
|
|
325
|
-
|
|
326
|
-
// if match is found - return the match, else create a new edge and assign
|
|
327
|
-
|
|
328
|
-
// TODO implement
|
|
329
|
-
}
|
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
import { bt_index_geometry_to_topology } from "./bt_index_geometry_to_topology.js";
|
|
2
|
-
import { BinaryTopology } from "./BinaryTopology.js";
|
|
3
|
-
|
|
4
|
-
test('Empty geometry work as expected', () => {
|
|
5
|
-
const mesh = new BinaryTopology();
|
|
6
|
-
bt_index_geometry_to_topology(mesh, [], []);
|
|
7
|
-
|
|
8
|
-
expect(mesh.faces.size).toBe(0);
|
|
9
|
-
expect(mesh.vertices.size).toBe(0);
|
|
10
|
-
expect(mesh.edges.size).toBe(0);
|
|
11
|
-
expect(mesh.loops.size).toBe(0);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
test('Single triangle', () => {
|
|
15
|
-
const mesh = new BinaryTopology();
|
|
16
|
-
bt_index_geometry_to_topology(mesh, [0, 1, 2], [
|
|
17
|
-
1, 3, 7,
|
|
18
|
-
11, 13, 17,
|
|
19
|
-
23, 27, -1
|
|
20
|
-
]);
|
|
21
|
-
|
|
22
|
-
expect(mesh.faces.size).toBe(1);
|
|
23
|
-
expect(mesh.loops.size).toBe(3);
|
|
24
|
-
expect(mesh.edges.size).toBe(3);
|
|
25
|
-
expect(mesh.vertices.size).toBe(3);
|
|
26
|
-
});
|