trid3d 0.0.0-stage → 0.1.1
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/README.md +104 -2
- package/package.json +45 -4
- package/trid/index.d.ts +783 -0
- package/trid/index.js +53 -0
- package/trid/loaders/obj.js +206 -0
- package/trid/loaders/texture.js +56 -0
- package/trid/math/projection.js +49 -0
- package/trid/math/transform.js +302 -0
- package/trid/render/canvasRenderer.js +634 -0
- package/trid/render/color.js +22 -0
- package/trid/render/draw.js +146 -0
- package/trid/render/effects.js +265 -0
- package/trid/render/loop.js +42 -0
- package/trid/render/normals.js +90 -0
- package/trid/render/shapes.js +230 -0
- package/trid/render/webglRenderer.js +613 -0
- package/trid/scene.js +167 -0
- package/trid/tools/dragObject.js +89 -0
- package/trid/tools/flyCamera.js +111 -0
- package/trid/tools/fpsMeter.js +51 -0
- package/trid/tools/helpers.js +85 -0
- package/trid/tools/hoverHighlight.js +62 -0
- package/trid/tools/index.js +8 -0
- package/trid/tools/manager.js +241 -0
- package/trid/tools/objUploader.js +153 -0
- package/trid/tools/orbitCamera.js +132 -0
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Creates a cube centered at origin.
|
|
3
|
+
* @param {number} size - full width/height/depth
|
|
4
|
+
* @returns {{ vertices: {x:number,y:number,z:number}[], edges: number[][] }}
|
|
5
|
+
*/
|
|
6
|
+
function cubeWire(size = 0.5) {
|
|
7
|
+
return rectPrismWire(size, size, size);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Creates a rectangular prism (box) centered at origin.
|
|
12
|
+
* @param {number} w - width (x)
|
|
13
|
+
* @param {number} h - height (y)
|
|
14
|
+
* @param {number} d - depth (z)
|
|
15
|
+
*/
|
|
16
|
+
function rectPrismWire(w = 0.6, h = 0.3, d = 0.4) {
|
|
17
|
+
const x = w / 2, y = h / 2, z = d / 2;
|
|
18
|
+
|
|
19
|
+
const mesh = {
|
|
20
|
+
vertices: [
|
|
21
|
+
{ x: x, y: y, z: z },
|
|
22
|
+
{ x: -x, y: y, z: z },
|
|
23
|
+
{ x: -x, y: -y, z: z },
|
|
24
|
+
{ x: x, y: -y, z: z },
|
|
25
|
+
|
|
26
|
+
{ x: x, y: y, z: -z },
|
|
27
|
+
{ x: -x, y: y, z: -z },
|
|
28
|
+
{ x: -x, y: -y, z: -z },
|
|
29
|
+
{ x: x, y: -y, z: -z },
|
|
30
|
+
],
|
|
31
|
+
edges: [
|
|
32
|
+
[0, 1, 2, 3],
|
|
33
|
+
[4, 5, 6, 7],
|
|
34
|
+
[0, 4],
|
|
35
|
+
[1, 5],
|
|
36
|
+
[2, 6],
|
|
37
|
+
[3, 7],
|
|
38
|
+
],
|
|
39
|
+
// Clockwise as seen from outside
|
|
40
|
+
faces: [
|
|
41
|
+
[4, 7, 6, 5], // front (-z)
|
|
42
|
+
[0, 1, 2, 3], // back (+z)
|
|
43
|
+
[0, 4, 5, 1], // top
|
|
44
|
+
[3, 2, 6, 7], // bottom
|
|
45
|
+
[0, 3, 7, 4], // right
|
|
46
|
+
[1, 5, 6, 2], // left
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
return withFaceUVs(mesh);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Gives every face the whole texture, upright as seen from outside:
|
|
54
|
+
* side faces have +Y up, the top face has +Z up, the bottom face -Z.
|
|
55
|
+
*/
|
|
56
|
+
function withFaceUVs(mesh) {
|
|
57
|
+
const uvs = [], faceUVs = [];
|
|
58
|
+
for (const f of mesh.faces) {
|
|
59
|
+
const p = f.map((i) => mesh.vertices[i]);
|
|
60
|
+
// Outward normal (faces are clockwise from outside)
|
|
61
|
+
let nx = 0, ny = 0, nz = 0;
|
|
62
|
+
for (let i = 0; i < p.length; i++) {
|
|
63
|
+
const a = p[i], b = p[(i + 1) % p.length];
|
|
64
|
+
nx += (a.y - b.y) * (a.z + b.z);
|
|
65
|
+
ny += (a.z - b.z) * (a.x + b.x);
|
|
66
|
+
nz += (a.x - b.x) * (a.y + b.y);
|
|
67
|
+
}
|
|
68
|
+
const up = Math.abs(ny) > Math.max(Math.abs(nx), Math.abs(nz))
|
|
69
|
+
? { x: 0, y: 0, z: ny > 0 ? 1 : -1 }
|
|
70
|
+
: { x: 0, y: 1, z: 0 };
|
|
71
|
+
// Right, as seen from outside: normal × up
|
|
72
|
+
const right = { x: ny * up.z - nz * up.y, y: nz * up.x - nx * up.z, z: nx * up.y - ny * up.x };
|
|
73
|
+
const us = p.map((q) => q.x * right.x + q.y * right.y + q.z * right.z);
|
|
74
|
+
const vs = p.map((q) => q.x * up.x + q.y * up.y + q.z * up.z);
|
|
75
|
+
const u0 = Math.min(...us), u1 = Math.max(...us), v0 = Math.min(...vs), v1 = Math.max(...vs);
|
|
76
|
+
faceUVs.push(p.map((_, k) => {
|
|
77
|
+
uvs.push({ u: (us[k] - u0) / (u1 - u0 || 1), v: (vs[k] - v0) / (v1 - v0 || 1) });
|
|
78
|
+
return uvs.length - 1;
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
mesh.uvs = uvs;
|
|
82
|
+
mesh.faceUVs = faceUVs;
|
|
83
|
+
return mesh;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Wireframe sphere made of latitude/longitude lines.
|
|
88
|
+
* (Not triangles yet, just lines.)
|
|
89
|
+
*
|
|
90
|
+
* Each pole is a single vertex, so `rings` latitude bands produce
|
|
91
|
+
* `rings - 1` drawn circles between the poles.
|
|
92
|
+
*
|
|
93
|
+
* @param {number} radius
|
|
94
|
+
* @param {number} rings - latitude bands from pole to pole (min 2)
|
|
95
|
+
* @param {number} segments - points per circle
|
|
96
|
+
*/
|
|
97
|
+
function sphereWire(radius = 0.35, rings = 10, segments = 16) {
|
|
98
|
+
rings = Math.max(2, rings);
|
|
99
|
+
|
|
100
|
+
const bottom = 0;
|
|
101
|
+
const vertices = [{ x: 0, y: -radius, z: 0 }];
|
|
102
|
+
const edges = [];
|
|
103
|
+
|
|
104
|
+
for (let r = 1; r < rings; r++) {
|
|
105
|
+
const v = r / rings;
|
|
106
|
+
const phi = (v * Math.PI) - Math.PI/2;
|
|
107
|
+
|
|
108
|
+
const y = Math.sin(phi) * radius;
|
|
109
|
+
const ringRadius = Math.cos(phi) * radius;
|
|
110
|
+
|
|
111
|
+
const startIndex = vertices.length;
|
|
112
|
+
|
|
113
|
+
for (let s = 0; s < segments; s++) {
|
|
114
|
+
const u = s / segments;
|
|
115
|
+
const theta = u * Math.PI * 2;
|
|
116
|
+
|
|
117
|
+
const x = Math.cos(theta) * ringRadius;
|
|
118
|
+
const z = Math.sin(theta) * ringRadius;
|
|
119
|
+
|
|
120
|
+
vertices.push({ x, y, z });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
edges.push(Array.from({ length: segments }, (_, i) => startIndex + i));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const top = vertices.length;
|
|
127
|
+
vertices.push({ x: 0, y: radius, z: 0 });
|
|
128
|
+
|
|
129
|
+
// Meridians: bottom pole -> each ring -> top pole
|
|
130
|
+
const lastRing = 1 + (rings - 2) * segments;
|
|
131
|
+
for (let s = 0; s < segments; s++) {
|
|
132
|
+
edges.push([bottom, 1 + s]);
|
|
133
|
+
for (let r = 0; r < rings - 2; r++) {
|
|
134
|
+
const ringA = 1 + r * segments;
|
|
135
|
+
const ringB = ringA + segments;
|
|
136
|
+
edges.push([ringA + s, ringB + s]);
|
|
137
|
+
}
|
|
138
|
+
edges.push([lastRing + s, top]);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Faces, clockwise as seen from outside: triangles at the poles, quads between rings.
|
|
142
|
+
// Texture coordinates wrap around like a world map: u around, v from bottom to top.
|
|
143
|
+
// The seam gets its own u = 1 coordinates, so the image doesn't smear across it.
|
|
144
|
+
const faces = [];
|
|
145
|
+
const uvs = [];
|
|
146
|
+
const faceUVs = [];
|
|
147
|
+
const uv = (s, r) => uvs.push({ u: s / segments, v: r / rings }) - 1;
|
|
148
|
+
for (let s = 0; s < segments; s++) {
|
|
149
|
+
const s1 = (s + 1) % segments;
|
|
150
|
+
faces.push([bottom, 1 + s, 1 + s1]);
|
|
151
|
+
faceUVs.push([uv(s + 0.5, 0), uv(s, 1), uv(s + 1, 1)]);
|
|
152
|
+
for (let r = 0; r < rings - 2; r++) {
|
|
153
|
+
const a = 1 + r * segments, b = a + segments;
|
|
154
|
+
faces.push([a + s, b + s, b + s1, a + s1]);
|
|
155
|
+
faceUVs.push([uv(s, r + 1), uv(s, r + 2), uv(s + 1, r + 2), uv(s + 1, r + 1)]);
|
|
156
|
+
}
|
|
157
|
+
faces.push([top, lastRing + s1, lastRing + s]);
|
|
158
|
+
faceUVs.push([uv(s + 0.5, rings), uv(s + 1, rings - 1), uv(s, rings - 1)]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return { vertices, edges, faces, uvs, faceUVs };
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Scales a mesh along the X, Y, and Z axes.
|
|
166
|
+
*
|
|
167
|
+
* This function modifies the mesh **in place** by multiplying
|
|
168
|
+
* each vertex coordinate by the given scale factors.
|
|
169
|
+
*
|
|
170
|
+
* Common use cases:
|
|
171
|
+
* - Change width / height / depth of a box
|
|
172
|
+
* - Stretch a sphere into an ellipsoid
|
|
173
|
+
* - Apply non-uniform scaling to any mesh
|
|
174
|
+
*
|
|
175
|
+
* NOTE:
|
|
176
|
+
* - This affects only vertex positions
|
|
177
|
+
* - Edge indices remain unchanged
|
|
178
|
+
*
|
|
179
|
+
* @param {{ vertices: {x:number,y:number,z:number}[] }} mesh - Mesh to scale
|
|
180
|
+
* @param {number} sx - Scale factor along X axis (default: 1)
|
|
181
|
+
* @param {number} sy - Scale factor along Y axis (default: 1)
|
|
182
|
+
* @param {number} sz - Scale factor along Z axis (default: 1)
|
|
183
|
+
* @returns The same mesh instance after scaling
|
|
184
|
+
*/
|
|
185
|
+
export function scaleMesh(mesh, sx = 1, sy = 1, sz = 1) {
|
|
186
|
+
for (const v of mesh.vertices) {
|
|
187
|
+
v.x *= sx;
|
|
188
|
+
v.y *= sy;
|
|
189
|
+
v.z *= sz;
|
|
190
|
+
}
|
|
191
|
+
return mesh;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Factory function that creates a predefined wireframe shape.
|
|
196
|
+
*
|
|
197
|
+
* This provides a unified API for constructing different meshes
|
|
198
|
+
* (cube, box, sphere) while keeping the rendering pipeline generic.
|
|
199
|
+
*
|
|
200
|
+
* Supported shapes:
|
|
201
|
+
* - "cube" → wireframe cube
|
|
202
|
+
* - "box" → rectangular prism (box)
|
|
203
|
+
* - "sphere" → wireframe sphere
|
|
204
|
+
*
|
|
205
|
+
* Each shape type accepts its own configuration options via `opts`.
|
|
206
|
+
* Unspecified options fall back to sensible defaults.
|
|
207
|
+
*
|
|
208
|
+
* @param {string} kind - Shape type identifier
|
|
209
|
+
* @param {Object} opts - Shape configuration options
|
|
210
|
+
* @param {number} [opts.size] - Cube size
|
|
211
|
+
* @param {number} [opts.w] - Box width
|
|
212
|
+
* @param {number} [opts.h] - Box height
|
|
213
|
+
* @param {number} [opts.d] - Box depth
|
|
214
|
+
* @param {number} [opts.r] - Sphere radius
|
|
215
|
+
* @param {number} [opts.rings] - Number of latitude rings (sphere)
|
|
216
|
+
* @param {number} [opts.segments] - Segments per ring (sphere)
|
|
217
|
+
*
|
|
218
|
+
* @returns {{ vertices: {x:number,y:number,z:number}[], edges: number[][] }}
|
|
219
|
+
* A wireframe mesh object
|
|
220
|
+
*
|
|
221
|
+
* @throws {Error} If the shape type is not supported
|
|
222
|
+
*/
|
|
223
|
+
export function makeShape(kind, opts = {}) {
|
|
224
|
+
switch (kind.toLowerCase()) {
|
|
225
|
+
case "cube": return cubeWire(opts.size ?? 0.5);
|
|
226
|
+
case "box": return rectPrismWire(opts.w ?? 0.6, opts.h ?? 0.6, opts.d ?? 0.6);
|
|
227
|
+
case "sphere":return sphereWire(opts.r ?? 0.45, opts.rings ?? 12, opts.segments ?? 24);
|
|
228
|
+
default: throw new Error("Unknown shape: " + kind);
|
|
229
|
+
}
|
|
230
|
+
}
|