Skip to content

Commit b1e8b85

Browse files
custom character loader
1 parent 59c3f4f commit b1e8b85

File tree

3 files changed

+167
-37
lines changed

3 files changed

+167
-37
lines changed

characterLoader.js

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
window.addEventListener('sceneReady', () => {
2+
document.getElementById('characterUpload').addEventListener('change', async (event) => {
3+
const file = event.target.files[0];
4+
if (!file) return;
5+
6+
const statusMessage = document.getElementById('statusMessage');
7+
statusMessage.textContent = 'Loading character...';
8+
9+
try {
10+
const scene = window.scene;
11+
if (!scene) throw new Error('Scene not initialized');
12+
13+
const arrayBuffer = await file.arrayBuffer();
14+
const blob = new Blob([arrayBuffer], { type: 'model/gltf-binary' });
15+
const blobUrl = URL.createObjectURL(blob);
16+
17+
BABYLON.SceneLoader.LoadAssetContainer("", blobUrl, scene, function (container) {
18+
// Clear previous character completely
19+
if (window.currentCharacter) {
20+
// Stop and dispose all animations
21+
scene.animationGroups.slice().forEach(group => {
22+
group.stop();
23+
group.dispose();
24+
});
25+
26+
// Remove all meshes related to current character
27+
scene.meshes.slice().forEach(mesh => {
28+
if (mesh !== scene.ground) { // Keep the ground
29+
mesh.dispose();
30+
}
31+
});
32+
33+
// Clear any skeletons
34+
scene.skeletons.slice().forEach(skeleton => {
35+
skeleton.dispose();
36+
});
37+
38+
window.currentCharacter = null;
39+
window.currentAnimationGroup = null;
40+
}
41+
42+
// Add new meshes and animations
43+
container.addAllToScene();
44+
window.currentCharacter = container.meshes[0];
45+
window.animationGroups = container.animationGroups;
46+
47+
// Position the new character
48+
window.currentCharacter.position = new BABYLON.Vector3(0, 0, 0);
49+
window.currentCharacter.rotation = new BABYLON.Vector3(0, Math.PI/2, 0);
50+
window.currentCharacter.scaling = new BABYLON.Vector3(1, 1, 1);
51+
52+
// Update animation list
53+
const animationList = document.getElementById('animationList');
54+
animationList.innerHTML = '';
55+
container.animationGroups.forEach((group, index) => {
56+
const option = document.createElement('option');
57+
option.value = index;
58+
option.text = group.name;
59+
animationList.appendChild(option);
60+
});
61+
62+
// Play first animation if available
63+
if (container.animationGroups.length > 0) {
64+
window.currentAnimationGroup = container.animationGroups[0];
65+
window.currentAnimationGroup.start(true);
66+
}
67+
68+
statusMessage.textContent = 'Character loaded successfully!';
69+
URL.revokeObjectURL(blobUrl);
70+
}, null, function (error) {
71+
statusMessage.textContent = 'Error loading character: ' + error.message;
72+
URL.revokeObjectURL(blobUrl);
73+
}, ".glb");
74+
75+
} catch (error) {
76+
statusMessage.textContent = 'Error: ' + error.message;
77+
}
78+
});
79+
});

index.html

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,33 @@
145145
#cameraControlPanel button:active {
146146
background-color: #888 !important;
147147
}
148+
149+
.upload-section {
150+
margin: 10px 0;
151+
position: relative;
152+
}
153+
154+
#characterUpload {
155+
opacity: 0;
156+
width: 0.1px;
157+
height: 0.1px;
158+
position: absolute;
159+
}
160+
161+
.upload-label {
162+
display: block;
163+
padding: 8px 12px;
164+
background-color: #4CAF50;
165+
color: white;
166+
border-radius: 4px;
167+
cursor: pointer;
168+
text-align: center;
169+
margin: 5px 0;
170+
}
171+
172+
.upload-label:hover {
173+
background-color: #45a049;
174+
}
148175
</style>
149176
<!-- BabylonJS and loaders -->
150177
<script src="https://cdn.babylonjs.com/babylon.js"></script>
@@ -156,6 +183,10 @@
156183
</div>
157184
<div id="controlPanel">
158185
<h3>Animation Controls</h3>
186+
<div class="upload-section">
187+
<input type="file" id="characterUpload" accept=".glb">
188+
<label for="characterUpload" class="upload-label">Upload Character (.glb)</label>
189+
</div>
159190
<select id="animationList"></select>
160191
<button id="playBtn">Play</button>
161192
<button id="pauseBtn">Pause</button>
@@ -203,6 +234,7 @@ <h3>Sprite Sheet Generator</h3>
203234

