Rev 1803 | Details | Compare with Previous | Last modification | View Log | RSS feed
| Rev | Author | Line No. | Line |
|---|---|---|---|
| 1718 | chris | 1 | package com.gebauz.bauzoid2.graphics.model; |
| 2 | |||
| 1738 | chris | 3 | import com.badlogic.gdx.Gdx; |
| 4 | import com.badlogic.gdx.graphics.Texture; |
||
| 1719 | chris | 5 | import com.badlogic.gdx.utils.JsonReader; |
| 6 | import com.badlogic.gdx.utils.JsonValue; |
||
| 1718 | chris | 7 | import com.gebauz.bauzoid2.file.FileUtil; |
| 1764 | chris | 8 | import com.gebauz.bauzoid2.game.Consts; |
| 1718 | chris | 9 | import com.gebauz.bauzoid2.game.Engine; |
| 1773 | chris | 10 | import com.gebauz.bauzoid2.graphics.animation.Animation; |
| 11 | import com.gebauz.bauzoid2.graphics.animation.BoneKeyframes; |
||
| 1720 | chris | 12 | import com.gebauz.bauzoid2.graphics.geometry.IndexStream; |
| 13 | import com.gebauz.bauzoid2.graphics.geometry.VertexAttribute; |
||
| 14 | import com.gebauz.bauzoid2.graphics.geometry.VertexStream; |
||
| 1802 | chris | 15 | import com.gebauz.bauzoid2.graphics.util.Transform; |
| 1723 | chris | 16 | import com.gebauz.bauzoid2.math.Matrix4; |
| 17 | import com.gebauz.bauzoid2.math.Quaternion; |
||
| 1773 | chris | 18 | import com.gebauz.bauzoid2.math.Vector3; |
| 1718 | chris | 19 | |
| 1747 | chris | 20 | import java.lang.reflect.Field; |
| 1750 | chris | 21 | import java.util.ArrayDeque; |
| 1719 | chris | 22 | import java.util.ArrayList; |
| 1750 | chris | 23 | import java.util.Deque; |
| 1718 | chris | 24 | |
| 1764 | chris | 25 | import sun.security.provider.certpath.Vertex; |
| 26 | |||
| 1718 | chris | 27 | public class GdxModelUtil |
| 28 | { |
||
| 1762 | chris | 29 | public final static int NUM_POSITION_COORDS = 3; |
| 30 | public final static int NUM_NORMAL_COORDS = 3; |
||
| 31 | public final static int NUM_TEXCOORD_COORDS = 2; |
||
| 32 | public final static int NUM_COLOR_COORDS = 4; |
||
| 33 | public final static int NUM_COLOR_PACKED_COORDS = 1; |
||
| 34 | public final static int NUM_TANGENT_COORDS = 3; |
||
| 35 | public final static int NUM_BINORMAL_COORDS = 3; |
||
| 36 | public final static int NUM_BLENDWEIGHT_COORDS = 2; |
||
| 1718 | chris | 37 | |
| 1762 | chris | 38 | public final static int NUM_COMBINED_BLENDINDICES_COORDS = 4; |
| 39 | public final static int NUM_COMBINED_BLENDWEIGHTS_COORDS = 4; |
||
| 40 | |||
| 1738 | chris | 41 | public static class MeshData |
| 1719 | chris | 42 | { |
| 43 | public String[] attributes = null; |
||
| 44 | public float[] vertices = null; |
||
| 1720 | chris | 45 | public int numIndices = 0; |
| 1719 | chris | 46 | |
| 47 | public ArrayList<PartData> parts = new ArrayList<PartData>(); |
||
| 48 | } |
||
| 49 | |||
| 1738 | chris | 50 | public static class PartData |
| 1719 | chris | 51 | { |
| 52 | public String id = null; |
||
| 53 | public String type = null; |
||
| 1720 | chris | 54 | public short[] indices = null; |
| 1719 | chris | 55 | } |
| 56 | |||
| 1738 | chris | 57 | public static class MaterialData |
| 1719 | chris | 58 | { |
| 59 | public String id = null; |
||
| 60 | |||
| 61 | public ArrayList<TextureData> textures = new ArrayList<TextureData>(); |
||
| 1736 | chris | 62 | float[] ambient = null; |
| 63 | float[] diffuse = null; |
||
| 64 | float[] emissive = null; |
||
| 65 | float opacity = 1.0f; |
||
| 66 | float[] specular = null; |
||
| 67 | float shininess = 1.0f; |
||
| 1719 | chris | 68 | } |
| 69 | |||
| 1738 | chris | 70 | public static class TextureData |
| 1719 | chris | 71 | { |
| 72 | public String id = null; |
||
| 73 | public String type = null; |
||
| 74 | public String filename = null; |
||
| 1738 | chris | 75 | |
| 76 | public boolean isAsync = false; |
||
| 1719 | chris | 77 | } |
| 78 | |||
| 1746 | chris | 79 | public static class NodePartData |
| 1719 | chris | 80 | { |
| 1720 | chris | 81 | public String subMeshId = null; |
| 1719 | chris | 82 | public String materialId = null; |
| 1746 | chris | 83 | |
| 84 | public ArrayList<BoneData> bones = new ArrayList<BoneData>(); |
||
| 85 | |||
| 86 | // TODO: uvmapping |
||
| 1719 | chris | 87 | } |
| 88 | |||
| 1738 | chris | 89 | public static class NodeData |
| 1719 | chris | 90 | { |
| 91 | public String id = null; |
||
| 92 | |||
| 1723 | chris | 93 | public float[] translation = null; |
| 94 | public float[] rotation = null; |
||
| 95 | public float[] scale = null; |
||
| 96 | |||
| 1746 | chris | 97 | public ArrayList<NodePartData> parts = new ArrayList<NodePartData>(); |
| 98 | |||
| 99 | public ArrayList<NodeData> children = new ArrayList<NodeData>(); |
||
| 1751 | chris | 100 | |
| 101 | public NodeData parent = null; |
||
| 1719 | chris | 102 | } |
| 103 | |||
| 1746 | chris | 104 | public static class BoneData |
| 105 | { |
||
| 106 | String nodeId = null; |
||
| 107 | |||
| 108 | public float[] translation = null; |
||
| 109 | public float[] rotation = null; |
||
| 110 | public float[] scale = null; |
||
| 111 | } |
||
| 112 | |||
| 1771 | chris | 113 | public static class KeyframeData |
| 1746 | chris | 114 | { |
| 1771 | chris | 115 | float time = 0.0f; |
| 1746 | chris | 116 | |
| 1771 | chris | 117 | public float[] translation = null; |
| 118 | public float[] rotation = null; |
||
| 119 | public float[] scale = null; |
||
| 1746 | chris | 120 | } |
| 121 | |||
| 1771 | chris | 122 | public static class BoneKeyframeData |
| 123 | { |
||
| 124 | public String boneId = null; |
||
| 125 | public ArrayList<KeyframeData> keyframes = new ArrayList<KeyframeData>(); |
||
| 126 | } |
||
| 127 | |||
| 128 | public static class AnimationData |
||
| 129 | { |
||
| 130 | public String id = null; |
||
| 131 | public ArrayList<BoneKeyframeData> boneKeys = new ArrayList<BoneKeyframeData>(); |
||
| 132 | } |
||
| 133 | |||
| 1737 | chris | 134 | public static class GdxModelDescriptor |
| 135 | { |
||
| 1747 | chris | 136 | public int[] version = null; |
| 137 | |||
| 1737 | chris | 138 | public String id = null; |
| 139 | public ArrayList<MeshData> meshes = new ArrayList<MeshData>(); |
||
| 1746 | chris | 140 | public ArrayList<MaterialData> materials = new ArrayList<MaterialData>(); |
| 141 | public ArrayList<NodeData> nodes = new ArrayList<NodeData>(); |
||
| 142 | public ArrayList<AnimationData> animations = new ArrayList<AnimationData>(); |
||
| 1719 | chris | 143 | |
| 1737 | chris | 144 | public GdxModelDescriptor() |
| 145 | { |
||
| 146 | } |
||
| 147 | } |
||
| 148 | |||
| 1718 | chris | 149 | private GdxModelUtil() {} |
| 150 | |||
| 1737 | chris | 151 | public static GdxModelDescriptor readModel(String filename) |
| 1718 | chris | 152 | { |
| 153 | String modelFile = FileUtil.loadString(filename); |
||
| 154 | |||
| 1719 | chris | 155 | JsonValue root = new JsonReader().parse(modelFile); |
| 1718 | chris | 156 | |
| 1737 | chris | 157 | GdxModelDescriptor descriptor = new GdxModelDescriptor(); |
| 1719 | chris | 158 | |
| 159 | for (JsonValue entry : root) |
||
| 1718 | chris | 160 | { |
| 1719 | chris | 161 | if (entry.name.equalsIgnoreCase("version")) |
| 1718 | chris | 162 | { |
| 1747 | chris | 163 | try |
| 164 | { |
||
| 165 | Field f = GdxModelDescriptor.class.getField(entry.name); |
||
| 166 | if (f.getType().isArray()) |
||
| 167 | { |
||
| 168 | if (f.getType().getComponentType() == int.class) |
||
| 169 | { |
||
| 170 | f.set(descriptor, entry.asIntArray()); |
||
| 171 | } |
||
| 172 | } |
||
| 173 | Engine.log("bla"); |
||
| 174 | } |
||
| 175 | catch (Exception ex) |
||
| 176 | { |
||
| 177 | Engine.log(ex.getMessage()); |
||
| 178 | } |
||
| 1718 | chris | 179 | |
| 1747 | chris | 180 | //int[] version = entry.asIntArray(); |
| 181 | |||
| 182 | Engine.log("G3DJ Version: " + descriptor.version[0] + "." + descriptor.version[1]); |
||
| 1719 | chris | 183 | } |
| 184 | else if (entry.name.equalsIgnoreCase("id")) |
||
| 185 | { |
||
| 1737 | chris | 186 | descriptor.id = entry.asString(); |
| 1719 | chris | 187 | } |
| 188 | else if (entry.name.equalsIgnoreCase("meshes")) |
||
| 189 | { |
||
| 190 | for (JsonValue meshEntry : entry) |
||
| 191 | { |
||
| 1737 | chris | 192 | descriptor.meshes.add(processMesh(meshEntry)); |
| 1719 | chris | 193 | } |
| 194 | } |
||
| 195 | else if (entry.name.equalsIgnoreCase("materials")) |
||
| 196 | { |
||
| 197 | for (JsonValue materialEntry : entry) |
||
| 198 | { |
||
| 1737 | chris | 199 | descriptor.materials.add(processMaterial(materialEntry)); |
| 1719 | chris | 200 | } |
| 201 | } |
||
| 202 | else if (entry.name.equalsIgnoreCase("nodes")) |
||
| 203 | { |
||
| 204 | for (JsonValue nodeEntry : entry) |
||
| 205 | { |
||
| 1737 | chris | 206 | descriptor.nodes.add(processNode(nodeEntry)); |
| 1719 | chris | 207 | } |
| 208 | } |
||
| 1746 | chris | 209 | else if (entry.name.equalsIgnoreCase("animations")) |
| 210 | { |
||
| 211 | for (JsonValue animationEntry : entry) |
||
| 212 | { |
||
| 213 | descriptor.animations.add(processAnimation(animationEntry)); |
||
| 214 | } |
||
| 215 | } |
||
| 1719 | chris | 216 | } |
| 1718 | chris | 217 | |
| 1737 | chris | 218 | return descriptor; |
| 219 | } |
||
| 1718 | chris | 220 | |
| 1737 | chris | 221 | public static Model buildModel(GdxModelDescriptor descriptor) |
| 222 | { |
||
| 1773 | chris | 223 | return buildModel(descriptor.id, descriptor.meshes, descriptor.materials, descriptor.nodes, descriptor.animations); |
| 1719 | chris | 224 | } |
| 1718 | chris | 225 | |
| 1773 | chris | 226 | public static Model buildModel(String id, ArrayList<MeshData> meshDatas, ArrayList<MaterialData> materialDatas, |
| 227 | ArrayList<NodeData> nodeDatas, ArrayList<AnimationData> anims) |
||
| 1719 | chris | 228 | { |
| 229 | Model model = new Model(id); |
||
| 1718 | chris | 230 | |
| 1720 | chris | 231 | Mesh[] meshes = new Mesh[meshDatas.size()]; |
| 1718 | chris | 232 | |
| 1720 | chris | 233 | for (int meshIndex = 0; meshIndex < meshDatas.size(); meshIndex++) |
| 1719 | chris | 234 | { |
| 1720 | chris | 235 | MeshData meshData = meshDatas.get(meshIndex); |
| 236 | meshes[meshIndex] = new Mesh(); |
||
| 1718 | chris | 237 | |
| 1757 | chris | 238 | ArrayList<VertexAttribute> attribs = new ArrayList<VertexAttribute>(); |
| 1718 | chris | 239 | |
| 1720 | chris | 240 | int byteOffset = 0; |
| 241 | int coords = 0; |
||
| 242 | |||
| 1764 | chris | 243 | ArrayList<VertexStream> vertexStreams = buildVertexStream(attribs, meshData.attributes, meshData.vertices); |
| 244 | meshes[meshIndex].getGeometry().setVertexStreams(vertexStreams.toArray(new VertexStream[vertexStreams.size()])); |
||
| 1718 | chris | 245 | |
| 1720 | chris | 246 | short[] indices = new short[meshData.numIndices]; |
| 247 | int indexOffset = 0; |
||
| 1718 | chris | 248 | |
| 1720 | chris | 249 | MeshGroup[] subMeshes = new MeshGroup[meshData.parts.size()]; |
| 250 | for (int partIndex = 0; partIndex < meshData.parts.size(); partIndex++) |
||
| 251 | { |
||
| 252 | PartData part = meshData.parts.get(partIndex); |
||
| 253 | |||
| 254 | System.arraycopy(part.indices, 0, indices, indexOffset, part.indices.length); |
||
| 255 | |||
| 256 | subMeshes[partIndex] = new MeshGroup(meshes[meshIndex], part.id, indexOffset, indexOffset + part.indices.length); |
||
| 257 | |||
| 258 | indexOffset += part.indices.length; |
||
| 1718 | chris | 259 | } |
| 1720 | chris | 260 | meshes[meshIndex].setMeshGroups(subMeshes); |
| 261 | |||
| 262 | IndexStream is = new IndexStream(); |
||
| 263 | is.setData(indices); |
||
| 1721 | chris | 264 | is.upload(); |
| 265 | meshes[meshIndex].getGeometry().setIndexStream(is); |
||
| 1718 | chris | 266 | } |
| 1719 | chris | 267 | |
| 1720 | chris | 268 | Material[] materials = new Material[materialDatas.size()]; |
| 269 | for (int materialIndex = 0; materialIndex < materialDatas.size(); materialIndex++) |
||
| 270 | { |
||
| 1736 | chris | 271 | // TODO: for now, only take first texture, always diffuse |
| 1720 | chris | 272 | MaterialData matData = materialDatas.get(materialIndex); |
| 1736 | chris | 273 | String textureFile = null; |
| 1738 | chris | 274 | Texture texture = null; |
| 1736 | chris | 275 | if (matData.textures.size() > 0) |
| 276 | { |
||
| 1738 | chris | 277 | TextureData texData = matData.textures.get(0); |
| 278 | textureFile = texData.filename; |
||
| 279 | if (texData.isAsync) |
||
| 280 | { |
||
| 281 | texture = Engine.assets.get(textureFile); |
||
| 282 | } |
||
| 283 | else |
||
| 284 | { |
||
| 285 | texture = new Texture(Gdx.files.internal(textureFile)); |
||
| 286 | } |
||
| 1736 | chris | 287 | } |
| 1738 | chris | 288 | materials[materialIndex] = new Material(matData.id, texture); |
| 1736 | chris | 289 | //matData.textures |
| 1720 | chris | 290 | } |
| 291 | |||
| 292 | model.setMeshes(meshes); |
||
| 293 | model.setMaterials(materials); |
||
| 294 | |||
| 1750 | chris | 295 | int nodeCount = getTotalNodeCount(nodeDatas); |
| 296 | |||
| 1780 | chris | 297 | ArrayList<ModelPart> partList = new ArrayList<ModelPart>(); |
| 298 | ArrayList<NodePartData> partDataList = new ArrayList<NodePartData>(); |
||
| 299 | |||
| 1751 | chris | 300 | ModelNode[] nodes = new ModelNode[nodeCount]; |
| 301 | int newNodeIndex = 0; |
||
| 1720 | chris | 302 | for (int nodeIndex = 0; nodeIndex < nodeDatas.size(); nodeIndex++) |
| 303 | { |
||
| 1751 | chris | 304 | Deque<NodeData> stack = new ArrayDeque<NodeData>(); |
| 305 | Deque<Integer> parentStack = new ArrayDeque<Integer>(); |
||
| 306 | stack.push(nodeDatas.get(nodeIndex)); |
||
| 307 | parentStack.push(-1); |
||
| 308 | |||
| 309 | while (!stack.isEmpty()) |
||
| 310 | { |
||
| 311 | NodeData nodeData = stack.pop(); |
||
| 312 | int parentIndex = parentStack.pop(); |
||
| 313 | |||
| 314 | nodes[newNodeIndex] = new ModelNode(nodeData.id, parentIndex); |
||
| 315 | |||
| 316 | if (nodeData.parts.size() > 0) |
||
| 317 | { |
||
| 318 | ModelPart[] parts = new ModelPart[nodeData.parts.size()]; |
||
| 319 | for (int partIndex = 0; partIndex < nodeData.parts.size(); partIndex++) |
||
| 320 | { |
||
| 321 | NodePartData pair = nodeData.parts.get(partIndex); |
||
| 322 | |||
| 323 | MeshGroup group = findSubMesh(meshes, pair.subMeshId); |
||
| 324 | Material material = findMaterial(materials, pair.materialId); |
||
| 325 | |||
| 326 | parts[partIndex] = new ModelPart(pair.subMeshId, group, material); |
||
| 1780 | chris | 327 | |
| 328 | // for later wiring |
||
| 329 | partList.add(parts[partIndex]); |
||
| 330 | partDataList.add(pair); |
||
| 1751 | chris | 331 | } |
| 332 | nodes[newNodeIndex].parts = parts; |
||
| 333 | } |
||
| 334 | |||
| 1774 | chris | 335 | if (nodeData.scale != null) |
| 1800 | chris | 336 | nodes[newNodeIndex].bindPose.scale.set(nodeData.scale); |
| 1778 | chris | 337 | if (nodeData.rotation != null) |
| 1800 | chris | 338 | nodes[newNodeIndex].bindPose.rotation.set(nodeData.rotation); |
| 1778 | chris | 339 | if (nodeData.translation != null) |
| 1800 | chris | 340 | nodes[newNodeIndex].bindPose.translation.set(nodeData.translation); |
| 1778 | chris | 341 | |
| 1800 | chris | 342 | nodes[newNodeIndex].transform.setFrom(nodes[newNodeIndex].bindPose); |
| 343 | |||
| 1751 | chris | 344 | for (NodeData child : nodeData.children) |
| 345 | { |
||
| 346 | stack.push(child); |
||
| 347 | parentStack.push(newNodeIndex); |
||
| 348 | } |
||
| 1753 | chris | 349 | |
| 350 | newNodeIndex++; |
||
| 1751 | chris | 351 | } |
| 1720 | chris | 352 | } |
| 353 | |||
| 1721 | chris | 354 | model.setNodes(nodes); |
| 355 | |||
| 1780 | chris | 356 | // wire together bone influences |
| 357 | for (int i = 0; i < partList.size(); i++) |
||
| 358 | { |
||
| 359 | ModelPart part = partList.get(i); |
||
| 360 | NodePartData partData = partDataList.get(i); |
||
| 361 | |||
| 1803 | chris | 362 | if (partData.bones.size() == 0) |
| 363 | continue; |
||
| 364 | |||
| 1780 | chris | 365 | ModelNode[] influences = new ModelNode[partData.bones.size()]; |
| 1802 | chris | 366 | Transform[] transforms = new Transform[partData.bones.size()]; |
| 1780 | chris | 367 | for (int j = 0; j < partData.bones.size(); j++) |
| 368 | { |
||
| 369 | influences[j] = model.findNode(partData.bones.get(j).nodeId); |
||
| 1802 | chris | 370 | transforms[j] = new Transform(); |
| 371 | transforms[j].translation.set(partData.bones.get(j).translation); |
||
| 372 | transforms[j].rotation.set(partData.bones.get(j).rotation); |
||
| 373 | transforms[j].scale.set(partData.bones.get(j).scale); |
||
| 1780 | chris | 374 | } |
| 1804 | chris | 375 | part.setBones(influences, transforms); |
| 1780 | chris | 376 | } |
| 377 | |||
| 1773 | chris | 378 | Animation[] animations = new Animation[anims.size()]; |
| 379 | |||
| 380 | for (int i = 0; i < anims.size(); i++) |
||
| 381 | { |
||
| 382 | AnimationData animData = anims.get(i); |
||
| 383 | animations[i] = new Animation(animData.id); |
||
| 384 | |||
| 1774 | chris | 385 | float totalTime = 0.0f; |
| 386 | |||
| 1773 | chris | 387 | BoneKeyframes[] boneKeys = new BoneKeyframes[animData.boneKeys.size()]; |
| 388 | for (int j = 0; j < animData.boneKeys.size(); j++) |
||
| 389 | { |
||
| 390 | BoneKeyframeData boneKeyData = animData.boneKeys.get(j); |
||
| 391 | |||
| 1774 | chris | 392 | ModelNode bone = model.findNode(boneKeyData.boneId); |
| 393 | boneKeys[j] = new BoneKeyframes(bone); |
||
| 394 | |||
| 1773 | chris | 395 | ArrayList<BoneKeyframes.RotationKey> rotations = new ArrayList<BoneKeyframes.RotationKey>(); |
| 396 | |||
| 397 | for (KeyframeData keyframe : boneKeyData.keyframes) |
||
| 398 | { |
||
| 399 | if (keyframe.translation != null) |
||
| 400 | { |
||
| 401 | |||
| 402 | } |
||
| 403 | if (keyframe.rotation != null) |
||
| 404 | { |
||
| 405 | rotations.add(new BoneKeyframes.RotationKey(keyframe.time, new Quaternion(keyframe.rotation))); |
||
| 406 | } |
||
| 407 | if (keyframe.scale != null) |
||
| 408 | { |
||
| 409 | |||
| 410 | } |
||
| 1774 | chris | 411 | |
| 412 | if (keyframe.time > totalTime) |
||
| 413 | totalTime = keyframe.time; |
||
| 1773 | chris | 414 | } |
| 415 | boneKeys[j].setRotationKeys(rotations.toArray(new BoneKeyframes.RotationKey[rotations.size()])); |
||
| 416 | } |
||
| 417 | |||
| 418 | animations[i].setBoneKeys(boneKeys); |
||
| 1774 | chris | 419 | animations[i].setTotalTime(totalTime); |
| 1773 | chris | 420 | } |
| 421 | |||
| 422 | model.setAnimations(animations); |
||
| 423 | |||
| 1719 | chris | 424 | return model; |
| 425 | } |
||
| 426 | |||
| 1773 | chris | 427 | private static float[] toFloatArray(ArrayList<Float> array) |
| 428 | { |
||
| 429 | float[] rt = new float[array.size()]; |
||
| 430 | for (int k = 0; k < array.size(); k++) |
||
| 431 | rt[k] = array.get(k); |
||
| 432 | |||
| 433 | return rt; |
||
| 434 | } |
||
| 435 | |||
| 1762 | chris | 436 | private static ArrayList<VertexStream> buildVertexStream(ArrayList<VertexAttribute> attribs, String[] attributes, float[] vertices) |
| 1760 | chris | 437 | { |
| 1762 | chris | 438 | int coordsPerVertex = calcVertexSize(attributes); |
| 439 | int numVertices = vertices.length / coordsPerVertex; |
||
| 440 | |||
| 1763 | chris | 441 | ArrayList<VertexStream> result = new ArrayList<VertexStream>(); |
| 442 | |||
| 1762 | chris | 443 | float[] coords = null; |
| 444 | float[] normals = null; |
||
| 445 | float[] texcoords = null; |
||
| 446 | float[] blendIndices = null; |
||
| 447 | float[] blendWeights = null; |
||
| 448 | |||
| 449 | int offset = 0; |
||
| 1786 | chris | 450 | int blendOffset = 0; |
| 1762 | chris | 451 | |
| 452 | for (String attr : attributes) |
||
| 453 | { |
||
| 454 | if (attr.equalsIgnoreCase("POSITION")) |
||
| 455 | { |
||
| 456 | coords = new float[numVertices*NUM_POSITION_COORDS]; |
||
| 1788 | chris | 457 | extractInterleaved(coords, 0, NUM_POSITION_COORDS, vertices, offset, coordsPerVertex, NUM_POSITION_COORDS); |
| 1762 | chris | 458 | offset += NUM_POSITION_COORDS; |
| 1763 | chris | 459 | |
| 1764 | chris | 460 | result.add(createIndividualVertexStream(coords, VertexAttribute.POSITION, NUM_POSITION_COORDS, "POSITION")); |
| 1762 | chris | 461 | } |
| 462 | else if (attr.equalsIgnoreCase("NORMAL")) |
||
| 463 | { |
||
| 464 | normals = new float[numVertices*NUM_NORMAL_COORDS]; |
||
| 1788 | chris | 465 | extractInterleaved(normals, 0, NUM_NORMAL_COORDS, vertices, offset, coordsPerVertex, NUM_NORMAL_COORDS); |
| 1762 | chris | 466 | offset += NUM_NORMAL_COORDS; |
| 1764 | chris | 467 | |
| 468 | result.add(createIndividualVertexStream(normals, VertexAttribute.NORMAL, NUM_NORMAL_COORDS, "NORMAL")); |
||
| 1762 | chris | 469 | } |
| 470 | else if (attr.equalsIgnoreCase("TEXCOORD0")) |
||
| 471 | { |
||
| 472 | texcoords = new float[numVertices*NUM_TEXCOORD_COORDS]; |
||
| 1788 | chris | 473 | extractInterleaved(texcoords, 0, NUM_TEXCOORD_COORDS, vertices, offset, coordsPerVertex, NUM_TEXCOORD_COORDS); |
| 1762 | chris | 474 | offset += NUM_TEXCOORD_COORDS; |
| 1764 | chris | 475 | |
| 476 | result.add(createIndividualVertexStream(texcoords, VertexAttribute.TEXCOORD0, NUM_TEXCOORD_COORDS, "TEXCOORD0")); |
||
| 1762 | chris | 477 | } |
| 1788 | chris | 478 | else if (attr.startsWith("BLENDWEIGHT")) |
| 1762 | chris | 479 | { |
| 480 | if (blendIndices == null) |
||
| 481 | blendIndices = new float[numVertices*NUM_COMBINED_BLENDINDICES_COORDS]; |
||
| 482 | if (blendWeights == null) |
||
| 483 | blendWeights = new float[numVertices*NUM_COMBINED_BLENDWEIGHTS_COORDS]; |
||
| 1788 | chris | 484 | extractInterleaved(blendIndices, blendOffset, NUM_COMBINED_BLENDINDICES_COORDS, vertices, offset, coordsPerVertex, 1); |
| 485 | extractInterleaved(blendWeights, blendOffset, NUM_COMBINED_BLENDWEIGHTS_COORDS, vertices, offset+1, coordsPerVertex, 1); |
||
| 1786 | chris | 486 | blendOffset++; |
| 1762 | chris | 487 | offset += NUM_BLENDWEIGHT_COORDS; |
| 488 | } |
||
| 489 | } |
||
| 490 | |||
| 1764 | chris | 491 | if ((blendIndices != null) && (blendWeights != null)) |
| 492 | { |
||
| 493 | result.add(createIndividualVertexStream(blendIndices, VertexAttribute.BLENDINDICES, NUM_COMBINED_BLENDINDICES_COORDS, "BLENDINDICES")); |
||
| 494 | result.add(createIndividualVertexStream(blendWeights, VertexAttribute.BLENDWEIGHTS, NUM_COMBINED_BLENDWEIGHTS_COORDS, "BLENDWEIGHTS")); |
||
| 495 | } |
||
| 496 | |||
| 497 | return result; |
||
| 1760 | chris | 498 | } |
| 499 | |||
| 1764 | chris | 500 | private static VertexStream createIndividualVertexStream(float[] data, int attribute, int numCoords, String name) |
| 501 | { |
||
| 502 | VertexAttribute[] vertexAttribs = new VertexAttribute[] |
||
| 503 | { |
||
| 504 | new VertexAttribute(attribute, numCoords, 0) |
||
| 505 | }; |
||
| 506 | VertexStream vs = new VertexStream(VertexStream.StreamType.INDIVIDUAL, name, vertexAttribs, false); |
||
| 507 | vs.setData(data, numCoords, numCoords * Consts.SIZEOF_FLOAT); |
||
| 508 | vs.upload(); |
||
| 509 | |||
| 510 | return vs; |
||
| 511 | } |
||
| 512 | |||
| 1787 | chris | 513 | private static void extractInterleaved(float[] dest, int destOffset, int destStride, float[] src, int srcOffset, int srcStride, int count) |
| 1762 | chris | 514 | { |
| 1787 | chris | 515 | int targetIndex = 0; |
| 516 | int sourceIndex = 0; |
||
| 517 | while (sourceIndex < src.length) |
||
| 1762 | chris | 518 | { |
| 519 | for (int j = 0; j < count; j++) |
||
| 1787 | chris | 520 | { |
| 521 | dest[targetIndex + destOffset + j] = src[sourceIndex + srcOffset + j]; |
||
| 522 | } |
||
| 1762 | chris | 523 | |
| 1787 | chris | 524 | targetIndex += destStride; |
| 525 | sourceIndex += srcStride; |
||
| 1762 | chris | 526 | } |
| 527 | } |
||
| 528 | |||
| 1755 | chris | 529 | public static int calcVertexSize(String[] attribs) |
| 530 | { |
||
| 531 | int result = 0; |
||
| 532 | |||
| 533 | for (String attr : attribs) |
||
| 534 | { |
||
| 535 | if (attr.equalsIgnoreCase("POSITION")) |
||
| 1762 | chris | 536 | result += NUM_POSITION_COORDS; |
| 1755 | chris | 537 | else if (attr.equalsIgnoreCase("NORMAL")) |
| 1762 | chris | 538 | result += NUM_NORMAL_COORDS; |
| 1755 | chris | 539 | else if (attr.equalsIgnoreCase("COLOR")) |
| 1762 | chris | 540 | result += NUM_COLOR_COORDS; |
| 1755 | chris | 541 | else if (attr.equalsIgnoreCase("COLORPACKED")) |
| 1762 | chris | 542 | result += NUM_COLOR_PACKED_COORDS; |
| 1755 | chris | 543 | else if (attr.equalsIgnoreCase("TANGENT")) |
| 1762 | chris | 544 | result += NUM_TANGENT_COORDS; |
| 1755 | chris | 545 | else if (attr.equalsIgnoreCase("BINORMAL")) |
| 1762 | chris | 546 | result += NUM_BINORMAL_COORDS; |
| 1755 | chris | 547 | else if (attr.startsWith("TEXCOORD")) |
| 1762 | chris | 548 | result += NUM_TEXCOORD_COORDS; |
| 1755 | chris | 549 | else if (attr.startsWith("BLENDWEIGHT")) |
| 1762 | chris | 550 | result += NUM_BLENDWEIGHT_COORDS; |
| 1755 | chris | 551 | } |
| 552 | |||
| 553 | return result; |
||
| 554 | } |
||
| 555 | |||
| 1750 | chris | 556 | public static int getTotalNodeCount(ArrayList<NodeData> nodeDatas) |
| 557 | { |
||
| 558 | int nodeCount = 0; |
||
| 559 | for (int nodeIndex = 0; nodeIndex < nodeDatas.size(); nodeIndex++) |
||
| 560 | { |
||
| 561 | NodeData rootNode = nodeDatas.get(nodeIndex); |
||
| 562 | |||
| 563 | Deque<NodeData> stack = new ArrayDeque<NodeData>(); |
||
| 564 | stack.push(rootNode); |
||
| 565 | while (!stack.isEmpty()) |
||
| 566 | { |
||
| 567 | NodeData node = stack.pop(); |
||
| 568 | nodeCount++; |
||
| 569 | |||
| 570 | for (int j = 0; j < node.children.size(); j++) |
||
| 571 | { |
||
| 572 | stack.push(node.children.get(j)); |
||
| 573 | } |
||
| 574 | } |
||
| 575 | } |
||
| 576 | return nodeCount; |
||
| 577 | } |
||
| 578 | |||
| 1720 | chris | 579 | public static MeshGroup findSubMesh(Mesh[] meshes, String id) |
| 580 | { |
||
| 581 | for (Mesh mesh : meshes) |
||
| 582 | { |
||
| 583 | for (MeshGroup group : mesh.getMeshGroups()) |
||
| 584 | { |
||
| 585 | if (id.equalsIgnoreCase(group.getName())) |
||
| 586 | return group; |
||
| 587 | } |
||
| 588 | } |
||
| 589 | return null; |
||
| 590 | } |
||
| 591 | |||
| 592 | public static Material findMaterial(Material[] materials, String id) |
||
| 593 | { |
||
| 594 | for (Material mat : materials) |
||
| 595 | { |
||
| 596 | if (id.equalsIgnoreCase(mat.getName())) |
||
| 597 | return mat; |
||
| 598 | } |
||
| 599 | return null; |
||
| 600 | } |
||
| 601 | |||
| 1719 | chris | 602 | public static MeshData processMesh(JsonValue meshesEntry) |
| 603 | { |
||
| 604 | MeshData result = new MeshData(); |
||
| 1718 | chris | 605 | |
| 1719 | chris | 606 | for (JsonValue entry : meshesEntry) |
| 607 | { |
||
| 608 | if (entry.name.equalsIgnoreCase("attributes")) |
||
| 609 | { |
||
| 610 | result.attributes = entry.asStringArray(); |
||
| 611 | } |
||
| 612 | else if (entry.name.equalsIgnoreCase("vertices")) |
||
| 613 | { |
||
| 614 | result.vertices = entry.asFloatArray(); |
||
| 615 | } |
||
| 616 | else if (entry.name.equalsIgnoreCase("parts")) |
||
| 617 | { |
||
| 1720 | chris | 618 | result.numIndices = 0; |
| 1719 | chris | 619 | for (JsonValue partEntry : entry) |
| 620 | { |
||
| 621 | result.parts.add(processParts(partEntry)); |
||
| 1720 | chris | 622 | result.numIndices += result.parts.get(result.parts.size()-1).indices.length; |
| 1719 | chris | 623 | } |
| 624 | } |
||
| 1718 | chris | 625 | } |
| 1719 | chris | 626 | |
| 627 | return result; |
||
| 1718 | chris | 628 | } |
| 629 | |||
| 1719 | chris | 630 | public static PartData processParts(JsonValue partsEntry) |
| 1718 | chris | 631 | { |
| 1719 | chris | 632 | PartData result = new PartData(); |
| 1718 | chris | 633 | |
| 1719 | chris | 634 | for (JsonValue entry : partsEntry) |
| 1718 | chris | 635 | { |
| 1719 | chris | 636 | if (entry.name.equalsIgnoreCase("id")) |
| 637 | { |
||
| 638 | result.id = entry.asString(); |
||
| 639 | } |
||
| 640 | else if (entry.name.equalsIgnoreCase("type")) |
||
| 641 | { |
||
| 642 | result.type = entry.asString(); |
||
| 643 | } |
||
| 644 | else if (entry.name.equalsIgnoreCase("indices")) |
||
| 645 | { |
||
| 1720 | chris | 646 | result.indices = entry.asShortArray(); |
| 1719 | chris | 647 | } |
| 1718 | chris | 648 | } |
| 649 | |||
| 650 | return result; |
||
| 651 | } |
||
| 652 | |||
| 1719 | chris | 653 | public static MaterialData processMaterial(JsonValue materialsEntry) |
| 1718 | chris | 654 | { |
| 1719 | chris | 655 | MaterialData result = new MaterialData(); |
| 1718 | chris | 656 | |
| 1719 | chris | 657 | for (JsonValue entry : materialsEntry) |
| 1718 | chris | 658 | { |
| 1719 | chris | 659 | if (entry.name.equalsIgnoreCase("id")) |
| 660 | { |
||
| 661 | result.id = entry.asString(); |
||
| 662 | } |
||
| 663 | else if (entry.name.equalsIgnoreCase("textures")) |
||
| 664 | { |
||
| 665 | for (JsonValue textureEntry : entry) |
||
| 666 | { |
||
| 667 | result.textures.add(processTexture(textureEntry)); |
||
| 668 | } |
||
| 669 | } |
||
| 1736 | chris | 670 | else if (entry.name.equalsIgnoreCase("ambient")) |
| 671 | { |
||
| 672 | result.ambient = entry.asFloatArray(); |
||
| 673 | } |
||
| 674 | else if (entry.name.equalsIgnoreCase("diffuse")) |
||
| 675 | { |
||
| 676 | result.diffuse = entry.asFloatArray(); |
||
| 677 | } |
||
| 678 | else if (entry.name.equalsIgnoreCase("emissive")) |
||
| 679 | { |
||
| 680 | result.emissive = entry.asFloatArray(); |
||
| 681 | } |
||
| 682 | else if (entry.name.equalsIgnoreCase("opacity")) |
||
| 683 | { |
||
| 684 | result.opacity = entry.asFloat(); |
||
| 685 | } |
||
| 686 | else if (entry.name.equalsIgnoreCase("specular")) |
||
| 687 | { |
||
| 688 | result.specular = entry.asFloatArray(); |
||
| 689 | } |
||
| 690 | else if (entry.name.equalsIgnoreCase("shininess")) |
||
| 691 | { |
||
| 692 | result.shininess = entry.asFloat(); |
||
| 693 | } |
||
| 1718 | chris | 694 | } |
| 695 | |||
| 696 | return result; |
||
| 697 | } |
||
| 698 | |||
| 1719 | chris | 699 | public static TextureData processTexture(JsonValue textureEntry) |
| 1718 | chris | 700 | { |
| 1719 | chris | 701 | TextureData result = new TextureData(); |
| 702 | |||
| 703 | for (JsonValue entry : textureEntry) |
||
| 704 | { |
||
| 705 | if (entry.name.equalsIgnoreCase("id")) |
||
| 706 | { |
||
| 707 | result.id = entry.asString(); |
||
| 708 | } |
||
| 709 | else if (entry.name.equalsIgnoreCase("filename")) |
||
| 710 | { |
||
| 711 | result.filename = entry.asString(); |
||
| 712 | } |
||
| 713 | else if (entry.name.equalsIgnoreCase("type")) |
||
| 714 | { |
||
| 715 | result.type = entry.asString(); |
||
| 716 | } |
||
| 717 | } |
||
| 718 | |||
| 719 | return result; |
||
| 1718 | chris | 720 | } |
| 721 | |||
| 1719 | chris | 722 | public static NodeData processNode(JsonValue nodeEntry) |
| 1718 | chris | 723 | { |
| 1719 | chris | 724 | NodeData result = new NodeData(); |
| 725 | |||
| 726 | for (JsonValue entry : nodeEntry) |
||
| 727 | { |
||
| 728 | if (entry.name.equalsIgnoreCase("id")) |
||
| 729 | { |
||
| 730 | result.id = entry.asString(); |
||
| 731 | } |
||
| 1723 | chris | 732 | else if (entry.name.equalsIgnoreCase("translation")) |
| 733 | { |
||
| 734 | result.translation = entry.asFloatArray(); |
||
| 735 | } |
||
| 736 | else if (entry.name.equalsIgnoreCase("rotation")) |
||
| 737 | { |
||
| 738 | result.rotation = entry.asFloatArray(); |
||
| 739 | } |
||
| 740 | else if (entry.name.equalsIgnoreCase("scale")) |
||
| 741 | { |
||
| 742 | result.scale = entry.asFloatArray(); |
||
| 743 | } |
||
| 1719 | chris | 744 | else if (entry.name.equalsIgnoreCase("parts")) |
| 745 | { |
||
| 746 | for (JsonValue partEntry : entry) |
||
| 747 | { |
||
| 1746 | chris | 748 | result.parts.add(processNodePart(partEntry)); |
| 1719 | chris | 749 | } |
| 750 | } |
||
| 1746 | chris | 751 | else if (entry.name.equalsIgnoreCase("children")) |
| 752 | { |
||
| 753 | for (JsonValue childEntry : entry) |
||
| 754 | { |
||
| 1751 | chris | 755 | NodeData node = processNode(childEntry); |
| 756 | node.parent = result; |
||
| 757 | result.children.add(node); |
||
| 1746 | chris | 758 | } |
| 759 | } |
||
| 1719 | chris | 760 | } |
| 761 | |||
| 762 | return result; |
||
| 1718 | chris | 763 | } |
| 764 | |||
| 1746 | chris | 765 | public static NodePartData processNodePart(JsonValue partEntry) |
| 1719 | chris | 766 | { |
| 1746 | chris | 767 | NodePartData result = new NodePartData(); |
| 1719 | chris | 768 | |
| 769 | for (JsonValue entry : partEntry) |
||
| 770 | { |
||
| 771 | if (entry.name.equalsIgnoreCase("meshpartid")) |
||
| 772 | { |
||
| 1720 | chris | 773 | result.subMeshId = entry.asString(); |
| 1719 | chris | 774 | } |
| 775 | else if (entry.name.equalsIgnoreCase("materialid")) |
||
| 776 | { |
||
| 777 | result.materialId = entry.asString(); |
||
| 778 | } |
||
| 1746 | chris | 779 | else if (entry.name.equalsIgnoreCase("bones")) |
| 780 | { |
||
| 781 | for (JsonValue boneEntry : entry) |
||
| 782 | { |
||
| 783 | result.bones.add(processBone(boneEntry)); |
||
| 784 | } |
||
| 785 | } |
||
| 1719 | chris | 786 | } |
| 787 | |||
| 788 | return result; |
||
| 789 | } |
||
| 1737 | chris | 790 | |
| 1746 | chris | 791 | public static BoneData processBone(JsonValue boneEntry) |
| 792 | { |
||
| 793 | BoneData result = new BoneData(); |
||
| 794 | |||
| 795 | for (JsonValue entry : boneEntry) |
||
| 796 | { |
||
| 797 | if (entry.name.equalsIgnoreCase("node")) |
||
| 798 | { |
||
| 799 | result.nodeId = entry.asString(); |
||
| 800 | } |
||
| 801 | else if (entry.name.equalsIgnoreCase("translation")) |
||
| 802 | { |
||
| 803 | result.translation = entry.asFloatArray(); |
||
| 804 | } |
||
| 805 | else if (entry.name.equalsIgnoreCase("rotation")) |
||
| 806 | { |
||
| 807 | result.rotation = entry.asFloatArray(); |
||
| 808 | } |
||
| 809 | else if (entry.name.equalsIgnoreCase("scale")) |
||
| 810 | { |
||
| 811 | result.scale = entry.asFloatArray(); |
||
| 812 | } |
||
| 813 | } |
||
| 1747 | chris | 814 | |
| 815 | return result; |
||
| 1746 | chris | 816 | } |
| 817 | |||
| 818 | public static AnimationData processAnimation(JsonValue animEntry) |
||
| 819 | { |
||
| 820 | AnimationData result = new AnimationData(); |
||
| 821 | |||
| 822 | for (JsonValue entry : animEntry) |
||
| 823 | { |
||
| 824 | if (entry.name.equalsIgnoreCase("id")) |
||
| 825 | { |
||
| 1771 | chris | 826 | result.id = entry.asString(); |
| 827 | } |
||
| 828 | else if (entry.name.equalsIgnoreCase("bones")) |
||
| 829 | { |
||
| 830 | for (JsonValue boneKeyEntry : entry) |
||
| 831 | { |
||
| 832 | result.boneKeys.add(processBoneKeyframe(boneKeyEntry)); |
||
| 833 | } |
||
| 834 | } |
||
| 835 | } |
||
| 1746 | chris | 836 | |
| 1771 | chris | 837 | return result; |
| 838 | } |
||
| 839 | |||
| 840 | public static BoneKeyframeData processBoneKeyframe(JsonValue boneKeyEntry) |
||
| 841 | { |
||
| 842 | BoneKeyframeData result = new BoneKeyframeData(); |
||
| 843 | |||
| 844 | for (JsonValue entry : boneKeyEntry) |
||
| 845 | { |
||
| 846 | if (entry.name.equalsIgnoreCase("boneId")) |
||
| 1746 | chris | 847 | { |
| 1771 | chris | 848 | result.boneId = entry.asString(); |
| 849 | } |
||
| 850 | else if (entry.name.equalsIgnoreCase("keyframes")) |
||
| 851 | { |
||
| 852 | for (JsonValue keyframeEntry : entry) |
||
| 853 | { |
||
| 854 | result.keyframes.add(processKeyframe(keyframeEntry)); |
||
| 855 | } |
||
| 856 | } |
||
| 857 | } |
||
| 1746 | chris | 858 | |
| 1771 | chris | 859 | return result; |
| 860 | } |
||
| 861 | |||
| 862 | public static KeyframeData processKeyframe(JsonValue keyframeEntry) |
||
| 863 | { |
||
| 864 | KeyframeData result = new KeyframeData(); |
||
| 865 | |||
| 866 | for (JsonValue entry : keyframeEntry) |
||
| 867 | { |
||
| 1774 | chris | 868 | if (entry.name.equalsIgnoreCase("keytime")) |
| 1771 | chris | 869 | { |
| 870 | result.time = entry.asFloat(); |
||
| 1746 | chris | 871 | } |
| 1771 | chris | 872 | else if (entry.name.equalsIgnoreCase("translation")) |
| 873 | { |
||
| 874 | result.translation = entry.asFloatArray(); |
||
| 875 | } |
||
| 876 | else if (entry.name.equalsIgnoreCase("rotation")) |
||
| 877 | { |
||
| 878 | result.rotation = entry.asFloatArray(); |
||
| 879 | } |
||
| 880 | else if (entry.name.equalsIgnoreCase("scale")) |
||
| 881 | { |
||
| 882 | result.scale = entry.asFloatArray(); |
||
| 883 | } |
||
| 1746 | chris | 884 | } |
| 885 | |||
| 886 | return result; |
||
| 887 | } |
||
| 888 | |||
| 1718 | chris | 889 | } |