JSON Model format 3

Mr.doob edited this page May 11, 2014 · 2 revisions

Type bitmask

00 00 00 00 = TRIANGLE
00 00 00 01 = QUAD
00 00 00 10 = FACE_MATERIAL
00 00 01 00 = FACE_UV
00 00 10 00 = FACE_VERTEX_UV
00 01 00 00 = FACE_NORMAL
00 10 00 00 = FACE_VERTEX_NORMAL
01 00 00 00 = FACE_COLOR
10 00 00 00 = FACE_VERTEX_COLOR

0: 0 = triangle (3 indices), 1 = quad (4 indices)
1: 0 = no face material, 1 = face material (1 index)
2: 0 = no face uvs, 1 = face uvs (1 index)
3: 0 = no face vertex uvs, 1 = face vertex uvs (3 indices or 4 indices)
4: 0 = no face normal, 1 = face normal (1 index)
5: 0 = no face vertex normals, 1 = face vertex normals (3 indices or 4 indices)
6: 0 = no face color, 1 = face color (1 index)
7: 0 = no face vertex colors, 1 = face vertex colors (3 indices or 4 indices)

Skinning is the result of three properties, influencesPerVertex, skinIndices, and skinWeights. The value influencesPerVertex will determine the number of elements in the other two. For every vertex, there should be an appropriate number of entries in skinIndices where the value is the index of the bone which is influencing the vertex. If there are 2 influences per vertex, index 0 and 1 of skinIndices and skinWeights will be applied to vertex 0, 2 and 3 will be applied to vertex 1, etc.

Changes

3.0 → 3.1

  • meaning of texture coordinates changed, there isn't anymore v flipping; workaround uv.v = 1 - uv.v or texture.flipY = false (see migration r49 → r50)

Examples

{
	"metadata": { "formatVersion" : 3 },	
	
	"materials": [ {
		"DbgColor" : 15658734, // => 0xeeeeee
		"DbgIndex" : 0,
		"DbgName" : "dummy",
		"colorDiffuse" : [ 1, 0, 0 ],
	} ],

	"vertices": [ 0,0,0, 0,0,1, 1,0,1, 1,0,0, ... ],
	"normals":  [ 0,1,0, ... ],
	"colors":   [ 1,0,0, 0,1,0, 0,0,1, 1,1,0, ... ],
	"uvs":      [ [ 0,0, 0,1, 1,0, 1,1 ], ... ],

	"faces": [ 

		// triangle
		// 00 00 00 00 = 0
		// 0, [vertex_index, vertex_index, vertex_index]
		0, 0,1,2,

		// quad
		// 00 00 00 01 = 1
		// 1, [vertex_index, vertex_index, vertex_index, vertex_index]
		1, 0,1,2,3,

		// triangle with material
		// 00 00 00 10 = 2
		// 2, [vertex_index, vertex_index, vertex_index],
		// [material_index]
		2, 0,1,2, 0,

		// triangle with material, vertex uvs and face normal
		// 00 10 01 10 = 38
		// 38, [vertex_index, vertex_index, vertex_index],
		// [material_index],
		// [vertex_uv, vertex_uv, vertex_uv],
		// [face_normal]
		38, 0,1,2, 0, 0,1,2, 0,
		
		// triangle with material, vertex uvs and vertex normals
		// 00 10 10 10 = 42
		// 42, [vertex_index, vertex_index, vertex_index],
		// [material_index],
		// [vertex_uv, vertex_uv, vertex_uv],
		// [vertex_normal, vertex_normal, vertex_normal]
		42, 0,1,2, 0, 0,1,2, 0,1,2,

		// quad with everything
		// 11 11 11 11 = 255
		// 255, [vertex_index, vertex_index, vertex_index, vertex_index],
		//  [material_index],
		//  [face_uv],
		//  [face_vertex_uv, face_vertex_uv, face_vertex_uv, face_vertex_uv],
		//  [face_normal],
		//  [face_vertex_normal, face_vertex_normal,
		//   face_vertex_normal, face_vertex_normal],
		//  [face_color]
		//  [face_vertex_color, face_vertex_color,
		//   face_vertex_color, face_vertex_color],
		255, 0,1,2,3, 0, 0, 0,1,2,3, 0, 0,1,2,3, 0, 0,1,2,3,
	]

}

Core

THREE.Face3 = function ( a, b, c, normal, materials ) {

	// vertex indices
	this.a = a;
	this.b = b;
	this.c = c;
	this.normal = Vector3;
	this.color = Color;
	this.vertexNormals = [ Vector3, Vector3, Vector3 ];
	this.vertexColors = [ Color, Color, Color ];
	this.materials = [];

}

THREE.Geometry = function () {

	this.vertices = [];
	this.faces = [ Face1, Face2, Face3 ... ];
	this.faceUvs = [ [ UV1, UV2, UV3, ... ], [ UV1, UV2, UV3, ... ], ... ];
	this.faceVertexUvs = [ [ [UV1, UV2, UV3], [UV4, UV5, UV6], ... ], [ [UV1, UV2, UV3], [UV4, UV5, UV6], ... ], ... ];

};