204235
<script src="js/SpriteSheetHelper.js"></script>
205236
<script src="js/fbxViewer.js"></script>
237+
<script src="characterLoader.js"></script>
206238

207239
<!-- Add compatibility check -->
208240
<script>

js/fbxViewer.js

Lines changed: 56 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
document.addEventListener('DOMContentLoaded', function() {
1+
document.addEventListener('DOMContentLoaded', async function() {
22
// Get references to HTML elements
33
const canvas = document.getElementById('renderCanvas');
44
const animationList = document.getElementById('animationList');
@@ -87,8 +87,8 @@ document.addEventListener('DOMContentLoaded', function() {
8787

8888
// Create the scene
8989
const createScene = async function() {
90-
// Create a basic scene
91-
scene = new BABYLON.Scene(engine);
90+
const scene = new BABYLON.Scene(engine);
91+
window.scene = scene; // Make scene globally accessible
9292

9393
// Set transparent background by setting alpha to 0
9494
scene.clearColor = new BABYLON.Color4(0, 0, 0, 0);
@@ -180,6 +180,9 @@ document.addEventListener('DOMContentLoaded', function() {
180180
// Create camera adjustment UI
181181
createCameraControls();
182182

183+
// Dispatch event when scene is ready
184+
window.dispatchEvent(new CustomEvent('sceneReady'));
185+
183186
return scene;
184187
};
185188

@@ -979,46 +982,62 @@ document.addEventListener('DOMContentLoaded', function() {
979982
}
980983

981984
// Create and set up the scene
982-
createScene().then(() => {
983-
// Register event listeners
984-
playBtn.addEventListener('click', () => {
985-
const selectedIndex = parseInt(animationList.value);
986-
playAnimation(selectedIndex);
987-
});
988-
989-
pauseBtn.addEventListener('click', () => {
990-
if (currentAnimationGroup) {
991-
currentAnimationGroup.pause();
992-
showStatus(`Paused animation: ${currentAnimationGroup.name}`);
993-
}
994-
});
995-
996-
animationList.addEventListener('change', () => {
997-
const selectedIndex = parseInt(animationList.value);
998-
playAnimation(selectedIndex);
999-
});
1000-
1001-
// Add sprite sheet recording event listener
1002-
recordBtn.addEventListener('click', startRecording);
985+
try {
986+
scene = await createScene();
1003987

1004-
// Run the render loop with frame capture REMOVED from here
988+
// Only start render loop after scene is created
1005989
engine.runRenderLoop(() => {
1006-
scene.render();
1007-
// We'll handle frame capture separately with the recordingLoop function
990+
if (scene) {
991+
scene.render();
992+
}
1008993
});
1009-
994+
1010995
// Handle window resize
1011996
window.addEventListener('resize', () => {
1012-
// Resize the engine
1013997
engine.resize();
1014-
1015-
// Ensure camera maintains square ratio
1016-
updateOrthoCamera();
1017-
1018-
// Update canvas size to remain square
1019-
const size = Math.min(window.innerHeight, window.innerWidth);
1020-
canvas.style.width = `${size}px`;
1021-
canvas.style.height = `${size}px`;
1022998
});
999+
} catch (error) {
1000+
console.error("Error creating scene:", error);
1001+
}
1002+
1003+
// Register event listeners
1004+
playBtn.addEventListener('click', () => {
1005+
const selectedIndex = parseInt(animationList.value);
1006+
playAnimation(selectedIndex);
1007+
});
1008+
1009+
pauseBtn.addEventListener('click', () => {
1010+
if (currentAnimationGroup) {
1011+
currentAnimationGroup.pause();
1012+
showStatus(`Paused animation: ${currentAnimationGroup.name}`);
1013+
}
1014+
});
1015+
1016+
animationList.addEventListener('change', () => {
1017+
const selectedIndex = parseInt(animationList.value);
1018+
playAnimation(selectedIndex);
1019+
});
1020+
1021+
// Add sprite sheet recording event listener
1022+
recordBtn.addEventListener('click', startRecording);
1023+
1024+
// Run the render loop with frame capture REMOVED from here
1025+
engine.runRenderLoop(() => {
1026+
scene.render();
1027+
// We'll handle frame capture separately with the recordingLoop function
1028+
});
1029+
1030+
// Handle window resize
1031+
window.addEventListener('resize', () => {
1032+
// Resize the engine
1033+
engine.resize();
1034+
1035+
// Ensure camera maintains square ratio
1036+
updateOrthoCamera();
1037+
1038+
// Update canvas size to remain square
1039+
const size = Math.min(window.innerHeight, window.innerWidth);
1040+
canvas.style.width = `${size}px`;
1041+
canvas.style.height = `${size}px`;
10231042
});
10241043
});

0 commit comments

Comments
 (0)