Przeglądaj źródła

Merge pull request #10859 from donmccurdy/test-parsetrackname

[PropertyBinding] Support for '.' in node names, and unit tests.
Mr.doob 8 lat temu
rodzic
commit
9ac27daefc

BIN
examples/models/gltf/RiggedSimple/glTF/RiggedSimple.bin


+ 588 - 0
examples/models/gltf/RiggedSimple/glTF/RiggedSimple.gltf

@@ -0,0 +1,588 @@
+{
+    "accessors": {
+        "IBM_Armature_Cylinder-skin": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 0,
+            "componentType": 5126,
+            "count": 2,
+            "type": "MAT4"
+        },
+        "accessor_16": {
+            "bufferView": "bufferView_44",
+            "byteOffset": 0,
+            "byteStride": 0,
+            "componentType": 5123,
+            "count": 564,
+            "type": "SCALAR"
+        },
+        "accessor_18": {
+            "bufferView": "bufferView_45",
+            "byteOffset": 0,
+            "byteStride": 12,
+            "componentType": 5126,
+            "count": 96,
+            "max": [
+                1,
+                1,
+                4.57508
+            ],
+            "min": [
+                -1,
+                -1,
+                -4.57508
+            ],
+            "type": "VEC3"
+        },
+        "accessor_20": {
+            "bufferView": "bufferView_45",
+            "byteOffset": 1152,
+            "byteStride": 12,
+            "componentType": 5126,
+            "count": 96,
+            "max": [
+                0.998198,
+                0.998198,
+                0.688838
+            ],
+            "min": [
+                -0.998198,
+                -0.998198,
+                -0.644473
+            ],
+            "type": "VEC3"
+        },
+        "accessor_37": {
+            "bufferView": "bufferView_45",
+            "byteOffset": 3840,
+            "byteStride": 16,
+            "componentType": 5126,
+            "count": 96,
+            "max": [
+                1,
+                0.261398,
+                0,
+                0
+            ],
+            "min": [
+                0.738602,
+                0,
+                0,
+                0
+            ],
+            "type": "VEC4"
+        },
+        "accessor_40": {
+            "bufferView": "bufferView_45",
+            "byteOffset": 2304,
+            "byteStride": 16,
+            "componentType": 5126,
+            "count": 96,
+            "max": [
+                1,
+                1,
+                0,
+                0
+            ],
+            "min": [
+                0,
+                0,
+                0,
+                0
+            ],
+            "type": "VEC4"
+        },
+        "animAccessor_0": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 128,
+            "componentType": 5126,
+            "count": 3,
+            "type": "SCALAR"
+        },
+        "animAccessor_1": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 140,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC3"
+        },
+        "animAccessor_2": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 176,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC3"
+        },
+        "animAccessor_3": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 212,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC4"
+        },
+        "animAccessor_4": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 260,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC3"
+        },
+        "animAccessor_5": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 296,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC3"
+        },
+        "animAccessor_6": {
+            "bufferView": "bufferView_43",
+            "byteOffset": 332,
+            "componentType": 5126,
+            "count": 3,
+            "type": "VEC4"
+        }
+    },
+    "animations": {
+        "animation_0": {
+            "channels": [
+                {
+                    "sampler": "animation_0_scale_sampler",
+                    "target": {
+                        "id": "Bone",
+                        "path": "scale"
+                    }
+                },
+                {
+                    "sampler": "animation_0_translation_sampler",
+                    "target": {
+                        "id": "Bone",
+                        "path": "translation"
+                    }
+                },
+                {
+                    "sampler": "animation_0_rotation_sampler",
+                    "target": {
+                        "id": "Bone",
+                        "path": "rotation"
+                    }
+                }
+            ],
+            "parameters": {
+                "TIME": "animAccessor_0",
+                "rotation": "animAccessor_3",
+                "scale": "animAccessor_1",
+                "translation": "animAccessor_2"
+            },
+            "samplers": {
+                "animation_0_rotation_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "rotation"
+                },
+                "animation_0_scale_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "scale"
+                },
+                "animation_0_translation_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "translation"
+                }
+            }
+        },
+        "animation_1": {
+            "channels": [
+                {
+                    "sampler": "animation_1_scale_sampler",
+                    "target": {
+                        "id": "Bone_001",
+                        "path": "scale"
+                    }
+                },
+                {
+                    "sampler": "animation_1_translation_sampler",
+                    "target": {
+                        "id": "Bone_001",
+                        "path": "translation"
+                    }
+                },
+                {
+                    "sampler": "animation_1_rotation_sampler",
+                    "target": {
+                        "id": "Bone_001",
+                        "path": "rotation"
+                    }
+                }
+            ],
+            "parameters": {
+                "TIME": "animAccessor_0",
+                "rotation": "animAccessor_6",
+                "scale": "animAccessor_4",
+                "translation": "animAccessor_5"
+            },
+            "samplers": {
+                "animation_1_rotation_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "rotation"
+                },
+                "animation_1_scale_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "scale"
+                },
+                "animation_1_translation_sampler": {
+                    "input": "TIME",
+                    "interpolation": "LINEAR",
+                    "output": "translation"
+                }
+            }
+        }
+    },
+    "asset": {
+        "generator": "collada2gltf@027f74366341d569dea42e9a68b7104cc3892054",
+        "premultipliedAlpha": true,
+        "profile": {
+            "api": "WebGL",
+            "version": "1.0.2"
+        },
+        "version": "1.0"
+    },
+    "bufferViews": {
+        "bufferView_43": {
+            "buffer": "RiggedSimple",
+            "byteLength": 380,
+            "byteOffset": 0
+        },
+        "bufferView_44": {
+            "buffer": "RiggedSimple",
+            "byteLength": 1128,
+            "byteOffset": 380,
+            "target": 34963
+        },
+        "bufferView_45": {
+            "buffer": "RiggedSimple",
+            "byteLength": 5376,
+            "byteOffset": 1508,
+            "target": 34962
+        }
+    },
+    "buffers": {
+        "RiggedSimple": {
+            "byteLength": 6884,
+            "type": "arraybuffer",
+            "uri": "RiggedSimple.bin"
+        }
+    },
+    "materials": {
+        "Material_001-effect": {
+            "name": "Material_001",
+            "technique": "technique0",
+            "values": {
+                "ambient": [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "diffuse": [
+                    0.279635,
+                    0.64,
+                    0.210944,
+                    1
+                ],
+                "emission": [
+                    0,
+                    0,
+                    0,
+                    1
+                ],
+                "shininess": 50,
+                "specular": [
+                    0.5,
+                    0.5,
+                    0.5,
+                    1
+                ]
+            }
+        }
+    },
+    "meshes": {
+        "Cylinder-mesh": {
+            "name": "Cylinder",
+            "primitives": [
+                {
+                    "attributes": {
+                        "JOINT": "accessor_40",
+                        "NORMAL": "accessor_20",
+                        "POSITION": "accessor_18",
+                        "WEIGHT": "accessor_37"
+                    },
+                    "indices": "accessor_16",
+                    "material": "Material_001-effect",
+                    "mode": 4
+                }
+            ]
+        }
+    },
+    "nodes": {
+        "Armature": {
+            "children": [
+                "Bone"
+            ],
+            "matrix": [
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1
+            ],
+            "name": "Armature"
+        },
+        "Bone": {
+            "children": [
+                "Bone_001"
+            ],
+            "jointName": "Bone",
+            "name": "Bone",
+            "rotation": [
+                0.70474,
+                0,
+                0,
+                0.709465
+            ],
+            "scale": [
+                1,
+                1,
+                1
+            ],
+            "translation": [
+                0,
+                -3.15606e-007,
+                -4.18033
+            ]
+        },
+        "Bone_001": {
+            "children": [],
+            "jointName": "Bone_001",
+            "name": "Bone.001",
+            "rotation": [
+                0.00205211,
+                9.94789e-008,
+                0.000291371,
+                0.999998
+            ],
+            "scale": [
+                1,
+                1,
+                1
+            ],
+            "translation": [
+                0,
+                4.18717,
+                0
+            ]
+        },
+        "Cylinder": {
+            "children": [],
+            "matrix": [
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1
+            ],
+            "meshes": [
+                "Cylinder-mesh"
+            ],
+            "name": "Cylinder",
+            "skeletons": [
+                "Bone"
+            ],
+            "skin": "Armature_Cylinder-skin"
+        },
+        "node_4": {
+            "children": [
+                "Armature",
+                "Cylinder"
+            ],
+            "matrix": [
+                1,
+                0,
+                0,
+                0,
+                0,
+                0,
+                -1,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                0,
+                1
+            ],
+            "name": "Y_UP_Transform"
+        }
+    },
+    "programs": {
+        "program_0": {
+            "attributes": [
+                "a_joint",
+                "a_normal",
+                "a_position",
+                "a_weight"
+            ],
+            "fragmentShader": "RiggedSimple0FS",
+            "vertexShader": "RiggedSimple0VS"
+        }
+    },
+    "scene": "defaultScene",
+    "scenes": {
+        "defaultScene": {
+            "nodes": [
+                "node_4"
+            ]
+        }
+    },
+    "shaders": {
+        "RiggedSimple0FS": {
+            "type": 35632,
+            "uri": "RiggedSimple0FS.glsl"
+        },
+        "RiggedSimple0VS": {
+            "type": 35633,
+            "uri": "RiggedSimple0VS.glsl"
+        }
+    },
+    "skins": {
+        "Armature_Cylinder-skin": {
+            "bindShapeMatrix": [
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1,
+                0,
+                0,
+                0,
+                0,
+                1
+            ],
+            "inverseBindMatrices": "IBM_Armature_Cylinder-skin",
+            "jointNames": [
+                "Bone",
+                "Bone_001"
+            ],
+            "name": "Armature"
+        }
+    },
+    "techniques": {
+        "technique0": {
+            "attributes": {
+                "a_joint": "joint",
+                "a_normal": "normal",
+                "a_position": "position",
+                "a_weight": "weight"
+            },
+            "parameters": {
+                "ambient": {
+                    "type": 35666
+                },
+                "diffuse": {
+                    "type": 35666
+                },
+                "emission": {
+                    "type": 35666
+                },
+                "joint": {
+                    "semantic": "JOINT",
+                    "type": 35666
+                },
+                "jointMat": {
+                    "count": 2,
+                    "semantic": "JOINTMATRIX",
+                    "type": 35676
+                },
+                "modelViewMatrix": {
+                    "semantic": "MODELVIEW",
+                    "type": 35676
+                },
+                "normal": {
+                    "semantic": "NORMAL",
+                    "type": 35665
+                },
+                "normalMatrix": {
+                    "semantic": "MODELVIEWINVERSETRANSPOSE",
+                    "type": 35675
+                },
+                "position": {
+                    "semantic": "POSITION",
+                    "type": 35665
+                },
+                "projectionMatrix": {
+                    "semantic": "PROJECTION",
+                    "type": 35676
+                },
+                "shininess": {
+                    "type": 5126
+                },
+                "specular": {
+                    "type": 35666
+                },
+                "weight": {
+                    "semantic": "WEIGHT",
+                    "type": 35666
+                }
+            },
+            "program": "program_0",
+            "states": {
+                "enable": [
+                    2929,
+                    2884
+                ]
+            },
+            "uniforms": {
+                "u_ambient": "ambient",
+                "u_diffuse": "diffuse",
+                "u_emission": "emission",
+                "u_jointMat": "jointMat",
+                "u_modelViewMatrix": "modelViewMatrix",
+                "u_normalMatrix": "normalMatrix",
+                "u_projectionMatrix": "projectionMatrix",
+                "u_shininess": "shininess",
+                "u_specular": "specular"
+            }
+        }
+    }
+}

