@carbonenginejs/runtime-utils 0.1.0
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/LICENSE +21 -0
- package/NOTICE +23 -0
- package/README.md +56 -0
- package/THIRD-PARTY-NOTICES.md +38 -0
- package/docs/README.md +81 -0
- package/docs/architecture.md +101 -0
- package/docs/concepts/foundation-consolidation.md +86 -0
- package/docs/const-kb.md +92 -0
- package/docs/core-types/DECORATOR-TODOS.md +25 -0
- package/docs/core-types/README.md +203 -0
- package/docs/reference/api.md +70 -0
- package/docs/reference/classes/README.md +115 -0
- package/package.json +132 -0
- package/src/arrays.js +5 -0
- package/src/audio/audioFormats.js +34 -0
- package/src/audio/index.js +1 -0
- package/src/box3.js +1385 -0
- package/src/bytes.js +56 -0
- package/src/compression.js +56 -0
- package/src/constants/index.js +6 -0
- package/src/constants.js +15 -0
- package/src/curve.js +419 -0
- package/src/d3d/dxgiFormats.js +46 -0
- package/src/d3d/index.js +2 -0
- package/src/d3d/primitiveTopology.js +11 -0
- package/src/document/CjsCarbonDocument.js +212 -0
- package/src/document/CjsClassRegistry.js +373 -0
- package/src/document/CjsDocumentDehydrator.js +142 -0
- package/src/document/CjsDocumentHydrator.js +156 -0
- package/src/document/CjsStructRegistry.js +348 -0
- package/src/document/hydrationAdapter.js +129 -0
- package/src/document/index.js +6 -0
- package/src/geometry/box.js +137 -0
- package/src/geometry/cylinder.js +244 -0
- package/src/geometry/helpers/LICENSE +15 -0
- package/src/geometry/helpers/earcut.js +766 -0
- package/src/geometry/helpers/misc.js +103 -0
- package/src/geometry/index.js +8 -0
- package/src/geometry/json.js +165 -0
- package/src/geometry/lathe.js +172 -0
- package/src/geometry/octahedron.js +0 -0
- package/src/geometry/plane.js +81 -0
- package/src/geometry/shape.js +95 -0
- package/src/geometry/sphere.js +123 -0
- package/src/geometry/torus.js +96 -0
- package/src/graphics/colorSpaces.js +22 -0
- package/src/graphics/index.js +4 -0
- package/src/graphics/pixelFormats.js +158 -0
- package/src/graphics/textureDimensions.js +22 -0
- package/src/graphics/trinityEnums.js +87 -0
- package/src/index.js +58 -0
- package/src/is.js +524 -0
- package/src/json.js +23 -0
- package/src/lifecycle/CjsLifecycleState.js +77 -0
- package/src/lifecycle/index.js +1 -0
- package/src/lne3.js +497 -0
- package/src/lookup.js +48 -0
- package/src/mat3.js +131 -0
- package/src/mat4.js +699 -0
- package/src/math/index.js +25 -0
- package/src/math/scalar.js +63 -0
- package/src/media/index.js +1 -0
- package/src/media/mediaTypes.js +50 -0
- package/src/mesh.js +394 -0
- package/src/model/CjsEventEmitter.js +333 -0
- package/src/model/CjsModel.js +1544 -0
- package/src/model/CjsModelState.js +72 -0
- package/src/model/index.js +4 -0
- package/src/model/sourceRecordUtils.js +54 -0
- package/src/noise.js +310 -0
- package/src/num.js +827 -0
- package/src/path.js +21 -0
- package/src/pln.js +762 -0
- package/src/pool.js +160 -0
- package/src/quat.js +144 -0
- package/src/ray3.js +1085 -0
- package/src/renderContext/formats.js +145 -0
- package/src/renderContext/index.js +5 -0
- package/src/renderContext/presentation.js +125 -0
- package/src/renderContext/resources.js +27 -0
- package/src/renderContext/upscaling.js +22 -0
- package/src/renderContext/window.js +20 -0
- package/src/runtime/CjsRuntimeState.js +50 -0
- package/src/schema/CjsSchema.js +1009 -0
- package/src/schema/index.js +17 -0
- package/src/shader/index.js +1 -0
- package/src/shader/shaderStages.js +37 -0
- package/src/sph3.js +754 -0
- package/src/tangent.js +288 -0
- package/src/text.js +40 -0
- package/src/tri3.js +650 -0
- package/src/types/carbonTypes.js +635 -0
- package/src/types/index.js +2 -0
- package/src/utils.js +58 -0
- package/src/validation.js +46 -0
- package/src/vec2.js +229 -0
- package/src/vec3.js +1172 -0
- package/src/vec4.js +347 -0
- package/src/vertex.js +108 -0
- package/src/webgpu/index.js +1 -0
- package/src/webgpu/textureFormats.js +121 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { num } from "../../num.js";
|
|
2
|
+
import { triangulate } from "./earcut.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
// Ported from ThreeJS
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Calculates the area of a points polygon
|
|
9
|
+
* @param {Array<Float32Array|Array>} points
|
|
10
|
+
* @returns {number}
|
|
11
|
+
*/
|
|
12
|
+
export function getShapeArea(points)
|
|
13
|
+
{
|
|
14
|
+
const n = points.length;
|
|
15
|
+
let a = 0.0;
|
|
16
|
+
for (let p = n - 1, q = 0; q < n; p = q++)
|
|
17
|
+
{
|
|
18
|
+
a += points[p][0] * points[q][1] - points[q][0] * points[p][1];
|
|
19
|
+
}
|
|
20
|
+
return a * 0.5;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Checks if clockwise oriented points
|
|
25
|
+
* @param {Array<Float32Array|Array>} pts
|
|
26
|
+
* @returns {boolean}
|
|
27
|
+
*/
|
|
28
|
+
export function isShapeClockWise(points)
|
|
29
|
+
{
|
|
30
|
+
return getShapeArea(points) < 0;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Removes duplicate start and end points
|
|
35
|
+
* @param {Array<Float32Array|Array>} points
|
|
36
|
+
*/
|
|
37
|
+
function removeDupEndPts(points)
|
|
38
|
+
{
|
|
39
|
+
const len = points.length;
|
|
40
|
+
if (len <= 2) return;
|
|
41
|
+
|
|
42
|
+
const
|
|
43
|
+
start = points[0],
|
|
44
|
+
end = points[len - 1];
|
|
45
|
+
|
|
46
|
+
for (let x = 0; x < start.length; x++)
|
|
47
|
+
{
|
|
48
|
+
if (!num.equals(start[x], end[x])) return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
points.pop();
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Adds a points to positions array
|
|
56
|
+
* @param {Array} positions
|
|
57
|
+
* @param {Array<Float32Array>} points
|
|
58
|
+
*/
|
|
59
|
+
function addContour(positions, points)
|
|
60
|
+
{
|
|
61
|
+
for (let i = 0; i < points.length; i++)
|
|
62
|
+
{
|
|
63
|
+
for (let x = 0; x < points[i].length; x++)
|
|
64
|
+
{
|
|
65
|
+
positions.push(points[i][x]);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
*
|
|
72
|
+
* @param points
|
|
73
|
+
* @param holes
|
|
74
|
+
* @returns {*[]}
|
|
75
|
+
*/
|
|
76
|
+
export function triangulateShape(points, holes = [])
|
|
77
|
+
{
|
|
78
|
+
const
|
|
79
|
+
positions = [], // flat array of positions like [ x0,y0, x1,y1, x2,y2, ... ]
|
|
80
|
+
holeIndices = [], // array of hole indices
|
|
81
|
+
faces = []; // final array of vertex indices like [ [ a,b,d ], [ b,c,d ] ]
|
|
82
|
+
|
|
83
|
+
removeDupEndPts(points);
|
|
84
|
+
addContour(positions, points);
|
|
85
|
+
|
|
86
|
+
let holeIndex = points.length;
|
|
87
|
+
holes.forEach(removeDupEndPts);
|
|
88
|
+
for (let i = 0; i < holes.length; i++)
|
|
89
|
+
{
|
|
90
|
+
holeIndices.push(holeIndex);
|
|
91
|
+
holeIndex += holes[i].length;
|
|
92
|
+
addContour(positions, holes[i]);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const triangles = triangulate(positions, holeIndices);
|
|
96
|
+
for (let i = 0; i < triangles.length; i += 3)
|
|
97
|
+
{
|
|
98
|
+
faces.push(triangles.slice(i, i + 3));
|
|
99
|
+
}
|
|
100
|
+
return faces;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { toArray } from "../utils.js";
|
|
2
|
+
import { calculateNormals, calculateTangents } from "../vertex.js";
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
function isAreaLike(value)
|
|
6
|
+
{
|
|
7
|
+
if (!value) return false;
|
|
8
|
+
if (Array.isArray(value)) return !!value.length && typeof value[0] === "object" && value[0] !== null && "start" in value[0];
|
|
9
|
+
return typeof value === "object" && "start" in value;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Converts index and vertex buffer data to geometry json
|
|
15
|
+
* @param {Array|TypedArray} indices
|
|
16
|
+
* @param {Array|TypedArray} positions
|
|
17
|
+
* @param {Array|TypedArray} uvs
|
|
18
|
+
* @param {Array|TypedArray|Array|Object} [normals]
|
|
19
|
+
* @param {Array|Object} [areas]
|
|
20
|
+
* @param {Array|TypedArray} [tangents]
|
|
21
|
+
* @throw If required data is not provided
|
|
22
|
+
* @returns {Object}
|
|
23
|
+
*/
|
|
24
|
+
export function toJSON(indices, positions, uvs, normals, areas, tangents)
|
|
25
|
+
{
|
|
26
|
+
if (!indices || !positions || !uvs || !indices.length || !positions.length || !uvs.length)
|
|
27
|
+
{
|
|
28
|
+
throw new Error("Invalid inputs");
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (isAreaLike(normals))
|
|
32
|
+
{
|
|
33
|
+
const temp = areas;
|
|
34
|
+
areas = normals;
|
|
35
|
+
normals = temp;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const vertexCount = positions.length / 3;
|
|
39
|
+
if (!Number.isInteger(vertexCount) ||
|
|
40
|
+
indices.length % 3 !== 0 ||
|
|
41
|
+
uvs.length !== vertexCount * 2)
|
|
42
|
+
{
|
|
43
|
+
throw new Error("Invalid geometry channel lengths");
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
for (let i = 0; i < indices.length; i++)
|
|
47
|
+
{
|
|
48
|
+
if (!Number.isInteger(indices[i]) || indices[i] < 0 || indices[i] >= vertexCount)
|
|
49
|
+
{
|
|
50
|
+
throw new Error(`Invalid vertex index at ${i}`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (normals && normals.length && normals.length !== positions.length)
|
|
55
|
+
{
|
|
56
|
+
throw new Error("Invalid normal channel length");
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
if (tangents && tangents.length &&
|
|
60
|
+
tangents.length !== vertexCount * 3 &&
|
|
61
|
+
tangents.length !== vertexCount * 4)
|
|
62
|
+
{
|
|
63
|
+
throw new Error("Invalid tangent channel length");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
areas = toArray(areas && areas.length !== 0 ? areas : { start: 0, count: indices.length });
|
|
67
|
+
for (const area of areas)
|
|
68
|
+
{
|
|
69
|
+
if (!area ||
|
|
70
|
+
!Number.isInteger(area.start) ||
|
|
71
|
+
!Number.isInteger(area.count) ||
|
|
72
|
+
area.start < 0 ||
|
|
73
|
+
area.count < 0 ||
|
|
74
|
+
area.start % 3 !== 0 ||
|
|
75
|
+
area.count % 3 !== 0 ||
|
|
76
|
+
area.start + area.count > indices.length)
|
|
77
|
+
{
|
|
78
|
+
throw new Error("Invalid geometry area");
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (!normals || !normals.length)
|
|
83
|
+
{
|
|
84
|
+
normals = calculateNormals(indices, positions);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (!tangents || !tangents.length)
|
|
88
|
+
{
|
|
89
|
+
tangents = calculateTangents(indices, positions, uvs, areas, normals);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
meshes: [ {
|
|
94
|
+
name: "",
|
|
95
|
+
vertex: {
|
|
96
|
+
position: positions,
|
|
97
|
+
texcoord0: uvs,
|
|
98
|
+
tangent: tangents,
|
|
99
|
+
normal: normals,
|
|
100
|
+
texcoord1: null,
|
|
101
|
+
binormal: null,
|
|
102
|
+
blendIndice: null,
|
|
103
|
+
blendWeight: null
|
|
104
|
+
},
|
|
105
|
+
indices: areas.map(area =>
|
|
106
|
+
{
|
|
107
|
+
const faces = indices.slice(area.start, area.start + area.count);
|
|
108
|
+
const requires32Bit = faces.some(index => index > 0xffff);
|
|
109
|
+
const bytesPerIndex = area.bytesPerIndex ?? (requires32Bit ? 4 : 2);
|
|
110
|
+
if ((bytesPerIndex !== 2 && bytesPerIndex !== 4) ||
|
|
111
|
+
(bytesPerIndex === 2 && requires32Bit))
|
|
112
|
+
{
|
|
113
|
+
throw new Error("Invalid bytes per index");
|
|
114
|
+
}
|
|
115
|
+
return {
|
|
116
|
+
bytesPerIndex,
|
|
117
|
+
start: area.start,
|
|
118
|
+
count: area.count,
|
|
119
|
+
faces
|
|
120
|
+
};
|
|
121
|
+
})
|
|
122
|
+
} ]
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Creates a mesh container from geometry json
|
|
129
|
+
* @param {Object} tw2
|
|
130
|
+
* @param {Object} json
|
|
131
|
+
* @param {String} [name=""]
|
|
132
|
+
* @param {Object} [autoCreateMeshAreas={}]
|
|
133
|
+
* @returns {Object}
|
|
134
|
+
*/
|
|
135
|
+
export function toContainer(tw2, json, name = "", autoCreateMeshAreas = {})
|
|
136
|
+
{
|
|
137
|
+
const
|
|
138
|
+
container = new tw2.EveChildMesh(),
|
|
139
|
+
mesh = container.mesh = new tw2.Tw2Mesh(),
|
|
140
|
+
res = mesh.geometryResource = new tw2.Tw2GeometryRes();
|
|
141
|
+
|
|
142
|
+
container.name = name;
|
|
143
|
+
res.UpdateFromJSON(json);
|
|
144
|
+
res.OnPrepared();
|
|
145
|
+
|
|
146
|
+
Object
|
|
147
|
+
.keys(autoCreateMeshAreas)
|
|
148
|
+
.forEach(areaName =>
|
|
149
|
+
{
|
|
150
|
+
const effect = autoCreateMeshAreas[areaName] || {};
|
|
151
|
+
for (let m = 0; m < res.meshes.length; m++)
|
|
152
|
+
{
|
|
153
|
+
for (let a = 0; a < res.meshes[m].areas.length; a++)
|
|
154
|
+
{
|
|
155
|
+
const meshArea = new tw2.Tw2MeshArea();
|
|
156
|
+
meshArea.meshIndex = m;
|
|
157
|
+
meshArea.index = a;
|
|
158
|
+
meshArea.effect = tw2.Tw2Effect.from(effect);
|
|
159
|
+
mesh[areaName].push(meshArea);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return container;
|
|
165
|
+
}
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { num } from "../num.js";
|
|
2
|
+
import { vec2 } from "../vec2.js";
|
|
3
|
+
import { vec3 } from "../vec3.js";
|
|
4
|
+
import { toJSON } from "./json.js";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a lathe
|
|
8
|
+
* @author Three.js converted
|
|
9
|
+
**/
|
|
10
|
+
export function createLathe(options = {})
|
|
11
|
+
{
|
|
12
|
+
let {
|
|
13
|
+
points = [
|
|
14
|
+
vec2.fromValues(0, -0.5),
|
|
15
|
+
vec2.fromValues(0.5, 0),
|
|
16
|
+
vec2.fromValues(0, 0.5)
|
|
17
|
+
],
|
|
18
|
+
segments = 12,
|
|
19
|
+
phiStart = 0,
|
|
20
|
+
phiLength = Math.PI * 2
|
|
21
|
+
} = options;
|
|
22
|
+
|
|
23
|
+
segments = Math.max(3, Math.floor(Number.isFinite(segments) ? segments : 12));
|
|
24
|
+
if (!Array.isArray(points) || points.length < 2)
|
|
25
|
+
{
|
|
26
|
+
throw new Error("Lathe requires at least two profile points");
|
|
27
|
+
}
|
|
28
|
+
if (!Number.isFinite(phiStart) || !Number.isFinite(phiLength) || phiLength <= 0)
|
|
29
|
+
{
|
|
30
|
+
throw new Error("Invalid lathe sweep");
|
|
31
|
+
}
|
|
32
|
+
let hasRadius = false;
|
|
33
|
+
for (let i = 0; i < points.length; i++)
|
|
34
|
+
{
|
|
35
|
+
const point = points[i];
|
|
36
|
+
if (!point || point.length < 2 || !Number.isFinite(point[0]) || !Number.isFinite(point[1]))
|
|
37
|
+
{
|
|
38
|
+
throw new Error("Invalid lathe profile point");
|
|
39
|
+
}
|
|
40
|
+
hasRadius ||= point[0] !== 0;
|
|
41
|
+
if (i > 0 && point[0] === points[i - 1][0] && point[1] === points[i - 1][1])
|
|
42
|
+
{
|
|
43
|
+
throw new Error("Lathe profile contains a zero-length segment");
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
if (!hasRadius) throw new Error("Lathe profile must leave the rotation axis");
|
|
47
|
+
|
|
48
|
+
// clamp phiLength so it's in range of [ 0, 2PI ]
|
|
49
|
+
phiLength = num.clamp(phiLength, 0, Math.PI * 2);
|
|
50
|
+
|
|
51
|
+
// buffers
|
|
52
|
+
const
|
|
53
|
+
indices = [],
|
|
54
|
+
positions = [],
|
|
55
|
+
uvs = [],
|
|
56
|
+
initNormals = [],
|
|
57
|
+
normals = [];
|
|
58
|
+
|
|
59
|
+
// helper variables
|
|
60
|
+
const
|
|
61
|
+
inverseSegments = 1.0 / segments,
|
|
62
|
+
normal = vec3.alloc(),
|
|
63
|
+
curNormal = vec3.alloc(),
|
|
64
|
+
prevNormal = vec3.alloc();
|
|
65
|
+
|
|
66
|
+
let dx = 0,
|
|
67
|
+
dy = 0;
|
|
68
|
+
|
|
69
|
+
// pre-compute normals for initial "meridian"
|
|
70
|
+
for (let j = 0; j <= (points.length - 1); j++)
|
|
71
|
+
{
|
|
72
|
+
switch (j)
|
|
73
|
+
{
|
|
74
|
+
case 0: // special handling for 1st vertex on path
|
|
75
|
+
dx = points[j + 1][0] - points[j][0];
|
|
76
|
+
dy = points[j + 1][1] - points[j][1];
|
|
77
|
+
|
|
78
|
+
normal[0] = dy * 1.0;
|
|
79
|
+
normal[1] = -dx;
|
|
80
|
+
normal[2] = dy * 0.0;
|
|
81
|
+
|
|
82
|
+
vec3.copy(prevNormal, normal);
|
|
83
|
+
vec3.normalize(normal, normal);
|
|
84
|
+
initNormals.push(normal[0], normal[1], normal[2]);
|
|
85
|
+
break;
|
|
86
|
+
|
|
87
|
+
case (points.length - 1): // special handling for last Vertex on path
|
|
88
|
+
vec3.normalize(prevNormal, prevNormal);
|
|
89
|
+
initNormals.push(prevNormal[0], prevNormal[1], prevNormal[2]);
|
|
90
|
+
break;
|
|
91
|
+
|
|
92
|
+
default: // default handling for all positions in between
|
|
93
|
+
dx = points[j + 1][0] - points[j][0];
|
|
94
|
+
dy = points[j + 1][1] - points[j][1];
|
|
95
|
+
|
|
96
|
+
normal[0] = dy * 1.0;
|
|
97
|
+
normal[1] = -dx;
|
|
98
|
+
normal[2] = dy * 0.0;
|
|
99
|
+
vec3.copy(curNormal, normal);
|
|
100
|
+
|
|
101
|
+
normal[0] += prevNormal[0];
|
|
102
|
+
normal[1] += prevNormal[1];
|
|
103
|
+
normal[2] += prevNormal[2];
|
|
104
|
+
vec3.normalize(normal, normal);
|
|
105
|
+
|
|
106
|
+
initNormals.push(normal[0], normal[1], normal[2]);
|
|
107
|
+
vec3.copy(prevNormal, curNormal);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// generate positions, uvs and normals
|
|
112
|
+
for (let i = 0; i <= segments; i++)
|
|
113
|
+
{
|
|
114
|
+
const
|
|
115
|
+
phi = phiStart + i * inverseSegments * phiLength,
|
|
116
|
+
sin = Math.sin(phi),
|
|
117
|
+
cos = Math.cos(phi);
|
|
118
|
+
|
|
119
|
+
for (let j = 0; j <= (points.length - 1); j++)
|
|
120
|
+
{
|
|
121
|
+
// vertex
|
|
122
|
+
positions.push(
|
|
123
|
+
points[j][0] * sin,
|
|
124
|
+
points[j][1],
|
|
125
|
+
points[j][0] * cos
|
|
126
|
+
);
|
|
127
|
+
// uv
|
|
128
|
+
uvs.push(
|
|
129
|
+
i / segments,
|
|
130
|
+
j / (points.length - 1)
|
|
131
|
+
);
|
|
132
|
+
// normal
|
|
133
|
+
normals.push(
|
|
134
|
+
initNormals[3 * j + 0] * sin,
|
|
135
|
+
initNormals[3 * j + 1],
|
|
136
|
+
initNormals[3 * j + 0] * cos
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
vec3.unalloc(normal);
|
|
142
|
+
vec3.unalloc(curNormal);
|
|
143
|
+
vec3.unalloc(prevNormal);
|
|
144
|
+
|
|
145
|
+
// indices
|
|
146
|
+
for (let i = 0; i < segments; i++)
|
|
147
|
+
{
|
|
148
|
+
for (let j = 0; j < (points.length - 1); j++)
|
|
149
|
+
{
|
|
150
|
+
const
|
|
151
|
+
base = j + i * points.length,
|
|
152
|
+
a = base,
|
|
153
|
+
b = base + points.length,
|
|
154
|
+
c = base + points.length + 1,
|
|
155
|
+
d = base + 1;
|
|
156
|
+
|
|
157
|
+
// faces
|
|
158
|
+
if (points[j][0] !== 0) indices.push(a, b, d);
|
|
159
|
+
if (points[j + 1][0] !== 0) indices.push(c, d, b);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const result = toJSON(indices, positions, uvs, normals);
|
|
164
|
+
result.factory = createLathe;
|
|
165
|
+
result.options = {
|
|
166
|
+
points,
|
|
167
|
+
segments,
|
|
168
|
+
phiStart,
|
|
169
|
+
phiLength,
|
|
170
|
+
};
|
|
171
|
+
return result;
|
|
172
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { toJSON } from "./json.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Creates a plane
|
|
5
|
+
* @author Three.js converted
|
|
6
|
+
* @param {Object} [options={}]
|
|
7
|
+
* @param {Number} [options.width=1]
|
|
8
|
+
* @param {Number} [options.height=1]
|
|
9
|
+
* @param {Number} [widthSegments=1]
|
|
10
|
+
* @param {Number} [heightSegments=1]
|
|
11
|
+
* @returns {Object}
|
|
12
|
+
*/
|
|
13
|
+
export function createPlane(options = {})
|
|
14
|
+
{
|
|
15
|
+
const {
|
|
16
|
+
width = 1,
|
|
17
|
+
height = 1,
|
|
18
|
+
widthSegments = 1,
|
|
19
|
+
heightSegments = 1
|
|
20
|
+
} = options;
|
|
21
|
+
|
|
22
|
+
if (!Number.isFinite(width) || !Number.isFinite(height) || width === 0 || height === 0)
|
|
23
|
+
{
|
|
24
|
+
throw new Error("Plane dimensions must be finite and non-zero");
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const
|
|
28
|
+
indices = [],
|
|
29
|
+
positions = [],
|
|
30
|
+
normals = [],
|
|
31
|
+
uvs = [];
|
|
32
|
+
|
|
33
|
+
const
|
|
34
|
+
widthHalf = width / 2,
|
|
35
|
+
heightHalf = height / 2,
|
|
36
|
+
gridX = Math.max(1, Math.floor(Number.isFinite(widthSegments) ? widthSegments : 1)),
|
|
37
|
+
gridY = Math.max(1, Math.floor(Number.isFinite(heightSegments) ? heightSegments : 1)),
|
|
38
|
+
gridX1 = gridX + 1,
|
|
39
|
+
gridY1 = gridY + 1,
|
|
40
|
+
segmentWidth = width / gridX,
|
|
41
|
+
segmentHeight = height / gridY;
|
|
42
|
+
|
|
43
|
+
for (let iy = 0; iy < gridY1; iy++)
|
|
44
|
+
{
|
|
45
|
+
const y = iy * segmentHeight - heightHalf;
|
|
46
|
+
for (let ix = 0; ix < gridX1; ix++)
|
|
47
|
+
{
|
|
48
|
+
const x = ix * segmentWidth - widthHalf;
|
|
49
|
+
positions.push(x, -y, 0);
|
|
50
|
+
normals.push(0, 0, 1);
|
|
51
|
+
uvs.push(ix / gridX);
|
|
52
|
+
uvs.push(1 - (iy / gridY));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
for (let iy = 0; iy < gridY; iy++)
|
|
57
|
+
{
|
|
58
|
+
for (let ix = 0; ix < gridX; ix++)
|
|
59
|
+
{
|
|
60
|
+
const
|
|
61
|
+
a = ix + gridX1 * iy,
|
|
62
|
+
b = ix + gridX1 * (iy + 1),
|
|
63
|
+
c = (ix + 1) + gridX1 * (iy + 1),
|
|
64
|
+
d = (ix + 1) + gridX1 * iy;
|
|
65
|
+
|
|
66
|
+
indices.push(a, b, d);
|
|
67
|
+
indices.push(b, c, d);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// Normalize
|
|
72
|
+
const result = toJSON(indices, positions, uvs, normals);
|
|
73
|
+
result.factory = createPlane;
|
|
74
|
+
result.options = {
|
|
75
|
+
width,
|
|
76
|
+
height,
|
|
77
|
+
widthSegments: gridX,
|
|
78
|
+
heightSegments: gridY
|
|
79
|
+
};
|
|
80
|
+
return result;
|
|
81
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { toArray } from "../utils.js";
|
|
2
|
+
import { isShapeClockWise, triangulateShape } from "./helpers/misc.js";
|
|
3
|
+
import { toJSON } from "./json.js";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Creates a shape
|
|
7
|
+
* @author Three.js converted
|
|
8
|
+
**/
|
|
9
|
+
export function createShape(shapes)
|
|
10
|
+
{
|
|
11
|
+
shapes = toArray(shapes);
|
|
12
|
+
|
|
13
|
+
const
|
|
14
|
+
indices = [],
|
|
15
|
+
positions = [],
|
|
16
|
+
normals = [],
|
|
17
|
+
uvs = [];
|
|
18
|
+
|
|
19
|
+
let areaStart = 0,
|
|
20
|
+
areaCount = 0,
|
|
21
|
+
areas = [];
|
|
22
|
+
|
|
23
|
+
for (let i = 0; i < shapes.length; i++)
|
|
24
|
+
{
|
|
25
|
+
addShape(shapes[i]);
|
|
26
|
+
addArea(areaStart, areaCount, i);
|
|
27
|
+
areaStart += areaCount;
|
|
28
|
+
areaCount = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function addArea(start, count, index)
|
|
32
|
+
{
|
|
33
|
+
areas.push({ start, count, index });
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function addShape(shape)
|
|
37
|
+
{
|
|
38
|
+
let shapeVertices = shape.positions.map(point => Array.from(point)),
|
|
39
|
+
shapeHoles = (shape.holes || []).map(hole => hole.map(point => Array.from(point)));
|
|
40
|
+
|
|
41
|
+
const indexOffset = positions.length / 3;
|
|
42
|
+
|
|
43
|
+
// check direction of positions
|
|
44
|
+
if (isShapeClockWise(shapeVertices) === false)
|
|
45
|
+
{
|
|
46
|
+
shapeVertices = shapeVertices.reverse();
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
for (let i = 0, l = shapeHoles.length; i < l; i++)
|
|
50
|
+
{
|
|
51
|
+
const shapeHole = shapeHoles[i];
|
|
52
|
+
if (isShapeClockWise(shapeHole) === true)
|
|
53
|
+
{
|
|
54
|
+
shapeHoles[i] = shapeHole.reverse();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const faces = triangulateShape(shapeVertices, shapeHoles);
|
|
59
|
+
|
|
60
|
+
// join positions of inner and outer paths to a single array
|
|
61
|
+
for (let i = 0, l = shapeHoles.length; i < l; i++)
|
|
62
|
+
{
|
|
63
|
+
const shapeHole = shapeHoles[i];
|
|
64
|
+
shapeVertices = shapeVertices.concat(shapeHole);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// positions, normals, uvs
|
|
68
|
+
for (let i = 0, l = shapeVertices.length; i < l; i++)
|
|
69
|
+
{
|
|
70
|
+
const vertex = shapeVertices[i];
|
|
71
|
+
positions.push(vertex[0], vertex[1], 0);
|
|
72
|
+
normals.push(0, 0, 1);
|
|
73
|
+
uvs.push(vertex[0], vertex[1]); // world uvs
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// indices
|
|
77
|
+
for (let i = 0, l = faces.length; i < l; i++)
|
|
78
|
+
{
|
|
79
|
+
const
|
|
80
|
+
face = faces[i],
|
|
81
|
+
a = face[0] + indexOffset,
|
|
82
|
+
b = face[1] + indexOffset,
|
|
83
|
+
c = face[2] + indexOffset;
|
|
84
|
+
|
|
85
|
+
indices.push(a, b, c);
|
|
86
|
+
areaCount += 3;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Normalize
|
|
91
|
+
const result = toJSON(indices, positions, uvs, areas, normals);
|
|
92
|
+
result.factory = createShape;
|
|
93
|
+
result.options = shapes;
|
|
94
|
+
return result;
|
|
95
|
+
}
|