bucciafico-lib 1.0.4-BETA → 1.0.5
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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bucciafico-lib",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "Modular 3D rendering engine for Minecraft skins based on Three.js",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./index.js",
|
|
@@ -36,4 +36,4 @@
|
|
|
36
36
|
"url": "https://github.com/HappyGFX/bucciafico-lib/issues"
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/HappyGFX/bucciafico-lib#readme"
|
|
39
|
-
}
|
|
39
|
+
}
|
package/src/core/SkinViewer.js
CHANGED
|
@@ -17,6 +17,7 @@ export class SkinViewer {
|
|
|
17
17
|
* @param {boolean} [config.transparent=false] - Background transparency.
|
|
18
18
|
* @param {number} [config.bgColor=0x141417] - Background hex color.
|
|
19
19
|
* @param {boolean} [config.cameraEnabled=true] - OrbitControls state.
|
|
20
|
+
* @param {boolean} [config.renderPaused=false] - If true, rendering only happens on interaction/change.
|
|
20
21
|
*/
|
|
21
22
|
constructor(containerElement, config = {}) {
|
|
22
23
|
this.container = containerElement;
|
|
@@ -25,6 +26,7 @@ export class SkinViewer {
|
|
|
25
26
|
transparent: config.transparent ?? false,
|
|
26
27
|
bgColor: config.bgColor ?? 0x141417,
|
|
27
28
|
cameraEnabled: config.cameraEnabled ?? true,
|
|
29
|
+
renderPaused: config.renderPaused ?? false,
|
|
28
30
|
...config
|
|
29
31
|
};
|
|
30
32
|
|
|
@@ -34,6 +36,9 @@ export class SkinViewer {
|
|
|
34
36
|
/** @type {boolean} Flag to stop the animation loop. */
|
|
35
37
|
this.isDisposed = false;
|
|
36
38
|
|
|
39
|
+
this.isVisible = true;
|
|
40
|
+
this.needsRender = true;
|
|
41
|
+
|
|
37
42
|
// --- 1. RENDERER SETUP ---
|
|
38
43
|
this.renderer = new THREE.WebGLRenderer({
|
|
39
44
|
antialias: true,
|
|
@@ -62,17 +67,38 @@ export class SkinViewer {
|
|
|
62
67
|
this.sceneSetup = new SceneSetup(this.scene);
|
|
63
68
|
this.sceneSetup.setGridVisible(this.config.showGrid);
|
|
64
69
|
|
|
65
|
-
this.cameraManager = new CameraManager(this.renderer.domElement, w, h)
|
|
70
|
+
this.cameraManager = new CameraManager(this.renderer.domElement, w, h, () => {
|
|
71
|
+
this.needsRender = true;
|
|
72
|
+
});
|
|
66
73
|
this.cameraManager.setEnabled(this.config.cameraEnabled);
|
|
67
74
|
|
|
68
75
|
this.skinModel = new SkinModel();
|
|
69
76
|
this.scene.add(this.skinModel.getGroup());
|
|
70
77
|
|
|
78
|
+
this.observer = new IntersectionObserver((entries) => {
|
|
79
|
+
if (entries[0].isIntersecting) {
|
|
80
|
+
this.isVisible = true;
|
|
81
|
+
this.needsRender = true;
|
|
82
|
+
this.animate();
|
|
83
|
+
} else {
|
|
84
|
+
this.isVisible = false;
|
|
85
|
+
}
|
|
86
|
+
}, { threshold: 0 });
|
|
87
|
+
this.observer.observe(this.container);
|
|
88
|
+
|
|
71
89
|
// --- 3. START LOOP ---
|
|
72
90
|
this.animate = this.animate.bind(this);
|
|
73
91
|
this.animate();
|
|
74
92
|
}
|
|
75
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Manually requests a frame to be rendered.
|
|
96
|
+
* Use this when changing model properties via code.
|
|
97
|
+
*/
|
|
98
|
+
requestRender() {
|
|
99
|
+
this.needsRender = true;
|
|
100
|
+
}
|
|
101
|
+
|
|
76
102
|
/**
|
|
77
103
|
* Registers and initializes a plugin.
|
|
78
104
|
* @param {Object} plugin - The plugin instance (must have an init() method).
|
|
@@ -84,6 +110,7 @@ export class SkinViewer {
|
|
|
84
110
|
|
|
85
111
|
if (plugin.init) plugin.init(this);
|
|
86
112
|
this.plugins.set(name, plugin);
|
|
113
|
+
this.requestRender();
|
|
87
114
|
|
|
88
115
|
return plugin;
|
|
89
116
|
}
|
|
@@ -118,6 +145,7 @@ export class SkinViewer {
|
|
|
118
145
|
this.skinModel.setPose(currentPose);
|
|
119
146
|
this.skinData = { type: 'url', value: imageUrl };
|
|
120
147
|
|
|
148
|
+
this.requestRender();
|
|
121
149
|
resolve(isSlim);
|
|
122
150
|
}, undefined, reject);
|
|
123
151
|
});
|
|
@@ -139,6 +167,7 @@ export class SkinViewer {
|
|
|
139
167
|
if (editor) editor.saveHistory();
|
|
140
168
|
|
|
141
169
|
this.skinModel.setPose(poseData);
|
|
170
|
+
this.requestRender();
|
|
142
171
|
}
|
|
143
172
|
|
|
144
173
|
/**
|
|
@@ -156,14 +185,20 @@ export class SkinViewer {
|
|
|
156
185
|
this.plugins.forEach(p => {
|
|
157
186
|
if (p.onResize) p.onResize(w, h);
|
|
158
187
|
});
|
|
188
|
+
|
|
189
|
+
this.requestRender();
|
|
159
190
|
}
|
|
160
191
|
|
|
161
192
|
animate() {
|
|
162
|
-
if (this.isDisposed) return;
|
|
193
|
+
if (this.isDisposed || !this.isVisible) return;
|
|
163
194
|
requestAnimationFrame(this.animate);
|
|
164
195
|
|
|
165
196
|
this.cameraManager.update();
|
|
166
197
|
|
|
198
|
+
if (this.config.renderPaused && !this.needsRender) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
167
202
|
const effects = this.getPlugin('EffectsPlugin');
|
|
168
203
|
|
|
169
204
|
if (effects) {
|
|
@@ -175,10 +210,13 @@ export class SkinViewer {
|
|
|
175
210
|
|
|
176
211
|
this.renderer.clearDepth();
|
|
177
212
|
this.renderer.render(this.overlayScene, this.cameraManager.camera);
|
|
213
|
+
|
|
214
|
+
this.needsRender = false;
|
|
178
215
|
}
|
|
179
216
|
|
|
180
217
|
dispose() {
|
|
181
218
|
this.isDisposed = true;
|
|
219
|
+
this.observer.disconnect();
|
|
182
220
|
|
|
183
221
|
if (this.container && this.renderer.domElement) {
|
|
184
222
|
this.container.removeChild(this.renderer.domElement);
|
|
@@ -5,7 +5,14 @@ import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
|
|
|
5
5
|
* Wraps Three.js Camera and OrbitControls.
|
|
6
6
|
*/
|
|
7
7
|
export class CameraManager {
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* @param {HTMLElement} domElement
|
|
11
|
+
* @param {number} width
|
|
12
|
+
* @param {number} height
|
|
13
|
+
* @param {Function} [onChange] - Callback fired when camera moves
|
|
14
|
+
*/
|
|
15
|
+
constructor(domElement, width, height, onChange) {
|
|
9
16
|
this.defaultFOV = 45;
|
|
10
17
|
this.defaultPosition = new THREE.Vector3(20, 10, 40);
|
|
11
18
|
this.defaultTarget = new THREE.Vector3(0, 0, 0);
|
|
@@ -17,6 +24,8 @@ export class CameraManager {
|
|
|
17
24
|
this.controls.enableDamping = true;
|
|
18
25
|
this.controls.dampingFactor = 0.05;
|
|
19
26
|
this.controls.target.copy(this.defaultTarget);
|
|
27
|
+
|
|
28
|
+
if (onChange) this.controls.addEventListener('change', onChange);
|
|
20
29
|
}
|
|
21
30
|
|
|
22
31
|
update() { this.controls.update(); }
|
|
@@ -18,7 +18,7 @@ export class PostProcessingManager {
|
|
|
18
18
|
this.bloomComposer.renderToScreen = false;
|
|
19
19
|
this.bloomComposer.addPass(new RenderPass(scene, camera));
|
|
20
20
|
|
|
21
|
-
this.bloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.
|
|
21
|
+
this.bloomPass = new UnrealBloomPass(new THREE.Vector2(width, height), 1.5, 0.4, 0.85);
|
|
22
22
|
this.bloomComposer.addPass(this.bloomPass);
|
|
23
23
|
|
|
24
24
|
// 2. FINAL COMPOSER
|
|
@@ -49,13 +49,26 @@ export class PostProcessingManager {
|
|
|
49
49
|
uniform sampler2D tDiffuse;
|
|
50
50
|
uniform sampler2D bloomTexture;
|
|
51
51
|
varying vec2 vUv;
|
|
52
|
+
|
|
52
53
|
void main() {
|
|
53
54
|
vec4 baseColor = texture2D(tDiffuse, vUv);
|
|
54
55
|
vec4 bloomColor = texture2D(bloomTexture, vUv);
|
|
55
56
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
vec3 bloomRGB = bloomColor.rgb;
|
|
58
|
+
float brightness = max(bloomRGB.r, max(bloomRGB.g, bloomRGB.b));
|
|
59
|
+
|
|
60
|
+
if (brightness < 0.15) {
|
|
61
|
+
bloomRGB = vec3(0.0);
|
|
62
|
+
brightness = 0.0;
|
|
63
|
+
} else {
|
|
64
|
+
bloomRGB = (bloomRGB - 0.15) * 1.2;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
vec3 finalColor = baseColor.rgb + (bloomRGB * 2.0);
|
|
68
|
+
float glowAlpha = clamp(brightness, 0.0, 1.0);
|
|
69
|
+
float finalAlpha = max(baseColor.a, glowAlpha);
|
|
70
|
+
|
|
71
|
+
gl_FragColor = vec4(finalColor, finalAlpha);
|
|
59
72
|
}
|
|
60
73
|
`
|
|
61
74
|
};
|
|
@@ -6,7 +6,6 @@ const vertexShader = `
|
|
|
6
6
|
varying vec3 vNormal;
|
|
7
7
|
void main() {
|
|
8
8
|
vNormal = normal;
|
|
9
|
-
// Extrude vertex along normal
|
|
10
9
|
vec3 newPos = position + normal * thickness;
|
|
11
10
|
vY = position.y;
|
|
12
11
|
gl_Position = projectionMatrix * modelViewMatrix * vec4(newPos, 1.0);
|
|
@@ -21,10 +20,8 @@ const fragmentShader = `
|
|
|
21
20
|
varying vec3 vNormal;
|
|
22
21
|
void main() {
|
|
23
22
|
if (opacity <= 0.01) discard;
|
|
24
|
-
// Discard bottom faces to avoid "floor" artifact
|
|
25
23
|
if (vNormal.y < -0.9) discard;
|
|
26
24
|
|
|
27
|
-
// Gradient logic
|
|
28
25
|
float normalizedY = (vY + (partHeight / 2.0)) / partHeight;
|
|
29
26
|
float alpha = smoothstep(1.0 - gradientLimit, 1.0, normalizedY);
|
|
30
27
|
alpha *= smoothstep(0.0, 0.2, normalizedY);
|
|
@@ -49,8 +46,11 @@ export function createGlowMaterial(partHeight) {
|
|
|
49
46
|
vertexShader,
|
|
50
47
|
fragmentShader,
|
|
51
48
|
transparent: true,
|
|
52
|
-
side: THREE.BackSide,
|
|
49
|
+
side: THREE.BackSide,
|
|
53
50
|
depthWrite: false,
|
|
54
|
-
blending: THREE.AdditiveBlending
|
|
51
|
+
blending: THREE.AdditiveBlending,
|
|
52
|
+
polygonOffset: true,
|
|
53
|
+
polygonOffsetFactor: 1.0,
|
|
54
|
+
polygonOffsetUnits: 4.0
|
|
55
55
|
});
|
|
56
56
|
}
|
|
@@ -35,7 +35,7 @@ export class EffectsPlugin {
|
|
|
35
35
|
if (config.height !== undefined) skin.updateGlowHeight(config.height);
|
|
36
36
|
|
|
37
37
|
// Update Composer Pass
|
|
38
|
-
this.composer.setBloom(config.enabled, config.strength, config.radius, 0.
|
|
38
|
+
this.composer.setBloom(config.enabled, config.strength, config.radius, 0.85);
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
/**
|