Created
May 16, 2020 03:18
-
-
Save sketchpunk/fab42db9d955915cb4a63d25be45d0cf to your computer and use it in GitHub Desktop.
GLTF Geometry Loading
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| class Gltf{ | |
| static TYPE_BYTE = 5120; | |
| static TYPE_UNSIGNED_BYTE = 5121; | |
| static TYPE_SHORT = 5122; | |
| static TYPE_UNSIGNED_SHORT = 5123; | |
| static TYPE_UNSIGNED_INT = 5125; | |
| static TYPE_FLOAT = 5126; | |
| static MODE_POINTS = 0; // Mode Constants for GLTF and WebGL are identical | |
| static MODE_LINES = 1; // https://developer.mozilla.org/en-US/docs/Web/API/WebGL_API/Constants | |
| static MODE_LINE_LOOP = 2; | |
| static MODE_LINE_STRIP = 3; | |
| static MODE_TRIANGLES = 4; | |
| static MODE_TRIANGLE_STRIP = 5; | |
| static MODE_TRIANGLE_FAN = 6; | |
| static COMP_SCALAR = 1; // Component Length based on Type | |
| static COMP_VEC2 = 2; | |
| static COMP_VEC3 = 3; | |
| static COMP_VEC4 = 4; | |
| static COMP_MAT2 = 4; | |
| static COMP_MAT3 = 9; | |
| static COMP_MAT4 = 16; | |
| static TARGET_ARY_BUF = 34962; // bufferview.target | |
| static TARGET_ELM_ARY_BUF = 34963; | |
| static parse_accessor( idx, json, bin, spec_only = false ){ | |
| let acc = json.accessors[ idx ], // Reference to Accessor JSON Element | |
| bView = json.bufferViews[ acc.bufferView ], // Buffer Information | |
| compLen = Gltf[ "COMP_" + acc.type ], // Component Length for Data Element | |
| ary = null, // Final Type array that will be filled with data | |
| byteStart = 0, | |
| byteLen = 0, | |
| TAry, // Reference to Type Array to create | |
| DFunc; // Reference to GET function in Type Array | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Figure out which Type Array we need to save the data in | |
| switch( acc.componentType ){ | |
| case Gltf.TYPE_FLOAT: TAry = Float32Array; DFunc = "getFloat32"; break; | |
| case Gltf.TYPE_SHORT: TAry = Int16Array; DFunc = "getInt16"; break; | |
| case Gltf.TYPE_UNSIGNED_SHORT: TAry = Uint16Array; DFunc = "getUint16"; break; | |
| case Gltf.TYPE_UNSIGNED_INT: TAry = Uint32Array; DFunc = "getUint32"; break; | |
| case Gltf.TYPE_UNSIGNED_BYTE: TAry = Uint8Array; DFunc = "getUint8"; break; | |
| default: console.log("ERROR processAccessor","componentType unknown",a.componentType); return null; break; | |
| } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| let out = { | |
| min : acc.min, | |
| max : acc.max, | |
| elm_cnt : acc.count, | |
| comp_len : compLen | |
| }; | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Data is Interleaved | |
| if( bView.byteStride ){ | |
| if( spec_only ) console.error( "GLTF STRIDE SPEC ONLY OPTION NEEDS TO BE IMPLEMENTED "); | |
| /* | |
| The RiggedSimple sample seems to be using stride the wrong way. The data all works out | |
| but Weight and Joint indicate stride length BUT the data is not Interleaved in the buffer. | |
| They both exists in their own individual block of data just like non-Interleaved data. | |
| In the sample, Vertices and Normals ARE actually Interleaved. This make it a bit | |
| difficult to parse when dealing with interlanced data with WebGL Buffers. | |
| TODO: Can prob check if not interlanced by seeing if the Stride Length equals the length | |
| of the data in question. | |
| For example related to the RiggedSimple sample. | |
| Stride Length == FloatByteLength(4) * Accessor.type's ComponentLength(Vec3||Vec4) | |
| -- So if Stride is 16 Bytes | |
| -- The data is grouped as Vec4 ( 4 Floats ) | |
| -- And Each Float = 4 bytes. | |
| -- Then Stride 16 Bytes == Vec4 ( 4f loats * 4 Bytes ) | |
| -- So the stride length equals the data we're looking for, So the BufferView in question | |
| IS NOT Interleaved. | |
| By the looks of things. If the Accessor.bufferView number is shared between BufferViews | |
| then there is a good chance its really Interleaved. Its ashame that things can be designed | |
| to be more straight forward when it comes to Interleaved and Non-Interleaved data. | |
| */ | |
| // console.log("BView", bView ); | |
| // console.log("Accessor", acc ); | |
| let stride = bView.byteStride, // Stride Length in bytes | |
| elmCnt = acc.count, // How many stride elements exist. | |
| bOffset = (bView.byteOffset || 0), // Buffer Offset | |
| sOffset = (acc.byteOffset || 0), // Stride Offset | |
| bPer = TAry.BYTES_PER_ELEMENT, // How many bytes to make one value of the data type | |
| aryLen = elmCnt * compLen, // How many "floats/ints" need for this array | |
| dView = new DataView( bin ), // Access to Binary Array Buffer | |
| p = 0, // Position Index of Byte Array | |
| j = 0, // Loop Component Length ( Like a Vec3 at a time ) | |
| k = 0; // Position Index of new Type Array | |
| ary = new TAry( aryLen ); //Final Array | |
| //Loop for each element of by stride | |
| for(var i=0; i < elmCnt; i++){ | |
| // Buffer Offset + (Total Stride * Element Index) + Sub Offset within Stride Component | |
| p = bOffset + ( stride * i ) + sOffset; //Calc Starting position for the stride of data | |
| //Then loop by compLen to grab stuff out of the DataView and into the Typed Array | |
| for(j=0; j < compLen; j++) ary[ k++ ] = dView[ DFunc ]( p + (j * bPer) , true ); | |
| } | |
| out.data = ary; | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Data is NOT Interleaved | |
| // https://github.com/KhronosGroup/glTF/tree/master/specification/2.0#data-alignment | |
| // TArray example from documentation works pretty well for data that is not interleaved. | |
| }else{ | |
| if( spec_only ){ | |
| out.array_type = TAry.name.substring( 0, TAry.name.length - 5 ); | |
| out.byte_start = ( acc.byteOffset || 0 ) + ( bView.byteOffset || 0 ); | |
| out.byte_cnt = acc.count * compLen * TAry.BYTES_PER_ELEMENT; | |
| //console.log( bin ); | |
| }else{ | |
| let bOffset = ( acc.byteOffset || 0 ) + ( bView.byteOffset || 0 ); | |
| out.data = new TAry( bin, bOffset, acc.count * compLen ); // ElementCount * ComponentLength | |
| } | |
| } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| return out; | |
| } | |
| static get_mesh( name, json, bin, spec_only = false ){ | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Find Mesh to parse out. | |
| let i, n = null, mesh_idx = null; | |
| for( i of json.nodes ){ | |
| if( i.name === name && i.mesh != undefined ){ n = i; mesh_idx = n.mesh; break; } | |
| } | |
| //No node Found, Try looking in mesh array for the name. | |
| if( !n ){ | |
| for( i=0; i < json.meshes.length; i++ ) if( json.meshes[i].name == name ){ mesh_idx = i; break; } | |
| } | |
| if( mesh_idx == null ){ | |
| console.error( "Node or Mesh by the name", name, "not found in GLTF" ); | |
| return null; | |
| } | |
| //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ | |
| // Loop through all the primatives that make up a single mesh | |
| let m = json.meshes[ mesh_idx ], | |
| pLen = m.primitives.length, | |
| ary = new Array( pLen ), | |
| itm, | |
| prim, | |
| attr; | |
| for( let i=0; i < pLen; i++ ){ | |
| //....................................... | |
| // Setup some vars | |
| prim = m.primitives[ i ]; | |
| attr = prim.attributes; // console.log( attr ); | |
| itm = { | |
| name : name + (( pLen != 1 )? "_p" + i : ""), | |
| mode : ( prim.mode != undefined )? prim.mode : Gltf.MODE_TRIANGLES | |
| }; | |
| //....................................... | |
| // Save Position, Rotation and Scale if Available. | |
| if( n ){ | |
| if( n.translation ) itm.position = n.translation.slice( 0 ); | |
| if( n.rotation ) itm.rotation = n.rotation.slice( 0 ); | |
| if( n.scale ) itm.scale = n.scale.slice( 0 ); | |
| } | |
| //....................................... | |
| // Parse out all the raw Geometry Data from the Bin file | |
| itm.vertices = Gltf.parse_accessor( attr.POSITION, json, bin, spec_only ); | |
| if( prim.indices != undefined ) itm.indices = Gltf.parse_accessor( prim.indices, json, bin, spec_only ); | |
| if( attr.NORMAL != undefined ) itm.normal = Gltf.parse_accessor( attr.NORMAL, json, bin, spec_only ); | |
| if( attr.TEXCOORD_0 != undefined ) itm.uv = Gltf.parse_accessor( attr.TEXCOORD_0, json, bin, spec_only ); | |
| if( attr.WEIGHTS_0 != undefined ) itm.weights = Gltf.parse_accessor( attr.WEIGHTS_0, json, bin, spec_only ); | |
| if( attr.JOINTS_0 != undefined ) itm.joints = Gltf.parse_accessor( attr.JOINTS_0, json, bin, spec_only ); | |
| if( attr.COLOR_0 != undefined ) itm.color = Gltf.parse_accessor( attr.COLOR_0, json, bin, spec_only ); | |
| //....................................... | |
| // Save to return array | |
| ary[ i ] = itm; | |
| } | |
| return ary; | |
| } | |
| static prim_to_geo( g ){ | |
| let geo = new THREE.BufferGeometry(); | |
| geo.setAttribute( "position", new THREE.BufferAttribute( g.vertices.data, g.vertices.comp_len ) ); | |
| if( g.indices ) geo.setIndex( new THREE.BufferAttribute( g.indices.data, 1 ) ); | |
| if( g.normal ) geo.setAttribute( "normal", new THREE.BufferAttribute( g.normal.data, g.normal.comp_len ) ); | |
| if( g.uv ) geo.setAttribute( "uv", new THREE.BufferAttribute( g.uv.data, g.uv.comp_len ) ); | |
| if( g.color ) geo.setAttribute( "color", new THREE.BufferAttribute( g.color.data, g.color.comp_len ) ); | |
| return geo; | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment