3d-spinner 0.9.2 → 0.9.3
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/README.md +66 -4
- package/dist/animation-label.d.ts +8 -0
- package/dist/animation-label.js +47 -0
- package/dist/animation.d.ts +2 -0
- package/dist/animations/charged-orb.d.ts +55 -0
- package/dist/animations/charged-orb.js +229 -0
- package/dist/animations/grid-assembly.d.ts +67 -0
- package/dist/animations/grid-assembly.js +268 -0
- package/dist/animations/object-motion.d.ts +19 -7
- package/dist/animations/object-motion.js +38 -32
- package/dist/animations/particles.d.ts +110 -0
- package/dist/animations/particles.js +209 -0
- package/dist/cjs/animations/charged-orb.cjs +1334 -0
- package/dist/cjs/animations/grid-assembly.cjs +1498 -0
- package/dist/cjs/animations/object-motion.cjs +124 -43
- package/dist/cjs/animations/particles.cjs +1940 -0
- package/dist/cjs/animations/spin.cjs +4 -3
- package/dist/cjs/composite-animation.cjs +58 -0
- package/dist/cjs/engines/little-3d-engine/little-3d-engine.cjs +131 -22
- package/dist/cjs/engines/little-3d-engine/loaders/obj.cjs +30 -1
- package/dist/cjs/engines/little-3d-engine/renderers/canvas2d-textured.cjs +345 -0
- package/dist/cjs/engines/little-3d-engine/renderers/webgl-textured.cjs +528 -0
- package/dist/cjs/engines/little-3d-engine/renderers/webgpu-textured.cjs +712 -0
- package/dist/cjs/prefabs/prefabs.cjs +3450 -0
- package/dist/composite-animation.d.ts +18 -0
- package/dist/composite-animation.js +39 -0
- package/dist/engines/little-3d-engine/little-3d-engine.d.ts +17 -12
- package/dist/engines/little-3d-engine/little-3d-engine.js +13 -8
- package/dist/engines/little-3d-engine/loaders/obj.d.ts +12 -4
- package/dist/engines/little-3d-engine/loaders/obj.js +44 -5
- package/dist/engines/little-3d-engine/renderer.d.ts +7 -1
- package/dist/engines/little-3d-engine/renderer.js +2 -0
- package/dist/engines/little-3d-engine/renderers/canvas2d-textured.d.ts +20 -0
- package/dist/engines/little-3d-engine/renderers/canvas2d-textured.js +135 -0
- package/dist/engines/little-3d-engine/renderers/textured-helpers.d.ts +9 -0
- package/dist/engines/little-3d-engine/renderers/textured-helpers.js +35 -0
- package/dist/engines/little-3d-engine/renderers/webgl-textured.d.ts +43 -0
- package/dist/engines/little-3d-engine/renderers/webgl-textured.js +211 -0
- package/dist/engines/little-3d-engine/renderers/webgpu-textured.d.ts +45 -0
- package/dist/engines/little-3d-engine/renderers/webgpu-textured.js +300 -0
- package/dist/engines/little-3d-engine/renderers/webgpu.d.ts +11 -6
- package/dist/engines/little-3d-engine/shapes/complex/plane.d.ts +3 -0
- package/dist/engines/little-3d-engine/shapes/complex/plane.js +33 -0
- package/dist/engines/little-3d-engine/shapes/{cube.d.ts → primitives/cube.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{octahedron.d.ts → primitives/octahedron.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{pyramid.d.ts → primitives/pyramid.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/primitives/quad.d.ts +9 -0
- package/dist/engines/little-3d-engine/shapes/primitives/quad.js +18 -0
- package/dist/engines/little-3d-engine/shapes/{cube-sphere.d.ts → primitives/spheres/cube-sphere.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{icosphere.d.ts → primitives/spheres/icosphere.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{icosphere.js → primitives/spheres/icosphere.js} +1 -1
- package/dist/engines/little-3d-engine/shapes/{octa-sphere.d.ts → primitives/spheres/octa-sphere.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{octa-sphere.js → primitives/spheres/octa-sphere.js} +1 -1
- package/dist/engines/little-3d-engine/shapes/{uv-sphere.d.ts → primitives/spheres/uv-sphere.d.ts} +1 -1
- package/dist/engines/little-3d-engine/shapes/{tetrahedron.d.ts → primitives/tetrahedron.d.ts} +1 -1
- package/dist/engines/little-3d-engine/textures/dynamic/canvas-texture.d.ts +1 -0
- package/dist/engines/little-3d-engine/textures/dynamic/canvas-texture.js +8 -0
- package/dist/engines/little-3d-engine/textures/dynamic/shine.d.ts +2 -0
- package/dist/engines/little-3d-engine/textures/dynamic/shine.js +14 -0
- package/dist/engines/little-3d-engine/textures/dynamic/star.d.ts +2 -0
- package/dist/engines/little-3d-engine/textures/dynamic/star.js +16 -0
- package/dist/engines/little-3d-engine/textures/dynamic/streak.d.ts +5 -0
- package/dist/engines/little-3d-engine/textures/dynamic/streak.js +20 -0
- package/dist/index.d.ts +2 -1
- package/dist/prefabs/charged-orb.d.ts +17 -0
- package/dist/prefabs/charged-orb.js +37 -0
- package/dist/prefabs/crystal-comet.d.ts +4 -0
- package/dist/prefabs/crystal-comet.js +41 -0
- package/dist/prefabs/ghost-train.d.ts +9 -0
- package/dist/prefabs/ghost-train.js +44 -0
- package/dist/prefabs/grid-assembly.d.ts +13 -0
- package/dist/prefabs/grid-assembly.js +15 -0
- package/dist/prefabs/monochrome-streak.d.ts +4 -0
- package/dist/prefabs/monochrome-streak.js +25 -0
- package/dist/prefabs/plane-star-trail.d.ts +4 -0
- package/dist/prefabs/plane-star-trail.js +40 -0
- package/dist/prefabs/prefabs.d.ts +10 -0
- package/dist/prefabs/prefabs.js +9 -0
- package/dist/prefabs/pulsing-starfield.d.ts +4 -0
- package/dist/prefabs/pulsing-starfield.js +27 -0
- package/dist/prefabs/rocket-launch.d.ts +8 -0
- package/dist/prefabs/rocket-launch.js +59 -0
- package/dist/prefabs/spinner.d.ts +7 -0
- package/dist/prefabs/spinner.js +17 -0
- package/dist/prefabs/star-swarm.d.ts +4 -0
- package/dist/prefabs/star-swarm.js +27 -0
- package/dist/prefabs/types.d.ts +46 -0
- package/dist/prefabs/types.js +1 -0
- package/dist/umd/spinner.global.js +3055 -1251
- package/dist/umd/spinner.global.min.js +60 -5
- package/package.json +42 -1
- /package/dist/engines/little-3d-engine/shapes/{cube.js → primitives/cube.js} +0 -0
- /package/dist/engines/little-3d-engine/shapes/{octahedron.js → primitives/octahedron.js} +0 -0
- /package/dist/engines/little-3d-engine/shapes/{pyramid.js → primitives/pyramid.js} +0 -0
- /package/dist/engines/little-3d-engine/shapes/{cube-sphere.js → primitives/spheres/cube-sphere.js} +0 -0
- /package/dist/engines/little-3d-engine/shapes/{uv-sphere.js → primitives/spheres/uv-sphere.js} +0 -0
- /package/dist/engines/little-3d-engine/shapes/{tetrahedron.js → primitives/tetrahedron.js} +0 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
5
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
6
|
+
var __esm = (fn, res, err) => function __init() {
|
|
7
|
+
if (err) throw err[0];
|
|
8
|
+
try {
|
|
9
|
+
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
+
} catch (e) {
|
|
11
|
+
throw err = [e], e;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
var __export = (target, all) => {
|
|
15
|
+
for (var name in all)
|
|
16
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
17
|
+
};
|
|
18
|
+
var __copyProps = (to, from, except, desc) => {
|
|
19
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
20
|
+
for (let key of __getOwnPropNames(from))
|
|
21
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
22
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
23
|
+
}
|
|
24
|
+
return to;
|
|
25
|
+
};
|
|
26
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
27
|
+
|
|
28
|
+
// src/engines/little-3d-engine/core/math.ts
|
|
29
|
+
function subtract(a, b) {
|
|
30
|
+
return { x: a.x - b.x, y: a.y - b.y, z: a.z - b.z };
|
|
31
|
+
}
|
|
32
|
+
function cross(a, b) {
|
|
33
|
+
return {
|
|
34
|
+
x: a.y * b.z - a.z * b.y,
|
|
35
|
+
y: a.z * b.x - a.x * b.z,
|
|
36
|
+
z: a.x * b.y - a.y * b.x
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
function normalize(v) {
|
|
40
|
+
const length = Math.hypot(v.x, v.y, v.z);
|
|
41
|
+
if (length === 0) return { x: 0, y: 0, z: 0 };
|
|
42
|
+
return { x: v.x / length, y: v.y / length, z: v.z / length };
|
|
43
|
+
}
|
|
44
|
+
var init_math = __esm({
|
|
45
|
+
"src/engines/little-3d-engine/core/math.ts"() {
|
|
46
|
+
"use strict";
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
// src/engines/little-3d-engine/core/geometry.ts
|
|
51
|
+
function parseColor(color) {
|
|
52
|
+
const hex = color.trim().replace("#", "");
|
|
53
|
+
const full = hex.length === 3 ? hex.split("").map((c) => c + c).join("") : hex;
|
|
54
|
+
const n = parseInt(full, 16);
|
|
55
|
+
return [n >> 16 & 255, n >> 8 & 255, n & 255];
|
|
56
|
+
}
|
|
57
|
+
function expandToTriangles(mesh) {
|
|
58
|
+
let triangles = 0;
|
|
59
|
+
for (const face of mesh.faces) triangles += Math.max(0, face.indices.length - 2);
|
|
60
|
+
const positions = new Float32Array(triangles * 9);
|
|
61
|
+
const normals = new Float32Array(triangles * 9);
|
|
62
|
+
const colors = new Float32Array(triangles * 9);
|
|
63
|
+
let o = 0;
|
|
64
|
+
for (const face of mesh.faces) {
|
|
65
|
+
const v0 = mesh.vertices[face.indices[0]];
|
|
66
|
+
const v1 = mesh.vertices[face.indices[1]];
|
|
67
|
+
const v2 = mesh.vertices[face.indices[2]];
|
|
68
|
+
const normal = normalize(cross(subtract(v1, v0), subtract(v2, v0)));
|
|
69
|
+
const [r, g, b] = parseColor(face.color);
|
|
70
|
+
const cr = r / 255;
|
|
71
|
+
const cg = g / 255;
|
|
72
|
+
const cb = b / 255;
|
|
73
|
+
for (let k = 1; k < face.indices.length - 1; k++) {
|
|
74
|
+
const tri = [face.indices[0], face.indices[k], face.indices[k + 1]];
|
|
75
|
+
for (const index of tri) {
|
|
76
|
+
const v = mesh.vertices[index];
|
|
77
|
+
positions[o] = v.x;
|
|
78
|
+
positions[o + 1] = v.y;
|
|
79
|
+
positions[o + 2] = v.z;
|
|
80
|
+
normals[o] = normal.x;
|
|
81
|
+
normals[o + 1] = normal.y;
|
|
82
|
+
normals[o + 2] = normal.z;
|
|
83
|
+
colors[o] = cr;
|
|
84
|
+
colors[o + 1] = cg;
|
|
85
|
+
colors[o + 2] = cb;
|
|
86
|
+
o += 3;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { positions, normals, colors, count: positions.length / 3 };
|
|
91
|
+
}
|
|
92
|
+
var init_geometry = __esm({
|
|
93
|
+
"src/engines/little-3d-engine/core/geometry.ts"() {
|
|
94
|
+
"use strict";
|
|
95
|
+
init_math();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
// src/engines/little-3d-engine/renderer.ts
|
|
100
|
+
function opacity(value, fallback) {
|
|
101
|
+
return Math.max(0, Math.min(1, value ?? fallback));
|
|
102
|
+
}
|
|
103
|
+
function resolveTwoSidedOpacity(transparency) {
|
|
104
|
+
const front = opacity(
|
|
105
|
+
transparency.frontOpacity ?? transparency.opacity,
|
|
106
|
+
DEFAULT_FRONT_OPACITY
|
|
107
|
+
);
|
|
108
|
+
const backFallback = transparency.opacity === void 0 ? DEFAULT_BACK_OPACITY : front * (2 / 3);
|
|
109
|
+
return {
|
|
110
|
+
front,
|
|
111
|
+
back: opacity(transparency.backOpacity, backFallback)
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
var DEFAULT_ONE_SIDED_OPACITY, DEFAULT_BACK_OPACITY, DEFAULT_FRONT_OPACITY;
|
|
115
|
+
var init_renderer = __esm({
|
|
116
|
+
"src/engines/little-3d-engine/renderer.ts"() {
|
|
117
|
+
"use strict";
|
|
118
|
+
DEFAULT_ONE_SIDED_OPACITY = 0.35;
|
|
119
|
+
DEFAULT_BACK_OPACITY = 0.84;
|
|
120
|
+
DEFAULT_FRONT_OPACITY = 0.56;
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
// src/engines/little-3d-engine/renderers/webgl.ts
|
|
125
|
+
function compile(gl, type, source) {
|
|
126
|
+
const shader = gl.createShader(type);
|
|
127
|
+
gl.shaderSource(shader, source);
|
|
128
|
+
gl.compileShader(shader);
|
|
129
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
130
|
+
throw new Error(`3d-spinner: shader compile failed: ${gl.getShaderInfoLog(shader)}`);
|
|
131
|
+
}
|
|
132
|
+
return shader;
|
|
133
|
+
}
|
|
134
|
+
function link(gl) {
|
|
135
|
+
const program = gl.createProgram();
|
|
136
|
+
gl.attachShader(program, compile(gl, gl.VERTEX_SHADER, VERTEX_SHADER));
|
|
137
|
+
gl.attachShader(program, compile(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER));
|
|
138
|
+
gl.linkProgram(program);
|
|
139
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
140
|
+
throw new Error(`3d-spinner: program link failed: ${gl.getProgramInfoLog(program)}`);
|
|
141
|
+
}
|
|
142
|
+
return program;
|
|
143
|
+
}
|
|
144
|
+
var VERTEX_SHADER, FRAGMENT_SHADER, WebGLRenderer;
|
|
145
|
+
var init_webgl = __esm({
|
|
146
|
+
"src/engines/little-3d-engine/renderers/webgl.ts"() {
|
|
147
|
+
"use strict";
|
|
148
|
+
init_geometry();
|
|
149
|
+
init_renderer();
|
|
150
|
+
VERTEX_SHADER = `#version 300 es
|
|
151
|
+
in vec3 aPos;
|
|
152
|
+
in vec3 aNormal;
|
|
153
|
+
in vec3 aColor;
|
|
154
|
+
uniform mat4 uViewProj;
|
|
155
|
+
uniform mat4 uModel;
|
|
156
|
+
out vec3 vNormal;
|
|
157
|
+
out vec3 vColor;
|
|
158
|
+
void main() {
|
|
159
|
+
vNormal = mat3(uModel) * aNormal;
|
|
160
|
+
vColor = aColor;
|
|
161
|
+
gl_Position = uViewProj * uModel * vec4(aPos, 1.0);
|
|
162
|
+
}`;
|
|
163
|
+
FRAGMENT_SHADER = `#version 300 es
|
|
164
|
+
precision mediump float;
|
|
165
|
+
in vec3 vNormal;
|
|
166
|
+
in vec3 vColor;
|
|
167
|
+
uniform vec3 uToLight;
|
|
168
|
+
uniform float uIntensity;
|
|
169
|
+
uniform float uAmbient;
|
|
170
|
+
uniform float uOpacity;
|
|
171
|
+
out vec4 fragColor;
|
|
172
|
+
void main() {
|
|
173
|
+
float lambert = max(dot(normalize(vNormal), normalize(uToLight)), 0.0);
|
|
174
|
+
float brightness = clamp(uAmbient + uIntensity * lambert, 0.0, 1.0);
|
|
175
|
+
fragColor = vec4(vColor * brightness, uOpacity);
|
|
176
|
+
}`;
|
|
177
|
+
WebGLRenderer = class {
|
|
178
|
+
constructor(options = {}) {
|
|
179
|
+
this.cache = /* @__PURE__ */ new Map();
|
|
180
|
+
if (options.background) {
|
|
181
|
+
const [r, g, b] = parseColor(options.background);
|
|
182
|
+
this.clearColor = [r / 255, g / 255, b / 255, 1];
|
|
183
|
+
} else {
|
|
184
|
+
this.clearColor = [0, 0, 0, 0];
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
init(canvas) {
|
|
188
|
+
const gl = canvas.getContext("webgl2");
|
|
189
|
+
if (!gl) throw new Error("3d-spinner: WebGL2 is not supported in this browser.");
|
|
190
|
+
this.gl = gl;
|
|
191
|
+
this.program = link(gl);
|
|
192
|
+
this.locations = {
|
|
193
|
+
aPos: gl.getAttribLocation(this.program, "aPos"),
|
|
194
|
+
aNormal: gl.getAttribLocation(this.program, "aNormal"),
|
|
195
|
+
aColor: gl.getAttribLocation(this.program, "aColor"),
|
|
196
|
+
uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
|
|
197
|
+
uModel: gl.getUniformLocation(this.program, "uModel"),
|
|
198
|
+
uToLight: gl.getUniformLocation(this.program, "uToLight"),
|
|
199
|
+
uIntensity: gl.getUniformLocation(this.program, "uIntensity"),
|
|
200
|
+
uAmbient: gl.getUniformLocation(this.program, "uAmbient"),
|
|
201
|
+
uOpacity: gl.getUniformLocation(this.program, "uOpacity")
|
|
202
|
+
};
|
|
203
|
+
gl.enable(gl.DEPTH_TEST);
|
|
204
|
+
gl.enable(gl.CULL_FACE);
|
|
205
|
+
gl.cullFace(gl.BACK);
|
|
206
|
+
gl.frontFace(gl.CCW);
|
|
207
|
+
}
|
|
208
|
+
resize() {
|
|
209
|
+
const gl = this.gl;
|
|
210
|
+
if (!gl) return;
|
|
211
|
+
const canvas = gl.canvas;
|
|
212
|
+
gl.viewport(0, 0, canvas.width, canvas.height);
|
|
213
|
+
}
|
|
214
|
+
buffers(mesh) {
|
|
215
|
+
const cached = this.cache.get(mesh);
|
|
216
|
+
if (cached) return cached;
|
|
217
|
+
const gl = this.gl;
|
|
218
|
+
const loc = this.locations;
|
|
219
|
+
const data = expandToTriangles(mesh);
|
|
220
|
+
const vao = gl.createVertexArray();
|
|
221
|
+
gl.bindVertexArray(vao);
|
|
222
|
+
const attribute = (location, array) => {
|
|
223
|
+
if (location < 0) return;
|
|
224
|
+
const buffer = gl.createBuffer();
|
|
225
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
226
|
+
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
|
|
227
|
+
gl.enableVertexAttribArray(location);
|
|
228
|
+
gl.vertexAttribPointer(location, 3, gl.FLOAT, false, 0, 0);
|
|
229
|
+
};
|
|
230
|
+
attribute(loc.aPos, data.positions);
|
|
231
|
+
attribute(loc.aNormal, data.normals);
|
|
232
|
+
attribute(loc.aColor, data.colors);
|
|
233
|
+
gl.bindVertexArray(null);
|
|
234
|
+
const result = { vao, count: data.count };
|
|
235
|
+
this.cache.set(mesh, result);
|
|
236
|
+
return result;
|
|
237
|
+
}
|
|
238
|
+
render(frame) {
|
|
239
|
+
const gl = this.gl;
|
|
240
|
+
const loc = this.locations;
|
|
241
|
+
if (!gl || !this.program || !loc) return;
|
|
242
|
+
gl.clearColor(...this.clearColor);
|
|
243
|
+
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
|
|
244
|
+
gl.useProgram(this.program);
|
|
245
|
+
gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
|
|
246
|
+
gl.uniform3f(loc.uToLight, frame.light.toLight.x, frame.light.toLight.y, frame.light.toLight.z);
|
|
247
|
+
gl.uniform1f(loc.uIntensity, frame.light.intensity);
|
|
248
|
+
gl.uniform1f(loc.uAmbient, frame.light.ambient);
|
|
249
|
+
gl.disable(gl.BLEND);
|
|
250
|
+
gl.depthMask(true);
|
|
251
|
+
gl.cullFace(gl.BACK);
|
|
252
|
+
for (const item of frame.items) {
|
|
253
|
+
if (item.transparency) continue;
|
|
254
|
+
const mesh = this.buffers(item.mesh);
|
|
255
|
+
gl.uniformMatrix4fv(loc.uModel, false, new Float32Array(item.model));
|
|
256
|
+
gl.uniform1f(loc.uOpacity, 1);
|
|
257
|
+
gl.bindVertexArray(mesh.vao);
|
|
258
|
+
gl.drawArrays(gl.TRIANGLES, 0, mesh.count);
|
|
259
|
+
}
|
|
260
|
+
gl.enable(gl.BLEND);
|
|
261
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
262
|
+
gl.depthMask(false);
|
|
263
|
+
for (const item of frame.items) {
|
|
264
|
+
const transparency = item.transparency;
|
|
265
|
+
if (!transparency) continue;
|
|
266
|
+
const mesh = this.buffers(item.mesh);
|
|
267
|
+
gl.uniformMatrix4fv(loc.uModel, false, new Float32Array(item.model));
|
|
268
|
+
gl.bindVertexArray(mesh.vao);
|
|
269
|
+
if (transparency.mode === "two-sided") {
|
|
270
|
+
const resolved = resolveTwoSidedOpacity(transparency);
|
|
271
|
+
gl.cullFace(gl.FRONT);
|
|
272
|
+
gl.uniform1f(loc.uOpacity, resolved.back);
|
|
273
|
+
gl.drawArrays(gl.TRIANGLES, 0, mesh.count);
|
|
274
|
+
gl.cullFace(gl.BACK);
|
|
275
|
+
gl.uniform1f(loc.uOpacity, resolved.front);
|
|
276
|
+
gl.drawArrays(gl.TRIANGLES, 0, mesh.count);
|
|
277
|
+
} else {
|
|
278
|
+
gl.cullFace(gl.BACK);
|
|
279
|
+
gl.uniform1f(loc.uOpacity, opacity(transparency.opacity, DEFAULT_ONE_SIDED_OPACITY));
|
|
280
|
+
gl.drawArrays(gl.TRIANGLES, 0, mesh.count);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
gl.depthMask(true);
|
|
284
|
+
gl.disable(gl.BLEND);
|
|
285
|
+
gl.cullFace(gl.BACK);
|
|
286
|
+
gl.bindVertexArray(null);
|
|
287
|
+
}
|
|
288
|
+
destroy() {
|
|
289
|
+
const gl = this.gl;
|
|
290
|
+
if (gl) {
|
|
291
|
+
for (const mesh of this.cache.values()) gl.deleteVertexArray(mesh.vao);
|
|
292
|
+
if (this.program) gl.deleteProgram(this.program);
|
|
293
|
+
}
|
|
294
|
+
this.cache.clear();
|
|
295
|
+
this.gl = void 0;
|
|
296
|
+
this.program = void 0;
|
|
297
|
+
this.locations = void 0;
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
}
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
// src/engines/little-3d-engine/renderers/webgl-textured.ts
|
|
304
|
+
var webgl_textured_exports = {};
|
|
305
|
+
__export(webgl_textured_exports, {
|
|
306
|
+
WebGLTexturedRenderer: () => WebGLTexturedRenderer
|
|
307
|
+
});
|
|
308
|
+
module.exports = __toCommonJS(webgl_textured_exports);
|
|
309
|
+
init_geometry();
|
|
310
|
+
init_renderer();
|
|
311
|
+
|
|
312
|
+
// src/engines/little-3d-engine/renderers/textured-helpers.ts
|
|
313
|
+
function planarUVs(mesh) {
|
|
314
|
+
let minX = Infinity;
|
|
315
|
+
let minY = Infinity;
|
|
316
|
+
let maxX = -Infinity;
|
|
317
|
+
let maxY = -Infinity;
|
|
318
|
+
for (const v of mesh.vertices) {
|
|
319
|
+
minX = Math.min(minX, v.x);
|
|
320
|
+
minY = Math.min(minY, v.y);
|
|
321
|
+
maxX = Math.max(maxX, v.x);
|
|
322
|
+
maxY = Math.max(maxY, v.y);
|
|
323
|
+
}
|
|
324
|
+
const width = maxX - minX || 1;
|
|
325
|
+
const height = maxY - minY || 1;
|
|
326
|
+
let triangles = 0;
|
|
327
|
+
for (const face of mesh.faces) triangles += Math.max(0, face.indices.length - 2);
|
|
328
|
+
const uvs = new Float32Array(triangles * 6);
|
|
329
|
+
let o = 0;
|
|
330
|
+
for (const face of mesh.faces) {
|
|
331
|
+
for (let k = 1; k < face.indices.length - 1; k++) {
|
|
332
|
+
for (const index of [face.indices[0], face.indices[k], face.indices[k + 1]]) {
|
|
333
|
+
const v = mesh.vertices[index];
|
|
334
|
+
uvs[o] = (v.x - minX) / width;
|
|
335
|
+
uvs[o + 1] = 1 - (v.y - minY) / height;
|
|
336
|
+
o += 2;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
return uvs;
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// src/engines/little-3d-engine/renderers/webgl-textured.ts
|
|
344
|
+
init_webgl();
|
|
345
|
+
var VERTEX_SHADER2 = `#version 300 es
|
|
346
|
+
in vec3 aPos;
|
|
347
|
+
in vec2 aUV;
|
|
348
|
+
in vec3 aColor;
|
|
349
|
+
uniform mat4 uViewProj;
|
|
350
|
+
uniform mat4 uModel;
|
|
351
|
+
out vec2 vUV;
|
|
352
|
+
out vec3 vColor;
|
|
353
|
+
void main() {
|
|
354
|
+
vUV = aUV;
|
|
355
|
+
vColor = aColor;
|
|
356
|
+
gl_Position = uViewProj * uModel * vec4(aPos, 1.0);
|
|
357
|
+
}`;
|
|
358
|
+
var FRAGMENT_SHADER2 = `#version 300 es
|
|
359
|
+
precision mediump float;
|
|
360
|
+
in vec2 vUV;
|
|
361
|
+
in vec3 vColor;
|
|
362
|
+
uniform sampler2D uTexture;
|
|
363
|
+
uniform float uOpacity;
|
|
364
|
+
out vec4 fragColor;
|
|
365
|
+
void main() {
|
|
366
|
+
vec4 t = texture(uTexture, vUV);
|
|
367
|
+
fragColor = vec4(t.rgb * vColor, t.a * uOpacity);
|
|
368
|
+
}`;
|
|
369
|
+
function compile2(gl, type, source) {
|
|
370
|
+
const shader = gl.createShader(type);
|
|
371
|
+
gl.shaderSource(shader, source);
|
|
372
|
+
gl.compileShader(shader);
|
|
373
|
+
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
|
|
374
|
+
throw new Error(`3d-spinner: shader compile failed: ${gl.getShaderInfoLog(shader)}`);
|
|
375
|
+
}
|
|
376
|
+
return shader;
|
|
377
|
+
}
|
|
378
|
+
function link2(gl) {
|
|
379
|
+
const program = gl.createProgram();
|
|
380
|
+
gl.attachShader(program, compile2(gl, gl.VERTEX_SHADER, VERTEX_SHADER2));
|
|
381
|
+
gl.attachShader(program, compile2(gl, gl.FRAGMENT_SHADER, FRAGMENT_SHADER2));
|
|
382
|
+
gl.linkProgram(program);
|
|
383
|
+
if (!gl.getProgramParameter(program, gl.LINK_STATUS)) {
|
|
384
|
+
throw new Error(`3d-spinner: program link failed: ${gl.getProgramInfoLog(program)}`);
|
|
385
|
+
}
|
|
386
|
+
return program;
|
|
387
|
+
}
|
|
388
|
+
function itemOpacity(transparency) {
|
|
389
|
+
if (!transparency) return 1;
|
|
390
|
+
if (transparency.mode === "two-sided") return resolveTwoSidedOpacity(transparency).front;
|
|
391
|
+
return opacity(transparency.opacity, DEFAULT_ONE_SIDED_OPACITY);
|
|
392
|
+
}
|
|
393
|
+
var WebGLTexturedRenderer = class {
|
|
394
|
+
constructor(options = {}) {
|
|
395
|
+
this.sources = /* @__PURE__ */ new Map();
|
|
396
|
+
this.textures = /* @__PURE__ */ new Map();
|
|
397
|
+
this.buffers = /* @__PURE__ */ new Map();
|
|
398
|
+
this.inner = new WebGLRenderer(options);
|
|
399
|
+
}
|
|
400
|
+
/** Texture every instance of `mesh` with `source`. Call any time, also before init. */
|
|
401
|
+
setTexture(mesh, source) {
|
|
402
|
+
this.sources.set(mesh, source);
|
|
403
|
+
}
|
|
404
|
+
init(canvas) {
|
|
405
|
+
this.inner.init(canvas);
|
|
406
|
+
const gl = canvas.getContext("webgl2");
|
|
407
|
+
this.gl = gl;
|
|
408
|
+
this.program = link2(gl);
|
|
409
|
+
this.locations = {
|
|
410
|
+
aPos: gl.getAttribLocation(this.program, "aPos"),
|
|
411
|
+
aUV: gl.getAttribLocation(this.program, "aUV"),
|
|
412
|
+
aColor: gl.getAttribLocation(this.program, "aColor"),
|
|
413
|
+
uViewProj: gl.getUniformLocation(this.program, "uViewProj"),
|
|
414
|
+
uModel: gl.getUniformLocation(this.program, "uModel"),
|
|
415
|
+
uTexture: gl.getUniformLocation(this.program, "uTexture"),
|
|
416
|
+
uOpacity: gl.getUniformLocation(this.program, "uOpacity")
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
resize() {
|
|
420
|
+
this.inner.resize();
|
|
421
|
+
}
|
|
422
|
+
textureFor(mesh) {
|
|
423
|
+
const cached = this.textures.get(mesh);
|
|
424
|
+
if (cached) return cached;
|
|
425
|
+
const gl = this.gl;
|
|
426
|
+
const texture = gl.createTexture();
|
|
427
|
+
gl.bindTexture(gl.TEXTURE_2D, texture);
|
|
428
|
+
gl.texImage2D(
|
|
429
|
+
gl.TEXTURE_2D,
|
|
430
|
+
0,
|
|
431
|
+
gl.RGBA,
|
|
432
|
+
1,
|
|
433
|
+
1,
|
|
434
|
+
0,
|
|
435
|
+
gl.RGBA,
|
|
436
|
+
gl.UNSIGNED_BYTE,
|
|
437
|
+
new Uint8Array([255, 255, 255, 255])
|
|
438
|
+
);
|
|
439
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);
|
|
440
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.LINEAR);
|
|
441
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
|
|
442
|
+
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
|
|
443
|
+
this.textures.set(mesh, texture);
|
|
444
|
+
const upload = (image) => {
|
|
445
|
+
if (!this.gl || this.textures.get(mesh) !== texture) return;
|
|
446
|
+
this.gl.bindTexture(this.gl.TEXTURE_2D, texture);
|
|
447
|
+
this.gl.texImage2D(this.gl.TEXTURE_2D, 0, this.gl.RGBA, this.gl.RGBA, this.gl.UNSIGNED_BYTE, image);
|
|
448
|
+
};
|
|
449
|
+
const source = this.sources.get(mesh);
|
|
450
|
+
if (typeof source === "string") {
|
|
451
|
+
const image = new Image();
|
|
452
|
+
image.onload = () => upload(image);
|
|
453
|
+
image.src = source;
|
|
454
|
+
} else {
|
|
455
|
+
upload(source);
|
|
456
|
+
}
|
|
457
|
+
return texture;
|
|
458
|
+
}
|
|
459
|
+
buffersFor(mesh) {
|
|
460
|
+
const cached = this.buffers.get(mesh);
|
|
461
|
+
if (cached) return cached;
|
|
462
|
+
const gl = this.gl;
|
|
463
|
+
const loc = this.locations;
|
|
464
|
+
const data = expandToTriangles(mesh);
|
|
465
|
+
const vao = gl.createVertexArray();
|
|
466
|
+
gl.bindVertexArray(vao);
|
|
467
|
+
const attribute = (location, array, size) => {
|
|
468
|
+
if (location < 0) return;
|
|
469
|
+
const buffer = gl.createBuffer();
|
|
470
|
+
gl.bindBuffer(gl.ARRAY_BUFFER, buffer);
|
|
471
|
+
gl.bufferData(gl.ARRAY_BUFFER, array, gl.STATIC_DRAW);
|
|
472
|
+
gl.enableVertexAttribArray(location);
|
|
473
|
+
gl.vertexAttribPointer(location, size, gl.FLOAT, false, 0, 0);
|
|
474
|
+
};
|
|
475
|
+
attribute(loc.aPos, data.positions, 3);
|
|
476
|
+
attribute(loc.aColor, data.colors, 3);
|
|
477
|
+
attribute(loc.aUV, planarUVs(mesh), 2);
|
|
478
|
+
gl.bindVertexArray(null);
|
|
479
|
+
const result = { vao, count: data.count };
|
|
480
|
+
this.buffers.set(mesh, result);
|
|
481
|
+
return result;
|
|
482
|
+
}
|
|
483
|
+
render(frame) {
|
|
484
|
+
const plain = [];
|
|
485
|
+
const textured = [];
|
|
486
|
+
for (const item of frame.items) {
|
|
487
|
+
(this.sources.has(item.mesh) ? textured : plain).push(item);
|
|
488
|
+
}
|
|
489
|
+
this.inner.render(textured.length ? { ...frame, items: plain } : frame);
|
|
490
|
+
if (!textured.length) return;
|
|
491
|
+
const gl = this.gl;
|
|
492
|
+
const loc = this.locations;
|
|
493
|
+
if (!gl || !this.program || !loc) return;
|
|
494
|
+
gl.useProgram(this.program);
|
|
495
|
+
gl.uniformMatrix4fv(loc.uViewProj, false, new Float32Array(frame.viewProjection));
|
|
496
|
+
gl.uniform1i(loc.uTexture, 0);
|
|
497
|
+
gl.activeTexture(gl.TEXTURE0);
|
|
498
|
+
gl.enable(gl.BLEND);
|
|
499
|
+
gl.blendFunc(gl.SRC_ALPHA, gl.ONE_MINUS_SRC_ALPHA);
|
|
500
|
+
gl.depthMask(false);
|
|
501
|
+
for (const item of textured) {
|
|
502
|
+
const buffers = this.buffersFor(item.mesh);
|
|
503
|
+
gl.bindTexture(gl.TEXTURE_2D, this.textureFor(item.mesh));
|
|
504
|
+
gl.uniformMatrix4fv(loc.uModel, false, new Float32Array(item.model));
|
|
505
|
+
gl.uniform1f(loc.uOpacity, itemOpacity(item.transparency));
|
|
506
|
+
gl.bindVertexArray(buffers.vao);
|
|
507
|
+
gl.drawArrays(gl.TRIANGLES, 0, buffers.count);
|
|
508
|
+
}
|
|
509
|
+
gl.depthMask(true);
|
|
510
|
+
gl.disable(gl.BLEND);
|
|
511
|
+
gl.bindVertexArray(null);
|
|
512
|
+
}
|
|
513
|
+
destroy() {
|
|
514
|
+
const gl = this.gl;
|
|
515
|
+
if (gl) {
|
|
516
|
+
for (const texture of this.textures.values()) gl.deleteTexture(texture);
|
|
517
|
+
for (const buffers of this.buffers.values()) gl.deleteVertexArray(buffers.vao);
|
|
518
|
+
if (this.program) gl.deleteProgram(this.program);
|
|
519
|
+
}
|
|
520
|
+
this.textures.clear();
|
|
521
|
+
this.buffers.clear();
|
|
522
|
+
this.sources.clear();
|
|
523
|
+
this.gl = void 0;
|
|
524
|
+
this.program = void 0;
|
|
525
|
+
this.locations = void 0;
|
|
526
|
+
this.inner.destroy();
|
|
527
|
+
}
|
|
528
|
+
};
|