material is undefined error when using ObjectLoader with BufferGeometry and materials #4423

Closed
benbro opened this Issue Feb 12, 2014 · 10 comments

Projects

None yet

2 participants

@benbro
benbro commented Feb 12, 2014

When trying to add object loaded with ObjectLoader that has a BufferGeometry and materials I'm getting an error (r65):

TypeError: material is undefined
if ( material.transparent ) {
three.js (line 23305)

I'm using the ObjectExporter to save a file with BufferGeometry and materials.
Object file:
https://drive.google.com/file/d/0B12AhxvnYHrAUlpBSFhyUUNTLUE/edit?usp=sharing

JSON model:
https://drive.google.com/file/d/0B12AhxvnYHrAU0ZxT0M0NzYtd28/edit?usp=sharing

Original collada model:
https://github.com/KhronosGroup/glTF/blob/master/model/duck/duck.dae

Exporter:

var loader = new THREE.JSONLoader();
loader.load('model/model.js', function(geometry, materials) {
    var bufferGeometry = THREE.BufferGeometryUtils.fromGeometry(geometry);
    var mesh = new THREE.Mesh(bufferGeometry, new THREE.MeshFaceMaterial(materials));
    var exporter = new THREE.ObjectExporter();
    var output = JSON.stringify( exporter.parse(mesh), null, '\t' );
    output = output.replace( /[\n\t]+([\d\.e\-\[\]]+)/g, '$1' );
    var blob = new Blob( [ output ], { type: 'text/plain' } );
    //window.navigator.msSaveBlob(blob, 'model.js');

    var a = document.querySelector("#a");
    a.href = window.URL.createObjectURL(blob);
    a.download = "model.js";
});

Trying to load the obkect:

var loader = new THREE.ObjectLoader();
loader.load('model.js', function(object) {
  scene.add(object);
});

By the way, using a with a download attribute might be nicer than opening a new browser window in the editor.
https://github.com/mrdoob/three.js/blob/master/editor/js/Menubar.File.js#L168

Thanks

@mrdoob
Owner
mrdoob commented Feb 12, 2014
var bufferGeometry = THREE.BufferGeometryUtils.fromGeometry(geometry);
var mesh = new THREE.Mesh(bufferGeometry, new THREE.MeshFaceMaterial(materials));

BufferGeometry doesn't support MeshFaceMaterial.

@mrdoob mrdoob added a commit that referenced this issue Feb 12, 2014
@mrdoob WebGLRenderer: Use materialIndex 0 when combining BufferGeometry and …
…MeshFaceMaterial. Fixes #4423.
785f8ed
@mrdoob
Owner
mrdoob commented Feb 12, 2014

Should be fixed now.
Thanks for reporting! :)

@mrdoob mrdoob closed this Feb 12, 2014
@mrdoob
Owner
mrdoob commented Feb 12, 2014

Well, WebGLRenderer now handles BufferGeometry and MeshFaceMaterial but you shouldn't be using it.

The right fix here I think would be to change this:

var bufferGeometry = THREE.BufferGeometryUtils.fromGeometry(geometry);
var mesh = new THREE.Mesh(bufferGeometry, new THREE.MeshFaceMaterial(materials));

to this:

var bufferGeometry = THREE.BufferGeometryUtils.fromGeometry(geometry);
var mesh = new THREE.Mesh(bufferGeometry, materials[0]);
@benbro
benbro commented Feb 12, 2014

Thank you for the quick fix.

How can I convert a Geometry with several materials to a BufferGeoemetry if it can only handle one material?

@mrdoob
Owner
mrdoob commented Feb 12, 2014

Hmmm, maybe THREE.BufferGeometryUtils.fromGeometry could have a parameter to return an array with per materialIndex geometries...

@benbro
benbro commented Feb 12, 2014

Does it means that if I have a geometry with 10 materials, it will create 10 BufferGeometry objects with 1 material and the exported file will be 10X larger?

@mrdoob
Owner
mrdoob commented Feb 12, 2014

The exported file won't be 10x larger. The geometry will be split, not duplicated. But yes, the rest is true.

@benbro
benbro commented Feb 12, 2014

In Three.BufferGeometry.Utils.FromGeometry, should I create a separate BufferGeoemtry for each face? Is this the enhancement you are suggesting?
https://github.com/mrdoob/three.js/blob/master/examples/js/BufferGeometryUtils.js#L67

@benbro
benbro commented Feb 12, 2014

KillerJim from IRC suggested this:

/**********************************************
 *
 * Function : convertMeshToBufferGeomArray
 *
 * Description : black dark and funky voodoo here..
 *
 **********************************************/

