parent
50eaa34143
commit
7951f5a987
@ -0,0 +1,77 @@ |
|||||||
|
/* |
||||||
|
* Copyright (c) 2009-2012 jMonkeyEngine |
||||||
|
* All rights reserved. |
||||||
|
* |
||||||
|
* Redistribution and use in source and binary forms, with or without |
||||||
|
* modification, are permitted provided that the following conditions are |
||||||
|
* met: |
||||||
|
* |
||||||
|
* * Redistributions of source code must retain the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer. |
||||||
|
* |
||||||
|
* * Redistributions in binary form must reproduce the above copyright |
||||||
|
* notice, this list of conditions and the following disclaimer in the |
||||||
|
* documentation and/or other materials provided with the distribution. |
||||||
|
* |
||||||
|
* * Neither the name of 'jMonkeyEngine' nor the names of its contributors |
||||||
|
* may be used to endorse or promote products derived from this software |
||||||
|
* without specific prior written permission. |
||||||
|
* |
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
||||||
|
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED |
||||||
|
* TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR |
||||||
|
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR |
||||||
|
* CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, |
||||||
|
* EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, |
||||||
|
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
||||||
|
* PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF |
||||||
|
* LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING |
||||||
|
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS |
||||||
|
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
||||||
|
*/ |
||||||
|
package jme3test.model; |
||||||
|
|
||||||
|
import com.jme3.app.SimpleApplication; |
||||||
|
import com.jme3.light.DirectionalLight; |
||||||
|
import com.jme3.light.PointLight; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.scene.Geometry; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.scene.shape.Sphere; |
||||||
|
|
||||||
|
public class TestGltfLoading extends SimpleApplication { |
||||||
|
|
||||||
|
|
||||||
|
public static void main(String[] args) { |
||||||
|
TestGltfLoading app = new TestGltfLoading(); |
||||||
|
app.start(); |
||||||
|
} |
||||||
|
|
||||||
|
public void simpleInitApp() { |
||||||
|
flyCam.setMoveSpeed(10f); |
||||||
|
viewPort.setBackgroundColor(ColorRGBA.DarkGray); |
||||||
|
|
||||||
|
// sunset light
|
||||||
|
// DirectionalLight dl = new DirectionalLight();
|
||||||
|
// dl.setDirection(new Vector3f(-1f, -1.0f, -1f).normalizeLocal());
|
||||||
|
// dl.setColor(new ColorRGBA(1f, 1f, 1f, 1.0f));
|
||||||
|
// rootNode.addLight(dl);
|
||||||
|
//
|
||||||
|
// DirectionalLight dl2 = new DirectionalLight();
|
||||||
|
// dl2.setDirection(new Vector3f(1f, 1.0f, 1f).normalizeLocal());
|
||||||
|
// dl2.setColor(new ColorRGBA(0.5f, 0.5f, 0.5f, 1.0f));
|
||||||
|
// rootNode.addLight(dl2);
|
||||||
|
|
||||||
|
PointLight pl = new PointLight(new Vector3f(5.0f, 5.0f, 5.0f), ColorRGBA.White, 30); |
||||||
|
rootNode.addLight(pl); |
||||||
|
PointLight pl1 = new PointLight(new Vector3f(-5.0f, -5.0f, -5.0f), ColorRGBA.White.mult(0.5f), 50); |
||||||
|
rootNode.addLight(pl1); |
||||||
|
|
||||||
|
//rootNode.attachChild(assetManager.loadModel("Models/gltf/box/box.gltf"));
|
||||||
|
rootNode.attachChild(assetManager.loadModel("Models/gltf/duck/Duck.gltf")); |
||||||
|
|
||||||
|
//rootNode.attachChild(assetManager.loadModel("Models/gltf/hornet/scene.gltf"));
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
package com.jme3.scene.plugins.gltf; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetInfo; |
||||||
|
import com.jme3.asset.AssetLoader; |
||||||
|
|
||||||
|
import java.io.IOException; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 08/08/2017. |
||||||
|
*/ |
||||||
|
public class BinLoader implements AssetLoader { |
||||||
|
@Override |
||||||
|
public Object load(AssetInfo assetInfo) throws IOException { |
||||||
|
return assetInfo.openStream(); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,399 @@ |
|||||||
|
package com.jme3.scene.plugins.gltf; |
||||||
|
|
||||||
|
import com.google.gson.*; |
||||||
|
import com.google.gson.stream.JsonReader; |
||||||
|
import com.jme3.asset.*; |
||||||
|
import com.jme3.material.Material; |
||||||
|
import com.jme3.math.*; |
||||||
|
import com.jme3.scene.*; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.nio.Buffer; |
||||||
|
import java.util.HashMap; |
||||||
|
import java.util.Map; |
||||||
|
|
||||||
|
import static com.jme3.scene.plugins.gltf.GltfUtils.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* GLTF 2.0 loader |
||||||
|
* Created by Nehon on 07/08/2017. |
||||||
|
*/ |
||||||
|
public class GltfLoader implements AssetLoader { |
||||||
|
|
||||||
|
//Data cache for already parsed JME objects
|
||||||
|
private Map<String, Object[]> dataCache = new HashMap<>(); |
||||||
|
private JsonArray scenes; |
||||||
|
private JsonArray nodes; |
||||||
|
private JsonArray meshes; |
||||||
|
private JsonArray accessors; |
||||||
|
private JsonArray bufferViews; |
||||||
|
private JsonArray buffers; |
||||||
|
private JsonArray materials; |
||||||
|
private Material defaultMat; |
||||||
|
private byte[] tmpByteArray; |
||||||
|
private AssetInfo info; |
||||||
|
|
||||||
|
@Override |
||||||
|
public Object load(AssetInfo assetInfo) throws IOException { |
||||||
|
try { |
||||||
|
dataCache.clear(); |
||||||
|
info = assetInfo; |
||||||
|
|
||||||
|
if (defaultMat == null) { |
||||||
|
defaultMat = new Material(assetInfo.getManager(), "Common/MatDefs/Light/PBRLighting.j3md"); |
||||||
|
defaultMat.setColor("BaseColor", ColorRGBA.White); |
||||||
|
defaultMat.setFloat("Metallic", 0f); |
||||||
|
defaultMat.setFloat("Roughness", 1f); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
JsonObject root = new JsonParser().parse(new JsonReader(new InputStreamReader(assetInfo.openStream()))).getAsJsonObject(); |
||||||
|
|
||||||
|
JsonObject asset = root.getAsJsonObject().get("asset").getAsJsonObject(); |
||||||
|
String generator = getAsString(asset, "generator"); |
||||||
|
String version = getAsString(asset, "version"); |
||||||
|
String minVersion = getAsString(asset, "minVersion"); |
||||||
|
if (!isSupported(version, minVersion)) { |
||||||
|
//TODO maybe just warn. gltf specs claims it will be backward compatible so at worst the user will miss some data.
|
||||||
|
throw new AssetLoadException("Gltf Loader doesn't support this gltf version: " + version + (minVersion != null ? ("/" + minVersion) : "")); |
||||||
|
} |
||||||
|
|
||||||
|
scenes = root.getAsJsonArray("scenes"); |
||||||
|
nodes = root.getAsJsonArray("nodes"); |
||||||
|
meshes = root.getAsJsonArray("meshes"); |
||||||
|
accessors = root.getAsJsonArray("accessors"); |
||||||
|
bufferViews = root.getAsJsonArray("bufferViews"); |
||||||
|
buffers = root.getAsJsonArray("buffers"); |
||||||
|
materials = root.getAsJsonArray("materials"); |
||||||
|
|
||||||
|
allocatedTmpByteArray(); |
||||||
|
|
||||||
|
JsonPrimitive defaultScene = root.getAsJsonPrimitive("scene"); |
||||||
|
|
||||||
|
Node n = loadScenes(defaultScene); |
||||||
|
//only one scene let's not return the root.
|
||||||
|
if (n.getChildren().size() == 1) { |
||||||
|
n = (Node) n.getChild(0); |
||||||
|
} |
||||||
|
//no name for the scene... let's set the file name.
|
||||||
|
if (n.getName() == null) { |
||||||
|
n.setName(assetInfo.getKey().getName()); |
||||||
|
} |
||||||
|
return n; |
||||||
|
} catch (Exception e) { |
||||||
|
throw new AssetLoadException("An error occurred loading " + assetInfo.getKey().getName(), e); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private void allocatedTmpByteArray() { |
||||||
|
//Allocate the tmpByteArray to the biggest bufferView
|
||||||
|
if (bufferViews == null) { |
||||||
|
throw new AssetLoadException("No buffer view defined but one is referenced by an accessor"); |
||||||
|
} |
||||||
|
int maxLength = 0; |
||||||
|
for (JsonElement bufferView : bufferViews) { |
||||||
|
Integer byteLength = getAsInteger(bufferView.getAsJsonObject(), "byteLength"); |
||||||
|
if (byteLength != null && maxLength < byteLength) { |
||||||
|
maxLength = byteLength; |
||||||
|
} |
||||||
|
} |
||||||
|
tmpByteArray = new byte[maxLength]; |
||||||
|
} |
||||||
|
|
||||||
|
private boolean isSupported(String version, String minVersion) { |
||||||
|
return "2.0".equals(version); |
||||||
|
} |
||||||
|
|
||||||
|
private Node loadScenes(JsonPrimitive defaultScene) throws IOException { |
||||||
|
if (scenes == null) { |
||||||
|
//no scene... lets handle this later...
|
||||||
|
throw new AssetLoadException("Gltf files with no scene is not yet supported"); |
||||||
|
} |
||||||
|
Node root = new Node(); |
||||||
|
for (JsonElement scene : scenes) { |
||||||
|
Node sceneNode = new Node(); |
||||||
|
//specs says that only the default scene should be rendered,
|
||||||
|
// if there are several scenes, they are attached to the rootScene, but they are culled
|
||||||
|
sceneNode.setCullHint(Spatial.CullHint.Always); |
||||||
|
|
||||||
|
sceneNode.setName(getAsString(scene.getAsJsonObject(), "name")); |
||||||
|
JsonArray sceneNodes = scene.getAsJsonObject().getAsJsonArray("nodes"); |
||||||
|
for (JsonElement node : sceneNodes) { |
||||||
|
sceneNode.attachChild(loadNode(node.getAsInt())); |
||||||
|
} |
||||||
|
root.attachChild(sceneNode); |
||||||
|
} |
||||||
|
|
||||||
|
//Setting the default scene cul hint to inherit.
|
||||||
|
int activeChild = 0; |
||||||
|
if (defaultScene != null) { |
||||||
|
activeChild = defaultScene.getAsInt(); |
||||||
|
} |
||||||
|
root.getChild(activeChild).setCullHint(Spatial.CullHint.Inherit); |
||||||
|
return root; |
||||||
|
} |
||||||
|
|
||||||
|
private Spatial loadNode(int nodeIndex) throws IOException { |
||||||
|
Spatial spatial = fetchFromCache("nodes", nodeIndex, Spatial.class); |
||||||
|
if (spatial != null) { |
||||||
|
//If a spatial is referenced several times, it may be attached to different parents,
|
||||||
|
// and it's not possible in JME, so we have to clone it.
|
||||||
|
return spatial.clone(); |
||||||
|
} |
||||||
|
JsonObject nodeData = nodes.get(nodeIndex).getAsJsonObject(); |
||||||
|
Integer meshIndex = getAsInteger(nodeData, "mesh"); |
||||||
|
if (meshIndex != null) { |
||||||
|
if (meshes == null) { |
||||||
|
throw new AssetLoadException("Can't find any mesh data, yet a node references a mesh"); |
||||||
|
} |
||||||
|
|
||||||
|
//TODO material
|
||||||
|
Material mat = defaultMat; |
||||||
|
|
||||||
|
//there is a mesh in this node, however gltf can split meshes in primitives (some kind of sub meshes),
|
||||||
|
//We don't have this in JME so we have to make one mesh and one Geometry for each primitive.
|
||||||
|
Mesh[] primitives = loadMeshPrimitives(meshIndex); |
||||||
|
if (primitives.length > 1) { |
||||||
|
//only one mesh, lets just make a geometry.
|
||||||
|
Geometry geometry = new Geometry(null, primitives[0]); |
||||||
|
geometry.setMaterial(mat); |
||||||
|
geometry.updateModelBound(); |
||||||
|
spatial = geometry; |
||||||
|
} else { |
||||||
|
//several meshes, let's make a parent Node and attach several geometries to it
|
||||||
|
Node node = new Node(); |
||||||
|
for (Mesh primitive : primitives) { |
||||||
|
Geometry geom = new Geometry(null, primitive); |
||||||
|
geom.setMaterial(mat); |
||||||
|
geom.updateModelBound(); |
||||||
|
node.attachChild(geom); |
||||||
|
} |
||||||
|
spatial = node; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
spatial.setName(loadMeshName(meshIndex)); |
||||||
|
|
||||||
|
} else { |
||||||
|
//no mesh, we have a node. Can be a camera node or a regular node.
|
||||||
|
//TODO handle camera nodes?
|
||||||
|
Node node = new Node(); |
||||||
|
JsonArray children = nodeData.getAsJsonArray("children"); |
||||||
|
if (children != null) { |
||||||
|
for (JsonElement child : children) { |
||||||
|
node.attachChild(loadNode(child.getAsInt())); |
||||||
|
} |
||||||
|
} |
||||||
|
spatial = node; |
||||||
|
} |
||||||
|
if (spatial.getName() == null) { |
||||||
|
spatial.setName(getAsString(nodeData.getAsJsonObject(), "name")); |
||||||
|
} |
||||||
|
spatial.setLocalTransform(loadTransforms(nodeData)); |
||||||
|
|
||||||
|
addToCache("nodes", nodeIndex, spatial, nodes.size()); |
||||||
|
return spatial; |
||||||
|
} |
||||||
|
|
||||||
|
private Transform loadTransforms(JsonObject nodeData) { |
||||||
|
Transform transform = new Transform(); |
||||||
|
JsonArray matrix = nodeData.getAsJsonArray("matrix"); |
||||||
|
if (matrix != null) { |
||||||
|
//transforms are given as a mat4
|
||||||
|
float[] tmpArray = new float[16]; |
||||||
|
for (int i = 0; i < tmpArray.length; i++) { |
||||||
|
tmpArray[i] = matrix.get(i).getAsFloat(); |
||||||
|
} |
||||||
|
Matrix4f mat = new Matrix4f(tmpArray); |
||||||
|
transform.fromTransformMatrix(mat); |
||||||
|
return transform; |
||||||
|
} |
||||||
|
//no matrix transforms: no transforms or transforms givens as translation/rotation/scale
|
||||||
|
JsonArray translation = nodeData.getAsJsonArray("translation"); |
||||||
|
if (translation != null) { |
||||||
|
transform.setTranslation( |
||||||
|
translation.get(0).getAsFloat(), |
||||||
|
translation.get(1).getAsFloat(), |
||||||
|
translation.get(2).getAsFloat()); |
||||||
|
} |
||||||
|
JsonArray rotation = nodeData.getAsJsonArray("rotation"); |
||||||
|
if (rotation != null) { |
||||||
|
transform.setRotation(new Quaternion( |
||||||
|
rotation.get(0).getAsFloat(), |
||||||
|
rotation.get(1).getAsFloat(), |
||||||
|
rotation.get(2).getAsFloat(), |
||||||
|
rotation.get(3).getAsFloat())); |
||||||
|
} |
||||||
|
JsonArray scale = nodeData.getAsJsonArray("scale"); |
||||||
|
if (scale != null) { |
||||||
|
transform.setScale( |
||||||
|
scale.get(0).getAsFloat(), |
||||||
|
scale.get(1).getAsFloat(), |
||||||
|
scale.get(2).getAsFloat()); |
||||||
|
} |
||||||
|
|
||||||
|
return transform; |
||||||
|
} |
||||||
|
|
||||||
|
private Mesh[] loadMeshPrimitives(int meshIndex) throws IOException { |
||||||
|
Mesh[] meshArray = (Mesh[]) fetchFromCache("meshes", meshIndex, Object.class); |
||||||
|
if (meshArray != null) { |
||||||
|
return meshArray; |
||||||
|
} |
||||||
|
JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); |
||||||
|
JsonArray primitives = meshData.getAsJsonArray("primitives"); |
||||||
|
if (primitives == null) { |
||||||
|
throw new AssetLoadException("Can't find any primitives in mesh " + meshIndex); |
||||||
|
} |
||||||
|
|
||||||
|
meshArray = new Mesh[primitives.size()]; |
||||||
|
int index = 0; |
||||||
|
for (JsonElement primitive : primitives) { |
||||||
|
JsonObject meshObject = primitive.getAsJsonObject(); |
||||||
|
Mesh mesh = new Mesh(); |
||||||
|
Integer mode = getAsInteger(meshObject, "mode"); |
||||||
|
mesh.setMode(getMeshMode(mode)); |
||||||
|
Integer indices = getAsInteger(meshObject, "indices"); |
||||||
|
if (indices != null) { |
||||||
|
mesh.setBuffer(loadVertexBuffer(indices, VertexBuffer.Type.Index)); |
||||||
|
|
||||||
|
} |
||||||
|
JsonObject attributes = meshObject.getAsJsonObject("attributes"); |
||||||
|
assertNotNull(attributes, "No attributes defined for mesh " + mesh); |
||||||
|
for (Map.Entry<String, JsonElement> entry : attributes.entrySet()) { |
||||||
|
mesh.setBuffer(loadVertexBuffer(entry.getValue().getAsInt(), getVertexBufferType(entry.getKey()))); |
||||||
|
} |
||||||
|
meshArray[index] = mesh; |
||||||
|
index++; |
||||||
|
|
||||||
|
//TODO material, targets(morph anim...)
|
||||||
|
} |
||||||
|
|
||||||
|
addToCache("meshes", meshIndex, meshArray, meshes.size()); |
||||||
|
return meshArray; |
||||||
|
} |
||||||
|
|
||||||
|
private VertexBuffer loadVertexBuffer(int accessorIndex, VertexBuffer.Type bufferType) throws IOException { |
||||||
|
|
||||||
|
if (accessors == null) { |
||||||
|
throw new AssetLoadException("No accessor attribute in the gltf file"); |
||||||
|
} |
||||||
|
JsonObject accessor = accessors.get(accessorIndex).getAsJsonObject(); |
||||||
|
Integer bufferViewIndex = getAsInteger(accessor, "bufferView"); |
||||||
|
int byteOffset = getAsInteger(accessor, "byteOffset", 0); |
||||||
|
Integer componentType = getAsInteger(accessor, "componentType"); |
||||||
|
assertNotNull(componentType, "No component type defined for accessor " + accessorIndex); |
||||||
|
boolean normalized = getAsBoolean(accessor, "normalized", false); |
||||||
|
Integer count = getAsInteger(accessor, "count"); |
||||||
|
assertNotNull(count, "No count attribute defined for accessor " + accessorIndex); |
||||||
|
String type = getAsString(accessor, "type"); |
||||||
|
assertNotNull(type, "No type attribute defined for accessor " + accessorIndex); |
||||||
|
|
||||||
|
VertexBuffer vb = new VertexBuffer(bufferType); |
||||||
|
VertexBuffer.Format format = getVertexBufferFormat(componentType); |
||||||
|
int numComponents = getNumberOfComponents(type); |
||||||
|
|
||||||
|
Buffer buff = VertexBuffer.createBuffer(format, numComponents, count); |
||||||
|
readBuffer(bufferViewIndex, byteOffset, numComponents * count, buff, numComponents); |
||||||
|
if (bufferType == VertexBuffer.Type.Index) { |
||||||
|
numComponents = 3; |
||||||
|
} |
||||||
|
vb.setupData(VertexBuffer.Usage.Dynamic, numComponents, format, buff); |
||||||
|
|
||||||
|
//TODO min / max
|
||||||
|
//TODO sparse
|
||||||
|
//TODO extensions?
|
||||||
|
//TODO extras?
|
||||||
|
return vb; |
||||||
|
} |
||||||
|
|
||||||
|
private void readBuffer(Integer bufferViewIndex, int byteOffset, int bufferSize, Buffer buff, int numComponents) throws IOException { |
||||||
|
if (bufferViewIndex == null) { |
||||||
|
//no referenced buffer, specs says to pad the buffer with zeros.
|
||||||
|
padBuffer(buff, bufferSize); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
JsonObject bufferView = bufferViews.get(bufferViewIndex).getAsJsonObject(); |
||||||
|
Integer bufferIndex = getAsInteger(bufferView, "buffer"); |
||||||
|
assertNotNull(bufferIndex, "No buffer defined for bufferView " + bufferViewIndex); |
||||||
|
int bvByteOffset = getAsInteger(bufferView, "byteOffset", 0); |
||||||
|
Integer byteLength = getAsInteger(bufferView, "byteLength"); |
||||||
|
assertNotNull(byteLength, "No byte length defined for bufferView " + bufferViewIndex); |
||||||
|
int byteStride = getAsInteger(bufferView, "byteStride", 0); |
||||||
|
|
||||||
|
//target defines ELEMENT_ARRAY_BUFFER or ARRAY_BUFFER, but we already know that since we know we load the indexbuffer or any other...
|
||||||
|
//not sure it's useful for us, but I guess it's useful when you map data directly to the GPU.
|
||||||
|
//int target = getAsInteger(bufferView, "target", 0);
|
||||||
|
|
||||||
|
byte[] data = readData(bufferIndex); |
||||||
|
populateBuffer(buff, data, bufferSize, byteOffset + bvByteOffset, byteStride, numComponents); |
||||||
|
|
||||||
|
//TODO extensions?
|
||||||
|
//TODO extras?
|
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private byte[] readData(int bufferIndex) throws IOException { |
||||||
|
|
||||||
|
if (buffers == null) { |
||||||
|
throw new AssetLoadException("No buffer defined"); |
||||||
|
} |
||||||
|
JsonObject buffer = buffers.get(bufferIndex).getAsJsonObject(); |
||||||
|
String uri = getAsString(buffer, "uri"); |
||||||
|
Integer bufferLength = getAsInteger(buffer, "byteLength"); |
||||||
|
assertNotNull(bufferLength, "No byteLength defined for buffer " + bufferIndex); |
||||||
|
if (uri != null) { |
||||||
|
if (uri.startsWith("data:")) { |
||||||
|
//inlined base64 data
|
||||||
|
//data:<mimeType>;base64,<base64 data>
|
||||||
|
//TODO handle inlined base64
|
||||||
|
throw new AssetLoadException("Inlined base64 data is not supported yet"); |
||||||
|
} else { |
||||||
|
//external file let's load it
|
||||||
|
if (!uri.endsWith(".bin")) { |
||||||
|
throw new AssetLoadException("Cannot load " + uri + ", a .bin extension is required."); |
||||||
|
} |
||||||
|
byte[] data = (byte[]) fetchFromCache("buffers", bufferIndex, Object.class); |
||||||
|
if (data != null) { |
||||||
|
return data; |
||||||
|
} |
||||||
|
InputStream input = (InputStream) info.getManager().loadAsset(info.getKey().getFolder() + uri); |
||||||
|
data = new byte[bufferLength]; |
||||||
|
input.read(data); |
||||||
|
addToCache("buffers", bufferIndex, data, buffers.size()); |
||||||
|
|
||||||
|
return data; |
||||||
|
} |
||||||
|
} else { |
||||||
|
//no URI we are in a binary file so the data is in the 2nd chunk
|
||||||
|
//TODO handle binary GLTF (GLB)
|
||||||
|
throw new AssetLoadException("Binary gltf is not supported yet"); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
private String loadMeshName(int meshIndex) { |
||||||
|
JsonObject meshData = meshes.get(meshIndex).getAsJsonObject(); |
||||||
|
return getAsString(meshData, "name"); |
||||||
|
} |
||||||
|
|
||||||
|
private <T> T fetchFromCache(String name, int index, Class<T> type) { |
||||||
|
Object[] data = dataCache.get(name); |
||||||
|
if (data == null) { |
||||||
|
return null; |
||||||
|
} |
||||||
|
return type.cast(data[index]); |
||||||
|
} |
||||||
|
|
||||||
|
private void addToCache(String name, int index, Object object, int maxLength) { |
||||||
|
Object[] data = dataCache.get(name); |
||||||
|
if (data == null) { |
||||||
|
data = new Object[maxLength]; |
||||||
|
dataCache.put(name, data); |
||||||
|
} |
||||||
|
data[index] = object; |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
@ -0,0 +1,285 @@ |
|||||||
|
package com.jme3.scene.plugins.gltf; |
||||||
|
|
||||||
|
import com.google.gson.JsonElement; |
||||||
|
import com.google.gson.JsonObject; |
||||||
|
import com.jme3.asset.AssetLoadException; |
||||||
|
import com.jme3.scene.Mesh; |
||||||
|
import com.jme3.scene.VertexBuffer; |
||||||
|
import com.jme3.util.LittleEndien; |
||||||
|
|
||||||
|
import java.io.*; |
||||||
|
import java.nio.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 07/08/2017. |
||||||
|
*/ |
||||||
|
public class GltfUtils { |
||||||
|
|
||||||
|
public static Mesh.Mode getMeshMode(Integer mode) { |
||||||
|
if (mode == null) { |
||||||
|
return Mesh.Mode.Triangles; |
||||||
|
} |
||||||
|
//too bad, we could have returned the enum value from the ordinal
|
||||||
|
//but LineLoop and LineStrip are inverted in the Mesh.Mode Enum declaration.
|
||||||
|
switch (mode) { |
||||||
|
case 0: |
||||||
|
return Mesh.Mode.Points; |
||||||
|
case 1: |
||||||
|
return Mesh.Mode.Lines; |
||||||
|
case 2: |
||||||
|
return Mesh.Mode.LineLoop; |
||||||
|
case 3: |
||||||
|
return Mesh.Mode.LineStrip; |
||||||
|
case 4: |
||||||
|
return Mesh.Mode.Triangles; |
||||||
|
case 5: |
||||||
|
return Mesh.Mode.TriangleStrip; |
||||||
|
case 6: |
||||||
|
return Mesh.Mode.TriangleFan; |
||||||
|
} |
||||||
|
return Mesh.Mode.Triangles; |
||||||
|
} |
||||||
|
|
||||||
|
public static VertexBuffer.Format getVertexBufferFormat(int componentType) { |
||||||
|
switch (componentType) { |
||||||
|
case 5120: |
||||||
|
return VertexBuffer.Format.Byte; |
||||||
|
case 5121: |
||||||
|
return VertexBuffer.Format.UnsignedByte; |
||||||
|
case 5122: |
||||||
|
return VertexBuffer.Format.Short; |
||||||
|
case 5123: |
||||||
|
return VertexBuffer.Format.UnsignedShort; |
||||||
|
case 5125: |
||||||
|
return VertexBuffer.Format.UnsignedInt; |
||||||
|
case 5126: |
||||||
|
return VertexBuffer.Format.Float; |
||||||
|
default: |
||||||
|
throw new AssetLoadException("Illegal component type: " + componentType); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static int getNumberOfComponents(String type) { |
||||||
|
switch (type) { |
||||||
|
case "SCALAR": |
||||||
|
return 1; |
||||||
|
case "VEC2": |
||||||
|
return 2; |
||||||
|
case "VEC3": |
||||||
|
return 3; |
||||||
|
case "VEC4": |
||||||
|
return 4; |
||||||
|
case "MAT2": |
||||||
|
return 4; |
||||||
|
case "MAT3": |
||||||
|
return 9; |
||||||
|
case "MAT4": |
||||||
|
return 16; |
||||||
|
default: |
||||||
|
throw new AssetLoadException("Illegal type: " + type); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static VertexBuffer.Type getVertexBufferType(String attribute) { |
||||||
|
switch (attribute) { |
||||||
|
case "POSITION": |
||||||
|
return VertexBuffer.Type.Position; |
||||||
|
case "NORMAL": |
||||||
|
return VertexBuffer.Type.Normal; |
||||||
|
case "TANGENT": |
||||||
|
return VertexBuffer.Type.Tangent; |
||||||
|
case "TEXCOORD_0": |
||||||
|
return VertexBuffer.Type.TexCoord; |
||||||
|
case "TEXCOORD_1": |
||||||
|
return VertexBuffer.Type.TexCoord2; |
||||||
|
case "TEXCOORD_2": |
||||||
|
return VertexBuffer.Type.TexCoord3; |
||||||
|
case "TEXCOORD_3": |
||||||
|
return VertexBuffer.Type.TexCoord4; |
||||||
|
case "TEXCOORD_4": |
||||||
|
return VertexBuffer.Type.TexCoord5; |
||||||
|
case "TEXCOORD_5": |
||||||
|
return VertexBuffer.Type.TexCoord6; |
||||||
|
case "TEXCOORD_6": |
||||||
|
return VertexBuffer.Type.TexCoord7; |
||||||
|
case "TEXCOORD_7": |
||||||
|
return VertexBuffer.Type.TexCoord8; |
||||||
|
case "COLOR_0": |
||||||
|
return VertexBuffer.Type.Color; |
||||||
|
case "JOINTS_0": |
||||||
|
return VertexBuffer.Type.BoneIndex; |
||||||
|
case "WEIGHT_0": |
||||||
|
return VertexBuffer.Type.BoneWeight; |
||||||
|
default: |
||||||
|
throw new AssetLoadException("Unsupported buffer attribute: " + attribute); |
||||||
|
|
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void padBuffer(Buffer buffer, int bufferSize) { |
||||||
|
buffer.clear(); |
||||||
|
if (buffer instanceof IntBuffer) { |
||||||
|
IntBuffer ib = (IntBuffer) buffer; |
||||||
|
for (int i = 0; i < bufferSize; i++) { |
||||||
|
ib.put(0); |
||||||
|
} |
||||||
|
} else if (buffer instanceof FloatBuffer) { |
||||||
|
FloatBuffer fb = (FloatBuffer) buffer; |
||||||
|
for (int i = 0; i < bufferSize; i++) { |
||||||
|
fb.put(0); |
||||||
|
} |
||||||
|
} else if (buffer instanceof ShortBuffer) { |
||||||
|
ShortBuffer sb = (ShortBuffer) buffer; |
||||||
|
for (int i = 0; i < bufferSize; i++) { |
||||||
|
sb.put((short) 0); |
||||||
|
} |
||||||
|
} else if (buffer instanceof ByteBuffer) { |
||||||
|
ByteBuffer bb = (ByteBuffer) buffer; |
||||||
|
for (int i = 0; i < bufferSize; i++) { |
||||||
|
bb.put((byte) 0); |
||||||
|
} |
||||||
|
} |
||||||
|
buffer.rewind(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void populateBuffer(Buffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents) throws IOException { |
||||||
|
buffer.clear(); |
||||||
|
|
||||||
|
if (buffer instanceof ByteBuffer) { |
||||||
|
populateByteBuffer((ByteBuffer) buffer, source, length, byteOffset, byteStride, numComponents); |
||||||
|
return; |
||||||
|
} |
||||||
|
LittleEndien stream = getStream(source); |
||||||
|
if (buffer instanceof ShortBuffer) { |
||||||
|
populateShortBuffer((ShortBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); |
||||||
|
} else if (buffer instanceof IntBuffer) { |
||||||
|
populateIntBuffer((IntBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); |
||||||
|
} else if (buffer instanceof FloatBuffer) { |
||||||
|
populateFloatBuffer((FloatBuffer) buffer, stream, length, byteOffset, byteStride, numComponents); |
||||||
|
} |
||||||
|
buffer.rewind(); |
||||||
|
} |
||||||
|
|
||||||
|
private static void populateByteBuffer(ByteBuffer buffer, byte[] source, int length, int byteOffset, int byteStride, int numComponents) { |
||||||
|
int index = byteOffset; |
||||||
|
int componentSize = 1; |
||||||
|
while (index < length + byteOffset) { |
||||||
|
for (int i = 0; i < numComponents; i++) { |
||||||
|
buffer.put(source[index + i]); |
||||||
|
} |
||||||
|
index += Math.max(componentSize * numComponents, byteStride); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void populateShortBuffer(ShortBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException { |
||||||
|
int index = byteOffset; |
||||||
|
int componentSize = 2; |
||||||
|
int end = length * componentSize + byteOffset; |
||||||
|
stream.skipBytes(byteOffset); |
||||||
|
while (index < end) { |
||||||
|
for (int i = 0; i < numComponents; i++) { |
||||||
|
buffer.put(stream.readShort()); |
||||||
|
} |
||||||
|
index += Math.max(componentSize * numComponents, byteStride); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void populateIntBuffer(IntBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException { |
||||||
|
int index = byteOffset; |
||||||
|
int componentSize = 4; |
||||||
|
int end = length * componentSize + byteOffset; |
||||||
|
stream.skipBytes(byteOffset); |
||||||
|
while (index < end) { |
||||||
|
for (int i = 0; i < numComponents; i++) { |
||||||
|
buffer.put(stream.readInt()); |
||||||
|
} |
||||||
|
index += Math.max(componentSize * numComponents, byteStride); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static void populateFloatBuffer(FloatBuffer buffer, LittleEndien stream, int length, int byteOffset, int byteStride, int numComponents) throws IOException { |
||||||
|
int index = byteOffset; |
||||||
|
int componentSize = 4; |
||||||
|
int end = length * componentSize + byteOffset; |
||||||
|
stream.skipBytes(byteOffset); |
||||||
|
while (index < end) { |
||||||
|
for (int i = 0; i < numComponents; i++) { |
||||||
|
buffer.put(stream.readFloat()); |
||||||
|
} |
||||||
|
index += Math.max(componentSize * numComponents, byteStride); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
private static LittleEndien getStream(byte[] buffer) { |
||||||
|
return new LittleEndien(new DataInputStream(new ByteArrayInputStream(buffer))); |
||||||
|
} |
||||||
|
|
||||||
|
public static String getAsString(JsonObject parent, String name) { |
||||||
|
JsonElement el = parent.get(name); |
||||||
|
return el == null ? null : el.getAsString(); |
||||||
|
} |
||||||
|
|
||||||
|
public static Integer getAsInteger(JsonObject parent, String name) { |
||||||
|
JsonElement el = parent.get(name); |
||||||
|
return el == null ? null : el.getAsInt(); |
||||||
|
} |
||||||
|
|
||||||
|
public static Integer getAsInteger(JsonObject parent, String name, int defaultValue) { |
||||||
|
JsonElement el = parent.get(name); |
||||||
|
return el == null ? defaultValue : el.getAsInt(); |
||||||
|
} |
||||||
|
|
||||||
|
public static Boolean getAsBoolean(JsonObject parent, String name) { |
||||||
|
JsonElement el = parent.get(name); |
||||||
|
return el == null ? null : el.getAsBoolean(); |
||||||
|
} |
||||||
|
|
||||||
|
public static Boolean getAsBoolean(JsonObject parent, String name, boolean defaultValue) { |
||||||
|
JsonElement el = parent.get(name); |
||||||
|
return el == null ? defaultValue : el.getAsBoolean(); |
||||||
|
} |
||||||
|
|
||||||
|
public static void assertNotNull(Object o, String errorMessage) { |
||||||
|
if (o == null) { |
||||||
|
throw new AssetLoadException(errorMessage); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public static void dumpMesh(Mesh m) { |
||||||
|
for (VertexBuffer vertexBuffer : m.getBufferList().getArray()) { |
||||||
|
System.err.println(vertexBuffer.getBufferType()); |
||||||
|
System.err.println(vertexBuffer.getFormat()); |
||||||
|
if (vertexBuffer.getData() instanceof FloatBuffer) { |
||||||
|
FloatBuffer b = (FloatBuffer) vertexBuffer.getData(); |
||||||
|
float[] arr = new float[b.capacity()]; |
||||||
|
b.rewind(); |
||||||
|
b.get(arr); |
||||||
|
b.rewind(); |
||||||
|
for (float v : arr) { |
||||||
|
System.err.print(v + ","); |
||||||
|
} |
||||||
|
} |
||||||
|
if (vertexBuffer.getData() instanceof ShortBuffer) { |
||||||
|
ShortBuffer b = (ShortBuffer) vertexBuffer.getData(); |
||||||
|
short[] arr = new short[b.capacity()]; |
||||||
|
b.rewind(); |
||||||
|
b.get(arr); |
||||||
|
b.rewind(); |
||||||
|
for (short v : arr) { |
||||||
|
System.err.print(v + ","); |
||||||
|
} |
||||||
|
} |
||||||
|
if (vertexBuffer.getData() instanceof IntBuffer) { |
||||||
|
IntBuffer b = (IntBuffer) vertexBuffer.getData(); |
||||||
|
int[] arr = new int[b.capacity()]; |
||||||
|
b.rewind(); |
||||||
|
b.get(arr); |
||||||
|
b.rewind(); |
||||||
|
for (int v : arr) { |
||||||
|
System.err.print(v + ","); |
||||||
|
} |
||||||
|
} |
||||||
|
System.err.println("\n---------------------------"); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,50 @@ |
|||||||
|
package com.jme3.scene.plugins.gltf; |
||||||
|
|
||||||
|
import com.jme3.asset.AssetManager; |
||||||
|
import com.jme3.material.plugin.TestMaterialWrite; |
||||||
|
import com.jme3.scene.Node; |
||||||
|
import com.jme3.scene.Spatial; |
||||||
|
import com.jme3.system.JmeSystem; |
||||||
|
import org.junit.Before; |
||||||
|
import org.junit.Test; |
||||||
|
|
||||||
|
import static org.junit.Assert.*; |
||||||
|
|
||||||
|
/** |
||||||
|
* Created by Nehon on 07/08/2017. |
||||||
|
*/ |
||||||
|
public class GltfLoaderTest { |
||||||
|
|
||||||
|
private final static String indentString = "\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t"; |
||||||
|
|
||||||
|
private AssetManager assetManager; |
||||||
|
|
||||||
|
@Before |
||||||
|
public void init() { |
||||||
|
assetManager = JmeSystem.newAssetManager( |
||||||
|
TestMaterialWrite.class.getResource("/com/jme3/asset/Desktop.cfg")); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@Test |
||||||
|
public void testLoad() { |
||||||
|
Spatial scene = assetManager.loadModel("gltf/box/box.gltf"); |
||||||
|
dumpScene(scene, 0); |
||||||
|
// scene = assetManager.loadModel("gltf/hornet/scene.gltf");
|
||||||
|
// dumpScene(scene, 0);
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private void dumpScene(Spatial s, int indent) { |
||||||
|
System.err.println(indentString.substring(0, indent) + s.getName() + " (" + s.getClass().getSimpleName() + ") / " + |
||||||
|
s.getLocalTransform().getTranslation().toString() + ", " + |
||||||
|
s.getLocalTransform().getRotation().toString() + ", " + |
||||||
|
s.getLocalTransform().getScale().toString()); |
||||||
|
if (s instanceof Node) { |
||||||
|
Node n = (Node) s; |
||||||
|
for (Spatial spatial : n.getChildren()) { |
||||||
|
dumpScene(spatial, indent + 1); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Binary file not shown.
@ -0,0 +1,142 @@ |
|||||||
|
{ |
||||||
|
"asset": { |
||||||
|
"generator": "COLLADA2GLTF", |
||||||
|
"version": "2.0" |
||||||
|
}, |
||||||
|
"scene": 0, |
||||||
|
"scenes": [ |
||||||
|
{ |
||||||
|
"nodes": [ |
||||||
|
0 |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"nodes": [ |
||||||
|
{ |
||||||
|
"children": [ |
||||||
|
1 |
||||||
|
], |
||||||
|
"matrix": [ |
||||||
|
1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
-1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0 |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"mesh": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"meshes": [ |
||||||
|
{ |
||||||
|
"primitives": [ |
||||||
|
{ |
||||||
|
"attributes": { |
||||||
|
"NORMAL": 1, |
||||||
|
"POSITION": 2 |
||||||
|
}, |
||||||
|
"indices": 0, |
||||||
|
"mode": 4, |
||||||
|
"material": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "Mesh" |
||||||
|
} |
||||||
|
], |
||||||
|
"accessors": [ |
||||||
|
{ |
||||||
|
"bufferView": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5123, |
||||||
|
"count": 36, |
||||||
|
"max": [ |
||||||
|
23 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
0 |
||||||
|
], |
||||||
|
"type": "SCALAR" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 24, |
||||||
|
"max": [ |
||||||
|
1.0, |
||||||
|
1.0, |
||||||
|
1.0 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-1.0, |
||||||
|
-1.0, |
||||||
|
-1.0 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 288, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 24, |
||||||
|
"max": [ |
||||||
|
0.5, |
||||||
|
0.5, |
||||||
|
0.5 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-0.5, |
||||||
|
-0.5, |
||||||
|
-0.5 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
} |
||||||
|
], |
||||||
|
"materials": [ |
||||||
|
{ |
||||||
|
"pbrMetallicRoughness": { |
||||||
|
"baseColorFactor": [ |
||||||
|
0.800000011920929, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0 |
||||||
|
], |
||||||
|
"metallicFactor": 0.0 |
||||||
|
}, |
||||||
|
"name": "Red" |
||||||
|
} |
||||||
|
], |
||||||
|
"bufferViews": [ |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 576, |
||||||
|
"byteLength": 72, |
||||||
|
"target": 34963 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"byteLength": 576, |
||||||
|
"byteStride": 12, |
||||||
|
"target": 34962 |
||||||
|
} |
||||||
|
], |
||||||
|
"buffers": [ |
||||||
|
{ |
||||||
|
"byteLength": 648, |
||||||
|
"uri": "Box0.bin" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
Binary file not shown.
@ -0,0 +1,142 @@ |
|||||||
|
{ |
||||||
|
"asset": { |
||||||
|
"generator": "COLLADA2GLTF", |
||||||
|
"version": "2.0" |
||||||
|
}, |
||||||
|
"scene": 0, |
||||||
|
"scenes": [ |
||||||
|
{ |
||||||
|
"nodes": [ |
||||||
|
0 |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"nodes": [ |
||||||
|
{ |
||||||
|
"children": [ |
||||||
|
1 |
||||||
|
], |
||||||
|
"matrix": [ |
||||||
|
1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
-1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0 |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"mesh": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"meshes": [ |
||||||
|
{ |
||||||
|
"primitives": [ |
||||||
|
{ |
||||||
|
"attributes": { |
||||||
|
"NORMAL": 1, |
||||||
|
"POSITION": 2 |
||||||
|
}, |
||||||
|
"indices": 0, |
||||||
|
"mode": 4, |
||||||
|
"material": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "Mesh" |
||||||
|
} |
||||||
|
], |
||||||
|
"accessors": [ |
||||||
|
{ |
||||||
|
"bufferView": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5123, |
||||||
|
"count": 36, |
||||||
|
"max": [ |
||||||
|
23 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
0 |
||||||
|
], |
||||||
|
"type": "SCALAR" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 24, |
||||||
|
"max": [ |
||||||
|
1.0, |
||||||
|
1.0, |
||||||
|
1.0 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-1.0, |
||||||
|
-1.0, |
||||||
|
-1.0 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 288, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 24, |
||||||
|
"max": [ |
||||||
|
0.5, |
||||||
|
0.5, |
||||||
|
0.5 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-0.5, |
||||||
|
-0.5, |
||||||
|
-0.5 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
} |
||||||
|
], |
||||||
|
"materials": [ |
||||||
|
{ |
||||||
|
"pbrMetallicRoughness": { |
||||||
|
"baseColorFactor": [ |
||||||
|
0.800000011920929, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0 |
||||||
|
], |
||||||
|
"metallicFactor": 0.0 |
||||||
|
}, |
||||||
|
"name": "Red" |
||||||
|
} |
||||||
|
], |
||||||
|
"bufferViews": [ |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 576, |
||||||
|
"byteLength": 72, |
||||||
|
"target": 34963 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"byteLength": 576, |
||||||
|
"byteStride": 12, |
||||||
|
"target": 34962 |
||||||
|
} |
||||||
|
], |
||||||
|
"buffers": [ |
||||||
|
{ |
||||||
|
"byteLength": 648, |
||||||
|
"uri": "Box0.bin" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
@ -0,0 +1,219 @@ |
|||||||
|
{ |
||||||
|
"asset": { |
||||||
|
"generator": "COLLADA2GLTF", |
||||||
|
"version": "2.0" |
||||||
|
}, |
||||||
|
"scene": 0, |
||||||
|
"scenes": [ |
||||||
|
{ |
||||||
|
"nodes": [ |
||||||
|
0 |
||||||
|
] |
||||||
|
} |
||||||
|
], |
||||||
|
"nodes": [ |
||||||
|
{ |
||||||
|
"children": [ |
||||||
|
2, |
||||||
|
1 |
||||||
|
], |
||||||
|
"matrix": [ |
||||||
|
0.009999999776482582, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.009999999776482582, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.009999999776482582, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
1.0 |
||||||
|
] |
||||||
|
}, |
||||||
|
{ |
||||||
|
"matrix": [ |
||||||
|
-0.7289686799049377, |
||||||
|
0.0, |
||||||
|
-0.6845470666885376, |
||||||
|
0.0, |
||||||
|
-0.4252049028873444, |
||||||
|
0.7836934328079224, |
||||||
|
0.4527972936630249, |
||||||
|
0.0, |
||||||
|
0.5364750623703003, |
||||||
|
0.6211478114128113, |
||||||
|
-0.571287989616394, |
||||||
|
0.0, |
||||||
|
400.1130065917969, |
||||||
|
463.2640075683594, |
||||||
|
-431.0780334472656, |
||||||
|
1.0 |
||||||
|
], |
||||||
|
"camera": 0 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"mesh": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"cameras": [ |
||||||
|
{ |
||||||
|
"perspective": { |
||||||
|
"aspectRatio": 1.5, |
||||||
|
"yfov": 0.6605925559997559, |
||||||
|
"zfar": 10000.0, |
||||||
|
"znear": 1.0 |
||||||
|
}, |
||||||
|
"type": "perspective" |
||||||
|
} |
||||||
|
], |
||||||
|
"meshes": [ |
||||||
|
{ |
||||||
|
"primitives": [ |
||||||
|
{ |
||||||
|
"attributes": { |
||||||
|
"NORMAL": 1, |
||||||
|
"POSITION": 2, |
||||||
|
"TEXCOORD_0": 3 |
||||||
|
}, |
||||||
|
"indices": 0, |
||||||
|
"mode": 4, |
||||||
|
"material": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"name": "LOD3spShape" |
||||||
|
} |
||||||
|
], |
||||||
|
"accessors": [ |
||||||
|
{ |
||||||
|
"bufferView": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5123, |
||||||
|
"count": 12636, |
||||||
|
"max": [ |
||||||
|
2398 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
0 |
||||||
|
], |
||||||
|
"type": "SCALAR" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 2399, |
||||||
|
"max": [ |
||||||
|
0.9995989799499512, |
||||||
|
0.999580979347229, |
||||||
|
0.9984359741210938 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-0.9990839958190918, |
||||||
|
-1.0, |
||||||
|
-0.9998319745063782 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 1, |
||||||
|
"byteOffset": 28788, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 2399, |
||||||
|
"max": [ |
||||||
|
96.17990112304688, |
||||||
|
163.97000122070313, |
||||||
|
53.92519760131836 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
-69.29850006103516, |
||||||
|
9.929369926452637, |
||||||
|
-61.32819747924805 |
||||||
|
], |
||||||
|
"type": "VEC3" |
||||||
|
}, |
||||||
|
{ |
||||||
|
"bufferView": 2, |
||||||
|
"byteOffset": 0, |
||||||
|
"componentType": 5126, |
||||||
|
"count": 2399, |
||||||
|
"max": [ |
||||||
|
0.9833459854125976, |
||||||
|
0.9800369739532472 |
||||||
|
], |
||||||
|
"min": [ |
||||||
|
0.026409000158309938, |
||||||
|
0.01996302604675293 |
||||||
|
], |
||||||
|
"type": "VEC2" |
||||||
|
} |
||||||
|
], |
||||||
|
"materials": [ |
||||||
|
{ |
||||||
|
"pbrMetallicRoughness": { |
||||||
|
"baseColorTexture": { |
||||||
|
"index": 0 |
||||||
|
}, |
||||||
|
"metallicFactor": 0.0 |
||||||
|
}, |
||||||
|
"emissiveFactor": [ |
||||||
|
0.0, |
||||||
|
0.0, |
||||||
|
0.0 |
||||||
|
], |
||||||
|
"name": "blinn3-fx" |
||||||
|
} |
||||||
|
], |
||||||
|
"textures": [ |
||||||
|
{ |
||||||
|
"sampler": 0, |
||||||
|
"source": 0 |
||||||
|
} |
||||||
|
], |
||||||
|
"images": [ |
||||||
|
{ |
||||||
|
"uri": "DuckCM.png" |
||||||
|
} |
||||||
|
], |
||||||
|
"samplers": [ |
||||||
|
{ |
||||||
|
"magFilter": 9729, |
||||||
|
"minFilter": 9986, |
||||||
|
"wrapS": 10497, |
||||||
|
"wrapT": 10497 |
||||||
|
} |
||||||
|
], |
||||||
|
"bufferViews": [ |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 76768, |
||||||
|
"byteLength": 25272, |
||||||
|
"target": 34963 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 0, |
||||||
|
"byteLength": 57576, |
||||||
|
"byteStride": 12, |
||||||
|
"target": 34962 |
||||||
|
}, |
||||||
|
{ |
||||||
|
"buffer": 0, |
||||||
|
"byteOffset": 57576, |
||||||
|
"byteLength": 19192, |
||||||
|
"byteStride": 8, |
||||||
|
"target": 34962 |
||||||
|
} |
||||||
|
], |
||||||
|
"buffers": [ |
||||||
|
{ |
||||||
|
"byteLength": 102040, |
||||||
|
"uri": "Duck0.bin" |
||||||
|
} |
||||||
|
] |
||||||
|
} |
Binary file not shown.
After Width: | Height: | Size: 16 KiB |
Loading…
Reference in new issue