refactoring meshUtils, added sceneScale, #74

This commit is contained in:
2026-03-24 20:25:50 +02:00
parent 0928ef8999
commit 008cc428d4
19 changed files with 199 additions and 189 deletions
@@ -22,7 +22,6 @@
<script>
import { GameEngine } from '@/lib/GameEngine.js';
import { autoScale } from '@/lib/MeshUtils';
let engine = null;
export default{
@@ -81,7 +80,7 @@ export default{
this.animations = gltf.animations.map(a => ({
name: a.name, id: a.uuid
}));
autoScale(gltf.scene);
engine.meshUtils.autoScale(gltf.scene);
let bb = new engine.$.Box3().setFromObject(gltf.scene);
engine.camera.position.set(bb.max.x, bb.max.y, bb.max.z*3);
engine.orbitControls.target.set((bb.max.x + bb.min.x) / 2, (bb.max.y + bb.min.y) / 2, (bb.max.z + bb.min.z) / 2)
@@ -1,9 +1,8 @@
import { bottomOrigin } from "@/lib/MeshUtils";
class CharacterObject{
constructor(engine, data){
return new Promise(async(resolve, reject)=>{
this.source = await engine.load(data.$go.asset.name);
this.object = bottomOrigin(this.source.scene);
this.object = engine.meshUtils.bottomOrigin(this.source.scene);
resolve(this);
})
}
@@ -1,6 +1,5 @@
import { Color, Group, DoubleSide, RepeatWrapping, MeshStandardMaterial, VideoTexture } from "three"
import { EventManager } from '@/lib/EventManager';
import { centerOrigin, clearMaterial, clearObject } from "@/lib/MeshUtils";
import Utils from "#/app/Utils";
class ClassicPuzzle extends EventManager {
@@ -100,11 +99,11 @@ class ClassicPuzzle extends EventManager {
done0.position.set(0,0,0);
engine.draggable.remove(done0)
container.add(dragZone);
this.object = centerOrigin(container);
this.object = engine.meshUtils.centerOrigin(container);
this.dispose = () => {
//console.log('disposing!!!!!!!')
clearMaterial(defaultMaterial);
clearObject(gltf.scene);
engine.meshUtils.clearMaterial(defaultMaterial);
engine.meshUtils.clearObject(gltf.scene);
super.dispose();
}
resolve(this);
@@ -1,4 +1,3 @@
import { getBoundingBox, getBoundingBoxCenterPoint, getBoundingBoxMaxLength, centerOrigin } from "@/lib/MeshUtils";
import { EventManager } from '@/lib/EventManager';
class GenericObject extends EventManager{
@@ -7,7 +6,7 @@ class GenericObject extends EventManager{
super();
return new Promise(async(resolve, reject)=>{
this.source = await engine.load(data.$go.asset.name);
this.object = centerOrigin(this.source.scene)
this.object = engine.meshUtils.bottomOrigin(this.source.scene)
if (!data.exclude){
engine.clickable.add(this.object, async e=>{
@@ -17,9 +16,9 @@ class GenericObject extends EventManager{
if (this.object._hud ){
engine.dashboard.detach(this.object);
}else{
let bb = getBoundingBox(this.object);
let scale = 0.5 * engine.dashboard.height/getBoundingBoxMaxLength(bb);
let position = getBoundingBoxCenterPoint(bb, this.object.position).multiplyScalar(scale).negate()
let bb = engine.meshUtils.getBoundingBox(this.object);
let scale = 0.5 * engine.dashboard.height/engine.meshUtils.getBoundingBoxMaxLength(bb);
let position = engine.meshUtils.getBoundingBoxCenterPoint(bb, this.object.position).multiplyScalar(scale).negate()
let placement = {
quaternion: { x:0, y:0, z:0, w:0 },
@@ -1,5 +1,4 @@
import { Vector3, AnimationMixer } from "three";
import { assignMaterial } from "@/lib/MeshUtils";
import { AnimationMixer } from "three";
class GltfObject {
constructor(engine, obj) {
@@ -13,7 +12,7 @@ class GltfObject {
}
});
assignMaterial(gltfObj, obj);
engine.meshUtils.assignMaterial(gltfObj, obj);
if (gltf.animations && gltf.animations.length) {
let mixer = new AnimationMixer(gltfObj);
engine.mixers.push(mixer);
@@ -19,7 +19,6 @@ import { SingleQuestion } from "./SingleQuestion";
import { MazeQuizGame } from "./MazeQuizGame/MazeQuizGame";
import { Particles } from "./Particles";
import { SceneSwitcher } from "./SceneSwitcher";
import { assignParams, wrapInGroup, getBoundingBoxMaxLength } from "@/lib/MeshUtils";
import { GameEngine } from "@/lib/GameEngine";
const InteractiveObjectsImports = {
@@ -79,11 +78,11 @@ class InteractiveObject extends EventManager{
break;
}
if (obj.shouldBeLocked){
this.object = wrapInGroup(this.object)
this.object = engine.meshUtils.wrapInGroup(this.object)
this.activator = new (obj.activationType == 'unlock' ? LockActivator : VisibilityActivator)(engine, this.object);
this.activator.deactivate();
}
assignParams(this.object, obj);
engine.meshUtils.assignParams(this.object, obj);
if (obj.motion){
engine.motionQueue.add({
o: this.object, ...obj.motion
@@ -124,7 +123,7 @@ class LockActivator{
transparent: true, opacity:1, color: 0xaaaaaa
})
constructor(engine, group){
const bckGeometry = new SphereGeometry(getBoundingBoxMaxLength(group.userData.bbox)/2,8,8)
const bckGeometry = new SphereGeometry(engine.meshUtils.getBoundingBoxMaxLength(group.userData.bbox)/2,8,8)
const bckMesh = new Mesh(bckGeometry, LockActivator.materialLocked);
bckMesh.visible = false;
group.add(bckMesh)
@@ -1,6 +1,5 @@
import { Group, Vector3, Matrix4, Quaternion, RepeatWrapping, Vector2} from 'three';
import { InteractiveObject } from '../InteractiveObject';
import { getBoundingBox, getBoundingBoxSize } from '@/lib/MeshUtils';
class MazeObject {
constructor(engine, def, params){
@@ -157,8 +156,8 @@ class MazeObject {
o[e] = mazeAsset.scene.getObjectByName(e);
if (o[e]){
o[e].scale.setScalar(scale);
let bb = getBoundingBox(o[e]);
o[e].userData.size = getBoundingBoxSize(bb);
let bb = engine.meshUtils.getBoundingBox(o[e]);
o[e].userData.size = engine.meshUtils.getBoundingBoxSize(bb);
}
});
this.mazeObject(def, room);
@@ -1,6 +1,5 @@
import { BoxGeometry, Mesh, MeshStandardMaterial, Group, Vector3, CatmullRomCurve3, Color } from 'three';
import { BoxGeometry, Mesh, MeshStandardMaterial, Group, Vector3, CatmullRomCurve3} from 'three';
import { LineMaterial, LineGeometry, Line2 } from 'three/examples/jsm/Addons.js';
import { centerOrigin, clearMaterial } from '@/lib/MeshUtils';
import { EventManager } from '@/lib/EventManager';
import Utils from '#/app/Utils';
@@ -109,10 +108,10 @@ class PairMatchingGame extends EventManager {
};
this.object = centerOrigin(container);
this.object = engine.meshUtils.centerOrigin(container);
this.dispose = () => {
clearMaterial(material);
engine.meshUtils.clearMaterial(material);
bm.dispose();
super.dispose();
}
@@ -1,5 +1,4 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group, VideoTexture } from 'three';
import { centerOrigin, clearMaterial } from '@/lib/MeshUtils';
import { EventManager } from '@/lib/EventManager';
class PuzzleGame1 extends EventManager {
@@ -123,11 +122,11 @@ class PuzzleGame1 extends EventManager {
engine.addEventListener('beforeRender', this.update)
this.object = centerOrigin(container);
this.object = engine.meshUtils.centerOrigin(container);
this.dispose = () => {
//console.log('disposing PG1')
clearMaterial(material);
engine.meshUtils.clearMaterial(material);
bm.dispose();
super.dispose();
}
@@ -1,5 +1,4 @@
import { BoxGeometry, Mesh, MeshBasicMaterial, Group, VideoTexture } from 'three';
import { centerOrigin } from '@/lib/MeshUtils';
import { EventManager } from '@/lib/EventManager';
class PuzzleGame2 extends EventManager {
@@ -166,7 +165,7 @@ class PuzzleGame2 extends EventManager {
};
engine.addEventListener('beforeRender', this.update)
this.object = centerOrigin(container)
this.object = engine.meshUtils.centerOrigin(container)
resolve(this)
});
}
@@ -1,5 +1,4 @@
import { Group, Color } from 'three';
import { centerOrigin } from '@/lib/MeshUtils';
import { EventManager } from '@/lib/EventManager';
import { TextObject } from './TextObject';
import Utils from '#/app/Utils';
@@ -38,7 +37,7 @@ class SingleQuestion extends EventManager {
}
container.add(question.object);
container.add(answers);
this.object = centerOrigin(container);
this.object = engine.meshUtils.centerOrigin(container);
resolve(this);
})
}
@@ -1,6 +1,5 @@
import { MeshBasicMaterial, Color, DoubleSide } from "three";
import { Text } from "troika-three-text";
import { assignParams } from "@/lib/MeshUtils";
class TextObject {
constructor(engine, obj) {
@@ -21,7 +20,7 @@ class TextObject {
txt.depthOffset = 0.1;
//txt.outlineBlur = '50%';
txt.color = new Color(0x0);
assignParams(txt, obj)
engine.meshUtils.assignParams(txt, obj)
let m = new MeshBasicMaterial({
// roughness: .73,
// metalness: .37,
+3 -2
View File
@@ -4,7 +4,7 @@ class Clickable {
constructor(engine, defaultDistance) {
const objects = [];
const raycaster = new Raycaster();
let v = new Vector3;
let v = new Vector3(), cv = new Vector3();
this.add = function (object, fn, distance) {
objects.push(object);
@@ -33,6 +33,7 @@ class Clickable {
this.handle = function(){
let forExecute = [];
engine.cameraWorld.getWorldPosition(cv);
objects.filter(o=>{
do {
if (o.__active === false || o.visible === false) return false;
@@ -41,7 +42,7 @@ class Clickable {
return true;
}).forEach(o => {
o.getWorldPosition(v);
if (engine.cameraWorld.position.distanceTo(v) <= o._clickable.distance) {
if (cv.distanceTo(v) <= o._clickable.distance / engine.scale) {
const intersects = raycaster.intersectObject(o);
if (intersects[0]) {
forExecute.push({ o, i: intersects[0] });
+2 -3
View File
@@ -18,7 +18,6 @@ class DashBoard extends EventManager {
this.object = dash;
hud.visible = false;
let hudAnimation, hudPlane, textPlane, sceneHeader;
this.group = dash;
dash.add(hud);
hud.add(hudTarget)
hudTarget.position.set(0,0,0.52);
@@ -285,11 +284,11 @@ class DashBoard extends EventManager {
}
get active(){
return this.group.visible;
return this.object.visible;
}
set active(v){
this.group.visible = v;
this.object.visible = v;
}
get points(){
+10 -3
View File
@@ -4,7 +4,7 @@ class Draggable{
constructor(engine, defaultDistance){
const objects = [];
const raycaster = new Raycaster();
let v = new Vector3;
let v = new Vector3(), cv = new Vector3();
let dragging = null;
this.add = function(object, dragZone, fn, distance){
objects.push(object);
@@ -29,9 +29,16 @@ class Draggable{
this.handle = function(action){
if (['start', 'selectstart'].includes(action)){
let forExecute = [];
objects.forEach(o=>{
engine.cameraWorld.getWorldPosition(cv);
objects.filter(o=>{
do {
if (o.__active === false || o.visible === false) return false;
o = o.parent;
} while (o);
return true;
}).forEach(o=>{
o.getWorldPosition(v);
if (engine.cameraWorld.position.distanceTo(v) <= o._draggable.distance && o.visible){
if (cv.distanceTo(v) <= o._draggable.distance / engine.scale){
const intersects = raycaster.intersectObject(o);
if (intersects[0]) forExecute.push({o, i:intersects[0]})
}
+19 -8
View File
@@ -20,12 +20,13 @@ import { MotionEngine } from './MotionEngine.js';
import { Draggable } from './Draggable.js';
import { EventManager } from "./EventManager";
import { Telemetry } from './Telemetry.js';
import { clearObject } from './MeshUtils.js';
import { MeshUtils } from './MeshUtils.js';
THREE.Cache.enabled = true
const assetPath = '/asset/default/';
const defaultLightIntensity = 11;
const sceneScale = 1//.33;
class GameEngine extends EventManager{
@@ -45,6 +46,11 @@ class GameEngine extends EventManager{
);
const scene = new THREE.Scene();
this.sceneWrapper = new THREE.Group();
this.sceneWrapper.scale.setScalar(this.scale);
scene.add(this.sceneWrapper);
this.scene = scene;
this.initCameraPivot()
@@ -84,7 +90,7 @@ class GameEngine extends EventManager{
this.ambientSound = new THREE.Audio(this.listener);
this.activeObjects = new THREE.Group();
scene.add(this.activeObjects);
this.sceneWrapper.add(this.activeObjects);
if (this.opts.gizmo) {
this.orbitControls = new OrbitControls(this.camera, this.renderer.domElement);
@@ -99,6 +105,7 @@ class GameEngine extends EventManager{
this.orthographicCamera.position.set(0, 0, 100);
this.cameraRig.rotation.y = 0;
}
//this.scene.scale.setScalar(1.25);
//const controls = new MapControls( camera, renderer.domElement );
@@ -145,11 +152,12 @@ class GameEngine extends EventManager{
}
async init(domNode, opts = {}) {
this.scale = sceneScale;
this.w = domNode.clientWidth || 1200, this.h = domNode.clientHeight || 800;
this.aspect = this.w / this.h
this.opts = opts;
const gameEngine = this;
this.raycaster = new THREE.Raycaster();
this.meshUtils = new MeshUtils(this);
const renderer = new THREE.WebGLRenderer({
antialias: true,
@@ -378,7 +386,7 @@ class GameEngine extends EventManager{
this.cameraRig.add(this.orthographicCamera);
this.cameraRig.rotation.y = Math.PI;
this.cameraWorld.add(this.cameraRig);
this.scene.add(this.cameraWorld);
this.sceneWrapper.add(this.cameraWorld);
}
async initPhysics() {
@@ -626,10 +634,11 @@ class GameEngine extends EventManager{
}
processHideIfFar(){
let v = new THREE.Vector3();
let v = new THREE.Vector3(), cv = new THREE.Vector3();
this.cameraWorld.getWorldPosition(cv);
this.farArray.forEach(e=>{
e.object.getWorldPosition(v);
let dst = this.cameraWorld.position.distanceTo(v);
let dst = v.distanceTo(cv) / this.scale;
if (dst <= e.distance && !e.object.visible) {
e.object.visible = true;
}else if (dst > e.distance && e.object.visible){
@@ -672,14 +681,14 @@ class GameEngine extends EventManager{
this.gizmo?.dispose();
this.motionQueue.clearAll();
this.ambientSound.stop();
this.loadedObjects.forEach(o=>clearObject(o.scene))
this.loadedObjects.forEach(o=>this.meshUtils.clearObject(o.scene))
GameEngine.loadedTextures.forEach(t=>{
t.dispose();
});
GameEngine.loadedTextures.splice(0, GameEngine.loadedTextures.length);
this.scene.background?.dispose?.();
this.scene.environment?.dispose?.();
clearObject(this.scene);
this.meshUtils.clearObject(this.scene);
this.scene = null;
this.tm?.setScene(null);
this.removeAllListenersOfType('beforeRender');
@@ -739,6 +748,8 @@ class GameEngine extends EventManager{
})
}
static scale = 1.33;
loadTexture = GameEngine.loadTexture
}
+6 -5
View File
@@ -1,11 +1,11 @@
import { InteractiveObject } from "@/components/InteractiveObjects/InteractiveObject";
import { VideoPlayer } from '@/components/InteractiveObjects/VideoPlayer';
import { Hero } from "./Hero";
import { getBoundingBox, getBoundingBoxSize, autoScale } from "./MeshUtils";
import { api } from "./Api";
class GameManager{
constructor(engine, gameData, scenarioData, opts = {}){
this.engine = engine
return new Promise(async (resolve, reject)=>{
if (typeof gameData != 'object'){
gameData = (await api.game.load(gameData)).data;
@@ -85,8 +85,8 @@ class GameManager{
}
if (!i.data.noPhysics){
let bb = getBoundingBox(io.object);
let bbs = getBoundingBoxSize(bb);
let bb = engine.meshUtils.getBoundingBox(io.object);
let bbs = engine.meshUtils.getBoundingBoxSize(bb);
engine.physics.add(io.object, 'fixed', false, undefined, 'capsule', {
radius: Math.max(bbs.x, bbs.z)/2, halfHeight: bbs.y/2
})
@@ -145,7 +145,7 @@ class GameManager{
engine.dashboard?.loading(1)
engine.physics.start();
//this.debug('Scene loaded', engine.renderer.info.memory)
//console.log('Scene loaded', engine.renderer.info.memory)
}
resolve(this);
})
@@ -173,7 +173,8 @@ class GameManager{
object3d[p].copy(sceneObjects[data.id][p])
})
}else if (!data.type || data.type == 'GenericObject'){
autoScale(object3d, autoScaleFactor);
console.log('autoscaling', data.id, autoScaleFactor);
this.engine.meshUtils.autoScale(object3d, autoScaleFactor);
}
sceneObjects[data.id] = sceneObjects[data.id] || {};
if (this.opts.onObjectLoad){
+6 -6
View File
@@ -1,5 +1,4 @@
import { AnimationMixer, Vector3, Vector2 } from 'three';
import { getBoundingBox, getBoundingBoxSize } from './MeshUtils';
import { QueryFilterFlags } from '@dimforge/rapier3d';
const zero2 = new Vector2(0,0);
@@ -66,8 +65,9 @@ class Hero{
// let bb = this.model.userData.bbox;
// let size = getBoundingBoxSize(bb);
// let center = getBoundingBoxCenterPoint(bb, this.model.userData.object.position)
let bb = getBoundingBox(io.object);
this.size = getBoundingBoxSize(bb);
let bb = engine.meshUtils.getBoundingBox(io.object);
this.size = engine.meshUtils.getBoundingBoxSize(bb);
console.log('Hero size is', this.size);
// let center = getBoundingBoxCenterPoint(bb, io.object.position)
// console.log('hero', size, center, size.y - 2*this.characterGapOffset)
@@ -307,9 +307,9 @@ class Hero{
this.camera.position.copy(cameraPosition)
if (!this.fpv){
this.camera.lookAt(
this.model.position.x,
this.cameraY -this.size.y * 0.5 + this.model.position.y,
this.model.position.z
this.engine.scale * this.model.position.x,
this.engine.scale * (this.cameraY -this.size.y * 0.5 + this.model.position.y),
this.engine.scale * this.model.position.z
)
}
}
+129 -126
View File
@@ -1,134 +1,137 @@
import { Box3, Vector3, Group } from "three";
import { GameEngine } from "./GameEngine";
function assignParams(mesh, params){
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
[
'visible', 'name', 'fontSize', 'color', 'lineHeight',
'maxWidth', 'anchorX', 'anchorY', 'outlineColor', 'outlineWidth', 'textAlign'
].forEach(p=>{
if (params[p]!==undefined) mesh[p] = params[p];
});
}
function assignMaterial(mesh, params){
if (params.name && params.material){
//let mp = params.material.metalness ? new MeshStandardMaterial(params.material) : new MeshBasicMaterial(params.material)
Object.assign(mesh.material, params.material)
if (params.dm){
GameEngine.loadTexture(params.dm, params.path, undefined, [mesh.material, 'map'])
// var dm = new TextureLoader().setPath(params.path).load(params.dm);
// mesh.material.map = dm;
}
if (params.nm){
GameEngine.loadTexture(params.nm, params.path, undefined, [mesh.material, 'normalMap'])
//mesh.material.normalMap = new TextureLoader().setPath(params.path).load(params.nm);
}
if (params.em) {
GameEngine.loadTexture(params.em, params.path, undefined, [mesh.material, 'emissiveMap'])
//mesh.material.emissiveMap = new TextureLoader().setPath(params.path).load(params.em);
}
//mesh.material = mp;
mesh.material.needsUpdate = true;
class MeshUtils {
constructor(engine) {
this.engine = engine;
}
}
function getBoundingBox(object){
return new Box3().setFromObject(object);
}
function getBoundingBoxSize(bb){
return new Vector3(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
}
function getBoundingBoxMaxLength(bb){
let size = getBoundingBoxSize(bb)
return Math.max(size.x, size.y, size.z)
}
function getBoundingBoxCenterPoint(bb, relativeTo){
relativeTo = relativeTo || new Vector3(0,0,0)
let size = getBoundingBoxSize(bb)
return new Vector3(
bb.min.x + (size.x)/2 - relativeTo.x,
bb.min.y + (size.y)/2 - relativeTo.y,
bb.min.z + (size.z)/2 - relativeTo.z
)
}
function autoScale(object, mk = 1) {
if (mk === null) return;
let bb = getBoundingBox(object);
let k = getBoundingBoxMaxLength(bb);
object.scale.multiplyScalar(mk / k);
}
function wrapInGroup(object){
if (object.isWrapper) return object;
let group = new Group();
group.userData.bbox = getBoundingBox(object);
group.add(object);
group.userData.object = object;
group.isWrapper = true;
return group;
}
function centerOrigin(object){
let group = wrapInGroup(object);
let position = getBoundingBoxCenterPoint(group.userData.bbox, object.position).negate();
object.position.copy(position)
return group;
}
function bottomOrigin(object){
let group = centerOrigin(object);
group.userData.object.position.y = -group.userData.bbox.min.y
return group;
}
function clearObject(o){
let disposables = []
o.traverse(object => {
if (object.isMesh) {
disposables.push(object);
assignParams(mesh, params){
['scale', 'rotation', 'position'].forEach(p=>params[p] && mesh[p].fromArray(params[p]));
[
'visible', 'name', 'fontSize', 'color', 'lineHeight',
'maxWidth', 'anchorX', 'anchorY', 'outlineColor', 'outlineWidth', 'textAlign'
].forEach(p=>{
if (params[p]!==undefined) mesh[p] = params[p];
});
}
assignMaterial(mesh, params){
if (params.name && params.material){
//let mp = params.material.metalness ? new MeshStandardMaterial(params.material) : new MeshBasicMaterial(params.material)
Object.assign(mesh.material, params.material)
if (params.dm){
this.engine.loadTexture(params.dm, params.path, undefined, [mesh.material, 'map'])
// var dm = new TextureLoader().setPath(params.path).load(params.dm);
// mesh.material.map = dm;
}
if (params.nm){
this.engine.loadTexture(params.nm, params.path, undefined, [mesh.material, 'normalMap'])
//mesh.material.normalMap = new TextureLoader().setPath(params.path).load(params.nm);
}
if (params.em) {
this.engine.loadTexture(params.em, params.path, undefined, [mesh.material, 'emissiveMap'])
//mesh.material.emissiveMap = new TextureLoader().setPath(params.path).load(params.em);
}
//mesh.material = mp;
mesh.material.needsUpdate = true;
}
if (object.isLight){
//console.log('Disposeing light', object)
object.dispose();
object.shadow?.dispose();
object.shadow?.map?.dispose();
}
if (object.userData?._io){
object.userData._io.dispose?.();
delete object.userData._io;
}
});
disposables.forEach(object=>{
object.removeFromParent();
object.geometry.dispose();
if (object.material.isMaterial) {
clearMaterial(object.material)
} else {
for (const material of object.material) clearMaterial(material)
}
})
o.clear();
}
function clearMaterial(material) {
material.dispose();
for (const key of Object.keys(material)) {
const value = material[key]
if (value && typeof value == 'object' && 'minFilter' in value) {
//console.log('Disposing', value.name, this.renderer.info.memory.textures );
value.dispose();
//console.log('Disposed', value.name, this.renderer.info.memory.textures );
}
getBoundingBox(object){
let bb = new Box3().setFromObject(object);
bb.min.multiplyScalar(1 / this.engine.scale);
bb.max.multiplyScalar(1 / this.engine.scale);
return bb;
}
getBoundingBoxSize(bb){
return new Vector3(bb.max.x - bb.min.x, bb.max.y - bb.min.y, bb.max.z - bb.min.z);
}
getBoundingBoxMaxLength(bb){
let size = this.getBoundingBoxSize(bb)
return Math.max(size.x, size.y, size.z)
}
getBoundingBoxCenterPoint(bb, relativeTo){
relativeTo = relativeTo || new Vector3(0,0,0)
let size = this.getBoundingBoxSize(bb)
return new Vector3(
bb.min.x + (size.x)/2 - relativeTo.x,
bb.min.y + (size.y)/2 - relativeTo.y,
bb.min.z + (size.z)/2 - relativeTo.z
)
}
autoScale(object, mk = 1) {
if (mk === null) return;
let bb = this.getBoundingBox(object);
let k = this.getBoundingBoxMaxLength(bb);
object.scale.multiplyScalar(mk / k);
}
wrapInGroup(object){
if (object.isWrapper) return object;
let group = new Group();
group.userData.bbox = this.getBoundingBox(object);
group.add(object);
group.userData.object = object;
group.isWrapper = true;
return group;
}
centerOrigin(object){
let group = this.wrapInGroup(object);
let position = this.getBoundingBoxCenterPoint(group.userData.bbox, object.position).negate();
object.position.copy(position)
return group;
}
bottomOrigin(object){
let group = this.centerOrigin(object);
group.userData.object.position.y = -group.userData.bbox.min.y
return group;
}
clearObject(o){
let disposables = []
o.traverse(object => {
if (object.isMesh) {
disposables.push(object);
}
if (object.isLight){
//console.log('Disposeing light', object)
object.dispose();
object.shadow?.dispose();
object.shadow?.map?.dispose();
}
if (object.userData?._io){
object.userData._io.dispose?.();
delete object.userData._io;
}
});
disposables.forEach(object=>{
object.removeFromParent();
object.geometry.dispose();
if (object.material.isMaterial) {
this.clearMaterial(object.material)
} else {
for (const material of object.material) this.clearMaterial(material)
}
})
o.clear();
}
clearMaterial(material) {
material.dispose();
for (const key of Object.keys(material)) {
const value = material[key]
if (value && typeof value == 'object' && 'minFilter' in value) {
//console.log('Disposing', value.name, this.renderer.info.memory.textures );
value.dispose();
//console.log('Disposed', value.name, this.renderer.info.memory.textures );
}
}
}
}
export {
assignParams, assignMaterial, autoScale, centerOrigin, wrapInGroup, bottomOrigin,
getBoundingBox, getBoundingBoxSize, getBoundingBoxMaxLength, getBoundingBoxCenterPoint,
clearObject, clearMaterial
}
export { MeshUtils }