+ 24 - 0
examples/models/gltf/RiggedSimple/glTF/RiggedSimple0FS.glsl

@@ -0,0 +1,24 @@
+precision highp float;
+varying vec3 v_normal;
+uniform vec4 u_ambient;
+uniform vec4 u_diffuse;
+uniform vec4 u_emission;
+uniform vec4 u_specular;
+uniform float u_shininess;
+void main(void) {
+vec3 normal = normalize(v_normal);
+vec4 color = vec4(0., 0., 0., 0.);
+vec4 diffuse = vec4(0., 0., 0., 1.);
+vec4 emission;
+vec4 ambient;
+vec4 specular;
+ambient = u_ambient;
+diffuse = u_diffuse;
+emission = u_emission;
+specular = u_specular;
+diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.);
+color.xyz += diffuse.xyz;
+color.xyz += emission.xyz;
+color = vec4(color.rgb * diffuse.a, diffuse.a);
+gl_FragColor = color;
+}

+ 19 - 0
examples/models/gltf/RiggedSimple/glTF/RiggedSimple0VS.glsl

@@ -0,0 +1,19 @@
+precision highp float;
+attribute vec3 a_position;
+attribute vec3 a_normal;
+varying vec3 v_normal;
+attribute vec4 a_joint;
+attribute vec4 a_weight;
+uniform mat4 u_jointMat[2];
+uniform mat3 u_normalMatrix;
+uniform mat4 u_modelViewMatrix;
+uniform mat4 u_projectionMatrix;
+void main(void) {
+mat4 skinMat = a_weight.x * u_jointMat[int(a_joint.x)];
+skinMat += a_weight.y * u_jointMat[int(a_joint.y)];
+skinMat += a_weight.z * u_jointMat[int(a_joint.z)];
+skinMat += a_weight.w * u_jointMat[int(a_joint.w)];
+vec4 pos = u_modelViewMatrix * skinMat * vec4(a_position,1.0);
+v_normal = u_normalMatrix * mat3(skinMat)* a_normal;
+gl_Position = u_projectionMatrix * pos;
+}

