Changing texture and color on Three.js collada object
Asked Answered
T

4

9

I recently got three.js example from the official site working with my collada objects (.dae) using the ColladaLoader.js. Now my question is, how do i change the loaded collada object color attribute and add a custom texture?? I tried adding the texture with no luck yet.

Here is my code (slightly changed from the original example):

function load_model(el) {

            if ( ! Detector.webgl ) Detector.addGetWebGLMessage();

            var container, stats;

            var camera, scene, renderer, objects;
            var particleLight, pointLight;
            var dae, skin;

            var loader = new THREE.ColladaLoader();
            loader.options.convertUpAxis = true;
            loader.load( '/site_media/models/model.dae', function ( collada ) {
                dae = collada.scene;
                skin = collada.skins[ 0 ];

                dae.scale.x = dae.scale.y = dae.scale.z = 0.90;
                dae.updateMatrix();

                init(el);
                animate();

            } );

            function init(el) {

                container = document.createElement( 'div' );
                el.append( container );

                camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 2000 );
                camera.position.set( 2, 2, 3 );

                scene = new THREE.Scene();


                scene.add( dae );

                particleLight = new THREE.Mesh( new THREE.SphereGeometry( 4, 8, 8 ), new THREE.MeshBasicMaterial( { color: 0xffffff } ) );
                scene.add( particleLight );

                // Lights

                scene.add( new THREE.AmbientLight( 0xcccccc ) );

                var directionalLight = new THREE.DirectionalLight(/*Math.random() * 0xffffff*/0xeeeeee );
                directionalLight.position.x = Math.random() - 0.5;
                directionalLight.position.y = Math.random() - 0.5;
                directionalLight.position.z = Math.random() - 0.5;
                directionalLight.position.normalize();
                scene.add( directionalLight );

                // pointLight = new THREE.PointLight( 0xffffff, 4 );
                // pointLight.position = particleLight.position;
                // scene.add( pointLight );

                renderer = new THREE.WebGLRenderer();
                renderer.setSize( window.innerWidth/2, window.innerHeight/2 );


                container.appendChild( renderer.domElement );

                stats = new Stats();
                stats.domElement.style.position = 'absolute';
                stats.domElement.style.top = '0px';
                container.appendChild( stats.domElement );

                //

                window.addEventListener( 'resize', onWindowResize, false );

            }

            function onWindowResize() {

                camera.aspect = window.innerWidth / window.innerHeight;
                camera.updateProjectionMatrix();

                renderer.setSize( window.innerWidth/2, window.innerHeight/2 );

            }

            //

            var t = 0;
            var clock = new THREE.Clock();

            function animate() {

                var delta = clock.getDelta();

                requestAnimationFrame( animate );

                if ( t > 1 ) t = 0;

                if ( skin ) {

                    // guess this can be done smarter...

                    // (Indeed, there are way more frames than needed and interpolation is not used at all
                    //  could be something like - one morph per each skinning pose keyframe, or even less,
                    //  animation could be resampled, morphing interpolation handles sparse keyframes quite well.
                    //  Simple animation cycles like this look ok with 10-15 frames instead of 100 ;)

                    for ( var i = 0; i < skin.morphTargetInfluences.length; i++ ) {

                        skin.morphTargetInfluences[ i ] = 0;

                    }

                    skin.morphTargetInfluences[ Math.floor( t * 30 ) ] = 1;

                    t += delta;

                }

                render();
                stats.update();

            }

            function render() {

                var timer = Date.now() * 0.0005;

                camera.position.x = Math.cos( timer ) * 10;
                camera.position.y = 2;
                camera.position.z = Math.sin( timer ) * 10;

                camera.lookAt( scene.position );

                particleLight.position.x = Math.sin( timer * 4 ) * 3009;
                particleLight.position.y = Math.cos( timer * 5 ) * 4000;
                particleLight.position.z = Math.cos( timer * 4 ) * 3009;

                renderer.render( scene, camera );

            }


}
Trot answered 22/2, 2013 at 13:26 Comment(1)
Possible duplicate of How to setup materials in THREE.js when loading Collada (dae) models?Supporter
T
2

After many problems, we wrote a small hack in ColladaLoader.js taking the idea from @gaitat witch basically replaces the old path to the textures from the images, passing some new ones in an array, and using regular expressions to parse the xml for the .png or .jpg under images tag. Not sure if there is an easier way but since support was limited we had to come up with a fix somehow

function parse( doc, imageReplace, callBack, url ) {

    COLLADA = doc;
    callBack = callBack || readyCallbackFunc;

    if ( url !== undefined ) {

        var parts = url.split( '/' );
        parts.pop();
        baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';

    }

    parseAsset();
    setUpConversion();
    images = parseLib( "//dae:library_images/dae:image", _Image, "image" );

    for(var i in imageReplace) {
        var iR = imageReplace[i];

        for(var i in images) {
            var image = images[i];

            var patt=new RegExp('[a-zA-Z0-9\-\_]*\/'+iR.name,'g');

            //if(image.id==iR.id)
            if(patt.test(image.init_from))
                image.init_from = iR.new_image; 
        }//for
    }

    materials = parseLib( "//dae:library_materials/dae:material", Material, "material" );
    effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
    geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" );
    cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" );
    controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" );
    animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" );
    visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" );

    morphs = [];
    skins = [];

    daeScene = parseScene();
    scene = new THREE.Object3D();

    for ( var i = 0; i < daeScene.nodes.length; i ++ ) {

        scene.add( createSceneGraph( daeScene.nodes[ i ] ) );

    }

// unit conversion
scene.position.multiplyScalar(colladaUnit);
scene.scale.multiplyScalar(colladaUnit);

    createAnimations();

    var result = {

        scene: scene,
        morphs: morphs,
        skins: skins,
        animations: animData,
        dae: {
            images: images,
            materials: materials,
            cameras: cameras,
            effects: effects,
            geometries: geometries,
            controllers: controllers,
            animations: animations,
            visualScenes: visualScenes,
            scene: daeScene
        }

    };

    if ( callBack ) {

        callBack( result );

    }

    return result;

};
Trot answered 26/2, 2013 at 18:6 Comment(0)
G
13

