diff --git a/example/web_gl_loader_gltf/models/gltf/duck/duck.bin b/example/web_gl_loader_gltf/models/gltf/duck/duck.bin new file mode 100644 index 00000000..9ec1399c Binary files /dev/null and b/example/web_gl_loader_gltf/models/gltf/duck/duck.bin differ diff --git a/example/web_gl_loader_gltf/models/gltf/duck/duck.json b/example/web_gl_loader_gltf/models/gltf/duck/duck.json new file mode 100644 index 00000000..6aa4f609 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/duck/duck.json @@ -0,0 +1,397 @@ +{ + "accessors": { + "attribute_23": { + "bufferView": "bufferView_29", + "byteOffset": 0, + "byteStride": 12, + "count": 2399, + "max": [ + 96.1799, + 163.97, + 53.9252 + ], + "min": [ + -69.2985, + 9.92937, + -61.3282 + ], + "type": 35665 + }, + "attribute_25": { + "bufferView": "bufferView_29", + "byteOffset": 28788, + "byteStride": 12, + "count": 2399, + "max": [ + 0.999599, + 0.999581, + 0.998436 + ], + "min": [ + -0.999084, + -1, + -0.999832 + ], + "type": 35665 + }, + "attribute_27": { + "bufferView": "bufferView_29", + "byteOffset": 57576, + "byteStride": 8, + "count": 2399, + "max": [ + 0.983346, + 0.980037 + ], + "min": [ + 0.026409, + 0.019963 + ], + "type": 35664 + }, + "indices_21": { + "bufferView": "bufferView_30", + "byteOffset": 0, + "count": 12636, + "type": 5123 + } + }, + "animations": {}, + "asset": { + "generator": "collada2gltf@75061f683116dc0ffdad48f33c226e933132e98c" + }, + "bufferViews": { + "bufferView_29": { + "buffer": "duck", + "byteLength": 76768, + "byteOffset": 0, + "target": 34962 + }, + "bufferView_30": { + "buffer": "duck", + "byteLength": 25272, + "byteOffset": 76768, + "target": 34963 + }, + "bufferView_31": { + "buffer": "duck", + "byteLength": 0, + "byteOffset": 102040 + } + }, + "buffers": { + "duck": { + "byteLength": 102040, + "path": "duck.bin", + "type": "arraybuffer" + } + }, + "cameras": { + "camera_0": { + "perspective": { + "aspect_ratio": 1.5, + "yfov": 37.8492, + "zfar": 10000, + "znear": 1 + }, + "type": "perspective" + } + }, + "images": { + "image_0": { + "path": "duckCM.png" + } + }, + "lights": { + "directionalLightShape1-lib": { + "directional": { + "color": [ + 1, + 1, + 1 + ] + }, + "type": "directional" + } + }, + "materials": { + "blinn3-fx": { + "instanceTechnique": { + "technique": "technique1", + "values": { + "ambient": [ + 0, + 0, + 0, + 1 + ], + "diffuse": "texture_image_0", + "emission": [ + 0, + 0, + 0, + 1 + ], + "shininess": 38.4, + "specular": [ + 0, + 0, + 0, + 1 + ] + } + }, + "name": "blinn3" + } + }, + "meshes": { + "LOD3spShape-lib": { + "name": "LOD3spShape", + "primitives": [ + { + "attributes": { + "NORMAL": "attribute_25", + "POSITION": "attribute_23", + "TEXCOORD_0": "attribute_27" + }, + "indices": "indices_21", + "material": "blinn3-fx", + "primitive": 4 + } + ] + } + }, + "nodes": { + "LOD3sp": { + "children": [], + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ], + "meshes": [ + "LOD3spShape-lib" + ], + "name": "LOD3sp" + }, + "camera1": { + "camera": "camera_0", + "children": [], + "matrix": [ + -0.728969, + 0, + -0.684547, + 0, + -0.425205, + 0.783693, + 0.452797, + 0, + 0.536475, + 0.621148, + -0.571288, + 0, + 400.113, + 463.264, + -431.078, + 1 + ], + "name": "camera1" + }, + "directionalLight1": { + "children": [], + "light": "directionalLightShape1-lib", + "matrix": [ + -0.954692, + 0.218143, + -0.202428, + 0, + 0.0146721, + 0.713885, + 0.700109, + 0, + 0.297235, + 0.665418, + -0.684741, + 0, + 148.654, + 183.672, + -292.179, + 1 + ], + "name": "directionalLight1" + } + }, + "profile": "WebGL 1.0.2", + "programs": { + "program_0": { + "attributes": [ + "a_normal", + "a_position", + "a_texcoord0" + ], + "fragmentShader": "duck0FS", + "vertexShader": "duck0VS" + } + }, + "samplers": { + "sampler_0": { + "magFilter": 9729, + "minFilter": 9987, + "wrapS": 10497, + "wrapT": 10497 + } + }, + "scene": "defaultScene", + "scenes": { + "defaultScene": { + "nodes": [ + "LOD3sp", + "camera1", + "directionalLight1" + ] + } + }, + "shaders": { + "duck0FS": { + "path": "duck0FS.glsl" + }, + "duck0VS": { + "path": "duck0VS.glsl" + } + }, + "skins": {}, + "techniques": { + "technique1": { + "parameters": { + "ambient": { + "type": 35666 + }, + "diffuse": { + "type": 35678 + }, + "emission": { + "type": 35666 + }, + "light0Color": { + "type": 35665, + "value": [ + 1, + 1, + 1 + ] + }, + "light0Transform": { + "source": "directionalLight1", + "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 + }, + "texcoord0": { + "semantic": "TEXCOORD_0", + "type": 35664 + } + }, + "pass": "defaultPass", + "passes": { + "defaultPass": { + "details": { + "commonProfile": { + "extras": { + "doubleSided": false + }, + "lightingModel": "Blinn", + "parameters": [ + "ambient", + "diffuse", + "emission", + "light0Color", + "light0Transform", + "modelViewMatrix", + "normalMatrix", + "projectionMatrix", + "shininess", + "specular" + ], + "texcoordBindings": { + "diffuse": "TEXCOORD_0" + } + }, + "type": "COLLADA-1.4.1/commonProfile" + }, + "instanceProgram": { + "attributes": { + "a_normal": "normal", + "a_position": "position", + "a_texcoord0": "texcoord0" + }, + "program": "program_0", + "uniforms": { + "u_ambient": "ambient", + "u_diffuse": "diffuse", + "u_emission": "emission", + "u_light0Color": "light0Color", + "u_light0Transform": "light0Transform", + "u_modelViewMatrix": "modelViewMatrix", + "u_normalMatrix": "normalMatrix", + "u_projectionMatrix": "projectionMatrix", + "u_shininess": "shininess", + "u_specular": "specular" + } + }, + "states": { + "blendEnable": 0, + "cullFaceEnable": 1, + "depthMask": 1, + "depthTestEnable": 1 + } + } + } + } + }, + "textures": { + "texture_image_0": { + "format": 6408, + "internalFormat": 6408, + "sampler": "sampler_0", + "source": "image_0", + "target": 3553 + } + } +} \ No newline at end of file diff --git a/example/web_gl_loader_gltf/models/gltf/duck/duck0FS.glsl b/example/web_gl_loader_gltf/models/gltf/duck/duck0FS.glsl new file mode 100644 index 00000000..ff217424 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/duck/duck0FS.glsl @@ -0,0 +1,41 @@ +precision highp float; +varying vec3 v_normal; +uniform vec3 u_light0Color; +varying vec3 v_light0Direction; +uniform float u_shininess; +uniform vec4 u_ambient; +varying vec2 v_texcoord0; +uniform sampler2D u_diffuse; +uniform vec4 u_emission; +uniform vec4 u_specular; +void main(void) { +vec3 normal = normalize(v_normal); +vec4 color = vec4(0., 0., 0., 0.); +vec4 diffuse = vec4(0., 0., 0., 1.); +vec3 diffuseLight = vec3(0., 0., 0.); +vec4 emission; +vec4 ambient; +vec4 specular; +vec3 specularLight = vec3(0., 0., 0.); +{ +float diffuseIntensity; +float specularIntensity; +vec3 l = normalize(v_light0Direction); +vec3 h = normalize(l+vec3(0.,0.,1.)); +diffuseIntensity = max(dot(normal,l), 0.); +specularIntensity = pow(max(0.0,dot(normal,h)),u_shininess); +specularLight += u_light0Color * specularIntensity; +diffuseLight += u_light0Color * diffuseIntensity; +} +ambient = u_ambient; +diffuse = texture2D(u_diffuse, v_texcoord0); +emission = u_emission; +specular = u_specular; +specular.xyz *= specularLight; +color.xyz += specular.xyz; +diffuse.xyz *= diffuseLight; +color.xyz += diffuse.xyz; +color.xyz += emission.xyz; +color = vec4(color.rgb * diffuse.a, diffuse.a); +gl_FragColor = color; +} diff --git a/example/web_gl_loader_gltf/models/gltf/duck/duck0VS.glsl b/example/web_gl_loader_gltf/models/gltf/duck/duck0VS.glsl new file mode 100644 index 00000000..841843f2 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/duck/duck0VS.glsl @@ -0,0 +1,18 @@ +precision highp float; +attribute vec3 a_position; +attribute vec3 a_normal; +varying vec3 v_normal; +uniform mat3 u_normalMatrix; +uniform mat4 u_modelViewMatrix; +uniform mat4 u_projectionMatrix; +uniform mat4 u_light0Transform; +varying vec3 v_light0Direction; +attribute vec2 a_texcoord0; +varying vec2 v_texcoord0; +void main(void) { +vec4 pos = u_modelViewMatrix * vec4(a_position,1.0); +v_normal = normalize(u_normalMatrix * a_normal); +v_light0Direction = normalize(mat3(u_light0Transform) * vec3(0.,0.,1.)); +v_texcoord0 = a_texcoord0; +gl_Position = u_projectionMatrix * pos; +} diff --git a/example/web_gl_loader_gltf/models/gltf/duck/duckCM.png b/example/web_gl_loader_gltf/models/gltf/duck/duckCM.png new file mode 100644 index 00000000..62d9200b Binary files /dev/null and b/example/web_gl_loader_gltf/models/gltf/duck/duckCM.png differ diff --git a/example/web_gl_loader_gltf/models/gltf/monster/monster.bin b/example/web_gl_loader_gltf/models/gltf/monster/monster.bin new file mode 100644 index 00000000..9a8cf3b0 Binary files /dev/null and b/example/web_gl_loader_gltf/models/gltf/monster/monster.bin differ diff --git a/example/web_gl_loader_gltf/models/gltf/monster/monster.jpg b/example/web_gl_loader_gltf/models/gltf/monster/monster.jpg new file mode 100644 index 00000000..4f62a704 Binary files /dev/null and b/example/web_gl_loader_gltf/models/gltf/monster/monster.jpg differ diff --git a/example/web_gl_loader_gltf/models/gltf/monster/monster.json b/example/web_gl_loader_gltf/models/gltf/monster/monster.json new file mode 100644 index 00000000..fb7867b9 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/monster/monster.json @@ -0,0 +1,3587 @@ +{ + "accessors": { + "accessor_10": { + "bufferView": "bufferView_185", + "byteOffset": 3112, + "count": 101, + "type": 5126 + }, + "accessor_100": { + "bufferView": "bufferView_185", + "byteOffset": 110576, + "count": 101, + "type": 35665 + }, + "accessor_101": { + "bufferView": "bufferView_185", + "byteOffset": 111788, + "count": 101, + "type": 35666 + }, + "accessor_102": { + "bufferView": "bufferView_185", + "byteOffset": 12808, + "count": 101, + "type": 5126 + }, + "accessor_103": { + "bufferView": "bufferView_185", + "byteOffset": 113404, + "count": 101, + "type": 35665 + }, + "accessor_104": { + "bufferView": "bufferView_185", + "byteOffset": 114616, + "count": 101, + "type": 35665 + }, + "accessor_105": { + "bufferView": "bufferView_185", + "byteOffset": 115828, + "count": 101, + "type": 35666 + }, + "accessor_106": { + "bufferView": "bufferView_185", + "byteOffset": 13212, + "count": 101, + "type": 5126 + }, + "accessor_107": { + "bufferView": "bufferView_185", + "byteOffset": 117444, + "count": 101, + "type": 35665 + }, + "accessor_108": { + "bufferView": "bufferView_185", + "byteOffset": 118656, + "count": 101, + "type": 35665 + }, + "accessor_109": { + "bufferView": "bufferView_185", + "byteOffset": 119868, + "count": 101, + "type": 35666 + }, + "accessor_11": { + "bufferView": "bufferView_185", + "byteOffset": 20484, + "count": 101, + "type": 35665 + }, + "accessor_110": { + "bufferView": "bufferView_185", + "byteOffset": 13616, + "count": 101, + "type": 5126 + }, + "accessor_111": { + "bufferView": "bufferView_185", + "byteOffset": 121484, + "count": 101, + "type": 35665 + }, + "accessor_112": { + "bufferView": "bufferView_185", + "byteOffset": 122696, + "count": 101, + "type": 35665 + }, + "accessor_113": { + "bufferView": "bufferView_185", + "byteOffset": 123908, + "count": 101, + "type": 35666 + }, + "accessor_114": { + "bufferView": "bufferView_185", + "byteOffset": 14020, + "count": 101, + "type": 5126 + }, + "accessor_115": { + "bufferView": "bufferView_185", + "byteOffset": 125524, + "count": 101, + "type": 35665 + }, + "accessor_116": { + "bufferView": "bufferView_185", + "byteOffset": 126736, + "count": 101, + "type": 35665 + }, + "accessor_117": { + "bufferView": "bufferView_185", + "byteOffset": 127948, + "count": 101, + "type": 35666 + }, + "accessor_118": { + "bufferView": "bufferView_185", + "byteOffset": 14424, + "count": 101, + "type": 5126 + }, + "accessor_119": { + "bufferView": "bufferView_185", + "byteOffset": 129564, + "count": 101, + "type": 35665 + }, + "accessor_12": { + "bufferView": "bufferView_185", + "byteOffset": 21696, + "count": 101, + "type": 35665 + }, + "accessor_120": { + "bufferView": "bufferView_185", + "byteOffset": 130776, + "count": 101, + "type": 35665 + }, + "accessor_121": { + "bufferView": "bufferView_185", + "byteOffset": 131988, + "count": 101, + "type": 35666 + }, + "accessor_122": { + "bufferView": "bufferView_185", + "byteOffset": 14828, + "count": 101, + "type": 5126 + }, + "accessor_123": { + "bufferView": "bufferView_185", + "byteOffset": 133604, + "count": 101, + "type": 35665 + }, + "accessor_124": { + "bufferView": "bufferView_185", + "byteOffset": 134816, + "count": 101, + "type": 35665 + }, + "accessor_125": { + "bufferView": "bufferView_185", + "byteOffset": 136028, + "count": 101, + "type": 35666 + }, + "accessor_126": { + "bufferView": "bufferView_185", + "byteOffset": 15232, + "count": 101, + "type": 5126 + }, + "accessor_127": { + "bufferView": "bufferView_185", + "byteOffset": 137644, + "count": 101, + "type": 35665 + }, + "accessor_128": { + "bufferView": "bufferView_185", + "byteOffset": 138856, + "count": 101, + "type": 35665 + }, + "accessor_129": { + "bufferView": "bufferView_185", + "byteOffset": 140068, + "count": 101, + "type": 35666 + }, + "accessor_13": { + "bufferView": "bufferView_185", + "byteOffset": 22908, + "count": 101, + "type": 35666 + }, + "accessor_130": { + "bufferView": "bufferView_185", + "byteOffset": 15636, + "count": 101, + "type": 5126 + }, + "accessor_131": { + "bufferView": "bufferView_185", + "byteOffset": 141684, + "count": 101, + "type": 35665 + }, + "accessor_132": { + "bufferView": "bufferView_185", + "byteOffset": 142896, + "count": 101, + "type": 35665 + }, + "accessor_133": { + "bufferView": "bufferView_185", + "byteOffset": 144108, + "count": 101, + "type": 35666 + }, + "accessor_14": { + "bufferView": "bufferView_185", + "byteOffset": 3516, + "count": 101, + "type": 5126 + }, + "accessor_15": { + "bufferView": "bufferView_185", + "byteOffset": 24524, + "count": 101, + "type": 35665 + }, + "accessor_16": { + "bufferView": "bufferView_185", + "byteOffset": 25736, + "count": 101, + "type": 35665 + }, + "accessor_17": { + "bufferView": "bufferView_185", + "byteOffset": 26948, + "count": 101, + "type": 35666 + }, + "accessor_18": { + "bufferView": "bufferView_185", + "byteOffset": 3920, + "count": 101, + "type": 5126 + }, + "accessor_19": { + "bufferView": "bufferView_185", + "byteOffset": 28564, + "count": 101, + "type": 35665 + }, + "accessor_20": { + "bufferView": "bufferView_185", + "byteOffset": 29776, + "count": 101, + "type": 35665 + }, + "accessor_21": { + "bufferView": "bufferView_185", + "byteOffset": 30988, + "count": 101, + "type": 35666 + }, + "accessor_22": { + "bufferView": "bufferView_185", + "byteOffset": 4324, + "count": 101, + "type": 5126 + }, + "accessor_23": { + "bufferView": "bufferView_185", + "byteOffset": 32604, + "count": 101, + "type": 35665 + }, + "accessor_24": { + "bufferView": "bufferView_185", + "byteOffset": 33816, + "count": 101, + "type": 35665 + }, + "accessor_25": { + "bufferView": "bufferView_185", + "byteOffset": 35028, + "count": 101, + "type": 35666 + }, + "accessor_26": { + "bufferView": "bufferView_185", + "byteOffset": 4728, + "count": 101, + "type": 5126 + }, + "accessor_27": { + "bufferView": "bufferView_185", + "byteOffset": 36644, + "count": 101, + "type": 35665 + }, + "accessor_28": { + "bufferView": "bufferView_185", + "byteOffset": 37856, + "count": 101, + "type": 35665 + }, + "accessor_29": { + "bufferView": "bufferView_185", + "byteOffset": 39068, + "count": 101, + "type": 35666 + }, + "accessor_30": { + "bufferView": "bufferView_185", + "byteOffset": 5132, + "count": 101, + "type": 5126 + }, + "accessor_31": { + "bufferView": "bufferView_185", + "byteOffset": 40684, + "count": 101, + "type": 35665 + }, + "accessor_32": { + "bufferView": "bufferView_185", + "byteOffset": 41896, + "count": 101, + "type": 35665 + }, + "accessor_33": { + "bufferView": "bufferView_185", + "byteOffset": 43108, + "count": 101, + "type": 35666 + }, + "accessor_34": { + "bufferView": "bufferView_185", + "byteOffset": 5536, + "count": 101, + "type": 5126 + }, + "accessor_35": { + "bufferView": "bufferView_185", + "byteOffset": 44724, + "count": 101, + "type": 35665 + }, + "accessor_36": { + "bufferView": "bufferView_185", + "byteOffset": 45936, + "count": 101, + "type": 35665 + }, + "accessor_37": { + "bufferView": "bufferView_185", + "byteOffset": 47148, + "count": 101, + "type": 35666 + }, + "accessor_38": { + "bufferView": "bufferView_185", + "byteOffset": 5940, + "count": 101, + "type": 5126 + }, + "accessor_39": { + "bufferView": "bufferView_185", + "byteOffset": 48764, + "count": 101, + "type": 35665 + }, + "accessor_40": { + "bufferView": "bufferView_185", + "byteOffset": 49976, + "count": 101, + "type": 35665 + }, + "accessor_41": { + "bufferView": "bufferView_185", + "byteOffset": 51188, + "count": 101, + "type": 35666 + }, + "accessor_42": { + "bufferView": "bufferView_185", + "byteOffset": 6344, + "count": 101, + "type": 5126 + }, + "accessor_43": { + "bufferView": "bufferView_185", + "byteOffset": 52804, + "count": 101, + "type": 35665 + }, + "accessor_44": { + "bufferView": "bufferView_185", + "byteOffset": 54016, + "count": 101, + "type": 35665 + }, + "accessor_45": { + "bufferView": "bufferView_185", + "byteOffset": 55228, + "count": 101, + "type": 35666 + }, + "accessor_46": { + "bufferView": "bufferView_185", + "byteOffset": 6748, + "count": 101, + "type": 5126 + }, + "accessor_47": { + "bufferView": "bufferView_185", + "byteOffset": 56844, + "count": 101, + "type": 35665 + }, + "accessor_48": { + "bufferView": "bufferView_185", + "byteOffset": 58056, + "count": 101, + "type": 35665 + }, + "accessor_49": { + "bufferView": "bufferView_185", + "byteOffset": 59268, + "count": 101, + "type": 35666 + }, + "accessor_50": { + "bufferView": "bufferView_185", + "byteOffset": 7152, + "count": 101, + "type": 5126 + }, + "accessor_51": { + "bufferView": "bufferView_185", + "byteOffset": 60884, + "count": 101, + "type": 35665 + }, + "accessor_52": { + "bufferView": "bufferView_185", + "byteOffset": 62096, + "count": 101, + "type": 35665 + }, + "accessor_53": { + "bufferView": "bufferView_185", + "byteOffset": 63308, + "count": 101, + "type": 35666 + }, + "accessor_54": { + "bufferView": "bufferView_185", + "byteOffset": 7556, + "count": 101, + "type": 5126 + }, + "accessor_55": { + "bufferView": "bufferView_185", + "byteOffset": 64924, + "count": 101, + "type": 35665 + }, + "accessor_56": { + "bufferView": "bufferView_185", + "byteOffset": 66136, + "count": 101, + "type": 35665 + }, + "accessor_57": { + "bufferView": "bufferView_185", + "byteOffset": 67348, + "count": 101, + "type": 35666 + }, + "accessor_58": { + "bufferView": "bufferView_185", + "byteOffset": 7960, + "count": 101, + "type": 5126 + }, + "accessor_59": { + "bufferView": "bufferView_185", + "byteOffset": 68964, + "count": 101, + "type": 35665 + }, + "accessor_6": { + "bufferView": "bufferView_185", + "byteOffset": 2708, + "count": 101, + "type": 5126 + }, + "accessor_60": { + "bufferView": "bufferView_185", + "byteOffset": 70176, + "count": 101, + "type": 35665 + }, + "accessor_61": { + "bufferView": "bufferView_185", + "byteOffset": 71388, + "count": 101, + "type": 35666 + }, + "accessor_62": { + "bufferView": "bufferView_185", + "byteOffset": 8364, + "count": 101, + "type": 5126 + }, + "accessor_63": { + "bufferView": "bufferView_185", + "byteOffset": 73004, + "count": 101, + "type": 35665 + }, + "accessor_64": { + "bufferView": "bufferView_185", + "byteOffset": 74216, + "count": 101, + "type": 35665 + }, + "accessor_65": { + "bufferView": "bufferView_185", + "byteOffset": 75428, + "count": 101, + "type": 35666 + }, + "accessor_66": { + "bufferView": "bufferView_185", + "byteOffset": 8768, + "count": 101, + "type": 5126 + }, + "accessor_67": { + "bufferView": "bufferView_185", + "byteOffset": 77044, + "count": 101, + "type": 35665 + }, + "accessor_68": { + "bufferView": "bufferView_185", + "byteOffset": 78256, + "count": 101, + "type": 35665 + }, + "accessor_69": { + "bufferView": "bufferView_185", + "byteOffset": 79468, + "count": 101, + "type": 35666 + }, + "accessor_7": { + "bufferView": "bufferView_185", + "byteOffset": 16444, + "count": 101, + "type": 35665 + }, + "accessor_70": { + "bufferView": "bufferView_185", + "byteOffset": 9576, + "count": 101, + "type": 5126 + }, + "accessor_71": { + "bufferView": "bufferView_185", + "byteOffset": 81084, + "count": 101, + "type": 35665 + }, + "accessor_72": { + "bufferView": "bufferView_185", + "byteOffset": 82296, + "count": 101, + "type": 35665 + }, + "accessor_73": { + "bufferView": "bufferView_185", + "byteOffset": 83508, + "count": 101, + "type": 35666 + }, + "accessor_74": { + "bufferView": "bufferView_185", + "byteOffset": 9980, + "count": 101, + "type": 5126 + }, + "accessor_75": { + "bufferView": "bufferView_185", + "byteOffset": 85124, + "count": 101, + "type": 35665 + }, + "accessor_76": { + "bufferView": "bufferView_185", + "byteOffset": 86336, + "count": 101, + "type": 35665 + }, + "accessor_77": { + "bufferView": "bufferView_185", + "byteOffset": 87548, + "count": 101, + "type": 35666 + }, + "accessor_78": { + "bufferView": "bufferView_185", + "byteOffset": 10384, + "count": 101, + "type": 5126 + }, + "accessor_79": { + "bufferView": "bufferView_185", + "byteOffset": 89164, + "count": 101, + "type": 35665 + }, + "accessor_8": { + "bufferView": "bufferView_185", + "byteOffset": 17656, + "count": 101, + "type": 35665 + }, + "accessor_80": { + "bufferView": "bufferView_185", + "byteOffset": 90376, + "count": 101, + "type": 35665 + }, + "accessor_81": { + "bufferView": "bufferView_185", + "byteOffset": 91588, + "count": 101, + "type": 35666 + }, + "accessor_82": { + "bufferView": "bufferView_185", + "byteOffset": 10788, + "count": 101, + "type": 5126 + }, + "accessor_83": { + "bufferView": "bufferView_185", + "byteOffset": 93204, + "count": 101, + "type": 35665 + }, + "accessor_84": { + "bufferView": "bufferView_185", + "byteOffset": 94416, + "count": 101, + "type": 35665 + }, + "accessor_85": { + "bufferView": "bufferView_185", + "byteOffset": 95628, + "count": 101, + "type": 35666 + }, + "accessor_86": { + "bufferView": "bufferView_185", + "byteOffset": 11192, + "count": 101, + "type": 5126 + }, + "accessor_87": { + "bufferView": "bufferView_185", + "byteOffset": 97244, + "count": 101, + "type": 35665 + }, + "accessor_88": { + "bufferView": "bufferView_185", + "byteOffset": 98456, + "count": 101, + "type": 35665 + }, + "accessor_89": { + "bufferView": "bufferView_185", + "byteOffset": 99668, + "count": 101, + "type": 35666 + }, + "accessor_9": { + "bufferView": "bufferView_185", + "byteOffset": 18868, + "count": 101, + "type": 35666 + }, + "accessor_90": { + "bufferView": "bufferView_185", + "byteOffset": 11596, + "count": 101, + "type": 5126 + }, + "accessor_91": { + "bufferView": "bufferView_185", + "byteOffset": 101284, + "count": 101, + "type": 35665 + }, + "accessor_92": { + "bufferView": "bufferView_185", + "byteOffset": 102496, + "count": 101, + "type": 35665 + }, + "accessor_93": { + "bufferView": "bufferView_185", + "byteOffset": 103708, + "count": 101, + "type": 35666 + }, + "accessor_94": { + "bufferView": "bufferView_185", + "byteOffset": 12000, + "count": 101, + "type": 5126 + }, + "accessor_95": { + "bufferView": "bufferView_185", + "byteOffset": 105324, + "count": 101, + "type": 35665 + }, + "accessor_96": { + "bufferView": "bufferView_185", + "byteOffset": 106536, + "count": 101, + "type": 35665 + }, + "accessor_97": { + "bufferView": "bufferView_185", + "byteOffset": 107748, + "count": 101, + "type": 35666 + }, + "accessor_98": { + "bufferView": "bufferView_185", + "byteOffset": 12404, + "count": 101, + "type": 5126 + }, + "accessor_99": { + "bufferView": "bufferView_185", + "byteOffset": 109364, + "count": 101, + "type": 35665 + }, + "attribute_163": { + "bufferView": "bufferView_183", + "byteOffset": 0, + "byteStride": 12, + "count": 818, + "max": [ + 1154.32, + 892.627, + 584.533 + ], + "min": [ + -1746.98, + -913.328, + -504.127 + ], + "type": 35665 + }, + "attribute_165": { + "bufferView": "bufferView_183", + "byteOffset": 9816, + "byteStride": 12, + "count": 818, + "max": [ + 0.997713, + 0.999992, + 0.998876 + ], + "min": [ + -0.976716, + -1, + -0.998876 + ], + "type": 35665 + }, + "attribute_167": { + "bufferView": "bufferView_183", + "byteOffset": 19632, + "byteStride": 8, + "count": 818, + "max": [ + 1.00961, + 0.991641 + ], + "min": [ + 0.005086, + -0.07435 + ], + "type": 35664 + }, + "attribute_176": { + "bufferView": "bufferView_183", + "byteOffset": 26176, + "byteStride": 16, + "count": 818, + "max": [ + 1, + 0.5, + 0.333333, + 0.297741 + ], + "min": [ + 0.202008, + 0, + 0, + 0 + ], + "type": 35666 + }, + "attribute_179": { + "bufferView": "bufferView_183", + "byteOffset": 39264, + "byteStride": 16, + "count": 818, + "max": [ + 34, + 34, + 28, + 33 + ], + "min": [ + 0, + 0, + 0, + 0 + ], + "type": 35666 + }, + "indices_161": { + "bufferView": "bufferView_184", + "byteOffset": 0, + "count": 2652, + "type": 5123 + } + }, + "animations": { + "animation_1": { + "channels": [ + { + "sampler": "animation_1_scale_sampler", + "target": { + "id": "Bip01_Spine-node", + "path": "scale" + } + }, + { + "sampler": "animation_1_translation_sampler", + "target": { + "id": "Bip01_Spine-node", + "path": "translation" + } + }, + { + "sampler": "animation_1_rotation_sampler", + "target": { + "id": "Bip01_Spine-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_6", + "rotation": "accessor_9", + "scale": "accessor_7", + "translation": "accessor_8" + }, + "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" + } + } + }, + "animation_10": { + "channels": [ + { + "sampler": "animation_10_scale_sampler", + "target": { + "id": "Bip01_L_Finger0Nub-node", + "path": "scale" + } + }, + { + "sampler": "animation_10_translation_sampler", + "target": { + "id": "Bip01_L_Finger0Nub-node", + "path": "translation" + } + }, + { + "sampler": "animation_10_rotation_sampler", + "target": { + "id": "Bip01_L_Finger0Nub-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_42", + "rotation": "accessor_45", + "scale": "accessor_43", + "translation": "accessor_44" + }, + "samplers": { + "animation_10_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_10_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_10_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_11": { + "channels": [ + { + "sampler": "animation_11_scale_sampler", + "target": { + "id": "Bip01_HeadNub-node", + "path": "scale" + } + }, + { + "sampler": "animation_11_translation_sampler", + "target": { + "id": "Bip01_HeadNub-node", + "path": "translation" + } + }, + { + "sampler": "animation_11_rotation_sampler", + "target": { + "id": "Bip01_HeadNub-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_46", + "rotation": "accessor_49", + "scale": "accessor_47", + "translation": "accessor_48" + }, + "samplers": { + "animation_11_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_11_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_11_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_12": { + "channels": [ + { + "sampler": "animation_12_scale_sampler", + "target": { + "id": "Bip01_R_Thigh-node", + "path": "scale" + } + }, + { + "sampler": "animation_12_translation_sampler", + "target": { + "id": "Bip01_R_Thigh-node", + "path": "translation" + } + }, + { + "sampler": "animation_12_rotation_sampler", + "target": { + "id": "Bip01_R_Thigh-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_50", + "rotation": "accessor_53", + "scale": "accessor_51", + "translation": "accessor_52" + }, + "samplers": { + "animation_12_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_12_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_12_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_13": { + "channels": [ + { + "sampler": "animation_13_scale_sampler", + "target": { + "id": "Bip01_R_Calf-node", + "path": "scale" + } + }, + { + "sampler": "animation_13_translation_sampler", + "target": { + "id": "Bip01_R_Calf-node", + "path": "translation" + } + }, + { + "sampler": "animation_13_rotation_sampler", + "target": { + "id": "Bip01_R_Calf-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_54", + "rotation": "accessor_57", + "scale": "accessor_55", + "translation": "accessor_56" + }, + "samplers": { + "animation_13_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_13_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_13_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_14": { + "channels": [ + { + "sampler": "animation_14_scale_sampler", + "target": { + "id": "Bip01_R_Foot-node", + "path": "scale" + } + }, + { + "sampler": "animation_14_translation_sampler", + "target": { + "id": "Bip01_R_Foot-node", + "path": "translation" + } + }, + { + "sampler": "animation_14_rotation_sampler", + "target": { + "id": "Bip01_R_Foot-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_58", + "rotation": "accessor_61", + "scale": "accessor_59", + "translation": "accessor_60" + }, + "samplers": { + "animation_14_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_14_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_14_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_15": { + "channels": [ + { + "sampler": "animation_15_scale_sampler", + "target": { + "id": "Bip01_R_Toe0-node", + "path": "scale" + } + }, + { + "sampler": "animation_15_translation_sampler", + "target": { + "id": "Bip01_R_Toe0-node", + "path": "translation" + } + }, + { + "sampler": "animation_15_rotation_sampler", + "target": { + "id": "Bip01_R_Toe0-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_62", + "rotation": "accessor_65", + "scale": "accessor_63", + "translation": "accessor_64" + }, + "samplers": { + "animation_15_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_15_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_15_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_16": { + "channels": [ + { + "sampler": "animation_16_scale_sampler", + "target": { + "id": "Bone01-node", + "path": "scale" + } + }, + { + "sampler": "animation_16_translation_sampler", + "target": { + "id": "Bone01-node", + "path": "translation" + } + }, + { + "sampler": "animation_16_rotation_sampler", + "target": { + "id": "Bone01-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_66", + "rotation": "accessor_69", + "scale": "accessor_67", + "translation": "accessor_68" + }, + "samplers": { + "animation_16_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_16_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_16_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_18": { + "channels": [ + { + "sampler": "animation_18_scale_sampler", + "target": { + "id": "Bip01_R_Clavicle-node", + "path": "scale" + } + }, + { + "sampler": "animation_18_translation_sampler", + "target": { + "id": "Bip01_R_Clavicle-node", + "path": "translation" + } + }, + { + "sampler": "animation_18_rotation_sampler", + "target": { + "id": "Bip01_R_Clavicle-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_70", + "rotation": "accessor_73", + "scale": "accessor_71", + "translation": "accessor_72" + }, + "samplers": { + "animation_18_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_18_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_18_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_19": { + "channels": [ + { + "sampler": "animation_19_scale_sampler", + "target": { + "id": "Bone03-node", + "path": "scale" + } + }, + { + "sampler": "animation_19_translation_sampler", + "target": { + "id": "Bone03-node", + "path": "translation" + } + }, + { + "sampler": "animation_19_rotation_sampler", + "target": { + "id": "Bone03-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_74", + "rotation": "accessor_77", + "scale": "accessor_75", + "translation": "accessor_76" + }, + "samplers": { + "animation_19_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_19_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_19_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_2": { + "channels": [ + { + "sampler": "animation_2_scale_sampler", + "target": { + "id": "Bip01_Spine1-node", + "path": "scale" + } + }, + { + "sampler": "animation_2_translation_sampler", + "target": { + "id": "Bip01_Spine1-node", + "path": "translation" + } + }, + { + "sampler": "animation_2_rotation_sampler", + "target": { + "id": "Bip01_Spine1-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_10", + "rotation": "accessor_13", + "scale": "accessor_11", + "translation": "accessor_12" + }, + "samplers": { + "animation_2_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_2_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_2_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_20": { + "channels": [ + { + "sampler": "animation_20_scale_sampler", + "target": { + "id": "Bone04-node", + "path": "scale" + } + }, + { + "sampler": "animation_20_translation_sampler", + "target": { + "id": "Bone04-node", + "path": "translation" + } + }, + { + "sampler": "animation_20_rotation_sampler", + "target": { + "id": "Bone04-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_78", + "rotation": "accessor_81", + "scale": "accessor_79", + "translation": "accessor_80" + }, + "samplers": { + "animation_20_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_20_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_20_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_21": { + "channels": [ + { + "sampler": "animation_21_scale_sampler", + "target": { + "id": "Bip01_R_UpperArm-node", + "path": "scale" + } + }, + { + "sampler": "animation_21_translation_sampler", + "target": { + "id": "Bip01_R_UpperArm-node", + "path": "translation" + } + }, + { + "sampler": "animation_21_rotation_sampler", + "target": { + "id": "Bip01_R_UpperArm-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_82", + "rotation": "accessor_85", + "scale": "accessor_83", + "translation": "accessor_84" + }, + "samplers": { + "animation_21_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_21_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_21_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_22": { + "channels": [ + { + "sampler": "animation_22_scale_sampler", + "target": { + "id": "Bip01_R_Forearm-node", + "path": "scale" + } + }, + { + "sampler": "animation_22_translation_sampler", + "target": { + "id": "Bip01_R_Forearm-node", + "path": "translation" + } + }, + { + "sampler": "animation_22_rotation_sampler", + "target": { + "id": "Bip01_R_Forearm-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_86", + "rotation": "accessor_89", + "scale": "accessor_87", + "translation": "accessor_88" + }, + "samplers": { + "animation_22_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_22_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_22_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_23": { + "channels": [ + { + "sampler": "animation_23_scale_sampler", + "target": { + "id": "Bip01_R_Hand-node", + "path": "scale" + } + }, + { + "sampler": "animation_23_translation_sampler", + "target": { + "id": "Bip01_R_Hand-node", + "path": "translation" + } + }, + { + "sampler": "animation_23_rotation_sampler", + "target": { + "id": "Bip01_R_Hand-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_90", + "rotation": "accessor_93", + "scale": "accessor_91", + "translation": "accessor_92" + }, + "samplers": { + "animation_23_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_23_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_23_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_24": { + "channels": [ + { + "sampler": "animation_24_scale_sampler", + "target": { + "id": "Bip01_R_Finger0-node", + "path": "scale" + } + }, + { + "sampler": "animation_24_translation_sampler", + "target": { + "id": "Bip01_R_Finger0-node", + "path": "translation" + } + }, + { + "sampler": "animation_24_rotation_sampler", + "target": { + "id": "Bip01_R_Finger0-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_94", + "rotation": "accessor_97", + "scale": "accessor_95", + "translation": "accessor_96" + }, + "samplers": { + "animation_24_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_24_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_24_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_25": { + "channels": [ + { + "sampler": "animation_25_scale_sampler", + "target": { + "id": "Bip01_R_Finger0Nub-node", + "path": "scale" + } + }, + { + "sampler": "animation_25_translation_sampler", + "target": { + "id": "Bip01_R_Finger0Nub-node", + "path": "translation" + } + }, + { + "sampler": "animation_25_rotation_sampler", + "target": { + "id": "Bip01_R_Finger0Nub-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_98", + "rotation": "accessor_101", + "scale": "accessor_99", + "translation": "accessor_100" + }, + "samplers": { + "animation_25_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_25_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_25_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_26": { + "channels": [ + { + "sampler": "animation_26_scale_sampler", + "target": { + "id": "Bip01_L_Thigh-node", + "path": "scale" + } + }, + { + "sampler": "animation_26_translation_sampler", + "target": { + "id": "Bip01_L_Thigh-node", + "path": "translation" + } + }, + { + "sampler": "animation_26_rotation_sampler", + "target": { + "id": "Bip01_L_Thigh-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_102", + "rotation": "accessor_105", + "scale": "accessor_103", + "translation": "accessor_104" + }, + "samplers": { + "animation_26_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_26_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_26_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_27": { + "channels": [ + { + "sampler": "animation_27_scale_sampler", + "target": { + "id": "Bip01_L_Calf-node", + "path": "scale" + } + }, + { + "sampler": "animation_27_translation_sampler", + "target": { + "id": "Bip01_L_Calf-node", + "path": "translation" + } + }, + { + "sampler": "animation_27_rotation_sampler", + "target": { + "id": "Bip01_L_Calf-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_106", + "rotation": "accessor_109", + "scale": "accessor_107", + "translation": "accessor_108" + }, + "samplers": { + "animation_27_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_27_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_27_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_28": { + "channels": [ + { + "sampler": "animation_28_scale_sampler", + "target": { + "id": "Bip01_L_Foot-node", + "path": "scale" + } + }, + { + "sampler": "animation_28_translation_sampler", + "target": { + "id": "Bip01_L_Foot-node", + "path": "translation" + } + }, + { + "sampler": "animation_28_rotation_sampler", + "target": { + "id": "Bip01_L_Foot-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_110", + "rotation": "accessor_113", + "scale": "accessor_111", + "translation": "accessor_112" + }, + "samplers": { + "animation_28_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_28_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_28_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_29": { + "channels": [ + { + "sampler": "animation_29_scale_sampler", + "target": { + "id": "Bip01_L_Toe0-node", + "path": "scale" + } + }, + { + "sampler": "animation_29_translation_sampler", + "target": { + "id": "Bip01_L_Toe0-node", + "path": "translation" + } + }, + { + "sampler": "animation_29_rotation_sampler", + "target": { + "id": "Bip01_L_Toe0-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_114", + "rotation": "accessor_117", + "scale": "accessor_115", + "translation": "accessor_116" + }, + "samplers": { + "animation_29_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_29_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_29_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_3": { + "channels": [ + { + "sampler": "animation_3_scale_sampler", + "target": { + "id": "Bip01_Neck-node", + "path": "scale" + } + }, + { + "sampler": "animation_3_translation_sampler", + "target": { + "id": "Bip01_Neck-node", + "path": "translation" + } + }, + { + "sampler": "animation_3_rotation_sampler", + "target": { + "id": "Bip01_Neck-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_14", + "rotation": "accessor_17", + "scale": "accessor_15", + "translation": "accessor_16" + }, + "samplers": { + "animation_3_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_3_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_3_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_30": { + "channels": [ + { + "sampler": "animation_30_scale_sampler", + "target": { + "id": "Bip01_Tail-node", + "path": "scale" + } + }, + { + "sampler": "animation_30_translation_sampler", + "target": { + "id": "Bip01_Tail-node", + "path": "translation" + } + }, + { + "sampler": "animation_30_rotation_sampler", + "target": { + "id": "Bip01_Tail-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_118", + "rotation": "accessor_121", + "scale": "accessor_119", + "translation": "accessor_120" + }, + "samplers": { + "animation_30_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_30_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_30_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_31": { + "channels": [ + { + "sampler": "animation_31_scale_sampler", + "target": { + "id": "Bip01_Tail1-node", + "path": "scale" + } + }, + { + "sampler": "animation_31_translation_sampler", + "target": { + "id": "Bip01_Tail1-node", + "path": "translation" + } + }, + { + "sampler": "animation_31_rotation_sampler", + "target": { + "id": "Bip01_Tail1-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_122", + "rotation": "accessor_125", + "scale": "accessor_123", + "translation": "accessor_124" + }, + "samplers": { + "animation_31_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_31_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_31_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_32": { + "channels": [ + { + "sampler": "animation_32_scale_sampler", + "target": { + "id": "Bip01_Tail2-node", + "path": "scale" + } + }, + { + "sampler": "animation_32_translation_sampler", + "target": { + "id": "Bip01_Tail2-node", + "path": "translation" + } + }, + { + "sampler": "animation_32_rotation_sampler", + "target": { + "id": "Bip01_Tail2-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_126", + "rotation": "accessor_129", + "scale": "accessor_127", + "translation": "accessor_128" + }, + "samplers": { + "animation_32_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_32_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_32_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_33": { + "channels": [ + { + "sampler": "animation_33_scale_sampler", + "target": { + "id": "Bip01_TailNub-node", + "path": "scale" + } + }, + { + "sampler": "animation_33_translation_sampler", + "target": { + "id": "Bip01_TailNub-node", + "path": "translation" + } + }, + { + "sampler": "animation_33_rotation_sampler", + "target": { + "id": "Bip01_TailNub-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_130", + "rotation": "accessor_133", + "scale": "accessor_131", + "translation": "accessor_132" + }, + "samplers": { + "animation_33_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_33_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_33_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_4": { + "channels": [ + { + "sampler": "animation_4_scale_sampler", + "target": { + "id": "Bip01_Head-node", + "path": "scale" + } + }, + { + "sampler": "animation_4_translation_sampler", + "target": { + "id": "Bip01_Head-node", + "path": "translation" + } + }, + { + "sampler": "animation_4_rotation_sampler", + "target": { + "id": "Bip01_Head-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_18", + "rotation": "accessor_21", + "scale": "accessor_19", + "translation": "accessor_20" + }, + "samplers": { + "animation_4_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_4_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_4_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_5": { + "channels": [ + { + "sampler": "animation_5_scale_sampler", + "target": { + "id": "Bip01_L_Clavicle-node", + "path": "scale" + } + }, + { + "sampler": "animation_5_translation_sampler", + "target": { + "id": "Bip01_L_Clavicle-node", + "path": "translation" + } + }, + { + "sampler": "animation_5_rotation_sampler", + "target": { + "id": "Bip01_L_Clavicle-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_22", + "rotation": "accessor_25", + "scale": "accessor_23", + "translation": "accessor_24" + }, + "samplers": { + "animation_5_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_5_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_5_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_6": { + "channels": [ + { + "sampler": "animation_6_scale_sampler", + "target": { + "id": "Bip01_L_UpperArm-node", + "path": "scale" + } + }, + { + "sampler": "animation_6_translation_sampler", + "target": { + "id": "Bip01_L_UpperArm-node", + "path": "translation" + } + }, + { + "sampler": "animation_6_rotation_sampler", + "target": { + "id": "Bip01_L_UpperArm-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_26", + "rotation": "accessor_29", + "scale": "accessor_27", + "translation": "accessor_28" + }, + "samplers": { + "animation_6_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_6_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_6_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_7": { + "channels": [ + { + "sampler": "animation_7_scale_sampler", + "target": { + "id": "Bip01_L_Forearm-node", + "path": "scale" + } + }, + { + "sampler": "animation_7_translation_sampler", + "target": { + "id": "Bip01_L_Forearm-node", + "path": "translation" + } + }, + { + "sampler": "animation_7_rotation_sampler", + "target": { + "id": "Bip01_L_Forearm-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_30", + "rotation": "accessor_33", + "scale": "accessor_31", + "translation": "accessor_32" + }, + "samplers": { + "animation_7_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_7_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_7_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_8": { + "channels": [ + { + "sampler": "animation_8_scale_sampler", + "target": { + "id": "Bip01_L_Hand-node", + "path": "scale" + } + }, + { + "sampler": "animation_8_translation_sampler", + "target": { + "id": "Bip01_L_Hand-node", + "path": "translation" + } + }, + { + "sampler": "animation_8_rotation_sampler", + "target": { + "id": "Bip01_L_Hand-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_34", + "rotation": "accessor_37", + "scale": "accessor_35", + "translation": "accessor_36" + }, + "samplers": { + "animation_8_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_8_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_8_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + }, + "animation_9": { + "channels": [ + { + "sampler": "animation_9_scale_sampler", + "target": { + "id": "Bip01_L_Finger0-node", + "path": "scale" + } + }, + { + "sampler": "animation_9_translation_sampler", + "target": { + "id": "Bip01_L_Finger0-node", + "path": "translation" + } + }, + { + "sampler": "animation_9_rotation_sampler", + "target": { + "id": "Bip01_L_Finger0-node", + "path": "rotation" + } + } + ], + "count": 101, + "parameters": { + "TIME": "accessor_38", + "rotation": "accessor_41", + "scale": "accessor_39", + "translation": "accessor_40" + }, + "samplers": { + "animation_9_rotation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "rotation" + }, + "animation_9_scale_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "scale" + }, + "animation_9_translation_sampler": { + "input": "TIME", + "interpolation": "LINEAR", + "output": "translation" + } + } + } + }, + "asset": { + "generator": "collada2gltf@fb6d6e65e66c3b0013c75a8d1e63650f4309bf77" + }, + "bufferViews": { + "bufferView_183": { + "buffer": "monster", + "byteLength": 52352, + "byteOffset": 0, + "target": 34962 + }, + "bufferView_184": { + "buffer": "monster", + "byteLength": 5304, + "byteOffset": 52352, + "target": 34963 + }, + "bufferView_185": { + "buffer": "monster", + "byteLength": 145724, + "byteOffset": 57656 + } + }, + "buffers": { + "monster": { + "byteLength": 203380, + "path": "monster.bin", + "type": "arraybuffer" + } + }, + "images": { + "image_0": { + "path": "monster.jpg" + } + }, + "materials": { + "monster-fx": { + "instanceTechnique": { + "technique": "technique1", + "values": { + "ambient": [ + 0.588235, + 0.588235, + 0.588235, + 1 + ], + "diffuse": "texture_image_0", + "shininess": 20, + "specular": [ + 0.1, + 0.1, + 0.1, + 1 + ] + } + }, + "name": "monster" + } + }, + "meshes": { + "monster-mesh-skin": { + "name": "monster", + "primitives": [ + { + "attributes": { + "JOINT": "attribute_179", + "NORMAL": "attribute_165", + "POSITION": "attribute_163", + "TEXCOORD_0": "attribute_167", + "WEIGHT": "attribute_176" + }, + "indices": "indices_161", + "material": "monster-fx", + "primitive": 4 + } + ] + } + }, + "nodes": { + "Bip01_Head-node": { + "children": [ + "Bip01_HeadNub-node" + ], + "jointId": "Bone5", + "name": "Bip01_Head", + "rotation": [ + 0.888952, + 0.414921, + 0.19392, + 0.0773304 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 181.732, + -8e-05, + -2e-06 + ] + }, + "Bip01_HeadNub-node": { + "children": [], + "jointId": "Bone6", + "name": "Bip01_HeadNub", + "rotation": [ + 1, + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 345.132, + 1.7e-05, + -7e-06 + ] + }, + "Bip01_L_Calf-node": { + "children": [ + "Bip01_L_Foot-node" + ], + "jointId": "Bone24", + "name": "Bip01_L_Calf", + "rotation": [ + 0, + 0, + -1, + 1.19016 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 547.819, + -5e-06, + -1.2e-05 + ] + }, + "Bip01_L_Clavicle-node": { + "children": [ + "Bip01_L_UpperArm-node" + ], + "jointId": "Bone7", + "name": "Bip01_L_Clavicle", + "rotation": [ + 0.821819, + 0.385425, + 0.419597, + 3.13687 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 7.49618, + 0.138573, + 42.5129 + ] + }, + "Bip01_L_Finger0-node": { + "children": [ + "Bip01_L_Finger0Nub-node" + ], + "jointId": "Bone11", + "name": "Bip01_L_Finger0", + "rotation": [ + 0.205207, + 0.494818, + -0.84442, + 0.520016 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 186.693, + -2.3e-05, + -9e-06 + ] + }, + "Bip01_L_Finger0Nub-node": { + "children": [], + "jointId": "Bone12", + "name": "Bip01_L_Finger0Nub", + "rotation": [ + 1, + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 167.761, + 1.9e-05, + 1.3e-05 + ] + }, + "Bip01_L_Foot-node": { + "children": [ + "Bip01_L_Toe0-node" + ], + "jointId": "Bone25", + "name": "Bip01_L_Foot", + "rotation": [ + 0.0131495, + -0.264758, + 0.964225, + 1.13836 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 532.748, + 5.1e-05, + -8e-06 + ] + }, + "Bip01_L_Forearm-node": { + "children": [ + "Bip01_L_Hand-node" + ], + "jointId": "Bone9", + "name": "Bip01_L_Forearm", + "rotation": [ + 0, + 0, + -1, + 1.07887 + ], + "scale": [ + 1, + 0.999999, + 1 + ], + "translation": [ + 370.017, + 2e-05, + 0 + ] + }, + "Bip01_L_Hand-node": { + "children": [ + "Bip01_L_Finger0-node" + ], + "jointId": "Bone10", + "name": "Bip01_L_Hand", + "rotation": [ + -0.468739, + 0.0882598, + 0.878916, + 0.630255 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 370.018, + -5.8e-05, + -4e-06 + ] + }, + "Bip01_L_Thigh-node": { + "children": [ + "Bip01_L_Calf-node" + ], + "jointId": "Bone23", + "name": "Bip01_L_Thigh", + "rotation": [ + 0.84539, + 0.505798, + 0.171709, + 3.10411 + ], + "scale": [ + 1, + 0.999999, + 1 + ], + "translation": [ + -115.92, + 125.412, + 124.981 + ] + }, + "Bip01_L_Toe0-node": { + "children": [ + "Bip01_L_Toe0Nub-node" + ], + "jointId": "Bone26", + "name": "Bip01_L_Toe0", + "rotation": [ + 0, + 0, + 1, + 1.5708 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 214.572, + 189.363, + 3e-05 + ] + }, + "Bip01_L_Toe0Nub-node": { + "children": [], + "jointId": "Bone27", + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + -1, + 0, + 166.968, + 0, + 8e-06, + 1 + ], + "name": "Bip01_L_Toe0Nub" + }, + "Bip01_L_UpperArm-node": { + "children": [ + "Bip01_L_Forearm-node" + ], + "jointId": "Bone8", + "name": "Bip01_L_UpperArm", + "rotation": [ + 0.977167, + -0.155223, + 0.14509, + 3.72504 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 175.071, + 7.1e-05, + 7e-06 + ] + }, + "Bip01_Neck-node": { + "children": [ + "Bip01_Head-node", + "Bip01_L_Clavicle-node", + "Bip01_R_Clavicle-node" + ], + "jointId": "Bone4", + "name": "Bip01_Neck", + "rotation": [ + -0.0848005, + -0.239893, + -0.967088, + 0.699229 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 402.56, + -0.112731, + -1.7e-05 + ] + }, + "Bip01_Pelvis-node": { + "children": [ + "Bip01_Spine-node" + ], + "jointId": "Bone1", + "name": "Bip01_Pelvis", + "rotation": [ + -0.959472, + -0.199266, + -0.199266, + 1.61216 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 524.412, + -3.8e-05, + 898.736 + ] + }, + "Bip01_R_Calf-node": { + "children": [ + "Bip01_R_Foot-node" + ], + "jointId": "Bone29", + "name": "Bip01_R_Calf", + "rotation": [ + 0, + 0, + -1, + 1.52677 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 547.819, + 3.7e-05, + -1.5e-05 + ] + }, + "Bip01_R_Clavicle-node": { + "children": [ + "Bip01_R_UpperArm-node" + ], + "jointId": "Bone13", + "name": "Bip01_R_Clavicle", + "rotation": [ + -0.404858, + -0.236589, + 0.883241, + 3.63415 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + -7.49618, + 0.150702, + -42.5129 + ] + }, + "Bip01_R_Finger0-node": { + "children": [ + "Bip01_R_Finger0Nub-node" + ], + "jointId": "Bone17", + "name": "Bip01_R_Finger0", + "rotation": [ + -0.100383, + -0.36566, + -0.925319, + 0.743378 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 186.693, + 5.6e-05, + 3e-06 + ] + }, + "Bip01_R_Finger0Nub-node": { + "children": [], + "jointId": "Bone18", + "name": "Bip01_R_Finger0Nub", + "rotation": [ + -0, + -0, + 1, + 3.14159 + ], + "scale": [ + -1, + -1, + -1 + ], + "translation": [ + 167.761, + -5e-06, + -4.4e-05 + ] + }, + "Bip01_R_Foot-node": { + "children": [ + "Bip01_R_Toe0-node" + ], + "jointId": "Bone30", + "name": "Bip01_R_Foot", + "rotation": [ + 0.725694, + 0.362838, + 0.584565, + 0.354579 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 532.748, + 4.1e-05, + -2e-06 + ] + }, + "Bip01_R_Forearm-node": { + "children": [ + "Bip01_R_Hand-node" + ], + "jointId": "Bone15", + "name": "Bip01_R_Forearm", + "rotation": [ + 0, + 0, + -1, + 1.45794 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 370.017, + 2.9e-05, + 3e-06 + ] + }, + "Bip01_R_Hand-node": { + "children": [ + "Bip01_R_Finger0-node" + ], + "jointId": "Bone16", + "name": "Bip01_R_Hand", + "rotation": [ + 0.983491, + -0.166018, + -0.0719935, + 0.544696 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 370.017, + -2e-06, + 6.4e-05 + ] + }, + "Bip01_R_Thigh-node": { + "children": [ + "Bip01_R_Calf-node" + ], + "jointId": "Bone28", + "name": "Bip01_R_Thigh", + "rotation": [ + 0.99724, + -0.00466794, + -0.0741011, + 3.2334 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + -64.1862, + 86.8699, + -181.92 + ] + }, + "Bip01_R_Toe0-node": { + "children": [ + "Bip01_R_Toe0Nub-node" + ], + "jointId": "Bone31", + "name": "Bip01_R_Toe0", + "rotation": [ + 0.0298918, + 0.0389551, + 0.998794, + 1.47183 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 214.572, + 189.364, + -0 + ] + }, + "Bip01_R_Toe0Nub-node": { + "children": [], + "jointId": "Bone32", + "name": "Bip01_R_Toe0Nub", + "rotation": [ + 1, + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 166.968, + 4e-06, + -3.3e-05 + ] + }, + "Bip01_R_UpperArm-node": { + "children": [ + "Bip01_R_Forearm-node" + ], + "jointId": "Bone14", + "name": "Bip01_R_UpperArm", + "rotation": [ + 0.747885, + -0.309236, + -0.587401, + 1.24893 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 175.071, + -0.000136, + 1.5e-05 + ] + }, + "Bip01_Spine-node": { + "children": [ + "Bip01_Spine1-node", + "Bip01_R_Thigh-node", + "Bip01_L_Thigh-node", + "Bip01_Tail-node" + ], + "jointId": "Bone2", + "name": "Bip01_Spine", + "rotation": [ + 0.0503303, + 0.236422, + 0.970346, + 0.884125 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 142.077, + -0.208117, + -0.038858 + ] + }, + "Bip01_Spine1-node": { + "children": [ + "Bip01_Neck-node", + "Bone01-node", + "Bone03-node" + ], + "jointId": "Bone3", + "name": "Bip01_Spine1", + "rotation": [ + 0.339141, + 0.634452, + 0.694589, + 0.318861 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 399.228, + -0.310882, + -0.041171 + ] + }, + "Bip01_Tail-node": { + "children": [ + "Bip01_Tail1-node" + ], + "jointId": "Bone33", + "name": "Bip01_Tail", + "rotation": [ + -0.0332696, + 0.042712, + 0.998533, + 2.87002 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + -217.547, + 76.1463, + -46.1944 + ] + }, + "Bip01_Tail1-node": { + "children": [ + "Bip01_Tail2-node" + ], + "jointId": "Bone34", + "name": "Bip01_Tail1", + "rotation": [ + 0.0281731, + -0.223504, + 0.974296, + 0.258944 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 275.134, + -0.261342, + 0 + ] + }, + "Bip01_Tail2-node": { + "children": [ + "Bip01_TailNub-node" + ], + "jointId": "Bone35", + "name": "Bip01_Tail2", + "rotation": [ + 0, + -0, + 1, + 0.226614 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 339.089, + -0.290226, + -7e-06 + ] + }, + "Bip01_TailNub-node": { + "children": [], + "jointId": "Bone36", + "name": "Bip01_TailNub", + "rotation": [ + 0.707388, + 0.706825, + 7.06825e-07, + 3.14159 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 374.193, + -3.8e-05, + -3e-06 + ] + }, + "Bone01-node": { + "children": [ + "Bone02-node" + ], + "jointId": "Bone21", + "name": "Bone01", + "rotation": [ + 0.998254, + -0.012858, + -0.0576427, + 3.00283 + ], + "scale": [ + 0.993689, + 1.09369, + 9.75437 + ], + "translation": [ + 179.053, + 1051.53, + -104.193 + ] + }, + "Bone02-node": { + "children": [], + "jointId": "Bone22", + "name": "Bone02", + "rotation": [ + 1, + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 17.8219, + 1.4e-05, + -2e-06 + ] + }, + "Bone03-node": { + "children": [ + "Bone04-node" + ], + "jointId": "Bone19", + "name": "Bone03", + "rotation": [ + 0.998968, + -0.0442843, + 0.0101098, + 3.11183 + ], + "scale": [ + 0.657025, + 0.613551, + 7.3972 + ], + "translation": [ + 164.819, + 1034.98, + -108.759 + ] + }, + "Bone04-node": { + "children": [], + "jointId": "Bone20", + "name": "Bone04", + "rotation": [ + 1, + 0, + 0, + 0 + ], + "scale": [ + 1, + 1, + 1 + ], + "translation": [ + 17.8219, + 7e-06, + -2e-06 + ] + }, + "monster-node": { + "children": [], + "instanceSkin": { + "skeletons": [ + "Bip01_Pelvis-node" + ], + "skin": "monster-mesh-skin-skin", + "sources": [ + "monster-mesh-skin" + ] + }, + "matrix": [ + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1, + 0, + 0, + 0, + 0, + 1 + ], + "name": "monster" + } + }, + "profile": "WebGL 1.0.2", + "programs": { + "program_0": { + "attributes": [ + "a_joint", + "a_normal", + "a_position", + "a_texcoord0", + "a_weight" + ], + "fragmentShader": "monster0FS", + "vertexShader": "monster0VS" + } + }, + "samplers": { + "sampler_0": { + "magFilter": 9729, + "minFilter": 9729, + "wrapS": 10497, + "wrapT": 10497 + } + }, + "scene": "defaultScene", + "scenes": { + "defaultScene": { + "nodes": [ + "Bip01_Pelvis-node", + "monster-node" + ] + } + }, + "shaders": { + "monster0FS": { + "path": "monster0FS.glsl" + }, + "monster0VS": { + "path": "monster0VS.glsl" + } + }, + "skins": { + "monster-mesh-skin-skin": { + "bindShapeMatrix": [ + 0, + -0.804999, + 0.172274, + 0, + 0, + 0.172274, + 0.804999, + 0, + -0.823226, + 0, + 0, + 0, + -127.093, + -393.418, + 597.2, + 1 + ], + "inverseBindMatrices": { + "bufferView": "bufferView_185", + "byteOffset": 0, + "count": 36, + "type": 35676 + }, + "joints": [ + "Bone1", + "Bone2", + "Bone3", + "Bone4", + "Bone5", + "Bone6", + "Bone7", + "Bone8", + "Bone9", + "Bone10", + "Bone11", + "Bone12", + "Bone13", + "Bone14", + "Bone15", + "Bone16", + "Bone17", + "Bone18", + "Bone19", + "Bone20", + "Bone21", + "Bone22", + "Bone23", + "Bone24", + "Bone25", + "Bone26", + "Bone27", + "Bone28", + "Bone29", + "Bone30", + "Bone31", + "Bone32", + "Bone33", + "Bone34", + "Bone35", + "Bone36" + ] + } + }, + "techniques": { + "technique1": { + "parameters": { + "ambient": { + "type": 35666 + }, + "diffuse": { + "type": 35678 + }, + "joint": { + "semantic": "JOINT", + "type": 35666 + }, + "jointMat": { + "semantic": "JOINT_MATRIX", + "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 + }, + "texcoord0": { + "semantic": "TEXCOORD_0", + "type": 35664 + }, + "weight": { + "semantic": "WEIGHT", + "type": 35666 + } + }, + "pass": "defaultPass", + "passes": { + "defaultPass": { + "details": { + "commonProfile": { + "extras": { + "doubleSided": false + }, + "lightingModel": "Blinn", + "parameters": [ + "ambient", + "diffuse", + "jointMat", + "modelViewMatrix", + "normalMatrix", + "projectionMatrix", + "shininess", + "specular" + ], + "texcoordBindings": { + "diffuse": "TEXCOORD_0" + } + }, + "type": "COLLADA-1.4.1/commonProfile" + }, + "instanceProgram": { + "attributes": { + "a_joint": "joint", + "a_normal": "normal", + "a_position": "position", + "a_texcoord0": "texcoord0", + "a_weight": "weight" + }, + "program": "program_0", + "uniforms": { + "u_ambient": "ambient", + "u_diffuse": "diffuse", + "u_jointMat": "jointMat", + "u_modelViewMatrix": "modelViewMatrix", + "u_normalMatrix": "normalMatrix", + "u_projectionMatrix": "projectionMatrix", + "u_shininess": "shininess", + "u_specular": "specular" + } + }, + "states": { + "blendEnable": 0, + "cullFaceEnable": 1, + "depthMask": 1, + "depthTestEnable": 1 + } + } + } + } + }, + "textures": { + "texture_image_0": { + "format": 6408, + "internalFormat": 6408, + "sampler": "sampler_0", + "source": "image_0", + "target": 3553 + } + } +} \ No newline at end of file diff --git a/example/web_gl_loader_gltf/models/gltf/monster/monster0FS.glsl b/example/web_gl_loader_gltf/models/gltf/monster/monster0FS.glsl new file mode 100644 index 00000000..b2e54543 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/monster/monster0FS.glsl @@ -0,0 +1,23 @@ +precision highp float; +varying vec3 v_normal; +varying vec4 v_joint; +varying vec4 v_weight; +uniform float u_shininess; +uniform vec4 u_ambient; +varying vec2 v_texcoord0; +uniform sampler2D u_diffuse; +uniform vec4 u_specular; +void main(void) { +vec3 normal = normalize(v_normal); +vec4 color = vec4(0., 0., 0., 0.); +vec4 diffuse = vec4(0., 0., 0., 1.); +vec4 ambient; +vec4 specular; +ambient = u_ambient; +diffuse = texture2D(u_diffuse, v_texcoord0); +specular = u_specular; +diffuse.xyz *= max(dot(normal,vec3(0.,0.,1.)), 0.); +color.xyz += diffuse.xyz; +color = vec4(color.rgb * diffuse.a, diffuse.a); +gl_FragColor = color; +} diff --git a/example/web_gl_loader_gltf/models/gltf/monster/monster0VS.glsl b/example/web_gl_loader_gltf/models/gltf/monster/monster0VS.glsl new file mode 100644 index 00000000..35c23d76 --- /dev/null +++ b/example/web_gl_loader_gltf/models/gltf/monster/monster0VS.glsl @@ -0,0 +1,24 @@ +precision highp float; +attribute vec3 a_position; +attribute vec3 a_normal; +varying vec3 v_normal; +attribute vec4 a_joint; +varying vec4 v_joint; +attribute vec4 a_weight; +varying vec4 v_weight; +uniform mat4 u_jointMat[60]; +uniform mat3 u_normalMatrix; +uniform mat4 u_modelViewMatrix; +uniform mat4 u_projectionMatrix; +attribute vec2 a_texcoord0; +varying vec2 v_texcoord0; +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 = normalize(u_normalMatrix * mat3(skinMat)* a_normal); +v_texcoord0 = a_texcoord0; +gl_Position = u_projectionMatrix * pos; +} diff --git a/example/web_gl_loader_gltf/web_gl_loader_gltf.dart b/example/web_gl_loader_gltf/web_gl_loader_gltf.dart new file mode 100644 index 00000000..62d3e071 --- /dev/null +++ b/example/web_gl_loader_gltf/web_gl_loader_gltf.dart @@ -0,0 +1,362 @@ +import 'dart:html' hide Animation; +import 'dart:math' as Math; +import 'package:vector_math/vector_math.dart' hide Ray; +import 'package:three/three.dart' as THREE; +import 'package:three/loaders/gltf.dart'; +import 'package:three/extras/controls/trackball_controls.dart'; + +var loader; +Element container; +THREE.Scene scene; +THREE.Camera camera; +THREE.WebGLRenderer renderer; + +var cameraIndex = 0; +var cameras = []; +var cameraNames = []; +THREE.Camera defaultCamera = null; +var gltf = null; + +TrackballControls controls; + +main() { + + document.querySelector("#scenes_list").onChange.listen(selectScene); + document.querySelector("#cameras_list").onChange.listen(selectCamera); + document.querySelector("#toggle_animations").onClick.listen(toggleAnimations); + document.querySelector("#buffer_geometry_checkbox").onClick.listen(toggleBufferGeometry); + + window.onResize.listen(_onWindowResize); + document.onKeyPress.listen(_onKeyPress); + buildSceneList(); + switchScene(0); + _animate(0); +} + +initScene(index) { + container = document.getElementById( 'container' ); + + scene = new THREE.Scene(); + + defaultCamera = new THREE.PerspectiveCamera( 45.0, container.offsetWidth / container.offsetHeight, 1.0, 20000.0 ); + + //defaultCamera.up = new THREE.Vector3( 0, 1, 0 ); + scene.add( defaultCamera ); + camera = defaultCamera; + + var sceneInfo = sceneList[index]; + + var spot1 = null; + if (sceneInfo["addLights"]) { + + var ambient = new THREE.AmbientLight( 0x888888 ); + scene.add( ambient ); + + var directionalLight = new THREE.DirectionalLight( 0xdddddd ); + directionalLight.position.setValues( 0.0, -1.0, 1.0 ).normalize(); + scene.add( directionalLight ); + + spot1 = new THREE.SpotLight( 0xffffff, 1.0 ); + spot1.position.setValues( -100.0, 200.0, 100.0 ); + spot1.target.position.setValues( 0.0, 0.0, 0.0 ); + + if (sceneInfo["shadows"]) { + + spot1.shadowCameraNear = 1.0; + spot1.shadowCameraFar = 1024.0; + spot1.castShadow = true; + spot1.shadowDarkness = 0.3; + spot1.shadowBias = 0.0001; + spot1.shadowMapWidth = 2048; + spot1.shadowMapHeight = 2048; + } + scene.add( spot1 ); + } + + // RENDERER + + renderer = new THREE.WebGLRenderer(antialias:true); + renderer.setSize( container.offsetWidth, container.offsetHeight ); + + if (sceneInfo["shadows"] != null) { + renderer.shadowMapEnabled = true; + //TODO(nfgs) - renderer.shadowMapSoft = true; + renderer.shadowMapType = THREE.PCFSoftShadowMap; + } + + container.append( renderer.domElement ); + + var ground = null; + if (sceneInfo["addGround"]) { + var groundMaterial = new THREE.MeshPhongMaterial( + color: 0xFFFFFF, + ambient: 0x888888, + shading: THREE.SmoothShading + ); + ground = new THREE.Mesh( new THREE.PlaneGeometry(1024.0, 1024.0), groundMaterial); + + if (sceneInfo["shadows"]) { + ground.receiveShadow = true; + } + + if (sceneInfo["groundPos"] != null) { + ground.position.copyFromArray(sceneInfo.groundPos); + } else { + ground.position.z = -70.0; + } + ground.rotation.x = -Math.PI / 2; + + scene.add(ground); + } + + GLTFLoader.useBufferGeometry = useBufferGeometry; + var loadStartTime = new DateTime.now().millisecondsSinceEpoch; + var status = document.getElementById("status"); + status.innerHtml = "Loading..."; + + loader = new GLTFLoader() + ..load( sceneInfo["url"]).then((result) { + + gltf = result; + + var object = gltf.scene; + + var loadEndTime = new DateTime.now().millisecondsSinceEpoch; + + var loadTime = (loadEndTime - loadStartTime) / 1000; + + status.innerHtml = "Load time: " + loadTime.toStringAsFixed(2) + " seconds."; + + if (sceneInfo["cameraPos"] != null) + defaultCamera.position.setFrom(sceneInfo["cameraPos"]); + + if (sceneInfo["center"] != null) { + controls.center.setFrom(sceneInfo["center"]); + } + + if (sceneInfo["objectPosition"] != null) { + object.position.setFrom(sceneInfo["objectPosition"]); + + if (spot1 != null) { + spot1.position.setValues(sceneInfo["objectPosition"].x - 100, sceneInfo["objectPosition"].y + 200, sceneInfo["objectPosition"].z - 100 ); + spot1.target.position.setFrom(sceneInfo["objectPosition"]); + } + } + + if (sceneInfo["objectRotation"] != null) + object.rotation.setFrom(sceneInfo["objectRotation"]); + + if (sceneInfo["objectScale"] != null) + object.scale.setFrom(sceneInfo["objectScale"]); + + cameraIndex = 0; + cameras = []; + cameraNames = []; + if (gltf.cameras != null && gltf.cameras.isNotEmpty) { + var i, len = gltf.cameras.length; + for (i = 0; i < len; i++) { + var addCamera = true; + var cameraName = gltf.cameras[i].parent.name; + if (sceneInfo["cameras"] != null && !(sceneInfo["cameras"].containsKey(cameraName))) { + addCamera = false; + } + + if (addCamera) { + cameraNames.add(cameraName); + cameras.add(gltf.cameras[i]); + } + } + + updateCamerasList(); + switchCamera(1); + } else { + updateCamerasList(); + switchCamera(0); + } + + gltf.animations.forEach((GLTFAnimation animation) { + animation.loop = true; + // There's .3333 seconds junk at the tail of the Monster animation that + // keeps it from looping cleanly. Clip it at 3 seconds + if (sceneInfo["animationTime"] != null) + animation.duration = sceneInfo["animationTime"]; + animation.play(); + }); + + scene.add( object ); + _onWindowResize(null); + + }); + + controls = new TrackballControls(defaultCamera, renderer.domElement); +} + +_onWindowResize(_) { + + defaultCamera.aspect = container.offsetWidth / container.offsetHeight; + defaultCamera.updateProjectionMatrix(); + + var i, len = cameras.length; + for (i = 0; i < len; i++) { + cameras[i].aspect = container.offsetWidth / container.offsetHeight; + cameras[i].updateProjectionMatrix(); + } + + renderer.setSize( container.offsetWidth, container.offsetHeight ); + +} + +_animate(num time) { + window.requestAnimationFrame( _animate ); + glTFAnimator.update(); + if (cameraIndex == 0) + controls.update(); + render(); +} + +render() { + renderer.render( scene, camera ); +} + +_onKeyPress(KeyboardEvent event) { + if (event.keyCode == KeyCode.SPACE) { + var index = cameraIndex + 1; + if (index > cameras.length) + index = 0; + switchCamera(index); + } else { + var index = event.keyCode - KeyCode.ZERO; + if (index <= cameras.length) { + switchCamera(index); + } + } +} + +var sceneList = [ + { "name" : "Duck", "url" : "./models/gltf/duck/duck.json", + "cameraPos": new Vector3(0.0, 30.0, -50.0), + "objectScale": new Vector3(0.1, 0.1, 0.1), + "addLights": true, + "addGround": true, + "shadows": true }, + { "name" : "Monster", "url" : "./models/gltf/monster/monster.json", + "cameraPos": new Vector3(30.0, 10.0, 70.0), + "objectScale": new Vector3(0.01, 0.01, 0.01), + "objectPosition": new Vector3(0.0, 1.0, 0.0), + "objectRotation": new Quaternion.identity().setEuler(-Math.PI / 2, 0.0, -Math.PI / 2), + "animationTime": 3, + "addLights": true, + "shadows": true, + "addGround": true }, +]; + +buildSceneList() { + var elt = document.getElementById('scenes_list'); + while( elt.children.isNotEmpty ){ + elt.children.removeLast(); + } + + var i, len = sceneList.length; + for (i = 0; i < len; i++) { + var option = document.createElement("option"); + option.text=sceneList[i]["name"]; + elt.children.add(option); + } +} + +switchScene(index) { + cleanup(); + initScene(index); + var elt = document.getElementById('scenes_list'); + elt.selectedIndex = index; +} + +selectScene([_]) { + var select = document.getElementById("scenes_list"); + var index = select.selectedIndex; + if (index >= 0) { + switchScene(index); + } +} + +switchCamera(index) { + cameraIndex = index; + + if (cameraIndex == 0) { + camera = defaultCamera; + } + if (cameraIndex >= 1 && cameraIndex <= cameras.length) { + camera = cameras[cameraIndex - 1]; + } + + var elt = document.getElementById('cameras_list'); + elt.selectedIndex = cameraIndex; +} + +updateCamerasList() { + var elt = document.getElementById('cameras_list'); + while( elt.children.isNotEmpty ){ + elt.children.removeLast(); + } + + var option = document.createElement("option"); + option.text="[default]"; + elt.children.add(option); + + var i, len = cameraNames.length; + for (i = 0; i < len; i++) { + option = document.createElement("option"); + option.text=cameraNames[i]; + elt.children.add(option); + } +} + +selectCamera([_]) { + var select = document.getElementById("cameras_list"); + var index = select.selectedIndex; + if (index >= 0) { + switchCamera(index); + } +} + +toggleAnimations([_]) { + var i, len = gltf.animations.length; + for (i = 0; i < len; i++) { + var animation = gltf.animations[i]; + if (animation.running) { + animation.stop(); + } else { + animation.play(); + } + } +} + +InputElement usebuf = document.getElementById("buffer_geometry_checkbox"); +var useBufferGeometry = usebuf.checked; +toggleBufferGeometry([_]) { + useBufferGeometry = !useBufferGeometry; + selectScene(); +} + +cleanup() { + + if (container != null && renderer != null) { + container.children.remove(renderer.domElement); + } + + cameraIndex = 0; + cameras = []; + cameraNames = []; + defaultCamera = null; + + if (gltf == null || gltf.animations == null) + return; + + var i, len = gltf.animations.length; + for (i = 0; i < len; i++) { + var animation = gltf.animations[i]; + if (animation.running) { + animation.stop(); + } + } +} \ No newline at end of file diff --git a/example/web_gl_loader_gltf/web_gl_loader_gltf.html b/example/web_gl_loader_gltf/web_gl_loader_gltf.html new file mode 100644 index 00000000..35c75327 --- /dev/null +++ b/example/web_gl_loader_gltf/web_gl_loader_gltf.html @@ -0,0 +1,134 @@ + + + + WebGL Loader - glTF + + + + + + +
+ three.js - + glTF loader - +
+ monster by 3drt - + COLLADA duck by Sony +
+
+
+
+
+
+
+ Model + +
+ +
+ Camera + +
+
+ Animations +
Play
+
+
+ Geometry +
+ BufferGeometry +
+
+
+ + + + diff --git a/lib/loaders/gltf.dart b/lib/loaders/gltf.dart new file mode 100644 index 00000000..be3fbf48 --- /dev/null +++ b/lib/loaders/gltf.dart @@ -0,0 +1,1511 @@ +/** + * r66 + */ + +library gltf_loader; + +import 'dart:async' show Completer, Future; +import 'dart:math' as Math; +import 'dart:html'; +import 'dart:typed_data'; +import 'dart:web_gl' as gl; + +import 'package:three/three.dart' as THREE; +import 'package:vector_math/vector_math.dart'; +import 'package:three/extras/image_utils.dart' as ImageUtils; + +import 'gltf/gltf_loader_utils.dart' as GLTFLoaderUtils; +import 'gltf/gltf_parser.dart'; + +part 'gltf/gltf_animation.dart'; + +// Utilities +log(msg) => print(msg); + +rgbArraytoHex(colorArray) { + if (colorArray == null) return 0xFFFFFFFF; + var r = (colorArray[0] * 255).floor(), g = (colorArray[1] * 255).floor(), b = (colorArray[2] * 255).floor(), a = 255; + + var color = (a << 24) + (r << 16) + (g << 8) + b; + + return color; +} + +convertAxisAngleToQuaternion(rotations, count) { + var q = new Quaternion.identity(); + var axis = new Vector3.zero(); + var euler = new Vector3.zero(); + + var i; + for (i = 0; i < count; i++) { + axis.setValues( + rotations[i * 4].toDouble(), + rotations[i * 4 + 1].toDouble(), + rotations[i * 4 + 2].toDouble()).normalize(); + var angle = rotations[i * 4 + 3].toDouble(); + q.setAxisAngle(axis, angle); + rotations[i * 4] = q.x; + rotations[i * 4 + 1] = q.y; + rotations[i * 4 + 2] = q.z; + rotations[i * 4 + 3] = q.w; + } +} + +componentsPerElementForGLType(glType) { + switch (glType) { + case gl.FLOAT : + case gl.UNSIGNED_BYTE : + case gl.UNSIGNED_SHORT : + return 1; + case gl.FLOAT_VEC2 : + return 2; + case gl.FLOAT_VEC3 : + return 3; + case gl.FLOAT_VEC4 : + return 4; + case gl.FLOAT_MAT4 : + return 16; + default: + return null; + } +} + +loadTexture([src]) => (src == null) ? null : ImageUtils.loadTexture(src); + +class GLTFLoaderResult { + THREE.Object3D scene; + List cameras; + List animations; +} + +class GLTFLoader extends GLTFParser { // implements THREE.Loader + + THREE.Object3D rootObj; + + static bool useBufferGeometry = false; + int meshesRequested = 0, + meshesLoaded = 0; + List pendingMeshes = []; + int animationsRequested = 0, + animationsLoaded = 0; + List animations = []; + int shadersRequested = 0, + shadersLoaded = 0; + Map shaders = {}; + Resources resources; + + var cameras = []; // enumerable: true, writable: true, + var lights = []; // enumerable: true, writable: true, + var joints = {}; + var skeletons = {}; + Map> nodeAnimationChannels = {}; + + var indicesDelegate; + var vertexAttributeDelegate; + var animationParameterDelegate; + var inverseBindMatricesDelegate; + var shaderDelegate; + + GLTFLoader([bool showStatus = false]) : super(); + + Completer completer; + + Future load(url) { + + indicesDelegate = new IndicesDelegate(); + vertexAttributeDelegate = new VertexAttributeDelegate(); + animationParameterDelegate = new AnimationParameterDelegate(); + inverseBindMatricesDelegate = new InverseBindMatricesDelegate(); + shaderDelegate = new ShaderDelegate(this); + + // Loader + + var rootObj = new THREE.Object3D(); + + var self = this; + + resources = new Resources(); + cameras = []; + lights = []; + animations = []; + joints = {}; + skeletons = {}; + GLTFLoaderUtils.init(); + + initWithPath(url); + doLoad(new Context(rootObj,(obj) {}), null); + + this.rootObj = rootObj; + + completer = new Completer(); + + return completer.future; + } + + // Implement WebGLTFLoader handlers + + handleBuffer(entryID, Map description, userInfo) { + resources.setEntry(entryID, null, description); + description["type"] = "ArrayBuffer"; + return true; + } + + handleBufferView(entryID, Map description, userInfo) { + resources.setEntry(entryID, null, description); + + var buffer = resources.getEntry(description["buffer"]); + description["type"] = "ArrayBufferView"; + + var bufferViewEntry = resources.getEntry(entryID); + bufferViewEntry.buffer = buffer; + return true; + } + + handleVideo(entryID, Map description, userInfo) { + log("UNIMPLEMENTED: handleVideo"); + } + + handleShader(entryID, Map description, userInfo) { + resources.setEntry(entryID, null, description); + var shaderRequest = new GLTFLoaderUtils.Request( + id: entryID, + path: description["path"] + ); + + var shaderContext = new ShaderContext(entryID, description["path"]); + + shadersRequested++; + GLTFLoaderUtils.getFile(shaderRequest, shaderDelegate, shaderContext); + + return true; + } + + handleProgram(entryID, Map description, userInfo) { + resources.setEntry(entryID, null, description); + return true; + } + + handleTechnique(entryID, Map description, userInfo) { + resources.setEntry(entryID, null, description); + return true; + } + + createShaderMaterial(material) { + + var fragmentShader = shaders[material.params.fragmentShader]; + if (!fragmentShader) { + log("ERROR: Missing fragment shader definition: " + material.params.fragmentShader); + return new THREE.MeshPhongMaterial(); + } + + var vertexShader = shaders[material.params.vertexShader]; + if (!fragmentShader) { + log("ERROR: Missing vertex shader definition: " + material.params.vertexShader); + return new THREE.MeshPhongMaterial(); + } + + var uniforms = {}; + var shaderMaterial = new THREE.ShaderMaterial( + fragmentShader: fragmentShader, + vertexShader: vertexShader, + uniforms: uniforms + ); + + return threeJSPhongMaterialFactory(material.params); + } + + createShaderParams(materialId, values, params, instanceProgram) { + var program = resources.getEntry(instanceProgram.program); + + if (program) { + params.fragmentShader = program.description.fragmentShader; + params.vertexShader = program.description.vertexShader; + params.attributes = instanceProgram.attributes; + params.uniforms = instanceProgram.uniforms; + } + } + + threeJSBasicMaterialFactory(params) => new THREE.MeshBasicMaterial(); + threeJSPhongMaterialFactory(params) => new THREE.MeshPhongMaterial( + map: params["map"], color: params["color"], opacity: params["opacity"], transparent: params["transparent"], + shininess: params["shininess"], emissive: params["emissive"], specular: params["specular"]); + threeJSLambertMaterialFactory(params) => new THREE.MeshLambertMaterial(); + + threeJSMaterialFactory(materialId, ResourceEntry technique, Map values, Map params) { + + var materialFactory = threeJSPhongMaterialFactory; + var defaultPass = null; + if (technique != null && technique.description != null && technique.description["passes"] != null) + defaultPass = technique.description["passes"]["defaultPass"]; + + if (defaultPass != null) { + if (defaultPass["details"] != null && defaultPass["details"]["commonProfile"] != null) { + var profile = defaultPass["details"]["commonProfile"]; + if (profile != null) { + switch (profile["lightingModel"]) { + case 'Blinn' : + case 'Phong' : + materialFactory = threeJSPhongMaterialFactory; + break; + + case 'Lambert' : + materialFactory = threeJSLambertMaterialFactory; + break; + + default : + materialFactory = threeJSBasicMaterialFactory; + break; + } + + if (profile["extras"] != null && profile["extras"]["doubleSided"]) { + params["side"] = THREE.DoubleSide; + } + } + } else if (defaultPass["instanceProgram"] != null) { + + var instanceProgram = defaultPass["instanceProgram"]; + + createShaderParams(materialId, values, params, instanceProgram); + + var loadshaders = true; + + if (loadshaders) { + materialFactory = (params) => new Material(params); + } + } + } + + var texturePath = null; + var textureParams = null; + var diffuse = values["diffuse"]; + if (diffuse != null) { + var texture = diffuse; + if (texture != null) { + var textureEntry = resources.getEntry(texture); + if (textureEntry != null) { + + var imageEntry = resources.getEntry(textureEntry.description["source"]); + if (imageEntry != null) { + texturePath = imageEntry.description["path"]; + } + + var samplerEntry = resources.getEntry(textureEntry.description["sampler"]); + if (samplerEntry != null) { + textureParams = samplerEntry.description; + } + + } + } + } + + var texture = loadTexture(texturePath); + if (texture != null && textureParams != null) { + + if (textureParams["wrapS"] == gl.REPEAT) + texture.wrapS = THREE.RepeatWrapping; + + if (textureParams["wrapT"] == gl.REPEAT) + texture.wrapT = THREE.RepeatWrapping; + + if (textureParams["magFilter"] == gl.LINEAR) + texture.magFilter = THREE.LinearFilter; + +// if (textureParams.minFilter == "LINEAR") +// texture.minFilter = THREE.LinearFilter; + + params["map"] = texture; + } + + var envMapPath = null; + var envMapParams = null; + var reflective = values["reflective"]; + if (reflective != null) { + var texture = reflective; + if (texture != null) { + var textureEntry = resources.getEntry(texture); + if (textureEntry != null) { + + var imageEntry = resources.getEntry(textureEntry.description["source"]); + if (imageEntry != null) { + envMapPath = imageEntry.description["path"]; + } + + var samplerEntry = resources.getEntry(textureEntry.description["sampler"]); + if (samplerEntry != null) { + envMapParams = samplerEntry.description; + } + + } + } + } + + texture = loadTexture(envMapPath); + if (texture != null && envMapParams != null) { + + if (envMapParams["wrapS"] == gl.REPEAT) + texture.wrapS = THREE.RepeatWrapping; + + if (envMapParams["wrapT"] == gl.REPEAT) + texture.wrapT = THREE.RepeatWrapping; + + if (envMapParams["magFilter"] == gl.LINEAR) + texture.magFilter = THREE.LinearFilter; + +// if (envMapParams.minFilter == WebGLRenderingContext.LINEAR) +// texture.minFilter = THREE.LinearFilter; + + params["envMap"] = texture; + } + + var shininess = values["shininess"]; //TODO(nfgs) || values.shininesss; // N.B.: typo in converter! + + var diffuseColor = (texturePath == null) ? diffuse : null; + var opacity = 1.0; + if (values.containsKey("transparency")) { + var USE_A_ONE = true; // for now, hack because file format isn't telling us + opacity = USE_A_ONE ? values["transparency"] : (1.0 - values["transparency"]); + } + + // if (diffuseColor) diffuseColor = [0, 1, 0]; + + params["color"] = rgbArraytoHex(diffuseColor); + params["opacity"] = opacity; + params["transparent"] = opacity < 1.0; + // hack hack hack + if (texturePath != null && texturePath.toLowerCase().indexOf(".png") != -1) + params["transparent"] = true; + + if (shininess != null) { + params["shininess"] = shininess; + } + + if (values["ambient"] != null && values["ambient"] is String) { + params["ambient"] = rgbArraytoHex(values["ambient"]); + } + + if (values["emission"] != null) { + params["emissive"] = rgbArraytoHex(values["emission"]); + } + + if (values["specular"] != null) { + params["specular"] = rgbArraytoHex(values["specular"]); + } + + return materialFactory; + + } + + handleMaterial(entryID, Map description, userInfo) { + //this should be rewritten using the meta datas that actually create the shader. + //here we will infer what needs to be pass to Three.js by looking inside the technique parameters. + var technique = resources.getEntry(description["instanceTechnique"]["technique"]); + var materialParams = {}; + var values = description["instanceTechnique"]["values"]; + + var materialFactory = threeJSMaterialFactory(entryID, technique, values, materialParams); + + var material = materialFactory(materialParams); + + resources.setEntry(entryID, material, description); + + return true; + } + + handleMesh(entryID, Map description, userInfo) { + var mesh = new Mesh(); + resources.setEntry(entryID, mesh, description); + var primitivesDescription = description["primitives"]; + if (primitivesDescription == null) { + //FIXME: not implemented in delegate + log("MISSING_PRIMITIVES for mesh:"+ entryID); + return false; + } + + for (var i = 0 ; i < primitivesDescription.length ; i++) { + var primitiveDescription = primitivesDescription[i]; + + if (primitiveDescription["primitive"] == gl.TRIANGLES) { + + var geometry = new ClassicGeometry(useBufferGeometry); + var materialEntry = resources.getEntry(primitiveDescription["material"]); + + mesh.addPrimitive(geometry, materialEntry.object); + + var indices = resources.getEntry(primitiveDescription["indices"]); + var bufferEntry = resources.getEntry(indices.description["bufferView"]); + var indicesObject = new GLTFLoaderUtils.WrappedBufferView( + bufferView : bufferEntry, + byteOffset : indices.description["byteOffset"], + count : indices.description["count"], + id : indices.entryID, + type : indices.description["type"] + ); + + var indicesContext = new IndicesContext(indicesObject, geometry); + var alreadyProcessedIndices = GLTFLoaderUtils.getBuffer(indicesObject, indicesDelegate, indicesContext); + /*if(alreadyProcessedIndices) { + indicesDelegate.resourceAvailable(alreadyProcessedIndices, indicesContext); + }*/ + + // Load Vertex Attributes + primitiveDescription["attributes"].forEach( (semantic, attributeID) { + geometry.totalAttributes++; + + var attribute, bufferEntry; + var attributeEntry = resources.getEntry(attributeID); + if (attributeEntry == null) { + //let's just use an anonymous object for the attribute + attribute = description["attributes"][attributeID]; + attribute.id = attributeID; + resources.setEntry(attributeID, attribute, attribute); + + bufferEntry = resources.getEntry(attribute.bufferView); + attributeEntry = resources.getEntry(attributeID); + + } else { + attribute = attributeEntry.object; + attribute["id"] = attributeID; + bufferEntry = resources.getEntry(attribute["bufferView"]); + } + + var attributeObject = new GLTFLoaderUtils.WrappedBufferView( + bufferView : bufferEntry, + byteOffset : attribute["byteOffset"], + byteStride : attribute["byteStride"], + count : attribute["count"], + max : attribute["max"], + min : attribute["min"], + type : attribute["type"], + id : attributeID + ); + + var attribContext = new VertexAttributeContext(attributeObject, semantic, geometry); + + var alreadyProcessedAttribute = GLTFLoaderUtils.getBuffer(attributeObject, vertexAttributeDelegate, attribContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }); + } + } + return true; + } + + handleCamera(entryID, Map description, userInfo) { + var camera; + if (description["type"] == "perspective") { + var znear = description["perspective"]["znear"]; + var zfar = description["perspective"]["zfar"]; + var yfov = description["perspective"]["yfov"]; + var xfov = description["perspective"]["xfov"]; + var aspect_ratio = description["perspective"]["aspect_ratio"]; + + if (aspect_ratio == null) + aspect_ratio = 1.0; + + if (yfov == null) { + if (xfov) { + // According to COLLADA spec... + // aspect_ratio = xfov / yfov + yfov = xfov / aspect_ratio; + } + + } + + if (yfov != null) { + camera = new THREE.PerspectiveCamera(yfov.toDouble(), aspect_ratio.toDouble(), znear.toDouble(), zfar.toDouble()); + } + } else { + camera = new THREE.OrthographicCamera( window.innerWidth / - 2, window.innerWidth / 2, window.innerHeight / 2, window.innerHeight / - 2, znear, zfar ); + } + + if (camera != null) { + resources.setEntry(entryID, camera, description); + } + + return true; + } + + handleLight(entryID, Map description, userInfo) { + + var light = null; + var type = description["type"]; + if (type != null && description[type] != null) { + var lparams = description[type]; + var color = rgbArraytoHex(lparams["color"]); + + switch (type) { + case "directional" : + light = new THREE.DirectionalLight(color); + light.position.setValues(0.0, 0.0, 1.0); + break; + + case "point" : + light = new THREE.PointLight(color); + break; + + case "spot " : + light = new THREE.SpotLight(color); + light.position.setValues(0.0, 0.0, 1.0); + break; + + case "ambient" : + light = new THREE.AmbientLight(color); + break; + } + } + + if (light != null) { + resources.setEntry(entryID, light, description); + } + + return true; + } + + addPendingMesh(mesh, threeNode) { + pendingMeshes.add({ + "mesh": mesh, + "node": threeNode + }); + } + + handleNode(entryID, Map description, userInfo) { + + var threeNode = null; + if (description["jointId"] != null) { + threeNode = new THREE.Bone(null); + threeNode["jointId"] = description["jointId"]; + this.joints[description["jointId"]] = entryID; + } else { + threeNode = new THREE.Object3D(); + } + + threeNode.name = description["name"]; + + resources.setEntry(entryID, threeNode, description); + + var m = description["matrix"]; + if(m != null) { + // Convert to doubles + m = m.map((e) => e.toDouble()).toList(); + threeNode.applyMatrix(new Matrix4( + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15] + )); + threeNode.matrixAutoUpdate = false; + threeNode.matrixWorldNeedsUpdate = true; + } + else { + var t = description["translation"]; + var r = description["rotation"]; + var s = description["scale"]; + + var position = (t != null) ? new Vector3(t[0].toDouble(), t[1].toDouble(), t[2].toDouble()) : new Vector3.zero(); + if (r != null) { + convertAxisAngleToQuaternion(r, 1); + } + var rotation = (r != null) ? new Quaternion(r[0].toDouble(), r[1].toDouble(), r[2].toDouble(), r[3].toDouble()) : new Quaternion.identity(); + var scale = (s != null) ? new Vector3(s[0].toDouble(), s[1].toDouble(), s[2].toDouble()) : new Vector3.zero(); + + var matrix = THREE.compose( new Matrix4.identity(), position, rotation, scale); + + threeNode.matrixAutoUpdate = false; + threeNode.matrixWorldNeedsUpdate = true; + threeNode.applyMatrix(matrix); + } + + // Iterate through all node meshes and attach the appropriate objects + //FIXME: decision needs to be made between these 2 ways, probably meshes will be discarded. + var meshEntry; + if (description["mesh"] != null) { + meshEntry = resources.getEntry(description["mesh"]); + meshesRequested++; + meshEntry.object.onComplete((mesh) { + addPendingMesh(mesh, threeNode); + meshesLoaded++; + checkComplete(); + }); + } + + if (description["meshes"] != null) { + description["meshes"].forEach( (meshID) { + meshEntry = resources.getEntry(meshID); + meshesRequested++; + meshEntry.object.onComplete((mesh) { + addPendingMesh(mesh, threeNode); + meshesLoaded++; + checkComplete(); + }); + }); + } + + if (description["instanceSkin"] != null) { + + var skinEntry = resources.getEntry(description["instanceSkin"]["skin"]); + + if (skinEntry != null) { + + var skin = skinEntry.object; + description["instanceSkin"]["skin"] = skin; + threeNode["instanceSkin"] = description["instanceSkin"]; + + var sources = description["instanceSkin"]["sources"]; + skin["meshes"] = []; + sources.forEach( (meshID) { + meshEntry = resources.getEntry(meshID); + meshesRequested++; + meshEntry.object.onComplete((mesh) { + + skin["meshes"].add(mesh); + meshesLoaded++; + checkComplete(); + }); + }); + + } + } + + if (description["camera"] != null) { + var cameraEntry = resources.getEntry(description["camera"]); + if (cameraEntry != null) { + threeNode.add(cameraEntry.object); + cameras.add(cameraEntry.object); + } + } + + if (description["light"] != null) { + var lightEntry = resources.getEntry(description["light"]); + if (lightEntry != null) { + threeNode.add(lightEntry.object); + lights.add(lightEntry.object); + } + } + + return true; + } + + buildNodeHirerachy(nodeEntryId, parentThreeNode) { + var nodeEntry = resources.getEntry(nodeEntryId); + var threeNode = nodeEntry.object; + parentThreeNode.add(threeNode); + + var children = nodeEntry.description["children"]; + if (children != null) { + children.forEach((childID) { + buildNodeHirerachy(childID, threeNode); + }); + } + + return threeNode; + } + + buildSkin(node) { + + var skin = node["instanceSkin"]["skin"]; + if (skin == null) { return; } + node["instanceSkin"]["skeletons"].forEach((skeleton) { + var nodeEntry = resources.getEntry(skeleton); + if (nodeEntry != null) { + + var rootSkeleton = nodeEntry.object; + + var dobones = true; + + skin["meshes"].forEach((Mesh mesh) { + var threeMesh = null; + mesh.primitives.forEach((primitive) { + + var material = primitive["material"]; + if (material is! THREE.Material) { + material = createShaderMaterial(material); + } + + threeMesh = new THREE.SkinnedMesh(primitive["geometry"].geometry, material, useVertexTexture: false); + threeMesh.add(rootSkeleton); + + var geometry = primitive["geometry"].geometry; + var j; + if ( geometry is! THREE.BufferGeometry) { + geometry.vertices.forEach((v) { + skin["bindShapeMatrix"].transform3(v); + }); + } else if (geometry.attributes[THREE.GeometryAttribute.POSITION]!= null) { + var a = geometry.attributes[THREE.GeometryAttribute.POSITION].array; + var v = new Vector3.zero(); + for ( j = 0; j < a.length / 3; j++ ) { + v.setValues(a[j * 3], a[j * 3 + 1], a[j * 3 + 2]); + skin["bindShapeMatrix"].transform3(v); + a[j * 3] = v.x; + a[j * 3 + 1] = v.y; + a[j * 3 + 2] = v.z; + } + } + + if (threeMesh != null && dobones) { + + material.skinning = true; + + threeMesh["boneInverses"] = []; + var jointsIds = skin["jointsIds"]; + var joints = []; + var i = 0; + jointsIds.forEach((jointId) { + var nodeForJoint = this.joints[jointId]; + var joint = resources.getEntry(nodeForJoint).object; + if (joint != null) { + + joint.skin = threeMesh; + joints.add(joint); + threeMesh.bones.add(joint); + + var m = skin["inverseBindMatrices"]; + var mat = new Matrix4( + m[i * 16 + 0], m[i * 16 + 4], m[i * 16 + 8], m[i * 16 + 12], + m[i * 16 + 1], m[i * 16 + 5], m[i * 16 + 9], m[i * 16 + 13], + m[i * 16 + 2], m[i * 16 + 6], m[i * 16 + 10], m[i * 16 + 14], + m[i * 16 + 3], m[i * 16 + 7], m[i * 16 + 11], m[i * 16 + 15] + ); + threeMesh["boneInverses"].add(mat); + threeMesh.pose(); + + } else { + log("WARNING: jointId:"+jointId+" cannot be found in skeleton:"+skeleton); + } + i++; + }); + } + + if (threeMesh != null) { + threeMesh.castShadow = true; + node.add(threeMesh); + } + + }); + }); + } + + }); + + } + + buildSkins(node) { + if (node["instanceSkin"] != null) + buildSkin(node); + var children = node.children.toList(); + children.forEach((child) { + buildSkins(child); + }); + } + + createMeshAnimations(root) { + buildSkins(root); + } + + handleScene(entryID, description, userInfo) { + + if (description["nodes"] == null) { + log("ERROR: invalid file required nodes property is missing from scene"); + return false; + } + + description["nodes"].forEach( (nodeUID) { + buildNodeHirerachy(nodeUID, userInfo.rootObj); + }); + + if (delegate != null) { + delegate.loadCompleted(userInfo.callback, userInfo.rootObj); + } + + return true; + } + + handleImage(entryID, description, userInfo) { + resources.setEntry(entryID, null, description); + return true; + } + + addNodeAnimationChannel(name, channel, interp) { + + if (nodeAnimationChannels[name] == null) { + nodeAnimationChannels[name] = []; + } + + nodeAnimationChannels[name].add(interp); + } + + createAnimations() { + animations.clear(); // Let's reuse this array to store the animations + this.nodeAnimationChannels.forEach((name, nodeAnimationChannels) { + var i, len = nodeAnimationChannels.length; + //console.log(" animation channels for node " + name); + //for (i = 0; i < len; i++) { + // console.log(nodeAnimationChannels[i]); + //} + var anim = new GLTFAnimation(nodeAnimationChannels); + anim.name = "animation_" + name; + animations.add(anim); + }); + } + + buildAnimation(Animation animation) { + + var interps = []; + animation.channels.forEach((channel) { + var sampler = animation.samplers[channel["sampler"]]; + if (sampler != null) { + + var input = animation.parameters[sampler["input"]]; + if (input != null && input.data != null) { + + var output = animation.parameters[sampler["output"]]; + if (output != null && output.data != null) { + + var target = channel["target"]; + var node = resources.getEntry(target["id"]); + if (node != null) { + + var path = target["path"]; + + if (path == "rotation") { + convertAxisAngleToQuaternion(output.data, output.count); + } + + var interp = new GLTFInterpolator(node.object, + keys : input.data, + values : output.data, + count : input.count, + path : path, + type : sampler["interpolation"] + ); + + addNodeAnimationChannel(target["id"], channel, interp); + interps.add(interp); + } + } + } + } + }); + } + + handleAnimation(entryID, description, userInfo) { + + animationsRequested++; + var animation = new Animation(); + animation.name = entryID; + animation.onload = () { + // self.buildAnimation(animation); + animationsLoaded++; + animations.add(animation); + checkComplete(); + }; + + animation.channels = description["channels"]; + animation.samplers = description["samplers"]; + resources.setEntry(entryID, animation, description); + var parameters = description["parameters"]; + if (parameters == null) { + //FIXME: not implemented in delegate + log("MISSING_PARAMETERS for animation:"+ entryID); + return false; + } + + // Load parameter buffers + parameters.forEach( (parameterKey, parameter) { + + animation.totalParameters++; + var resource = resources.getEntry(parameter); + var accessor = resource.object; + var bufferView = resources.getEntry(accessor["bufferView"]); + var paramObject = new GLTFLoaderUtils.WrappedBufferView( + bufferView : bufferView, + byteOffset : accessor["byteOffset"], + count : accessor["count"], + type : accessor["type"], + id : accessor["bufferView"], + name : parameterKey + ); + + var paramContext = new AnimationParameterContext(paramObject, animation); + + var alreadyProcessedAttribute = GLTFLoaderUtils.getBuffer(paramObject, animationParameterDelegate, paramContext); + /*if(alreadyProcessedAttribute) { + vertexAttributeDelegate.resourceAvailable(alreadyProcessedAttribute, attribContext); + }*/ + }); + + return true; + } + + handleAccessor(entryID, description, userInfo) { + // Save attribute entry + resources.setEntry(entryID, description, description); + return true; + } + + handleSkin(entryID, Map description, userInfo) { + // Save skin entry + + var skin = { + }; + + var m = description["bindShapeMatrix"].map((e) => e.toDouble()).toList(); + skin["bindShapeMatrix"] = new Matrix4( + m[0], m[4], m[8], m[12], + m[1], m[5], m[9], m[13], + m[2], m[6], m[10], m[14], + m[3], m[7], m[11], m[15] + ); + + skin["jointsIds"] = description["joints"]; + var inverseBindMatricesDescription = description["inverseBindMatrices"]; + skin["inverseBindMatricesDescription"] = inverseBindMatricesDescription; + skin["inverseBindMatricesDescription"]["id"] = "${entryID}_inverseBindMatrices"; + + var bufferEntry = this.resources.getEntry(inverseBindMatricesDescription["bufferView"]); + + var paramObject = new GLTFLoaderUtils.WrappedBufferView( + bufferView : bufferEntry, + byteOffset : inverseBindMatricesDescription["byteOffset"], + count : inverseBindMatricesDescription["count"], + type : inverseBindMatricesDescription["type"], + id : inverseBindMatricesDescription["bufferView"], + name : skin["inverseBindMatricesDescription"]["id"] + ); + + var context = new InverseBindMatricesContext(paramObject, skin); + + var alreadyProcessedAttribute = GLTFLoaderUtils.getBuffer(paramObject, inverseBindMatricesDelegate, context); + + var bufferView = resources.getEntry(skin["inverseBindMatricesDescription"]["bufferView"]); + skin["inverseBindMatricesDescription"]["bufferView"] = bufferView.object; + resources.setEntry(entryID, skin, description); + + return true; + } + + handleSampler(entryID, description, userInfo) { + // Save attribute entry + resources.setEntry(entryID, description, description); + return true; + } + + handleTexture(entryID, description, userInfo) { + // Save attribute entry + resources.setEntry(entryID, null, description); + return true; + } + + handleError(msg) { + throw new Exception(msg); + return true; + } + + var delegate = new LoadDelegate(); // writable: true + + checkComplete() { + if (meshesLoaded == meshesRequested + && shadersLoaded == shadersRequested + && animationsLoaded == animationsRequested) + { + + pendingMeshes.forEach((pending) { + (pending["mesh"] as Mesh).attachToNode(pending["node"], createShaderMaterial); + }); + + for (var i = 0; i < animationsLoaded; i++) { + var animation = animations[i]; + buildAnimation(animation); + } + + createAnimations(); + createMeshAnimations(rootObj); + + // Return the result + completer.complete(new GLTFLoaderResult() + ..scene = rootObj + ..cameras = cameras + ..animations = animations); + + } + } +} + + +class Context { + var rootObj, callback; + Context(this.rootObj, this.callback); +} + +class ClassicGeometry { + + THREE.Geometry geometry; + int totalAttributes = 0; + int loadedAttributes = 0; + bool indicesLoaded = false; + bool finished = false; + bool useBufferGeometry; + + var onload = null; + + var uvs = null; + var indexArray = null; + + ClassicGeometry(this.useBufferGeometry) { + + if (useBufferGeometry) { + geometry = new THREE.BufferGeometry(); + } else { + geometry = new THREE.Geometry(); + } + + } + + buildArrayGeometry() { + + // Build indexed mesh + var normals = geometry.normals; + var a, b, c; + var i, l; + var faceNormals = null; + var faceTexcoords = null; + + var length = indexArray.length; + for (i = 0; i < length; i += 3) { + a = indexArray[i]; + b = indexArray[i + 1]; + c = indexArray[i + 2]; + if (normals.isNotEmpty) { + faceNormals = [normals[a], normals[b], normals[c]]; + } + geometry.faces.add(new THREE.Face3(a, b, c, faceNormals, null, null)); + if (uvs != null) { + geometry.faceVertexUvs[0].add([uvs[a], uvs[b], uvs[c] ]); + } + } + + // Allow Three.js to calculate some values for us + geometry.computeBoundingBox(); + geometry.computeBoundingSphere(); + geometry.computeFaceNormals(); + if (normals.isEmpty) { + geometry.computeVertexNormals(); + } + + } + + buildBufferGeometry() { + // Build indexed mesh + + (geometry as THREE.BufferGeometry).attributes[THREE.GeometryAttribute.INDEX] = + new THREE.GeometryAttribute.uint16(indexArray.length)..array = indexArray; + + + var offset = new THREE.Chunk( + start: 0, index: 0, count: this.indexArray.length + ); + + (geometry as THREE.BufferGeometry).offsets.add(offset); + + geometry.computeBoundingSphere(); + } + + checkFinished() { + if (indexArray != null && loadedAttributes == totalAttributes) { + + if (useBufferGeometry) { + buildBufferGeometry(); + } else { + buildArrayGeometry(); + } + + finished = true; + + if (onload != null) { + onload(); + } + } + } +} + +abstract class Delegate { + handleError(error, info); + convert(what, ctx); + resourceAvailable(res, ctx); +} + +// Delegate for processing index buffers +class IndicesDelegate implements Delegate { + + handleError(errorCode, info) { + // FIXME: report error + log("ERROR(IndicesDelegate): $errorCode : $info"); + } + + convert(ByteBuffer resource, IndicesContext ctx) { + return new Uint16List.view(resource, 0, ctx.indices.count); + } + + resourceAvailable(glResource, ctx) { + var geometry = ctx.geometry; + geometry.indexArray = glResource; + geometry.checkFinished(); + return true; + } +} + + +class IndicesContext { + var indices, geometry; + IndicesContext(this.indices, this.geometry); +} + +// Delegate for processing vertex attribute buffers +class VertexAttributeDelegate implements Delegate { + + handleError(errorCode, info) { + // FIXME: report error + log("ERROR(VertexAttributeDelegate):" + errorCode + ":" + info); + } + + convert(resource, ctx) { + return resource; + } + + arrayResourceAvailable(ByteBuffer glResource, ctx) { + var geom = ctx.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + //FIXME: Float32 is assumed here, but should be checked. + + if (semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32List.view(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + l = floatArray.length; + for (i = 0; i < l; i += 3) { + geom.geometry.vertices.add(new Vector3(floatArray[i], floatArray[i + 1], floatArray[i + 2])); + } + } else if (semantic == "NORMAL") { + geom.geometry.normals = []; + floatArray = new Float32List.view(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + l = floatArray.length; + for (i = 0; i < l; i += 3) { + geom.geometry.normals.add(new Vector3(floatArray[i], floatArray[i + 1], floatArray[i + 2])); + } + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + geom.uvs = []; + floatArray = new Float32List.view(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + l = floatArray.length; + for (i = 0; i < l; i += 2) { + geom.uvs.add(new THREE.UV(floatArray[i], 1.0 - floatArray[i + 1])); + } + } else if (semantic == "WEIGHT") { + var nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32List.view(glResource, 0, attribute.count * nComponents); + l = floatArray.length; + for (i = 0; i < l; i += 4) { + geom.geometry.skinWeights.add(new Vector4(floatArray[i], floatArray[i + 1], floatArray[i + 2], floatArray[i + 3])); + } + } else if (semantic == "JOINT") { + var nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32List.view(glResource, 0, attribute.count * nComponents); + l = floatArray.length; + for (i = 0; i < l; i += 4) { + geom.geometry.skinIndices.add(new Vector4(floatArray[i], floatArray[i + 1], floatArray[i + 2], floatArray[i + 3])); + } + } + } + + bufferResourceAvailable(glResource, ctx) { + ClassicGeometry geom = ctx.geometry; + THREE.BufferGeometry geometry = geom.geometry; + var attribute = ctx.attribute; + var semantic = ctx.semantic; + var floatArray; + var i, l; + var nComponents; + //FIXME: Float32 is assumed here, but should be checked. + + if (semantic == "POSITION") { + // TODO: Should be easy to take strides into account here + floatArray = new Float32List.view(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + geometry.attributes[THREE.GeometryAttribute.POSITION] = new THREE.GeometryAttribute.float32(attribute.count, 3)..array = floatArray; + } else if (semantic == "NORMAL") { + floatArray = new Float32List.view(glResource, 0, attribute.count * componentsPerElementForGLType(attribute.type)); + geometry.attributes[THREE.GeometryAttribute.NORMAL] = new THREE.GeometryAttribute.float32(attribute.count, 3)..array = floatArray; + } else if ((semantic == "TEXCOORD_0") || (semantic == "TEXCOORD" )) { + + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32List.view(glResource, 0, attribute.count * nComponents); + // N.B.: flip Y value... should we just set texture.flipY everywhere? + for (i = 0; i < floatArray.length / 2; i++) { + floatArray[i * 2 + 1] = 1.0 - floatArray[i * 2 + 1]; + } + geometry.attributes[THREE.GeometryAttribute.UV] = new THREE.GeometryAttribute.float32(attribute.count, nComponents)..array = floatArray; + } else if (semantic == "WEIGHT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32List.view(glResource, 0, attribute.count * nComponents); + geometry.attributes["skinIndex"] = new THREE.GeometryAttribute.float32(attribute.count, nComponents)..array = floatArray; + } else if (semantic == "JOINT") { + nComponents = componentsPerElementForGLType(attribute.type); + floatArray = new Float32List.view(glResource, 0, attribute.count * nComponents); + geometry.attributes["skinIndex"] = new THREE.GeometryAttribute.float32(attribute.count, nComponents)..array = floatArray; + } + } + + resourceAvailable(glResource, ctx) { + if (GLTFLoader.useBufferGeometry) { + bufferResourceAvailable(glResource, ctx); + } else { + arrayResourceAvailable(glResource, ctx); + } + + var geom = ctx.geometry; + geom.loadedAttributes++; + geom.checkFinished(); + return true; + } +} + +class VertexAttributeContext { + var attribute, semantic, geometry; + VertexAttributeContext(this.attribute, this.semantic, this.geometry); +} + +class Mesh { + var primitives = []; + var materialsPending = []; + int loadedGeometry = 0; + var onCompleteCallbacks = []; + + addPrimitive(geometry, material) { + geometry.onload = () { + loadedGeometry++; + checkComplete(); + }; + + primitives.add({ + "geometry": geometry, + "material": material, + "mesh": null + }); + } + + onComplete(callback) { + onCompleteCallbacks.add(callback); + checkComplete(); + } + + checkComplete() { + if(onCompleteCallbacks.isNotEmpty && primitives.length == loadedGeometry) { + onCompleteCallbacks.forEach((callback) { + callback(this); + }); + onCompleteCallbacks = []; + } + } + + attachToNode(threeNode, createShaderMaterial) { + // Assumes that the geometry is complete + primitives.forEach((primitive) { + /*if(!primitive.mesh) { + primitive.mesh = new THREE.Mesh(primitive.geometry, primitive.material); + }*/ + var material = primitive["material"]; + if (!(material is THREE.Material)) { + material = createShaderMaterial(material); + } + + var threeMesh = new THREE.Mesh(primitive["geometry"].geometry, material); + threeMesh.castShadow = true; + threeNode.add(threeMesh); + }); + } +} + +// Delayed-loaded material +class Material { + var params; + Material(this.params); +} + +// Delegate for processing animation parameter buffers +class AnimationParameterDelegate implements Delegate { + + handleError(errorCode, info) { + // FIXME: report error + log("ERROR(AnimationParameterDelegate):"+errorCode+":"+info); + } + + convert(resource, ctx) { + var parameter = ctx.parameter; + + var glResource = null; + switch (parameter.type) { + case gl.FLOAT : + case gl.FLOAT_VEC2 : + case gl.FLOAT_VEC3 : + case gl.FLOAT_VEC4 : + glResource = new Float32List.view(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + } + + resourceAvailable(glResource, AnimationParameterContext ctx) { + var animation = ctx.animation; + var parameter = ctx.parameter; + parameter.data = glResource; + animation.handleParameterLoaded(parameter); + return true; + } +} + + +class AnimationParameterContext { + var parameter; + Animation animation; + AnimationParameterContext(this.parameter, this.animation); + } + +// Animations +class Animation { + var name, channels, samplers; + // create Three.js keyframe here + int totalParameters = 0; + int loadedParameters = 0; + var parameters = {}; + bool finishedLoading = false; + var onload = null; + + Animation(); + + handleParameterLoaded(parameter) { + parameters[parameter.name] = parameter; + loadedParameters++; + checkFinished(); + } + + checkFinished() { + if(loadedParameters == totalParameters) { + // Build animation + finishedLoading = true; + + if (onload != null) { + onload(); + } + } + } +} + +/// Delegate for processing inverse bind matrices buffer +class InverseBindMatricesDelegate implements Delegate { + + handleError(errorCode, info) { + // FIXME: report error + log("ERROR(InverseBindMatricesDelegate):"+errorCode+":"+info); + } + + convert(resource, InverseBindMatricesContext ctx) { + var parameter = ctx.param; + + var glResource = null; + switch (parameter.type) { + case gl.FLOAT_MAT4 : + glResource = new Float32List.view(resource, 0, parameter.count * componentsPerElementForGLType(parameter.type)); + break; + default: + break; + } + + return glResource; + } + + resourceAvailable(glResource, ctx) { + var skin = ctx.skin; + skin["inverseBindMatrices"] = glResource; + return true; + } +} + + +class InverseBindMatricesContext { + var param, skin; + InverseBindMatricesContext(this.param, this.skin); +} + +/// Delegate for processing shaders from external files +class ShaderDelegate implements Delegate { + + GLTFLoader loader; + + ShaderDelegate(this.loader); + + handleError(errorCode, info) { + // FIXME: report error + log("ERROR(ShaderDelegate): $errorCode : $info"); + } + + convert(resource, ShaderContext ctx) { + return resource; + } + + resourceAvailable(data, ctx) { + loader.shadersLoaded++; + loader.shaders[ctx.id] = data; + return true; + } +} + + +class ShaderContext { + var id, path; + ShaderContext(this.id, this.path); +} + +/// Resource management +class ResourceEntry { + +String entryID; +var object, buffer; +Map description; + +ResourceEntry(this.entryID, this.object, this.description); +} + +class Resources { + Map _entries = {}; + + + setEntry(entryID, object, description) { + if (entryID == null) { + log("No EntryID provided, cannot store " + description); + return; + } + + if (_entries[entryID] != null) { + log("entry["+entryID+"] is being overwritten"); + } + + _entries[entryID] = new ResourceEntry(entryID, object, description ); + } + + ResourceEntry getEntry(entryID) => _entries[entryID]; + + clearEntries() { + _entries = {}; + } +} + +class LoadDelegate { + + loadCompleted(callback, obj) { + callback(obj); + } + } \ No newline at end of file diff --git a/lib/loaders/gltf/gltf_animation.dart b/lib/loaders/gltf/gltf_animation.dart new file mode 100644 index 00000000..3ebd11ec --- /dev/null +++ b/lib/loaders/gltf/gltf_animation.dart @@ -0,0 +1,225 @@ +part of gltf_loader; + +final glTFAnimator = new _GLTFAnimator(); + +class _GLTFAnimator { + + var animators = []; + + void add(animator) { + animators.add(animator); + } + + remove(animator) { + animators.remove(animator); + } + + update() { + animators.forEach((a) { a.update(); }); + } +} + + +// Construction/initialization +class GLTFAnimation { + String name; + bool running = false; + bool loop = false; + num duration = 0; + num startTime = 0; + List interps = []; + + GLTFAnimation([interps]) { + if (interps != null) { + addInterpolators(interps); + } + } + + addInterpolators(interps) { + this.interps.addAll(interps); + interps.forEach((interp) { + duration = Math.max(duration, interp.duration); + }); + } + +// Start/stop + play() { + if (running) + return; + + startTime = new DateTime.now().millisecondsSinceEpoch; + running = true; + glTFAnimator.add(this); + } + + stop() { + running = false; + glTFAnimator.remove(this); + } + + // Update - drive key frame evaluation + update() { + if (!running) + return; + + var now = new DateTime.now().millisecondsSinceEpoch; + var deltat = (now - startTime) / 1000; + var t = deltat % duration; + var nCycles = (deltat / duration).floor(); + + if (nCycles >= 1 && !loop) { + running = false; + var i, len = interps.length; + for (i = 0; i < len; i++) { + interps[i].interp(duration); + } + stop(); + return; + } else { + var i, len = interps.length; + for (i = 0; i < len; i++) { + interps[i].interp(t); + } + } + } +} + +//Interpolator class +//Construction/initialization +class GLTFInterpolator { + + List keys; + List values; + int count; + String type; + String path; + bool isRot; + + var target; + var originalValue; + var duration; + + Vector3 vec1, vec2, vec3; + Quaternion quat1, quat2, quat3; + + GLTFInterpolator(target, {this.keys, this.values, this.count, this.type, this.path, this.isRot: false}) { + + var node = target; + node.updateMatrix(); + node.matrixAutoUpdate = true; + + switch (path) { + case "translation" : + this.target = node.position; + originalValue = node.position.clone(); + break; + case "rotation" : + this.target = node.quaternion; + originalValue = node.quaternion.clone(); + isRot = true; + break; + case "scale" : + this.target = node.scale; + originalValue = node.scale.clone(); + break; + } + + duration = keys[count - 1]; + + vec1 = new Vector3.zero(); + vec2 = new Vector3.zero(); + vec3 = new Vector3.zero(); + quat1 = new Quaternion.identity(); + quat2 = new Quaternion.identity(); + quat3 = new Quaternion.identity(); + } + + //Interpolation and tweening methods + interp(t) { + var i, j; + if (t == keys[0]) { + if (isRot) { + quat3 + ..x = values[0] + ..y = values[1] + ..z = values[2] + ..w = values[3]; + } else { + vec3.setValues(values[0], values[1], values[2]); + } + } else if (t < keys[0]) { + if (isRot) { + quat1..copyFrom(originalValue); + quat2..x = values[0] + ..y = values[1] + ..z = values[2] + ..w = values[3]; + THREE.slerp(quat1, quat2, quat3, t / keys[0]); + } else { + vec3.setValues(originalValue.x, + originalValue.y, + originalValue.z); + vec2.setValues(values[0], + values[1], + values[2]); + + //vec3.lerp(vec2, t / keys[0]); + vec3.add((vec2 - vec3) * (t / keys[0])); + } + } else if (t >= keys[count - 1]) { + if (isRot) { + quat3..x = values[(count - 1) * 4] + ..y = values[(count - 1) * 4 + 1] + ..z = values[(count - 1) * 4 + 2] + ..w = values[(count - 1) * 4 + 3]; + } else { + vec3.setValues(values[(count - 1) * 3], + values[(count - 1) * 3 + 1], + values[(count - 1) * 3 + 2]); + } + } else { + for (i = 0; i < count - 1; i++) { + var key1 = keys[i]; + var key2 = keys[i + 1]; + + if (t >= key1 && t <= key2) { + if (isRot) { + quat1 + ..x = values[i * 4] + ..y = values[i * 4 + 1] + ..z = values[i * 4 + 2] + ..w = values[i * 4 + 3]; + quat2 + ..x = values[(i + 1) * 4] + ..y = values[(i + 1) * 4 + 1] + ..z = values[(i + 1) * 4 + 2] + ..w = values[(i + 1) * 4 + 3]; + THREE.slerp(quat1, quat2, quat3, (t - key1) / (key2 - key1)); + } else { + vec3.setValues(values[i * 3], + values[i * 3 + 1], + values[i * 3 + 2]); + vec2.setValues(values[(i + 1) * 3], + values[(i + 1) * 3 + 1], + values[(i + 1) * 3 + 2]); + + //vec3.lerp(vec2, (t - key1) / (key2 - key1)); + vec3.add((vec2 - vec3) * ((t - key1) / (key2 - key1))); + } + } + } + } + + if (target != null) { + copyValue(target); + } + } + + copyValue(target) { + if (isRot) { + target.copyFrom(quat3); + } else { + target.setFrom(vec3); + } + } +} \ No newline at end of file diff --git a/lib/loaders/gltf/gltf_loader_utils.dart b/lib/loaders/gltf/gltf_loader_utils.dart new file mode 100644 index 00000000..17afc33a --- /dev/null +++ b/lib/loaders/gltf/gltf_loader_utils.dart @@ -0,0 +1,219 @@ +library gltf_loader_utils; + +import 'dart:html'; +import 'dart:typed_data'; +import 'dart:web_gl' as gl; + +// errors +const MISSING_DESCRIPTION = "MISSING_DESCRIPTION"; +const INVALID_PATH = "INVALID_PATH"; +const INVALID_TYPE = "INVALID_TYPE"; +const XMLHTTPREQUEST_STATUS_ERROR = "XMLHTTPREQUEST_STATUS_ERROR"; +const NOT_FOUND = "NOT_FOUND"; +// misc constants +const ARRAY_BUFFER = "ArrayBuffer"; + +var _streams = {}, + _streamsStatus = {}, + _resources = {}, + _resourcesStatus = {}; + +// initialization +init() { + _streams = {}; + _streamsStatus = {}; + _resources = {}; + _resourcesStatus = {}; +} + +//manage entries +_containsResource(resourceID) =>_resources.containsKey(resourceID); + +_log(msg) => print(msg); + +_storeResource(resourceID, resource) { + if (resourceID == null) { + _log("ERROR: entry does not contain id, cannot store"); + return; + } + + if (_containsResource(resourceID)) { + _log("WARNING: resource:"+resourceID+" is already stored, overriding"); + } + + _resources[resourceID] = resource; +} + +_getResource(resourceID) => _resources[resourceID]; + +_loadStream(String path, String type, ProcessResourceDelegate delegate) { + if (type == null) { + delegate.handleError(INVALID_TYPE, null); + return; + } + + if (path == null) { + delegate.handleError(INVALID_PATH); + return; + } + + HttpRequest.request(path, responseType: (type == ARRAY_BUFFER) ? "arraybuffer" : "text") + .then((req) { + delegate.streamAvailable(path, req.response); + }); + // TODO(nfgs) - Catch errors here + // .catchError((e) { + // delegate.handleError(XMLHTTPREQUEST_STATUS_ERROR, e.target.status); + // }); +} + +int send = 0, + requested = 0; + +_handleRequest(Request request) { + var resourceStatus = _resourcesStatus[request.id]; + if (resourceStatus != null) { + _resourcesStatus[request.id]++; + } else { + _resourcesStatus[request.id] = 1; + } + + var streamStatus = _streamsStatus[request.path]; + if (streamStatus != null && streamStatus.status == "loading" ) { + streamStatus.requests.add(request); + return; + } + + _streamsStatus[request.path] = new _StreamStatus(status : "loading", requests : [request]); + + var processResourceDelegate = new ProcessResourceDelegate(request); + + _loadStream(request.path, request.type, processResourceDelegate); +} + +class _StreamStatus { + String status; + List requests; + _StreamStatus({this.status, this.requests}); +} + +class ProcessResourceDelegate { + + Request request; + + ProcessResourceDelegate(this.request); + + streamAvailable(path, res_) { + _StreamStatus streamStatus = _streamsStatus[path]; + var requests = streamStatus.requests; + requests.forEach((req_) { + var end = (req_.range.length > 1) ? req_.range[1] : req_.range[0] + res_.length; + print("${res_.length}[${req_.range[0]}..${(req_.range.length > 1) ? req_.range[1] : ''}]"); + var subArray; + if (res_ is String) { + subArray = res_.substring(req_.range[0], end); + } else if (res_ is List) { + subArray = res_.sublist(req_.range[0], end); + } + print("${subArray.length}"); + var convertedResource = req_.delegate.convert(subArray, req_.ctx); + _storeResource(req_.id, convertedResource); + req_.delegate.resourceAvailable(convertedResource, req_.ctx); + --_resourcesStatus[req_.id]; + }); + + _streamsStatus.remove(path); + } + + + handleError(errorCode, [info]) { + request.delegate.handleError(errorCode, info); + } +} + +_elementSizeForGLType(glType) { + switch (glType) { + case gl.FLOAT: + return Float32List.BYTES_PER_ELEMENT; + case gl.UNSIGNED_BYTE : + return Uint8List.BYTES_PER_ELEMENT; + case gl.UNSIGNED_SHORT : + return Uint16List.BYTES_PER_ELEMENT; + case gl.FLOAT_VEC2 : + return Float32List.BYTES_PER_ELEMENT * 2; + case gl.FLOAT_VEC3 : + return Float32List.BYTES_PER_ELEMENT * 3; + case gl.FLOAT_VEC4 : + return Float32List.BYTES_PER_ELEMENT * 4; + case gl.FLOAT_MAT3 : + return Float32List.BYTES_PER_ELEMENT * 9; + case gl.FLOAT_MAT4 : + return Float32List.BYTES_PER_ELEMENT * 16; + default: + return null; + } +} + +_handleWrappedBufferViewResourceLoading(WrappedBufferView wrappedBufferView, delegate, ctx) { + var bufferView = wrappedBufferView.bufferView; + var buffer = bufferView.buffer; + var byteOffset = wrappedBufferView.byteOffset + bufferView.description["byteOffset"]; + var range = [byteOffset , (_elementSizeForGLType(wrappedBufferView.type) * wrappedBufferView.count) + byteOffset]; + print("['${wrappedBufferView.id}'] : offset = ${wrappedBufferView.byteOffset} + ${bufferView.description['byteOffset']} range = [$byteOffset..${range[1]}]"); + _handleRequest(new Request( + id : wrappedBufferView.id, + range : range, + type : buffer.description["type"], + path : buffer.description["path"], + delegate : delegate, + ctx : ctx)); +} + +getBuffer(WrappedBufferView wrappedBufferView, delegate, ctx) { + + var savedBuffer = _getResource(wrappedBufferView.id); + if (savedBuffer != null) { + return savedBuffer; + } else { + _handleWrappedBufferViewResourceLoading(wrappedBufferView, delegate, ctx); + } + + return null; +} + +getFile(Request request, delegate, ctx) { + + request.delegate = delegate; + request.ctx = ctx; + + _handleRequest(new Request( + id : request.id, + path : request.path, + range : [0], + type : "text", + delegate : delegate, + ctx : ctx )); + + return null; +} + +class Request { + String id; + String path; + List range; + String type; + var delegate; + var ctx; + Request({this.id, this.path, this.range, this.type, this.delegate, this.ctx}); +} + +class WrappedBufferView { + var bufferView; + int byteOffset, byteStride, count; + List min, max; + String id; + int type; + String name; + var data; + WrappedBufferView({this.bufferView, this.byteOffset, this.byteStride, this.min, this.max, this.count, this.id, this.type, this.name}); +} \ No newline at end of file diff --git a/lib/loaders/gltf/gltf_parser.dart b/lib/loaders/gltf/gltf_parser.dart new file mode 100644 index 00000000..13090dfc --- /dev/null +++ b/lib/loaders/gltf/gltf_parser.dart @@ -0,0 +1,253 @@ +library gltf_parser; + +import 'dart:async' show Future; +import 'dart:convert' show JSON; +import 'dart:html' show HttpRequest; + +typedef bool _EntryHandler(entryID, description, userInfo); + +abstract class GLTFParser extends Object { + var categoriesDepsOrder = ["buffers", "bufferViews", "images", "videos", "samplers", "textures", "shaders", "programs", "techniques", "materials", "accessors", "meshes", "cameras", "lights", "skins", "nodes", "scenes", "animations"]; + + Map rootDescription; // "writable": true + + String baseURL; // "writable": true + + Map methodForType = {}; + + GLTFParser() { + methodForType = { + "buffers" : handleBuffer, + "bufferViews" : handleBufferView, + "shaders" : handleShader, + "programs" : handleProgram, + "techniques" : handleTechnique, + "materials" : handleMaterial, + "meshes" : handleMesh, + "cameras" : handleCamera, + "lights" : handleLight, + "nodes" : handleNode, + "scenes" : handleScene, + "images" : handleImage, + "animations" : handleAnimation, + "accessors" : handleAccessor, + "skins" : handleSkin, + "samplers" : handleSampler, + "textures" : handleTexture, + "videos" : handleVideo + + }; + } + //detect absolute path following the same protocol than window.location + bool _isAbsolutePath(String path) => path.startsWith("http://"); + + resolvePathIfNeeded(path) { + if (_isAbsolutePath(path)) { + return path; + } + return baseURL + path; + } + + _resolvePathsForCategories(categories) { + categories.forEach( (category) { + var descriptions = json[category]; + if (descriptions != null) { + descriptions.forEach( (descriptionKey, description) { + description["path"] = resolvePathIfNeeded(description["path"]); + }); + } + }); + } + + var _json; // writable: true + + get json => _json; + + set json(value) { + if (_json != value) { + _json = value; + _resolvePathsForCategories(["buffers", "shaders", "images", "videos"]); + } + } + + var _path; //writable: true + + getEntryDescription(entryID, entryType) { + var entries = null; + + var category = entryType; + entries = rootDescription[category]; + if (entries == null) { + print("ERROR:CANNOT find expected category named:"+category); + return null; + } + + return (entries != null) ? entries[entryID] : null; + } + + _stepToNextCategory() { + _state["categoryIndex"] = getNextCategoryIndex(_state["categoryIndex"] + 1); + if (_state["categoryIndex"] != -1) { + _state["categoryState"]["index"] = 0; + return true; + } + + return false; + } + + _stepToNextDescription() { + var categoryState = _state["categoryState"]; + var keys = categoryState["keys"]; + if (keys == null) { + print("INCONSISTENCY ERROR"); + return false; + } + + categoryState["index"]++; + categoryState["keys"] = null; + if (categoryState["index"] >= keys.length) { + return this._stepToNextCategory(); + } + return false; + } + + bool hasCategory(category) => rootDescription.containsKey(category); + + // Abstract methods to implement + bool handleBuffer(centryID, Map description, userInfo); + bool handleBufferView(entryID, Map description, userInfo); + bool handleShader(entryID, Map description, userInfo); + bool handleProgram(entryID, Map description, userInfo); + bool handleTechnique(entryID, Map description, userInfo); + bool handleMaterial(entryID, Map description, userInfo); + bool handleMesh(entryID, Map description, userInfo); + bool handleCamera(entryID, Map description, userInfo); + bool handleLight(entryID, Map description, userInfo); + bool handleNode(entryID, Map description, userInfo); + bool handleScene(entryID, Map description, userInfo); + bool handleImage(entryID, Map description, userInfo); + bool handleAnimation(entryID, Map description, userInfo); + bool handleAccessor(entryID, Map description, userInfo); + bool handleSkin(entryID, Map description, userInfo); + bool handleSampler(entryID, Map description, userInfo); + bool handleTexture(entryID, Map description, userInfo); + bool handleVideo(entryID, Map description, userInfo); + + bool _handleState() { + + var success = true; + while (_state["categoryIndex"] != -1) { + var category = categoriesDepsOrder[_state["categoryIndex"]]; + var categoryState = _state["categoryState"]; + var keys = categoryState["keys"]; + if (keys == null) { + categoryState["keys"] = keys = rootDescription[category].keys.toList(); + if (keys != null) { + if (keys.length == 0) { + _stepToNextDescription(); + continue; + } + } + } + + var type = category; + var entryID = keys[categoryState["index"]]; + var description = getEntryDescription(entryID, type); + if (description == null) { + throw new Exception("INCONSISTENCY ERROR: no description found for entry $entryID"); + } else { + + if (methodForType[type] != null) { + if (!methodForType[type](entryID, description, _state["userInfo"])) { + success = false; + break; + } + } + + _stepToNextDescription(); + } + } + + return success; + + } + + Future _load() { + //enumerable: true, + if (_json != null) { + return Future.value(_json); + } + + var jsonPath = _path; + var i = jsonPath.lastIndexOf("/"); + baseURL = (i != 0) ? jsonPath.substring(0, i + 1) : ''; + + return HttpRequest.request(jsonPath).then((req) { + json = JSON.decode(req.responseText); + rootDescription = _json; + }); + } + + var _state; // writable: true + + _getEntryType(entryID) { + var rootKeys = categoriesDepsOrder; + for (var i = 0 ; i < rootKeys.length ; i++) { + var rootValues = this.rootDescription[rootKeys[i]]; + if (rootValues) { + return rootKeys[i]; + } + } + return null; + } + + getNextCategoryIndex(currentIndex) { + for (var i = currentIndex ; i < categoriesDepsOrder.length ; i++) { + if (hasCategory(categoriesDepsOrder[i])) { + return i; + } + } + + return -1; + } + + doLoad(userInfo, options) { // enumerable: true + _load().then((_) { + var startCategory = getNextCategoryIndex(0); + if (startCategory != -1) { + _state = { "userInfo" : userInfo, + "options" : options, + "categoryIndex" : startCategory, + "categoryState" : { "index" : 0 } }; + return _handleState(); + } + }); + } + + initWithPath(path) { + _path = path; + _json = null; + return this; + } + + //this is meant to be global and common for all instances + var _knownURLs = {}; //writable: true + + //to be invoked by subclass, so that ids can be ensured to not overlap + loaderContext() { + if (_knownURLs[_path] == null) { + _knownURLs[_path] = _knownURLs.length; + } + return "__${_knownURLs[_path]}"; + } + + initWithJSON(json, baseURL) { + this.json = json; + this.baseURL = baseURL; + if (!baseURL) { + print("WARNING: no base URL passed to Reader:initWithJSON"); + } + return this; + } + +} \ No newline at end of file diff --git a/lib/src/core/buffer_geometry.dart b/lib/src/core/buffer_geometry.dart index 2f65d257..46ea7fdd 100644 --- a/lib/src/core/buffer_geometry.dart +++ b/lib/src/core/buffer_geometry.dart @@ -23,6 +23,9 @@ class GeometryAttribute { factory GeometryAttribute.int16(int numItems, [int itemSize = 1]) => new GeometryAttribute._internal(numItems, itemSize, new Int16List(numItems)); + factory GeometryAttribute.uint16(int numItems, [int itemSize = 1]) => + new GeometryAttribute._internal(numItems, itemSize, new Uint16List(numItems)); + } class Chunk { @@ -495,7 +498,7 @@ class BufferGeometry implements Geometry { GeometryAttribute get aNormal => attributes[GeometryAttribute.NORMAL]; set aNormal(a){ attributes[GeometryAttribute.NORMAL] = a; } - GeometryAttribute get aIndex => attributes[GeometryAttribute.INDEX]; + GeometryAttribute get aIndex => attributes[GeometryAttribute.INDEX]; set aIndex(a){ attributes[GeometryAttribute.INDEX] = a; } GeometryAttribute get aUV => attributes[GeometryAttribute.UV];