+ 9 - 0
examples/webgl_loader_gltf.html

@@ -407,6 +407,15 @@
 					shadows:true,
 					extensions: ["glTF", "glTF-MaterialsCommon", "glTF-Binary"]
 				},
+				{
+					name : "Rigged Simple",
+					url : "./models/gltf/RiggedSimple/%s/RiggedSimple.gltf",
+					cameraPos: new THREE.Vector3(0, 5, 15),
+					objectRotation: new THREE.Euler(0, 90, 0),
+					addLights:true,
+					shadows:true,
+					extensions: ["glTF"]
+				},
 				{
 					name : "Snowflake",
 					url : "./models/gltf/snowflake/snowFlake.gltf",

+ 66 - 30
src/animation/PropertyBinding.js

@@ -102,47 +102,83 @@ Object.assign( PropertyBinding, {
 
 	},
 
-	parseTrackName: function ( trackName ) {
+	parseTrackName: function () {
 
-		// matches strings in the form of:
-		//    nodeName.property
-		//    nodeName.property[accessor]
-		//    nodeName.material.property[accessor]
-		//    uuid.property[accessor]
-		//    uuid.objectName[objectIndex].propertyName[propertyIndex]
-		//    parentName/nodeName.property
-		//    parentName/parentName/nodeName.property[index]
-		//    .bone[Armature.DEF_cog].position
-		//    scene:helium_balloon_model:helium_balloon_model.position
-		// created and tested via https://regex101.com/#javascript
+		// Parent directories, delimited by '/' or ':'. Currently unused, but must
+		// be matched to parse the rest of the track name.
+		var directoryRe = /((?:[\w-]+[\/:])*)/;
 
-		var re = /^((?:[\w-]+[\/:])*)([\w-]+)?(?:\.([\w-]+)(?:\[(.+)\])?)?\.([\w-]+)(?:\[(.+)\])?$/;
-		var matches = re.exec( trackName );
+		// Target node. May contain word characters (a-zA-Z0-9_) and '.' or '-'.
+		var nodeRe = /([\w-\.]+)?/;
 
-		if ( ! matches ) {
+		// Object on target node, and accessor. Name may contain only word
+		// characters. Accessor may contain any character except closing bracket.
+		var objectRe = /(?:\.([\w-]+)(?:\[(.+)\])?)?/;
 
-			throw new Error( "cannot parse trackName at all: " + trackName );
+		// Property and accessor. May contain only word characters. Accessor may
+		// contain any non-bracket characters.
+		var propertyRe = /\.([\w-]+)(?:\[(.+)\])?/;
 
-		}
+		var trackRe = new RegExp(''
+			+ '^'
+			+ directoryRe.source
+			+ nodeRe.source
+			+ objectRe.source
+			+ propertyRe.source
+			+ '$'
+		);
 
-		var results = {
-			// directoryName: matches[ 1 ], // (tschw) currently unused
-			nodeName: matches[ 2 ], 	// allowed to be null, specified root node.
-			objectName: matches[ 3 ],
-			objectIndex: matches[ 4 ],
-			propertyName: matches[ 5 ],
-			propertyIndex: matches[ 6 ]	// allowed to be null, specifies that the whole property is set.
-		};
+		var supportedObjectNames = [ 'material', 'materials', 'bones' ];
 
-		if ( results.propertyName === null || results.propertyName.length === 0 ) {
+		return function ( trackName ) {
 
-			throw new Error( "can not parse propertyName from trackName: " + trackName );
+				var matches = trackRe.exec( trackName );
 
-		}
+				if ( ! matches ) {
 
-		return results;
+					throw new Error( 'PropertyBinding: Cannot parse trackName: ' + trackName );
 
-	},
+				}
+
+				var results = {
+					// directoryName: matches[ 1 ], // (tschw) currently unused
+					nodeName: matches[ 2 ],
+					objectName: matches[ 3 ],
+					objectIndex: matches[ 4 ],
+					propertyName: matches[ 5 ],     // required
+					propertyIndex: matches[ 6 ]
+				};
+
+				var lastDot = results.nodeName && results.nodeName.lastIndexOf( '.' );
+
+				if ( lastDot !== undefined && lastDot !== -1 ) {
+
+					var objectName = results.nodeName.substring( lastDot + 1 );
+
+					// Object names must be checked against a whitelist. Otherwise, there
+					// is no way to parse 'foo.bar.baz': 'baz' must be a property, but
+					// 'bar' could be the objectName, or part of a nodeName (which can
+					// include '.' characters).
+					if ( supportedObjectNames.indexOf( objectName ) !== -1 ) {
+
+						results.nodeName = results.nodeName.substring( 0, lastDot );
+						results.objectName = objectName;
+
+					}
+
+				}
+
+				if ( results.propertyName === null || results.propertyName.length === 0 ) {
+
+					throw new Error( 'PropertyBinding: can not parse propertyName from trackName: ' + trackName );
+
+				}
+
+				return results;
+
+			};
+
+	}(),
 
 	findNode: function ( root, nodeName ) {
 

+ 196 - 2
test/unit/src/animation/PropertyBinding.js

@@ -2,5 +2,199 @@
  * @author TristanVALCKE / https://github.com/TristanVALCKE
  */
 
-//Todo
-console.warn("Todo: Unit tests of PropertyBinding")
+QUnit.module( 'PropertyBinding' );
+
+QUnit.test( 'parseTrackName' , function( assert ) {
+
+	var paths = [
+
+		[
+			'.property',
+			{
+				nodeName: undefined,
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'nodeName.property',
+			{
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'a.property',
+			{
+				nodeName: 'a',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.de.Name.property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.d-e.Name.property',
+			{
+				nodeName: 'no.d-e.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'nodeName.property[accessor]',
+			{
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'nodeName.material.property[accessor]',
+			{
+				nodeName: 'nodeName',
+				objectName: 'material',
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'no.de.Name.material.property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: 'material',
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'no.de.Name.material[materialIndex].property',
+			{
+				nodeName: 'no.de.Name',
+				objectName: 'material',
+				objectIndex: 'materialIndex',
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'uuid.property[accessor]',
+			{
+				nodeName: 'uuid',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'accessor'
+			}
+		],
+
+		[
+			'uuid.objectName[objectIndex].propertyName[propertyIndex]',
+			{
+				nodeName: 'uuid',
+				objectName: 'objectName',
+				objectIndex: 'objectIndex',
+				propertyName: 'propertyName',
+				propertyIndex: 'propertyIndex'
+			}
+		],
+
+		[
+			'parentName/nodeName.property',
+			{
+				// directoryName is currently unused.
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'parentName/no.de.Name.property',
+			{
+				// directoryName is currently unused.
+				nodeName: 'no.de.Name',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'parentName/parentName/nodeName.property[index]',
+			{
+				// directoryName is currently unused.
+				nodeName: 'nodeName',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'property',
+				propertyIndex: 'index'
+			}
+		],
+
+		[
+			'.bone[Armature.DEF_cog].position',
+			{
+				nodeName: undefined,
+				objectName: 'bone',
+				objectIndex: 'Armature.DEF_cog',
+				propertyName: 'position',
+				propertyIndex: undefined
+			}
+		],
+
+		[
+			'scene:helium_balloon_model:helium_balloon_model.position',
+			{
+				nodeName: 'helium_balloon_model',
+				objectName: undefined,
+				objectIndex: undefined,
+				propertyName: 'position',
+				propertyIndex: undefined
+			}
+		]
+	];
+
+	paths.forEach( function ( path, i ) {
+
+		assert.smartEqual(
+			THREE.PropertyBinding.parseTrackName( path[ 0 ] ),
+			path[ 1 ],
+			'Parses track name: ' +  path[ 0 ]
+		);
+
+	} );
+});