OSDN Git Service

Merge pull request #806 from jrenner/run-header-tool
[mikumikustudio/libgdx-mikumikustudio.git] / gdx / src / com / badlogic / gdx / graphics / g3d / loader / G3dModelLoader.java
1 /*******************************************************************************\r
2  * Copyright 2011 See AUTHORS file.\r
3  * \r
4  * Licensed under the Apache License, Version 2.0 (the "License");\r
5  * you may not use this file except in compliance with the License.\r
6  * You may obtain a copy of the License at\r
7  * \r
8  *   http://www.apache.org/licenses/LICENSE-2.0\r
9  * \r
10  * Unless required by applicable law or agreed to in writing, software\r
11  * distributed under the License is distributed on an "AS IS" BASIS,\r
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\r
13  * See the License for the specific language governing permissions and\r
14  * limitations under the License.\r
15  ******************************************************************************/
16
17 package com.badlogic.gdx.graphics.g3d.loader;
18
19 import com.badlogic.gdx.assets.AssetLoaderParameters;
20 import com.badlogic.gdx.assets.loaders.FileHandleResolver;
21 import com.badlogic.gdx.assets.loaders.ModelLoader;
22 import com.badlogic.gdx.files.FileHandle;
23 import com.badlogic.gdx.graphics.Color;
24 import com.badlogic.gdx.graphics.GL10;
25 import com.badlogic.gdx.graphics.VertexAttribute;
26 import com.badlogic.gdx.graphics.g3d.Model;
27 import com.badlogic.gdx.graphics.g3d.model.data.ModelAnimation;
28 import com.badlogic.gdx.graphics.g3d.model.data.ModelData;
29 import com.badlogic.gdx.graphics.g3d.model.data.ModelMaterial;
30 import com.badlogic.gdx.graphics.g3d.model.data.ModelMesh;
31 import com.badlogic.gdx.graphics.g3d.model.data.ModelMeshPart;
32 import com.badlogic.gdx.graphics.g3d.model.data.ModelNode;
33 import com.badlogic.gdx.graphics.g3d.model.data.ModelNodeAnimation;
34 import com.badlogic.gdx.graphics.g3d.model.data.ModelNodeKeyframe;
35 import com.badlogic.gdx.graphics.g3d.model.data.ModelNodePart;
36 import com.badlogic.gdx.graphics.g3d.model.data.ModelTexture;
37 import com.badlogic.gdx.math.Matrix4;
38 import com.badlogic.gdx.math.Quaternion;
39 import com.badlogic.gdx.math.Vector2;
40 import com.badlogic.gdx.math.Vector3;
41 import com.badlogic.gdx.utils.Array;
42 import com.badlogic.gdx.utils.ArrayMap;
43 import com.badlogic.gdx.utils.BaseJsonReader;
44 import com.badlogic.gdx.utils.GdxRuntimeException;
45 import com.badlogic.gdx.utils.JsonValue;
46 import com.badlogic.gdx.utils.UBJsonReader;
47
48 public class G3dModelLoader extends ModelLoader<AssetLoaderParameters<Model>> {
49         public static final short VERSION_HI = 0;
50         public static final short VERSION_LO = 1;
51         protected final BaseJsonReader reader;
52         
53         public G3dModelLoader(final BaseJsonReader reader) {
54                 this(reader, null);
55         }
56         
57         public G3dModelLoader(BaseJsonReader reader, FileHandleResolver resolver) {
58                 super(resolver);
59                 this.reader = reader;
60         }
61
62         @Override
63         public ModelData loadModelData (FileHandle fileHandle, AssetLoaderParameters<Model> parameters) {
64                 return parseModel(fileHandle);
65         }
66
67         public ModelData parseModel (FileHandle handle) {
68                 JsonValue json = reader.parse(handle);
69                 ModelData model = new ModelData();
70                 JsonValue version = json.require("version");
71                 model.version[0] = (short)version.getInt(0);
72                 model.version[1] = (short)version.getInt(1);
73                 if (model.version[0] != VERSION_HI || model.version[1] != VERSION_LO)
74                         throw new GdxRuntimeException("Model version not supported");
75
76                 model.id = json.getString("id", "");
77                 parseMeshes(model, json);
78                 parseMaterials(model, json, handle.parent().path());
79                 parseNodes(model, json);
80                 parseAnimations(model, json);
81                 return model;
82         }
83
84         private void parseMeshes (ModelData model, JsonValue json) {
85                 JsonValue meshes = json.require("meshes");
86                 
87                 model.meshes.ensureCapacity(meshes.size);
88                 for (JsonValue mesh = meshes.child(); mesh != null; mesh = mesh.next()) {
89                         ModelMesh jsonMesh = new ModelMesh();
90                         
91                         String id = mesh.getString("id", "");
92                         jsonMesh.id = id;
93                         
94                         JsonValue attributes = mesh.require("attributes");
95                         jsonMesh.attributes = parseAttributes(attributes);
96                         
97                         JsonValue vertices = mesh.require("vertices");
98                         float[] verts = new float[vertices.size];
99                         int j = 0;
100                         for (JsonValue value = vertices.child(); value != null; value = value.next(), j++) {
101                                 verts[j] = value.asFloat();
102                         }
103                         jsonMesh.vertices = verts;
104                         
105                         JsonValue meshParts = mesh.require("parts");
106                         Array<ModelMeshPart> parts = new Array<ModelMeshPart>();
107                         for (JsonValue meshPart = meshParts.child(); meshPart != null; meshPart = meshPart.next()) {
108                                 ModelMeshPart jsonPart = new ModelMeshPart();
109                                 String partId = meshPart.getString("id", null);
110                                 if(id == null) {
111                                         throw new GdxRuntimeException("Not id given for mesh part");
112                                 }
113                                 for(ModelMeshPart other: parts) {
114                                         if(other.id.equals(partId)) {
115                                                 throw new GdxRuntimeException("Mesh part with id '" + partId + "' already in defined");
116                                         }
117                                 }
118                                 jsonPart.id = partId;
119                                 
120                                 String type = meshPart.getString("type", null);
121                                 if(type == null) {
122                                         throw new GdxRuntimeException("No primitive type given for mesh part '" + partId + "'");
123                                 }
124                                 jsonPart.primitiveType = parseType(type);
125                                 
126                                 JsonValue indices = meshPart.require("indices");
127                                 short[] partIndices = new short[indices.size];
128                                 int k = 0;
129                                 for (JsonValue value = indices.child(); value != null; value = value.next(), k++) {
130                                         partIndices[k] = (short)value.asInt();
131                                 }
132                                 jsonPart.indices = partIndices;
133                                 parts.add(jsonPart);
134                         }
135                         jsonMesh.parts = parts.toArray(ModelMeshPart.class);
136                         model.meshes.add(jsonMesh);
137                 }
138         }
139         
140         private int parseType (String type) {
141                 if(type.equals("TRIANGLES")) {
142                         return GL10.GL_TRIANGLES;
143                 } else if(type.equals("LINES")) {
144                         return GL10.GL_LINES;
145                 } else if(type.equals("POINTS")) {
146                         return GL10.GL_POINTS;
147                 } else if(type.equals("TRIANGLE_STRIP")) {
148                         return GL10.GL_TRIANGLE_STRIP;
149                 } else if(type.equals("LINE_STRIP")) {
150                         return GL10.GL_LINE_STRIP;
151                 } else { 
152                         throw new GdxRuntimeException("Unknown primitive type '" + type + "', should be one of triangle, trianglestrip, line, linestrip, lineloop or point");
153                 }
154         }
155
156         private VertexAttribute[] parseAttributes (JsonValue attributes) {
157                 Array<VertexAttribute> vertexAttributes = new Array<VertexAttribute>();
158                 int unit = 0;
159                 int blendWeightCount = 0;
160                 for (JsonValue value = attributes.child(); value != null; value = value.next()) {
161                         String attribute = value.asString();
162                         String attr = (String)attribute;
163                         if(attr.equals("POSITION")) {
164                                 vertexAttributes.add(VertexAttribute.Position());
165                         } else if(attr.equals("NORMAL")) {
166                                 vertexAttributes.add(VertexAttribute.Normal());
167                         } else if(attr.equals("COLOR")) {
168                                 vertexAttributes.add(VertexAttribute.ColorUnpacked());
169                         } else if(attr.equals("COLORPACKED")) {
170                                 vertexAttributes.add(VertexAttribute.Color());
171                         } else if(attr.equals("TANGENT")) {
172                                 vertexAttributes.add(VertexAttribute.Tangent());
173                         } else if(attr.equals("BINORMAL")) {
174                                 vertexAttributes.add(VertexAttribute.Binormal());
175                         } else if(attr.startsWith("TEXCOORD")) {
176                                 vertexAttributes.add(VertexAttribute.TexCoords(unit++));
177                         } else if(attr.startsWith("BLENDWEIGHT")) {
178                                 vertexAttributes.add(VertexAttribute.BoneWeight(blendWeightCount++));
179                         } else {
180                                 throw new GdxRuntimeException("Unknown vertex attribute '" + attr + "', should be one of position, normal, uv, tangent or binormal");
181                         }
182                 }
183                 return vertexAttributes.toArray(VertexAttribute.class);
184         }
185
186         private void parseMaterials (ModelData model, JsonValue json, String materialDir) {
187                 JsonValue materials = json.get("materials");
188                 if(materials == null) {
189                         // we should probably create some default material in this case
190                 }
191                 else {
192                         model.materials.ensureCapacity(materials.size);
193                         for (JsonValue material = materials.child(); material != null; material = material.next()) {
194                                 ModelMaterial jsonMaterial = new ModelMaterial();
195                                 
196                                 String id = material.getString("id", null);
197                                 if(id == null)
198                                         throw new GdxRuntimeException("Material needs an id.");
199
200                                 jsonMaterial.id = id;
201                                                         
202                                 // Read material colors
203                                 final JsonValue diffuse = material.get("diffuse");
204                                 if (diffuse != null)
205                                         jsonMaterial.diffuse = parseColor(diffuse);
206                                 final JsonValue ambient = material.get("ambient");
207                                 if (ambient != null)
208                                         jsonMaterial.ambient = parseColor(ambient);
209                                 final JsonValue emissive= material.get("emissive");
210                                 if (emissive!= null)
211                                         jsonMaterial.emissive = parseColor(emissive);
212                                 final JsonValue specular= material.get("specular");
213                                 if (specular!= null)
214                                         jsonMaterial.specular = parseColor(specular);
215                                 final JsonValue reflection= material.get("reflection");
216                                 if (reflection!= null)
217                                         jsonMaterial.reflection = parseColor(reflection);
218                                 // Read shininess
219                                 jsonMaterial.shininess = material.getFloat("shininess", 0.0f);
220                                 // Read opacity
221                                 jsonMaterial.opacity = material.getFloat("opacity", 1.0f);
222                                 
223                                 // Read textures
224                                 JsonValue textures = material.get("textures");
225                                 if(textures != null){
226                                         for (JsonValue texture = textures.child(); texture != null; texture = texture.next()) {
227                                                 ModelTexture jsonTexture = new ModelTexture();
228                                                 
229                                                 String textureId = texture.getString("id", null);
230                                                 if(textureId == null)
231                                                         throw new GdxRuntimeException("Texture has no id.");
232                                                 jsonTexture.id = textureId;
233                                                 
234                                                 String fileName = texture.getString("filename", null);
235                                                 if(fileName == null)
236                                                         throw new GdxRuntimeException("Texture needs filename.");
237                                                 jsonTexture.fileName = materialDir + (materialDir.length() == 0 || materialDir.endsWith("/") ? "" : "/") + fileName;
238                                                 
239                                                 jsonTexture.uvTranslation = readVector2(texture.get("uvTranslation"), 0f, 0f);
240                                                 jsonTexture.uvScaling = readVector2(texture.get("uvScaling"), 1f, 1f);
241                                                 
242                                                 String textureType = texture.getString("type", null);
243                                                 if(textureType == null)
244                                                         throw new GdxRuntimeException("Texture needs type.");
245                                                 
246                                                 jsonTexture.usage = parseTextureUsage(textureType);
247                                                 
248                                                 if(jsonMaterial.textures == null)
249                                                         jsonMaterial.textures = new Array<ModelTexture>();
250                                                 jsonMaterial.textures.add(jsonTexture);
251                                         }
252                                 }
253
254                                 model.materials.add(jsonMaterial);
255                         }
256                 }
257         }
258         
259         private int parseTextureUsage(final String value) {
260                 if (value.equalsIgnoreCase("AMBIENT"))
261                         return ModelTexture.USAGE_AMBIENT;
262                 else if (value.equalsIgnoreCase("BUMP"))
263                         return ModelTexture.USAGE_BUMP;
264                 else if (value.equalsIgnoreCase("DIFFUSE"))
265                         return ModelTexture.USAGE_DIFFUSE;
266                 else if (value.equalsIgnoreCase("EMISSIVE"))
267                         return ModelTexture.USAGE_EMISSIVE;
268                 else if (value.equalsIgnoreCase("NONE"))
269                         return ModelTexture.USAGE_NONE;
270                 else if (value.equalsIgnoreCase("NORMAL"))
271                         return ModelTexture.USAGE_NORMAL;
272                 else if (value.equalsIgnoreCase("REFLECTION"))
273                         return ModelTexture.USAGE_REFLECTION;
274                 else if (value.equalsIgnoreCase("SHININESS"))
275                         return ModelTexture.USAGE_SHININESS;
276                 else if (value.equalsIgnoreCase("SPECULAR"))
277                         return ModelTexture.USAGE_SPECULAR;
278                 else if (value.equalsIgnoreCase("TRANSPARENCY"))
279                         return ModelTexture.USAGE_TRANSPARENCY;
280                 return ModelTexture.USAGE_UNKNOWN;
281         }
282
283         private Color parseColor (JsonValue colorArray) {
284                 if(colorArray.size >= 3)
285                         return new Color(colorArray.getFloat(0), colorArray.getFloat(1), colorArray.getFloat(2), 1.0f);
286                 else
287                         throw new GdxRuntimeException("Expected Color values <> than three.");
288         }
289
290         private Vector2 readVector2 (JsonValue vectorArray, float x, float y) {
291                 if(vectorArray == null)
292                         return new Vector2(x, y);
293                 else if(vectorArray.size == 2)
294                         return new Vector2(vectorArray.getFloat(0), vectorArray.getFloat(1));
295                 else
296                         throw new GdxRuntimeException("Expected Vector2 values <> than two.");
297         }
298
299         private Array<ModelNode> parseNodes (ModelData model, JsonValue json) {
300                 JsonValue nodes = json.get("nodes");
301                 if(nodes == null) {
302                         throw new GdxRuntimeException("At least one node is required.");
303                 }
304                 
305                 model.nodes.ensureCapacity(nodes.size);
306                 for (JsonValue node = nodes.child(); node != null; node = node.next()) {
307                         model.nodes.add(parseNodesRecursively(node));
308                 }
309                 return model.nodes;
310         }
311         
312         private final Quaternion tempQ = new Quaternion(); 
313         private ModelNode parseNodesRecursively(JsonValue json){
314                 ModelNode jsonNode = new ModelNode();
315                 
316                 String id = json.getString("id", null);
317                 if(id == null)
318                         throw new GdxRuntimeException("Node id missing.");
319                 jsonNode.id = id;
320                 
321                 JsonValue translation = json.get("translation");
322                 if (translation != null && translation.size != 3)
323                         throw new GdxRuntimeException("Node translation incomplete");
324                 jsonNode.translation = translation == null ? null : new Vector3(translation.getFloat(0), translation.getFloat(1), translation.getFloat(2));
325                 
326                 JsonValue rotation = json.get("rotation");
327                 if(rotation != null && rotation.size != 4)
328                         throw new GdxRuntimeException("Node rotation incomplete");
329                 jsonNode.rotation = rotation == null ? null : new Quaternion(rotation.getFloat(0), rotation.getFloat(1), rotation.getFloat(2), rotation.getFloat(3));
330                 
331                 JsonValue scale = json.get("scale");
332                 if(scale != null && scale.size != 3)
333                         throw new GdxRuntimeException("Node scale incomplete");
334                 jsonNode.scale = scale == null ? null : new Vector3(scale.getFloat(0), scale.getFloat(1), scale.getFloat(2));
335                 
336                 String meshId = json.getString("mesh", null);
337                 if(meshId != null)
338                         jsonNode.meshId = meshId;
339                 
340                 JsonValue materials = json.get("parts");
341                 if(materials != null){
342                         jsonNode.parts = new ModelNodePart[materials.size];
343                         int i = 0;
344                         for (JsonValue material = materials.child(); material != null; material = material.next(), i++) {
345                                 ModelNodePart nodePart = new ModelNodePart();
346                                 
347                                 String meshPartId = material.getString("meshpartid", null);
348                                 String materialId = material.getString("materialid", null);
349                                 if(meshPartId == null || materialId == null){
350                                         throw new GdxRuntimeException("Node "+id+" part is missing meshPartId or materialId");
351                                 }
352                                 nodePart.materialId = materialId;
353                                 nodePart.meshPartId = meshPartId;
354                                 
355                                 JsonValue bones = material.get("bones");
356                                 if (bones != null) {
357                                         nodePart.bones = new ArrayMap<String, Matrix4>(true, bones.size, String.class, Matrix4.class);
358                                         int j = 0;
359                                         for (JsonValue bone = bones.child(); bone != null; bone = bone.next(), j++) {
360                                                 String nodeId = bone.getString("node", null);
361                                                 if (nodeId == null)
362                                                         throw new GdxRuntimeException("Bone node ID missing");
363                                                 
364                                                 Matrix4 transform = new Matrix4();
365                                                 
366                                                 JsonValue val = bone.get("translation");
367                                                 if (val != null && val.size >= 3)
368                                                         transform.translate(val.getFloat(0), val.getFloat(1), val.getFloat(2));
369                                                 
370                                                 val = bone.get("rotation");
371                                                 if(val != null && val.size >= 4)
372                                                         transform.rotate(tempQ.set(val.getFloat(0), val.getFloat(1), val.getFloat(2), val.getFloat(3)));
373                                                 
374                                                 val = bone.get("scale");
375                                                 if(val != null && val.size >= 3)
376                                                         transform.scale(val.getFloat(0), val.getFloat(1), val.getFloat(2));
377                                                 
378                                                 nodePart.bones.put(nodeId, transform);
379                                         }
380                                 }
381                                 
382                                 jsonNode.parts[i] = nodePart;
383                         }
384                 }
385                 
386                 JsonValue children = json.get("children");
387                 if(children != null){
388                         jsonNode.children = new ModelNode[children.size];
389
390                         int i = 0;
391                         for (JsonValue child = children.child(); child != null; child = child.next(), i++) {
392                                 jsonNode.children[i] = parseNodesRecursively(child);
393                         }
394                 }
395                 
396                 return jsonNode;
397         }
398         
399         private void parseAnimations (ModelData model, JsonValue json) {
400                 JsonValue animations = json.get("animations");
401                 if(animations == null)
402                         return;
403                 
404                 model.animations.ensureCapacity(animations.size);
405                 
406                 for (JsonValue anim = animations.child(); anim != null; anim = anim.next()) {
407                         JsonValue nodes = anim.get("bones");
408                         if (nodes == null)
409                                 continue;
410                         ModelAnimation animation = new ModelAnimation();
411                         model.animations.add(animation);
412                         animation.nodeAnimations.ensureCapacity(nodes.size);
413                         animation.id = anim.getString("id");
414                         for (JsonValue node = nodes.child(); node != null; node = node.next()) {
415                                 JsonValue keyframes = node.get("keyframes");
416
417                                 ModelNodeAnimation nodeAnim = new ModelNodeAnimation();
418                                 animation.nodeAnimations.add(nodeAnim);
419                                 nodeAnim.nodeId = node.getString("boneId");
420                                 nodeAnim.keyframes.ensureCapacity(keyframes.size);
421
422                                 for (JsonValue keyframe = keyframes.child(); keyframe != null; keyframe = keyframe.next()) {
423                                         ModelNodeKeyframe kf = new ModelNodeKeyframe();
424                                         nodeAnim.keyframes.add(kf);
425                                         kf.keytime = keyframe.getFloat("keytime") / 1000.f;
426                                         JsonValue translation = keyframe.get("translation");
427                                         if (translation != null && translation.size == 3)
428                                                 kf.translation = new Vector3(translation.getFloat(0), translation.getFloat(1), translation.getFloat(2));
429                                         JsonValue rotation = keyframe.get("rotation");
430                                         if (rotation != null && rotation.size == 4)
431                                                 kf.rotation = new Quaternion(rotation.getFloat(0), rotation.getFloat(1), rotation.getFloat(2), rotation.getFloat(3));
432                                         JsonValue scale = keyframe.get("scale");
433                                         if (scale != null && scale.size == 3)
434                                                 kf.scale = new Vector3(scale.getFloat(0), scale.getFloat(1), scale.getFloat(2));
435                                 }
436                         }
437                 }
438         }
439 }