@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.
Files changed (101) hide show
  1. package/LICENSE +21 -0
  2. package/NOTICE +23 -0
  3. package/README.md +56 -0
  4. package/THIRD-PARTY-NOTICES.md +38 -0
  5. package/docs/README.md +81 -0
  6. package/docs/architecture.md +101 -0
  7. package/docs/concepts/foundation-consolidation.md +86 -0
  8. package/docs/const-kb.md +92 -0
  9. package/docs/core-types/DECORATOR-TODOS.md +25 -0
  10. package/docs/core-types/README.md +203 -0
  11. package/docs/reference/api.md +70 -0
  12. package/docs/reference/classes/README.md +115 -0
  13. package/package.json +132 -0
  14. package/src/arrays.js +5 -0
  15. package/src/audio/audioFormats.js +34 -0
  16. package/src/audio/index.js +1 -0
  17. package/src/box3.js +1385 -0
  18. package/src/bytes.js +56 -0
  19. package/src/compression.js +56 -0
  20. package/src/constants/index.js +6 -0
  21. package/src/constants.js +15 -0
  22. package/src/curve.js +419 -0
  23. package/src/d3d/dxgiFormats.js +46 -0
  24. package/src/d3d/index.js +2 -0
  25. package/src/d3d/primitiveTopology.js +11 -0
  26. package/src/document/CjsCarbonDocument.js +212 -0
  27. package/src/document/CjsClassRegistry.js +373 -0
  28. package/src/document/CjsDocumentDehydrator.js +142 -0
  29. package/src/document/CjsDocumentHydrator.js +156 -0
  30. package/src/document/CjsStructRegistry.js +348 -0
  31. package/src/document/hydrationAdapter.js +129 -0
  32. package/src/document/index.js +6 -0
  33. package/src/geometry/box.js +137 -0
  34. package/src/geometry/cylinder.js +244 -0
  35. package/src/geometry/helpers/LICENSE +15 -0
  36. package/src/geometry/helpers/earcut.js +766 -0
  37. package/src/geometry/helpers/misc.js +103 -0
  38. package/src/geometry/index.js +8 -0
  39. package/src/geometry/json.js +165 -0
  40. package/src/geometry/lathe.js +172 -0
  41. package/src/geometry/octahedron.js +0 -0
  42. package/src/geometry/plane.js +81 -0
  43. package/src/geometry/shape.js +95 -0
  44. package/src/geometry/sphere.js +123 -0
  45. package/src/geometry/torus.js +96 -0
  46. package/src/graphics/colorSpaces.js +22 -0
  47. package/src/graphics/index.js +4 -0
  48. package/src/graphics/pixelFormats.js +158 -0
  49. package/src/graphics/textureDimensions.js +22 -0
  50. package/src/graphics/trinityEnums.js +87 -0
  51. package/src/index.js +58 -0
  52. package/src/is.js +524 -0
  53. package/src/json.js +23 -0
  54. package/src/lifecycle/CjsLifecycleState.js +77 -0
  55. package/src/lifecycle/index.js +1 -0
  56. package/src/lne3.js +497 -0
  57. package/src/lookup.js +48 -0
  58. package/src/mat3.js +131 -0
  59. package/src/mat4.js +699 -0
  60. package/src/math/index.js +25 -0
  61. package/src/math/scalar.js +63 -0
  62. package/src/media/index.js +1 -0
  63. package/src/media/mediaTypes.js +50 -0
  64. package/src/mesh.js +394 -0
  65. package/src/model/CjsEventEmitter.js +333 -0
  66. package/src/model/CjsModel.js +1544 -0
  67. package/src/model/CjsModelState.js +72 -0
  68. package/src/model/index.js +4 -0
  69. package/src/model/sourceRecordUtils.js +54 -0
  70. package/src/noise.js +310 -0
  71. package/src/num.js +827 -0
  72. package/src/path.js +21 -0
  73. package/src/pln.js +762 -0
  74. package/src/pool.js +160 -0
  75. package/src/quat.js +144 -0
  76. package/src/ray3.js +1085 -0
  77. package/src/renderContext/formats.js +145 -0
  78. package/src/renderContext/index.js +5 -0
  79. package/src/renderContext/presentation.js +125 -0
  80. package/src/renderContext/resources.js +27 -0
  81. package/src/renderContext/upscaling.js +22 -0
  82. package/src/renderContext/window.js +20 -0
  83. package/src/runtime/CjsRuntimeState.js +50 -0
  84. package/src/schema/CjsSchema.js +1009 -0
  85. package/src/schema/index.js +17 -0
  86. package/src/shader/index.js +1 -0
  87. package/src/shader/shaderStages.js +37 -0
  88. package/src/sph3.js +754 -0
  89. package/src/tangent.js +288 -0
  90. package/src/text.js +40 -0
  91. package/src/tri3.js +650 -0
  92. package/src/types/carbonTypes.js +635 -0
  93. package/src/types/index.js +2 -0
  94. package/src/utils.js +58 -0
  95. package/src/validation.js +46 -0
  96. package/src/vec2.js +229 -0
  97. package/src/vec3.js +1172 -0
  98. package/src/vec4.js +347 -0
  99. package/src/vertex.js +108 -0
  100. package/src/webgpu/index.js +1 -0
  101. 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,8 @@
1
+ export * from "./box.js";
2
+ export * from "./cylinder.js";
3
+ export * from "./plane.js";
4
+ export * from "./shape.js";
5
+ export * from "./sphere.js";
6
+ export * from "./torus.js";
7
+ export * from "./json.js";
8
+ export * from "./lathe.js";
@@ -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
+ }