@carbonenginejs/core-math 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 +24 -0
- package/README.md +110 -0
- package/THIRD-PARTY-NOTICES.md +38 -0
- package/package.json +74 -0
- package/src/box3.js +1329 -0
- package/src/constants.js +16 -0
- package/src/curve.js +390 -0
- package/src/geometry/box.js +132 -0
- package/src/geometry/cylinder.js +234 -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 +7 -0
- package/src/geometry/json.js +115 -0
- package/src/geometry/lathe.js +148 -0
- package/src/geometry/octahedron.js +0 -0
- package/src/geometry/plane.js +76 -0
- package/src/geometry/shape.js +95 -0
- package/src/geometry/sphere.js +116 -0
- package/src/geometry/torus.js +88 -0
- package/src/index.js +29 -0
- package/src/lne3.js +488 -0
- package/src/mat3.js +131 -0
- package/src/mat4.js +600 -0
- package/src/mesh.js +298 -0
- package/src/noise.js +225 -0
- package/src/num.js +735 -0
- package/src/pln.js +740 -0
- package/src/pool.js +160 -0
- package/src/quat.js +110 -0
- package/src/ray3.js +1056 -0
- package/src/sph3.js +718 -0
- package/src/tangent.js +256 -0
- package/src/tri3.js +650 -0
- package/src/utils.js +15 -0
- package/src/vec2.js +229 -0
- package/src/vec3.js +1116 -0
- package/src/vec4.js +315 -0
- package/src/vertex.js +109 -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[p][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,115 @@
|
|
|
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
|
+
areas = toArray(areas || { start: 0, count: indices.length });
|
|
39
|
+
|
|
40
|
+
if (!normals || !normals.length)
|
|
41
|
+
{
|
|
42
|
+
normals = calculateNormals(indices, positions);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!tangents || !tangents.length)
|
|
46
|
+
{
|
|
47
|
+
tangents = calculateTangents(indices, positions, uvs, areas, normals);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
meshes: [ {
|
|
52
|
+
name: "",
|
|
53
|
+
vertex: {
|
|
54
|
+
position: positions,
|
|
55
|
+
texcoord0: uvs,
|
|
56
|
+
tangent: tangents,
|
|
57
|
+
normal: normals,
|
|
58
|
+
texcoord1: null,
|
|
59
|
+
binormal: null,
|
|
60
|
+
blendIndice: null,
|
|
61
|
+
blendWeight: null
|
|
62
|
+
},
|
|
63
|
+
indices: areas.map(area =>
|
|
64
|
+
{
|
|
65
|
+
return {
|
|
66
|
+
bytesPerIndex: area.bytesPerIndex || 2,
|
|
67
|
+
start: area.start,
|
|
68
|
+
count: area.count,
|
|
69
|
+
faces: indices.slice(area.start, area.start + area.count)
|
|
70
|
+
};
|
|
71
|
+
})
|
|
72
|
+
} ]
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Creates a mesh container from geometry json
|
|
79
|
+
* @param {Object} tw2
|
|
80
|
+
* @param {Object} json
|
|
81
|
+
* @param {String} [name=""]
|
|
82
|
+
* @param {Object} [autoCreateMeshAreas={}]
|
|
83
|
+
* @returns {Object}
|
|
84
|
+
*/
|
|
85
|
+
export function toContainer(tw2, json, name = "", autoCreateMeshAreas = {})
|
|
86
|
+
{
|
|
87
|
+
const
|
|
88
|
+
container = new tw2.EveChildMesh(),
|
|
89
|
+
mesh = container.mesh = new tw2.Tw2Mesh(),
|
|
90
|
+
res = mesh.geometryResource = new tw2.Tw2GeometryRes();
|
|
91
|
+
|
|
92
|
+
container.name = name;
|
|
93
|
+
res.UpdateFromJSON(json);
|
|
94
|
+
res.OnPrepared();
|
|
95
|
+
|
|
96
|
+
Object
|
|
97
|
+
.keys(autoCreateMeshAreas)
|
|
98
|
+
.forEach(areaName =>
|
|
99
|
+
{
|
|
100
|
+
const effect = autoCreateMeshAreas[areaName] || {};
|
|
101
|
+
for (let m = 0; m < res.meshes.length; m++)
|
|
102
|
+
{
|
|
103
|
+
for (let a = 0; a < res.meshes[m].areas.length; a++)
|
|
104
|
+
{
|
|
105
|
+
const meshArea = new tw2.Tw2MeshArea();
|
|
106
|
+
meshArea.meshIndex = m;
|
|
107
|
+
meshArea.index = a;
|
|
108
|
+
meshArea.effect = tw2.Tw2Effect.from(effect);
|
|
109
|
+
mesh[areaName].push(meshArea);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
return container;
|
|
115
|
+
}
|
|
@@ -0,0 +1,148 @@
|
|
|
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.floor(segments);
|
|
24
|
+
|
|
25
|
+
// clamp phiLength so it's in range of [ 0, 2PI ]
|
|
26
|
+
phiLength = num.clamp(phiLength, 0, Math.PI * 2);
|
|
27
|
+
|
|
28
|
+
// buffers
|
|
29
|
+
const
|
|
30
|
+
indices = [],
|
|
31
|
+
positions = [],
|
|
32
|
+
uvs = [],
|
|
33
|
+
initNormals = [],
|
|
34
|
+
normals = [];
|
|
35
|
+
|
|
36
|
+
// helper variables
|
|
37
|
+
const
|
|
38
|
+
inverseSegments = 1.0 / segments,
|
|
39
|
+
normal = vec3.alloc(),
|
|
40
|
+
curNormal = vec3.alloc(),
|
|
41
|
+
prevNormal = vec3.alloc();
|
|
42
|
+
|
|
43
|
+
let dx = 0,
|
|
44
|
+
dy = 0;
|
|
45
|
+
|
|
46
|
+
// pre-compute normals for initial "meridian"
|
|
47
|
+
for (let j = 0; j <= (points.length - 1); j++)
|
|
48
|
+
{
|
|
49
|
+
switch (j)
|
|
50
|
+
{
|
|
51
|
+
case 0: // special handling for 1st vertex on path
|
|
52
|
+
dx = points[j + 1][0] - points[j][0];
|
|
53
|
+
dy = points[j + 1][1] - points[j][1];
|
|
54
|
+
|
|
55
|
+
normal[0] = dy * 1.0;
|
|
56
|
+
normal[1] = -dx;
|
|
57
|
+
normal[2] = dy * 0.0;
|
|
58
|
+
|
|
59
|
+
vec3.copy(prevNormal, normal);
|
|
60
|
+
vec3.normalize(normal, normal);
|
|
61
|
+
initNormals.push(normal[0], normal[1], normal[2]);
|
|
62
|
+
break;
|
|
63
|
+
|
|
64
|
+
case (points.length - 1): // special handling for last Vertex on path
|
|
65
|
+
initNormals.push(prevNormal[0], prevNormal[1], prevNormal[2]);
|
|
66
|
+
break;
|
|
67
|
+
|
|
68
|
+
default: // default handling for all positions in between
|
|
69
|
+
dx = points[j + 1][0] - points[j][0];
|
|
70
|
+
dy = points[j + 1][1] - points[j][1];
|
|
71
|
+
|
|
72
|
+
normal[0] = dy * 1.0;
|
|
73
|
+
normal[1] = -dx;
|
|
74
|
+
normal[2] = dy * 0.0;
|
|
75
|
+
vec3.copy(curNormal, normal);
|
|
76
|
+
|
|
77
|
+
normal[0] += prevNormal[0];
|
|
78
|
+
normal[1] += prevNormal[1];
|
|
79
|
+
normal[2] += prevNormal[2];
|
|
80
|
+
vec3.normalize(normal, normal);
|
|
81
|
+
|
|
82
|
+
initNormals.push(normal[0], normal[1], normal[2]);
|
|
83
|
+
vec3.copy(prevNormal, curNormal);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// generate positions, uvs and normals
|
|
88
|
+
for (let i = 0; i <= segments; i++)
|
|
89
|
+
{
|
|
90
|
+
const
|
|
91
|
+
phi = phiStart + i * inverseSegments * phiLength,
|
|
92
|
+
sin = Math.sin(phi),
|
|
93
|
+
cos = Math.cos(phi);
|
|
94
|
+
|
|
95
|
+
for (let j = 0; j <= (points.length - 1); j++)
|
|
96
|
+
{
|
|
97
|
+
// vertex
|
|
98
|
+
positions.push(
|
|
99
|
+
points[j][0] * sin,
|
|
100
|
+
points[j][1],
|
|
101
|
+
points[j][0] * cos
|
|
102
|
+
);
|
|
103
|
+
// uv
|
|
104
|
+
uvs.push(
|
|
105
|
+
i / segments,
|
|
106
|
+
j / (points.length - 1)
|
|
107
|
+
);
|
|
108
|
+
// normal
|
|
109
|
+
normals.push(
|
|
110
|
+
initNormals[3 * j + 0] * sin,
|
|
111
|
+
initNormals[3 * j + 1],
|
|
112
|
+
initNormals[3 * j + 0] * cos
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
vec3.unalloc(normal);
|
|
118
|
+
vec3.unalloc(curNormal);
|
|
119
|
+
vec3.unalloc(prevNormal);
|
|
120
|
+
|
|
121
|
+
// indices
|
|
122
|
+
for (let i = 0; i < segments; i++)
|
|
123
|
+
{
|
|
124
|
+
for (let j = 0; j < (points.length - 1); j++)
|
|
125
|
+
{
|
|
126
|
+
const
|
|
127
|
+
base = j + i * points.length,
|
|
128
|
+
a = base,
|
|
129
|
+
b = base + points.length,
|
|
130
|
+
c = base + points.length + 1,
|
|
131
|
+
d = base + 1;
|
|
132
|
+
|
|
133
|
+
// faces
|
|
134
|
+
indices.push(a, b, d);
|
|
135
|
+
indices.push(c, d, b);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const result = toJSON(indices, positions, uvs, normals);
|
|
140
|
+
result.factory = createLathe;
|
|
141
|
+
result.options = {
|
|
142
|
+
points,
|
|
143
|
+
segments,
|
|
144
|
+
phiStart,
|
|
145
|
+
phiLength,
|
|
146
|
+
};
|
|
147
|
+
return result;
|
|
148
|
+
}
|
|
File without changes
|
|
@@ -0,0 +1,76 @@
|
|
|
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
|
+
const
|
|
23
|
+
indices = [],
|
|
24
|
+
positions = [],
|
|
25
|
+
normals = [],
|
|
26
|
+
uvs = [];
|
|
27
|
+
|
|
28
|
+
const
|
|
29
|
+
widthHalf = width / 2,
|
|
30
|
+
heightHalf = height / 2,
|
|
31
|
+
gridX = Math.floor(widthSegments),
|
|
32
|
+
gridY = Math.floor(heightSegments),
|
|
33
|
+
gridX1 = gridX + 1,
|
|
34
|
+
gridY1 = gridY + 1,
|
|
35
|
+
segmentWidth = width / gridX,
|
|
36
|
+
segmentHeight = height / gridY;
|
|
37
|
+
|
|
38
|
+
for (let iy = 0; iy < gridY1; iy++)
|
|
39
|
+
{
|
|
40
|
+
const y = iy * segmentHeight - heightHalf;
|
|
41
|
+
for (let ix = 0; ix < gridX1; ix++)
|
|
42
|
+
{
|
|
43
|
+
const x = ix * segmentWidth - widthHalf;
|
|
44
|
+
positions.push(x, -y, 0);
|
|
45
|
+
normals.push(0, 0, 1);
|
|
46
|
+
uvs.push(ix / gridX);
|
|
47
|
+
uvs.push(1 - (iy / gridY));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
for (let iy = 0; iy < gridY; iy++)
|
|
52
|
+
{
|
|
53
|
+
for (let ix = 0; ix < gridX; ix++)
|
|
54
|
+
{
|
|
55
|
+
const
|
|
56
|
+
a = ix + gridX1 * iy,
|
|
57
|
+
b = ix + gridX1 * (iy + 1),
|
|
58
|
+
c = (ix + 1) + gridX1 * (iy + 1),
|
|
59
|
+
d = (ix + 1) + gridX1 * iy;
|
|
60
|
+
|
|
61
|
+
indices.push(a, b, d);
|
|
62
|
+
indices.push(b, c, d);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Normalize
|
|
67
|
+
const result = toJSON(indices, positions, uvs, normals);
|
|
68
|
+
result.factory = createPlane;
|
|
69
|
+
result.options = {
|
|
70
|
+
width,
|
|
71
|
+
height,
|
|
72
|
+
widthSegments,
|
|
73
|
+
heightSegments
|
|
74
|
+
};
|
|
75
|
+
return result;
|
|
76
|
+
}
|
|
@@ -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,
|
|
39
|
+
shapeHoles = shape.holes || [];
|
|
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
|
+
}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { vec3 } from "../vec3.js";
|
|
2
|
+
import { toJSON } from "./json.js";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Creates a sphere
|
|
6
|
+
* @author Three.js converted
|
|
7
|
+
*
|
|
8
|
+
*
|
|
9
|
+
**/
|
|
10
|
+
export function createSphere(options = {})
|
|
11
|
+
{
|
|
12
|
+
|
|
13
|
+
let {
|
|
14
|
+
radius = 1,
|
|
15
|
+
widthSegments = 32,
|
|
16
|
+
heightSegments = 16,
|
|
17
|
+
phiStart = 0,
|
|
18
|
+
phiLength = Math.PI * 2,
|
|
19
|
+
thetaStart = 0,
|
|
20
|
+
thetaLength = Math.PI
|
|
21
|
+
} = options;
|
|
22
|
+
|
|
23
|
+
widthSegments = Math.max(3, Math.floor(widthSegments));
|
|
24
|
+
heightSegments = Math.max(2, Math.floor(heightSegments));
|
|
25
|
+
|
|
26
|
+
const
|
|
27
|
+
thetaEnd = Math.min(thetaStart + thetaLength, Math.PI),
|
|
28
|
+
grid = [],
|
|
29
|
+
vertex = vec3.alloc(),
|
|
30
|
+
normal = vec3.alloc();
|
|
31
|
+
|
|
32
|
+
let index = 0;
|
|
33
|
+
|
|
34
|
+
// buffers
|
|
35
|
+
const
|
|
36
|
+
indices = [],
|
|
37
|
+
vertices = [],
|
|
38
|
+
normals = [],
|
|
39
|
+
uvs = [];
|
|
40
|
+
|
|
41
|
+
// generate vertices, normals and uvs
|
|
42
|
+
for (let iy = 0; iy <= heightSegments; iy++)
|
|
43
|
+
{
|
|
44
|
+
const
|
|
45
|
+
verticesRow = [],
|
|
46
|
+
v = iy / heightSegments;
|
|
47
|
+
|
|
48
|
+
// special case for the poles
|
|
49
|
+
let uOffset = 0;
|
|
50
|
+
if (iy === 0 && thetaStart === 0)
|
|
51
|
+
{
|
|
52
|
+
uOffset = 0.5 / widthSegments;
|
|
53
|
+
}
|
|
54
|
+
else if (iy === heightSegments && thetaEnd === Math.PI)
|
|
55
|
+
{
|
|
56
|
+
uOffset = -0.5 / widthSegments;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
for (let ix = 0; ix <= widthSegments; ix++)
|
|
60
|
+
{
|
|
61
|
+
const u = ix / widthSegments;
|
|
62
|
+
|
|
63
|
+
// vertex
|
|
64
|
+
vertex[0] = -radius * Math.cos(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
65
|
+
vertex[1] = radius * Math.cos(thetaStart + v * thetaLength);
|
|
66
|
+
vertex[2] = radius * Math.sin(phiStart + u * phiLength) * Math.sin(thetaStart + v * thetaLength);
|
|
67
|
+
vertices.push(vertex[0], vertex[1], vertex[2]);
|
|
68
|
+
|
|
69
|
+
// normal
|
|
70
|
+
vec3.copy(normal, vertex);
|
|
71
|
+
vec3.normalize(normal, normal);
|
|
72
|
+
normals.push(normal[0], normal[1], normal[2]);
|
|
73
|
+
|
|
74
|
+
// uv
|
|
75
|
+
uvs.push(u + uOffset, 1 - v);
|
|
76
|
+
verticesRow.push(index++);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
grid.push(verticesRow);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// indices
|
|
83
|
+
for (let iy = 0; iy < heightSegments; iy++)
|
|
84
|
+
{
|
|
85
|
+
for (let ix = 0; ix < widthSegments; ix++)
|
|
86
|
+
{
|
|
87
|
+
const
|
|
88
|
+
a = grid[iy][ix + 1],
|
|
89
|
+
b = grid[iy][ix],
|
|
90
|
+
c = grid[iy + 1][ix],
|
|
91
|
+
d = grid[iy + 1][ix + 1];
|
|
92
|
+
|
|
93
|
+
if (iy !== 0 || thetaStart > 0) indices.push(a, b, d);
|
|
94
|
+
if (iy !== heightSegments - 1 || thetaEnd < Math.PI) indices.push(b, c, d);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
vec3.unalloc(vertex);
|
|
99
|
+
vec3.unalloc(normal);
|
|
100
|
+
|
|
101
|
+
const result = toJSON(indices, vertices, uvs, normals);
|
|
102
|
+
result.shape = {
|
|
103
|
+
factory: createSphere,
|
|
104
|
+
options: {
|
|
105
|
+
radius,
|
|
106
|
+
widthSegments,
|
|
107
|
+
heightSegments,
|
|
108
|
+
phiStart,
|
|
109
|
+
phiLength,
|
|
110
|
+
thetaStart,
|
|
111
|
+
thetaLength
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return result;
|
|
116
|
+
}
|