You can override your collada scene materials recursively with this kind of function. It goes through the whole hierarchy and assigns a material.

var setMaterial = function(node, material) {
  node.material = material;
  if (node.children) {
    for (var i = 0; i < node.children.length; i++) {
      setMaterial(node.children[i], material);
    }
  }
}

Use it like setMaterial(dae, new THREE.MeshBasicMaterial({color: 0xff0000}));

You could probably adapt that to modify the existing material properties instead of assigning a new one, if needed.

Grandioso answered 22/2, 2013 at 14:50 Comment(5)
You should call the function inside your loader.load callback, so you assign new materials after the collada is loaded. Actually you could do it any time after loading the collada, if you need to dynamically change materials. And the function itself can go anywhere.Grandioso
yes it works for the color. can this work for the texture also? if yes, how?Trot
There are plenty of examples how to create materials with texture map. webgl_materials.html in the example folder is a good starting point.Grandioso
i am getting a "WebGL: INVALID_VALUE: texImage2D: invalid image" . Wonder if its beacause my object is rather complex and i was hoping the texture got around it automatically or if i have to do it manuallyTrot
I found that I need to add scene to the loader. Example code. loader.load( // resource URL 'test/object.dae', // Function when resource is loaded function (collada) { setMaterial(collada.scene, new THREE.MeshBasicMaterial({color: 0xff0000})); scene.add(collada.scene); },Oozy
T
2

After many problems, we wrote a small hack in ColladaLoader.js taking the idea from @gaitat witch basically replaces the old path to the textures from the images, passing some new ones in an array, and using regular expressions to parse the xml for the .png or .jpg under images tag. Not sure if there is an easier way but since support was limited we had to come up with a fix somehow

function parse( doc, imageReplace, callBack, url ) {

    COLLADA = doc;
    callBack = callBack || readyCallbackFunc;

    if ( url !== undefined ) {

        var parts = url.split( '/' );
        parts.pop();
        baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';

    }

    parseAsset();
    setUpConversion();
    images = parseLib( "//dae:library_images/dae:image", _Image, "image" );

    for(var i in imageReplace) {
        var iR = imageReplace[i];

        for(var i in images) {
            var image = images[i];

            var patt=new RegExp('[a-zA-Z0-9\-\_]*\/'+iR.name,'g');

            //if(image.id==iR.id)
            if(patt.test(image.init_from))
                image.init_from = iR.new_image; 
        }//for
    }

    materials = parseLib( "//dae:library_materials/dae:material", Material, "material" );
    effects = parseLib( "//dae:library_effects/dae:effect", Effect, "effect" );
    geometries = parseLib( "//dae:library_geometries/dae:geometry", Geometry, "geometry" );
    cameras = parseLib( ".//dae:library_cameras/dae:camera", Camera, "camera" );
    controllers = parseLib( "//dae:library_controllers/dae:controller", Controller, "controller" );
    animations = parseLib( "//dae:library_animations/dae:animation", Animation, "animation" );
    visualScenes = parseLib( ".//dae:library_visual_scenes/dae:visual_scene", VisualScene, "visual_scene" );

    morphs = [];
    skins = [];

    daeScene = parseScene();
    scene = new THREE.Object3D();

    for ( var i = 0; i < daeScene.nodes.length; i ++ ) {

        scene.add( createSceneGraph( daeScene.nodes[ i ] ) );

    }

// unit conversion
scene.position.multiplyScalar(colladaUnit);
scene.scale.multiplyScalar(colladaUnit);

    createAnimations();

    var result = {

        scene: scene,
        morphs: morphs,
        skins: skins,
        animations: animData,
        dae: {
            images: images,
            materials: materials,
            cameras: cameras,
            effects: effects,
            geometries: geometries,
            controllers: controllers,
            animations: animations,
            visualScenes: visualScenes,
            scene: daeScene
        }

    };

    if ( callBack ) {

        callBack( result );

    }

    return result;

};
Trot answered 26/2, 2013 at 18:6 Comment(0)
I
1

One thing you can do is modify your collada model (dae file) locate the texture reference there and change it to your liking.

Inaugurate answered 22/2, 2013 at 13:33 Comment(1)
kinda need a dynamic solutionTrot
D
0
if ( url !== undefined ) {
    var parts = url.split( '/' );
    parts.pop();
    baseUrl = ( parts.length < 1 ? '.' : parts.join( '/' ) ) + '/';

}

parseAsset();
setUpConversion();
images = parseLib( "//dae:library_images/dae:image", _Image, "image" );

for(var i in imageReplace) {
    var iR = imageReplace[i];

    for(var i in images) {
        var image = images[i];

        var patt=new RegExp('[a-zA-Z0-9\-\_]*\/'+iR.name,'g');

        //if(image.id==iR.id)
        if(patt.test(image.init_from))
            image.init_from = iR.new_image; 
    }//for
}
Diploid answered 18/9, 2013 at 14:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.