this.faceUvs[A] => uv layer A
this.faceUvs[A][X] => THREE.UV for this.faces[X]
this.faceVertexUvs[A] => uv layer A
this.faceVertexUvs[A][X] = [THREE.UV, THREE.UV, THREE.UV] for this.faces[X]

Reader

function parse( json ) {

	if ( json.metadata === undefined || json.metadata.formatVersion === undefined || json.metadata.formatVersion !== 3 ) {

		console.error( 'Deprecated file format.' );
		return;

	}

	function isBitSet( value, position ) {

		return value & ( 1 << position );

	};

	var i, j, 

	offset, zLength,

	type,
	isQuad, 
	hasMaterial, 
	hasFaceUv, hasFaceVertexUv,
	hasFaceNormal, hasFaceVertexNormal,
	hasFaceColor, hasFaceVertexColor,

	vertex, face,

	faces = json.faces,
	vertices = json.vertices,
	normals = json.normals,
	colors = json.colors,

	nUvLayers = 0;

	// disregard empty arrays

	for ( i = 0; i < json.uvs.length; i++ ) {

		if ( json.uvs[ i ].length ) nUvLayers ++;

	}

	for ( i = 0; i < nUvLayers; i++ ) {

		scope.faceUvs[ i ] = [];
		scope.faceVertexUvs[ i ] = [];

	}

	offset = 0;
	zLength = vertices.length;

	while ( offset < zLength ) {

		vertex = new THREE.Vertex();

		vertex.position.x = vertices[ offset ++ ];
		vertex.position.y = vertices[ offset ++ ];
		vertex.position.z = vertices[ offset ++ ];

		scope.vertices.push( vertex );

	}

	offset = 0;
	zLength = faces.length;

	while ( offset < zLength ) {

		type = faces[ offset ++ ];

		isQuad          	= isBitSet( type, 0 );
		hasMaterial         = isBitSet( type, 1 );
		hasFaceUv           = isBitSet( type, 2 );
		hasFaceVertexUv     = isBitSet( type, 3 );
		hasFaceNormal       = isBitSet( type, 4 );
		hasFaceVertexNormal = isBitSet( type, 5 );
		hasFaceColor	    = isBitSet( type, 6 );
		hasFaceVertexColor  = isBitSet( type, 7 );

		if ( isQuad ) {

			face = new THREE.Face4();

			face.a = faces[ offset ++ ];
			face.b = faces[ offset ++ ];
			face.c = faces[ offset ++ ];
			face.d = faces[ offset ++ ];

			nVertices = 4;

		} else {

			face = new THREE.Face3();

			face.a = faces[ offset ++ ];
			face.b = faces[ offset ++ ];
			face.c = faces[ offset ++ ];

			nVertices = 3;

		}

		if ( hasMaterial ) {

			materialIndex = faces[ offset ++ ];
			face.materials = scope.materials[ materialIndex ];

		}

		if ( hasFaceUv ) {

			for ( i = 0; i < nUvLayers; i++ ) {

				uvLayer = json.uvs[ i ];

				uvIndex = faces[ offset ++ ];

				u = uvLayer[ uvIndex * 2 ];
				v = uvLayer[ uvIndex * 2 + 1 ];

				scope.faceUvs[ i ].push( new THREE.UV( u, v ) );

			}

		}

		if ( hasFaceVertexUv ) {

			for ( i = 0; i < nUvLayers; i++ ) {

				uvLayer = json.uvs[ i ];

				uvs = [];

				for ( j = 0; j < nVertices; j ++ ) {

					uvIndex = faces[ offset ++ ];

					u = uvLayer[ uvIndex * 2 ];
					v = uvLayer[ uvIndex * 2 + 1 ];

					uvs[ j ] = new THREE.UV( u, v );

				}

				scope.faceVertexUvs[ i ].push( uvs );

			}

		}

		if ( hasFaceNormal ) {

			normalIndex = faces[ offset ++ ] * 3;

			normal = new THREE.Vector3();

			normal.x = normals[ normalIndex ++ ];
			normal.y = normals[ normalIndex ++ ];
			normal.z = normals[ normalIndex ];

			face.normal = normal;

		}

		if ( hasFaceVertexNormal ) {

			for ( i = 0; i < nVertices; i++ ) {

				normalIndex = faces[ offset ++ ] * 3;

				normal = new THREE.Vector3();

				normal.x = normals[ normalIndex ++ ];
				normal.y = normals[ normalIndex ++ ];
				normal.z = normals[ normalIndex ];

				face.vertexNormals.push( normal );

			}

		}

		if ( hasFaceColor ) {

			color = new THREE.Color( faces[ offset ++ ] );
			face.color = color;

		}

		if ( hasFaceVertexColor ) {

			for ( i = 0; i < nVertices; i++ ) {

				colorIndex = faces[ offset ++ ];

				color = new THREE.Color( colors[ colorIndex ] );
				face.vertexColors.push( color );

			}

		}

		scope.faces.push( face );

	}

};