You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
210 lines
9.0 KiB
210 lines
9.0 KiB
import * as THREE from 'three';
|
|
import { GLTFLoader } from 'three/examples/jsm/Addons.js';
|
|
import { DRACOLoader } from 'three/examples/jsm/Addons.js';
|
|
import { createCustomJitterMaterial } from './materials/CustomJitterMaterial';
|
|
import { TextureLoader } from 'three';
|
|
|
|
export class ModelLoader {
|
|
constructor() {
|
|
this.gltfLoader = new GLTFLoader();
|
|
this.textureLoader = new TextureLoader();
|
|
const dracoLoader = new DRACOLoader();
|
|
dracoLoader.setDecoderPath('/draco/');
|
|
this.gltfLoader.setDRACOLoader(dracoLoader)
|
|
}
|
|
|
|
|
|
_applyCustomMaterial(object) {
|
|
let texture = null;
|
|
if (object.material && object.material.map) {
|
|
texture = object.material.map;
|
|
}
|
|
|
|
const canvas = document.createElement('canvas');
|
|
const context = canvas.getContext('2d');
|
|
|
|
if (texture && texture.image) {
|
|
canvas.width = texture.image.width;
|
|
canvas.height = texture.image.height;
|
|
context.drawImage(texture.image, 0, 0);
|
|
} else {
|
|
// Fallback if no texture is found
|
|
canvas.width = 1024;
|
|
canvas.height = 1024;
|
|
context.fillStyle = '#0044ffff';
|
|
context.fillRect(0, 0, 1024, 1024);
|
|
}
|
|
|
|
const drawableTexture = new THREE.CanvasTexture(canvas);
|
|
drawableTexture.flipY = false;
|
|
drawableTexture.needsUpdate = true;
|
|
drawableTexture.encoding = THREE.sRGBEncoding;
|
|
|
|
object.material = createCustomJitterMaterial(100, drawableTexture);
|
|
}
|
|
|
|
loadStaticWorld(modelURL, worldScale) {
|
|
return new Promise((resolve, reject) => {
|
|
this.gltfLoader.load(modelURL, (gltf) => {
|
|
const staticGroup = gltf.scene.getObjectByName('Static');
|
|
const processedObjects = [];
|
|
|
|
if (staticGroup) {
|
|
// We need to handle children carefully as we might be reparenting them
|
|
const children = [...staticGroup.children];
|
|
children.forEach(object => {
|
|
if (object.isMesh) {
|
|
// 1. Apply world scale to position and object scale
|
|
object.position.multiplyScalar(worldScale);
|
|
object.scale.multiplyScalar(worldScale);
|
|
|
|
// Detach from the original parent ('Static' group)
|
|
// so it can be added directly to the scene later.
|
|
gltf.scene.attach(object);
|
|
this._applyCustomMaterial(object);
|
|
processedObjects.push(object);
|
|
}
|
|
});
|
|
resolve(processedObjects);
|
|
} else {
|
|
console.warn("Could not find 'Static' group in the loaded model.");
|
|
resolve([]); // Resolve with an empty array if group not found
|
|
}
|
|
}, undefined, (error) => {
|
|
console.error('An error happened while loading the world model:', error);
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
loadDynamicGroups(modelURL, worldScale) {
|
|
return new Promise((resolve, reject) => {
|
|
this.gltfLoader.load(modelURL, (gltf) => {
|
|
const dynamicGroup = gltf.scene.getObjectByName('Dynamic');
|
|
const groups = [];
|
|
|
|
if (dynamicGroup) {
|
|
// Iterate over children of 'dynamic', which are the subgroups (e.g., 'reddit')
|
|
const subgroups = [...dynamicGroup.children];
|
|
subgroups.forEach(subgroup => {
|
|
if (subgroup.isGroup || subgroup.isObject3D) { // Treat empties as groups
|
|
const groupData = {
|
|
name: subgroup.name,
|
|
objects: []
|
|
};
|
|
|
|
// Iterate over the actual mesh objects within the subgroup
|
|
const objects = [...subgroup.children];
|
|
objects.forEach(object => {
|
|
if (object.isMesh) {
|
|
// 1. Apply world scale
|
|
object.position.multiplyScalar(worldScale);
|
|
object.scale.multiplyScalar(worldScale);
|
|
|
|
// 2. Detach from original parent and apply material
|
|
gltf.scene.attach(object);
|
|
this._applyCustomMaterial(object);
|
|
|
|
groupData.objects.push(object);
|
|
}
|
|
});
|
|
groups.push(groupData);
|
|
}
|
|
});
|
|
resolve(groups);
|
|
} else {
|
|
console.warn("Could not find 'dynamic' group in the loaded model.");
|
|
resolve([]);
|
|
}
|
|
}, undefined, (error) => {
|
|
console.error('An error happened while loading the dynamic groups:', error);
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|
|
|
|
loadDRACOModelURL(modelURL, textureURL, scale) {
|
|
return new Promise((resolve, reject) => {
|
|
const applyMaterial = (object, texture) => {
|
|
object.traverse((child) => {
|
|
if (child.isMesh) {
|
|
texture.encoding = THREE.sRGBEncoding;
|
|
texture.needsUpdate = true;
|
|
child.material = createCustomJitterMaterial(100, texture);
|
|
}
|
|
});
|
|
};
|
|
|
|
const processObject = (object, texture) => {
|
|
object.scale.copy(scale);
|
|
const box = new THREE.Box3().setFromObject(object);
|
|
const height = box.max.y - box.min.y;
|
|
object.position.y = height / 2;
|
|
object.position.x = 0;
|
|
applyMaterial(object, texture);
|
|
resolve(object);
|
|
};
|
|
|
|
if (textureURL) {
|
|
this.textureLoader.load(textureURL, function (baseTexture) {
|
|
baseTexture.flipY = false;
|
|
const canvas = document.createElement('canvas');
|
|
const context = canvas.getContext('2d');
|
|
canvas.width = baseTexture.image.width;
|
|
canvas.height = baseTexture.image.height;
|
|
context.drawImage(baseTexture.image, 0, 0);
|
|
const drawableTexture = new THREE.CanvasTexture(canvas);
|
|
drawableTexture.flipY = false;
|
|
|
|
this.gltfLoader.load(modelURL, function (gltf) {
|
|
processObject(gltf.scene, drawableTexture);
|
|
}, undefined, function (error) {
|
|
reject(error);
|
|
});
|
|
}, undefined, function (error) {
|
|
reject(error);
|
|
});
|
|
} else {
|
|
this.gltfLoader.load(modelURL, function (gltf) {
|
|
|
|
let extractedTexture = null;
|
|
gltf.scene.traverse((child) => {
|
|
|
|
if (child.isMesh && child.material && child.material.map) {
|
|
if (!extractedTexture) {
|
|
extractedTexture = child.material.map;
|
|
}
|
|
}
|
|
});
|
|
|
|
let finalTexture;
|
|
|
|
if (extractedTexture && extractedTexture.image) {
|
|
// The texture from the GLTF is valid, make it a drawable CanvasTexture
|
|
const canvas = document.createElement('canvas');
|
|
const context = canvas.getContext('2d');
|
|
canvas.width = extractedTexture.image.width;
|
|
canvas.height = extractedTexture.image.height;
|
|
context.drawImage(extractedTexture.image, 0, 0);
|
|
finalTexture = new THREE.CanvasTexture(canvas);
|
|
} else {
|
|
// fallback: create a white texture
|
|
const canvas = document.createElement('canvas');
|
|
canvas.width = 1024;
|
|
canvas.height = 1024;
|
|
const ctx = canvas.getContext('2d');
|
|
ctx.fillStyle = '#0044ffff';
|
|
ctx.fillRect(0, 0, 1024, 1024);
|
|
finalTexture = new THREE.CanvasTexture(canvas);
|
|
}
|
|
|
|
finalTexture.flipY = false;
|
|
|
|
processObject(gltf.scene, finalTexture);
|
|
}, undefined, function (error) {
|
|
reject(error);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|