GLTFLoader OOP Pattern

Super cool. Thanks again donmccurdy !!

Just to confirm, strictly for learning purposes, the working solution actually involves no “factory” pattern? It would have to involve new ModelLoader().loadModel()? Or more specifically new ModelLoader().createLoadableModel() or something like that? @emdax why were you asking for a factory specifically and not just a generic solution to the problem?

True – the solution I offerred did not use a Factory pattern. I assumed the OP’s suggestion of a Factory pattern was really attempting to clarify the question, rather than a hard requirement, and that simplicity was at least as valuable as the use of a specific OOP pattern.

For a large, complex project with many engineers, you might reasonably choose a more complex pattern in order to have consistency in the codebase — too many simple but inconsistent solutions create their own kind of complexity. And it’s those larger projects where I see Factory patterns. Totally a reasonable option, too, just somewhat less common in small projects.

2 Likes

Hi Don, would you mind helping me here? In your object cacher,

            if ( models[ url ] ) {
				return models[ url ].then( ( theMesh ) => {

					theMesh.clone() ;

				});
			}

This works great! Very fast and efficient. However, the TEXTURE is still baked on to that object. Later on in my code, I am trying to apply a different texture, but it doesn’t work. It looks like this is also cloning the texture. How can I strip it off?

This is how I am applying the texture later in the program:

                              gltf.traverse ( ( gltfObject ) => {
							    if ( gltfObject.isMesh ) {
							      // note: for a multi-material mesh, `o.material` may be an array,
							      // in which case you'd need to set `.map` on each value.
							      console.log("MAPPING THE TEXTURE");
							      gltfObject.material.map = texture;
							    }
							  } );

This doesn’t work on cloned objects. I’ve tried to set the material.map = null, but nota.

I am trying to apply a different texture, but it doesn’t work.

What result do you see, if it doesn’t work? I assume you reuse an object multiple times, but the different instances are ending up with the same texture when you try to assign different textures to each?

When you clone an object, source.material === copy.material. So you will need to manually clone the materials before trying to assign different textures.

1 Like

Hrmm. Manually clear materials… Trying to figure out how and where exactly this takes place and what that looks like?

Care to share some psudo code example? :smiley:

Edit: I see the same texture as the original model, despite trying to load a new texture onto the clone.

I think something like this would be the idea:

if ( models[ url ] ) {
  return models[ url ].then( ( theMesh ) => {
    const clone = theMesh.clone();
    clone.traverse((o) => {
      if (o.isMesh) o.material = o.material.clone();
    });
    return clone;
  });
}

Ahhh yes – right on. But now I wish to replace that texture, on the clone.

Here:

                          gltf.traverse ( ( gltfObject ) => {
						    if ( gltfObject.isMesh ) {
						      // note: for a multi-material mesh, `o.material` may be an array,
						      // in which case you'd need to set `.map` on each value.
						      console.log("MAPPING THE TEXTURE");
						      gltfObject.material.map = texture;
						    }
						  } );

But this: gltfObject.material.map = texture;

Doesn’t seem to work with the new texture. Will your above code fix this issue? Or am I not loading textures correctly on to the clone?

I think i’d need to reproduce what you’re doing to answer that. Can you create a demo of a simplified case?

Hey, i had a similar approach, but I had the problem that same objs which are loaded at the same time, will be stored multiple times. I made a function to remove all duplicates from the array, but I guess there must be a better way?

Also Im having a scene inside a Angular 2+ component which can be destroyed and revoked on button click. After clicking the button several times, my scene (or actually my computer) is starting to lag. Is it possible that the carbage collection is getting filled fast? Or where does this come from?
Im on a iMac 2009~ using Google Chrome

export class ModelManager {

static _instance: ModelManager

public models: { path: string, model: THREE.Object3D }[] = []

private loadingManager: THREE.LoadingManager
private gltfLoader: GLTFLoader

private nonLoaded: boolean

public onLoad: Function

public static instance() {

    if(ModelManager._instance == null) 
        ModelManager._instance = new ModelManager()

    return ModelManager._instance
}

constructor() {

    this.nonLoaded = true
    
    this.loadingManager = new THREE.LoadingManager()
    this.gltfLoader = new GLTFLoader(this.loadingManager)
}

public load(path: string, onComplete: Function) {

    let found: boolean = false

    // onLoad callback
    this.loadingManager.onLoad = ()=> {

            if(this.onLoad != null && this.nonLoaded == false){

                this.nonLoaded = true // Reset
                this.onLoad()
            }
    }

    // Check stored models for needed
    if(this.models.length > 0) {

        this.models.forEach(model => {

            if(model.path == path) {
                
                found = true

                let clone = model.model.clone()

                onComplete(clone)
            }
        })
    }

    // not found -> Load and store model
    if(!found){
        this.nonLoaded = false

        this.loadGLTFModel(path, onComplete)
    }
}

private loadGLTFModel(path: string, onLoad: Function) {

    this.gltfLoader.load(path, (model)=> {

        this.models.push({
            path: path,
            model: model.scene
        })

        if(this.models.length > 1)
            this.models = Utility.removeDuplicates(this.models)

        let clone = model.scene.clone()
        onLoad(clone)

    }, undefined, (error)=> {})
}

}