UTILx.prototype.convertMeshToBufferGeomArray = function ( mergedMesh, mergedMaterials )
{
    /* SideNote - this converts a mesh into a number of bufferGeoms */
    var bufferGeom = [] ;

    console.log("[convertAndSaveMergedMeshAsBufferGeom] Begin.. c : " + mergedMesh.faces.length + ", mc : " + mergedMaterials.length) ;

    /* Spin around each material first.. */
    for (var materialCounter = 0 ; materialCounter < mergedMaterials.length; materialCounter++ )
    {

        /* First, see how many triangles we actually have on this material */
        var totalMaterialFaces = 0 ;
        for (var faceCounter = 0; faceCounter < mergedMesh.faces.length; faceCounter++)
            if ( mergedMesh.faces[faceCounter].materialIndex == materialCounter)
                totalMaterialFaces++ ;

        console.log("Total faces for materialIndex " + materialCounter + " is " + totalMaterialFaces) ;

        /* Generate a buffer geometry for each material */
        var geometry = new THREE.BufferGeometry();
        geometry.attributes = {
            index: {
                itemSize: 1,
                array: new Uint16Array( totalMaterialFaces * 3 ),
                numItems: totalMaterialFaces * 3
            },
            position: {
                itemSize: 3,
                array: new Float32Array( totalMaterialFaces * 3 * 3 ),
                numItems: totalMaterialFaces * 3 * 3
            },
            normal: {
                itemSize: 3,
                array: new Float32Array( totalMaterialFaces * 3 * 3 ),
                numItems: totalMaterialFaces * 3 * 3
            },
            uv: {
                itemSize: 2,
                array: new Float32Array( totalMaterialFaces * 3 * 3 ),
                numItems: totalMaterialFaces * 3 * 3
            },
            material: {
                item : mergedMaterials[materialCounter].clone()
            }
        } ;

        var chunkSize = 21845 ;

        var indices = geometry.attributes.index.array;

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

            indices[ i ] = i % ( 3 * chunkSize );

        }

        var positions = geometry.attributes.position.array;
        var uvs = geometry.attributes.uv.array;
        var normals = geometry.attributes.normal.array;

        /* For each face, belonging to this material Index */
        var i = 0;
        var uvi = 0 ;
        for (var faceCounter = 0; faceCounter < mergedMesh.faces.length; faceCounter++)
            if ( mergedMesh.faces[faceCounter].materialIndex == materialCounter)
            {
                /* Position of vertices in face */
                positions[ i ]     = mergedMesh.vertices[mergedMesh.faces[faceCounter].a].x ;
                positions[ i + 1 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].a].y ;
                positions[ i + 2 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].a].z ;

                positions[ i + 3 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].b].x ;
                positions[ i + 4 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].b].y ;
                positions[ i + 5 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].b].z ;

                positions[ i + 6 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].c].x ;
                positions[ i + 7 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].c].y ;
                positions[ i + 8 ] = mergedMesh.vertices[mergedMesh.faces[faceCounter].c].z ;

                /* UV corrds */
                uvs[ uvi ]      = mergedMesh.faceVertexUvs[0][faceCounter][0].x ;
                uvs[ uvi + 1]   = mergedMesh.faceVertexUvs[0][faceCounter][0].y ;
                uvs[ uvi + 2]   = mergedMesh.faceVertexUvs[0][faceCounter][1].x ;
                uvs[ uvi + 3]   = mergedMesh.faceVertexUvs[0][faceCounter][1].y ;
                uvs[ uvi + 4]   = mergedMesh.faceVertexUvs[0][faceCounter][2].x ;
                uvs[ uvi + 5]   = mergedMesh.faceVertexUvs[0][faceCounter][2].y ;

                /* Normals */
                normals[ i ]     = 0;
                normals[ i + 1 ] = 0;
                normals[ i + 2 ] = 0;

                normals[ i + 3 ] = 0;
                normals[ i + 4 ] = 0;
                normals[ i + 5 ] = 0;

                normals[ i + 6 ] = 0;
                normals[ i + 7 ] = 0;
                normals[ i + 8 ] = 0;

                /* Next... */
                i+= 9 ;
                uvi+= 6 ;


            }

        geometry.offsets = [];

        var offsets = totalMaterialFaces / chunkSize;

        for ( var i = 0; i < offsets; i ++ ) {

            var offset = {
                start: i * chunkSize * 3,
                index: i * chunkSize * 3,
                count: Math.min( totalMaterialFaces - ( i * chunkSize ), chunkSize ) * 3
            };

            geometry.offsets.push( offset );

        }

        geometry.computeBoundingSphere();
        geometry.computeBoundingBox() ;
        geometry.computeVertexNormals() ;

        bufferGeom.push(geometry) ;

    }

    return bufferGeom ;

} ;
for (var bufferGeomCounter = 0; bufferGeomCounter < bufferGeom.length; bufferGeomCounter++)
                    {
                        var newMesh = new THREE.Mesh( bufferGeom[bufferGeomCounter], bufferGeom[bufferGeomCounter].attributes.material.item ) ;
                        newMesh.position = this.position.clone() ;
                        newMesh.castShadow = true ;
                        newMesh.receiveShadow = true ;
                        this.RENDERx.getTHREEScene().add(newMesh) ;
                    }
@mrdoob
Owner
mrdoob commented Feb 12, 2014

Hmmm, sounds about right.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment