Implemented model-index.json, Separated individual variants, Italicized scientific name, updated Frog Zoo

This commit is contained in:
2026-05-28 16:53:21 +08:00
parent 5314f859b6
commit 386aa5958a
42 changed files with 196 additions and 71 deletions
+95 -64
View File
@@ -13,6 +13,16 @@ const sizes = {
}
let loadData = null;
fetch('model-index.json')
.then(response => response.json())
.then(data => {
loadData = data;
startApp();
});
//RENDERER
const renderer = new THREE.WebGLRenderer({antialias:true});
renderer.setSize( sizes.width, sizes.height );
@@ -26,18 +36,14 @@ document.body.appendChild( renderer.domElement );
//CAMERA
const DEFAULT_CAMERA_POS = new THREE.Vector3(0, 0, 2); // Camera default position
const DEFAULT_CAMERA_POS = new THREE.Vector3(-0.4, 0.5, 1.5); // Camera default position
const DEFAULT_TARGET = new THREE.Vector3(0, 0, 0); // Camera default target
const camera = new THREE.PerspectiveCamera( 65, sizes.width / sizes.height, 0.1, 1000 );
const camera = new THREE.PerspectiveCamera( 65, sizes.width / sizes.height, 0.001, 1000 );
// Apply default position initially
camera.position.copy(DEFAULT_CAMERA_POS);
camera.lookAt(DEFAULT_TARGET);
// camera.position.x = 0;
// camera.position.y = 0;
// camera.position.z = 2;
const controls = new OrbitControls( camera, renderer.domElement );
controls.enableDamping = true;
controls.dampingFactor = 0.05; // Friction
@@ -63,44 +69,26 @@ const loader = new GLTFLoader();
let currentModel = null;
// MODEL PATHS
const modelPaths = {
'[MBS-2351] Ansonia muelleri': 'models/MBS-2351 Ansonia muelleri.glb',
'[MBS-2355] Platymantis corrugatus': 'models/MBS-2355 Platymantis corrugatus.glb',
'[MBS-2357] Hylarana grandocula': 'models/MBS-2357 Hylarana grandocula.glb',
'[MBS-2358] Sanguirana mearnsi': 'models/MBS-2358 Sanguirana mearnsi.glb',
'[MBS-2362] Hoplobatrachus rugulosus': 'models/MBS-2362 Hoplobatrachus rugulosus.glb',
'[MBS-2407] Staurois natator': 'models/MBS-2407 Staurois natator.glb',
'[MBS-2408] Oreophryne anulata': 'models/MBS-2408 Oreophryne anulata.glb',
'[MBS-2410] Platymantis guentheri': 'models/MBS-2410 Platymantis guentheri.glb',
'[MBS-2415] Kaloula pulchra': 'models/MBS-2415 Kaloula pulchra.glb',
'[MBS-2416] Rhinella marina': 'models/MBS-2416 Rhinella marina.glb',
'Frog Zoo': 'models/-------- Frog Zoo.glb'
};
const params = {
selectedModel: modelPaths['[MBS-2351] Ansonia muelleri'] // default selected option
};
// UPDATE NAME FUNCTION
function updateNameText(name) {
const recordtitleElement = document.getElementById('recordnumbertext');
const titleElement = document.getElementById('nametext');
//Record Number
const recnumberstart = name.slice(7);
const trimmedrecnumber = recnumberstart.slice(0,8);
recordtitleElement.textContent = trimmedrecnumber;
const recnumber = name.slice(0,11);
recordtitleElement.textContent = recnumber;
//Scientific Name
const trimmedstart = name.slice(16);
const trimmedsciname = trimmedstart.slice(0, -4);
titleElement.textContent = trimmedsciname;
const trimmedstart = name.slice(11);
titleElement.textContent = trimmedstart;
titleElement.style.fontStyle = "italic"; //italicize scientific name
}
// LOAD MODEL FUNCTION
function loadNewModel(url) {
const gui = new GUI({ width: 425 }); //New UI
//Load Function
function loadModel(modelPath) {
if (currentModel) {
scene.remove(currentModel);
currentModel.traverse((child) => {
@@ -115,44 +103,87 @@ function loadNewModel(url) {
});
currentModel = null;
}
console.log(`Loaded: ${modelPath}`);
//Load the new model
loader.load(
url,
(glb) => {
currentModel = glb.scene;
scene.add(currentModel);
loader.load(modelPath, (glb) => {
currentModel = glb.scene;
scene.add(glb.scene);
});
}
function startApp() {
// 1. Create an object to hold the currently selected values
const modelsData = loadData;
const params = {
name: modelsData[0].name,
variant: Object.keys(modelsData[0].variants)[0]
};
// 2. Extract just the names for the first dropdown
const modelNames = modelsData.map(item => item.name);
// 3. Add the Controllers
const nameController = gui.add(params, 'name', modelNames).name('Model Name');
// Initialize the variants dropdown with the first model's options
let variantOptions = Object.keys(modelsData[0].variants);
let variantController = gui.add(params, 'variant', variantOptions).name('Variant');
// 4. Handle dynamic updates
nameController.onChange((selectedName) => {
const selectedModelData = modelsData.find(item => item.name === selectedName); // Find the data for the newly selected model
const newVariants = Object.keys(selectedModelData.variants);
params.variant = newVariants[0]; // Update the param state to the first available variant of the new model
// Destroy the old variant dropdown and create a new one with updated options
variantController.destroy();
variantController = gui.add(params, 'variant', newVariants).name('Variant');
// Re-attach the onChange listener to the new dropdown
attachVariantListener(variantController, selectedModelData);
//Load Model
loadModel(selectedModelData.variants[params.variant]);
// Reset camera position and target
resetcam();
//Call function to update name text
updateNameText(selectedName);
});
// Helper function to handle variant changes
function attachVariantListener(controller, modelData) {
controller.onChange((selectedVariant) => {
const modelPath = modelData.variants[selectedVariant];
// Load Model
loadModel(modelPath);
});
}
// Attach listener to the very first variant dropdown
attachVariantListener(variantController, modelsData[0]);
const initialModelPath = modelsData[0].variants[params.variant];
// Load default model right away
loadModel(initialModelPath);
updateNameText(modelsData[0].name);
// Reset camera transforms
function resetcam(){
camera.position.copy(DEFAULT_CAMERA_POS);
if (controls) {
controls.target.copy(DEFAULT_TARGET);
controls.update();
} else {
camera.lookAt(DEFAULT_TARGET);
}
);
//Call function to update name text
updateNameText(params.selectedModel);
}
}
// GUI
const gui = new GUI({ width: 425 });
gui.add(params, 'selectedModel', modelPaths)
.name('Displayed Model:')
.onChange((newUrl) => {
loadNewModel(newUrl);
// Reset camera position and target
camera.position.copy(DEFAULT_CAMERA_POS);
if (controls) {
controls.target.copy(DEFAULT_TARGET);
controls.update();
} else {
camera.lookAt(DEFAULT_TARGET);
}
});
// Load the initial default model right away
loadNewModel(params.selectedModel);
gui.domElement.style.right = '0px';
//ENVIRONMENT
const env_loader = new UltraHDRLoader();
const env_texture = await env_loader.loadAsync( 'assets/studio_small_08_4k.jpg' );