bucciafico-lib 1.0.4-BETA → 1.0.6

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.4-BETA",
3
+ "version": "1.0.6",
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
+ }
@@ -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,28 +67,50 @@ 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).
79
105
  * @returns {Object} The registered plugin instance.
80
106
  */
81
107
  addPlugin(plugin) {
82
- const name = plugin.constructor.name;
108
+ const name = plugin.name || plugin.constructor.name;
83
109
  if (this.plugins.has(name)) return this.plugins.get(name);
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
- constructor(domElement, width, height) {
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.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
- // Add only colors (RGB), keep the Base Alpha.
57
- // This prevents the black background of the bloom pass from overwriting transparency.
58
- gl_FragColor = vec4(baseColor.rgb + (bloomColor.rgb * 2.5), baseColor.a);
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, // Render inside of the extruded box
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.1);
38
+ this.composer.setBloom(config.enabled, config.strength, config.radius, 0.85);
39
39
  }
40
40
 
41
41
  /**
@@ -24,7 +24,7 @@ export class IOPlugin {
24
24
  const state = {
25
25
  meta: {
26
26
  generator: "Bucciafico Studio",
27
- version: "1.0.4-BETA",
27
+ version: "1.0.7-BETA",
28
28
  timestamp: Date.now()
29
29
  },
30
30
  core: {}