Scott Barta | 59b2e68 | 2012-03-01 12:35:35 -0800 | [diff] [blame] | 1 | /*
|
| 2 | * Copyright (c) 2009-2010 jMonkeyEngine
|
| 3 | * All rights reserved.
|
| 4 | *
|
| 5 | * Redistribution and use in source and binary forms, with or without
|
| 6 | * modification, are permitted provided that the following conditions are
|
| 7 | * met:
|
| 8 | *
|
| 9 | * * Redistributions of source code must retain the above copyright
|
| 10 | * notice, this list of conditions and the following disclaimer.
|
| 11 | *
|
| 12 | * * Redistributions in binary form must reproduce the above copyright
|
| 13 | * notice, this list of conditions and the following disclaimer in the
|
| 14 | * documentation and/or other materials provided with the distribution.
|
| 15 | *
|
| 16 | * * Neither the name of 'jMonkeyEngine' nor the names of its contributors
|
| 17 | * may be used to endorse or promote products derived from this software
|
| 18 | * without specific prior written permission.
|
| 19 | *
|
| 20 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
| 21 | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
|
| 22 | * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
|
| 23 | * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
| 24 | * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
| 25 | * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
| 26 | * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
| 27 | * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
| 28 | * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
| 29 | * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
| 30 | * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| 31 | */
|
| 32 | package com.jme3.scene.plugins.blender.meshes;
|
| 33 |
|
Scott Barta | a6b4465 | 2012-03-09 13:52:20 -0800 | [diff] [blame] | 34 | import java.nio.ByteBuffer;
|
| 35 | import java.util.ArrayList;
|
| 36 | import java.util.HashMap;
|
| 37 | import java.util.LinkedList;
|
| 38 | import java.util.List;
|
| 39 | import java.util.Map;
|
| 40 | import java.util.Map.Entry;
|
| 41 |
|
Scott Barta | 59b2e68 | 2012-03-01 12:35:35 -0800 | [diff] [blame] | 42 | import com.jme3.asset.BlenderKey.FeaturesToLoad;
|
| 43 | import com.jme3.material.Material;
|
| 44 | import com.jme3.math.FastMath;
|
| 45 | import com.jme3.math.Vector2f;
|
| 46 | import com.jme3.math.Vector3f;
|
| 47 | import com.jme3.renderer.queue.RenderQueue.Bucket;
|
| 48 | import com.jme3.scene.Geometry;
|
| 49 | import com.jme3.scene.Mesh;
|
| 50 | import com.jme3.scene.VertexBuffer;
|
| 51 | import com.jme3.scene.VertexBuffer.Format;
|
| 52 | import com.jme3.scene.VertexBuffer.Type;
|
| 53 | import com.jme3.scene.VertexBuffer.Usage;
|
| 54 | import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
| 55 | import com.jme3.scene.plugins.blender.BlenderContext;
|
| 56 | import com.jme3.scene.plugins.blender.BlenderContext.LoadedFeatureDataType;
|
| 57 | import com.jme3.scene.plugins.blender.exceptions.BlenderFileException;
|
| 58 | import com.jme3.scene.plugins.blender.file.DynamicArray;
|
| 59 | import com.jme3.scene.plugins.blender.file.Pointer;
|
| 60 | import com.jme3.scene.plugins.blender.file.Structure;
|
| 61 | import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
| 62 | import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
| 63 | import com.jme3.scene.plugins.blender.objects.Properties;
|
| 64 | import com.jme3.scene.plugins.blender.textures.TextureHelper;
|
| 65 | import com.jme3.scene.plugins.blender.textures.UVCoordinatesGenerator;
|
| 66 | import com.jme3.texture.Texture;
|
| 67 | import com.jme3.util.BufferUtils;
|
Scott Barta | 59b2e68 | 2012-03-01 12:35:35 -0800 | [diff] [blame] | 68 |
|
| 69 | /**
|
| 70 | * A class that is used in mesh calculations.
|
| 71 | *
|
| 72 | * @author Marcin Roguski (Kaelthas)
|
| 73 | */
|
| 74 | public class MeshHelper extends AbstractBlenderHelper {
|
| 75 |
|
| 76 | /**
|
| 77 | * This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
|
| 78 | * versions.
|
| 79 | *
|
| 80 | * @param blenderVersion
|
| 81 | * the version read from the blend file
|
| 82 | * @param fixUpAxis
|
| 83 | * a variable that indicates if the Y asxis is the UP axis or not
|
| 84 | */
|
| 85 | public MeshHelper(String blenderVersion, boolean fixUpAxis) {
|
| 86 | super(blenderVersion,fixUpAxis);
|
| 87 | }
|
| 88 |
|
| 89 | /**
|
| 90 | * This method reads converts the given structure into mesh. The given structure needs to be filled with the appropriate data.
|
| 91 | *
|
| 92 | * @param structure
|
| 93 | * the structure we read the mesh from
|
| 94 | * @return the mesh feature
|
| 95 | * @throws BlenderFileException
|
| 96 | */
|
| 97 | @SuppressWarnings("unchecked")
|
| 98 | public List<Geometry> toMesh(Structure structure, BlenderContext blenderContext) throws BlenderFileException {
|
| 99 | List<Geometry> geometries = (List<Geometry>) blenderContext.getLoadedFeature(structure.getOldMemoryAddress(),
|
| 100 | LoadedFeatureDataType.LOADED_FEATURE);
|
| 101 | if (geometries != null) {
|
| 102 | List<Geometry> copiedGeometries = new ArrayList<Geometry>(geometries.size());
|
| 103 | for (Geometry geometry : geometries) {
|
| 104 | copiedGeometries.add(geometry.clone());
|
| 105 | }
|
| 106 | return copiedGeometries;
|
| 107 | }
|
| 108 |
|
| 109 | // helpers
|
| 110 | TextureHelper textureHelper = blenderContext.getHelper(TextureHelper.class);
|
| 111 |
|
| 112 | // reading mesh data
|
| 113 | String name = structure.getName();
|
| 114 | MeshContext meshContext = new MeshContext();
|
| 115 |
|
| 116 | // reading vertices
|
| 117 | Vector3f[] vertices = this.getVertices(structure, blenderContext);
|
| 118 | int verticesAmount = vertices.length;
|
| 119 |
|
| 120 | // vertices Colors
|
| 121 | List<byte[]> verticesColors = this.getVerticesColors(structure, blenderContext);
|
| 122 |
|
| 123 | // reading faces
|
| 124 | // the following map sorts faces by material number (because in jme Mesh can have only one material)
|
| 125 | Map<Integer, List<Integer>> meshesMap = new HashMap<Integer, List<Integer>>();
|
| 126 | Pointer pMFace = (Pointer) structure.getFieldValue("mface");
|
| 127 | List<Structure> mFaces = null;
|
| 128 | if (pMFace.isNotNull()) {
|
| 129 | mFaces = pMFace.fetchData(blenderContext.getInputStream());
|
| 130 | if (mFaces == null || mFaces.size() == 0) {
|
| 131 | return new ArrayList<Geometry>(0);
|
| 132 | }
|
| 133 | } else{
|
| 134 | mFaces = new ArrayList<Structure>(0);
|
| 135 | }
|
| 136 |
|
| 137 | Pointer pMTFace = (Pointer) structure.getFieldValue("mtface");
|
| 138 | List<Vector2f> uvCoordinates = null;
|
| 139 | List<Structure> mtFaces = null;
|
| 140 |
|
| 141 | if (pMTFace.isNotNull()) {
|
| 142 | mtFaces = pMTFace.fetchData(blenderContext.getInputStream());
|
| 143 | int facesAmount = ((Number) structure.getFieldValue("totface")).intValue();
|
| 144 | if (mtFaces.size() != facesAmount) {
|
| 145 | throw new BlenderFileException("The amount of faces uv coordinates is not equal to faces amount!");
|
| 146 | }
|
| 147 | uvCoordinates = new ArrayList<Vector2f>();
|
| 148 | }
|
| 149 |
|
| 150 | // normalMap merges normals of faces that will be rendered smooth
|
| 151 | Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>(verticesAmount);
|
| 152 |
|
| 153 | List<Vector3f> normalList = new ArrayList<Vector3f>();
|
| 154 | List<Vector3f> vertexList = new ArrayList<Vector3f>();
|
| 155 | // indicates if the material with the specified number should have a texture attached
|
| 156 | Map<Integer, Texture> materialNumberToTexture = new HashMap<Integer, Texture>();
|
| 157 | // this map's key is the vertex index from 'vertices 'table and the value are indices from 'vertexList'
|
| 158 | // positions (it simply tells which vertex is referenced where in the result list)
|
| 159 | Map<Integer, List<Integer>> vertexReferenceMap = new HashMap<Integer, List<Integer>>(verticesAmount);
|
| 160 | int vertexColorIndex = 0;
|
| 161 | for (int i = 0; i < mFaces.size(); ++i) {
|
| 162 | Structure mFace = mFaces.get(i);
|
| 163 | boolean smooth = (((Number) mFace.getFieldValue("flag")).byteValue() & 0x01) != 0x00;
|
| 164 | DynamicArray<Number> uvs = null;
|
| 165 | boolean materialWithoutTextures = false;
|
| 166 | Pointer pImage = null;
|
| 167 | if (mtFaces != null) {
|
| 168 | Structure mtFace = mtFaces.get(i);
|
| 169 | pImage = (Pointer) mtFace.getFieldValue("tpage");
|
| 170 | materialWithoutTextures = pImage.isNull();
|
| 171 | // uvs always must be added wheater we have texture or not
|
| 172 | uvs = (DynamicArray<Number>) mtFace.getFieldValue("uv");
|
| 173 | uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
| 174 | uvCoordinates.add(new Vector2f(uvs.get(1, 0).floatValue(), uvs.get(1, 1).floatValue()));
|
| 175 | uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
| 176 | }
|
| 177 | int matNr = ((Number) mFace.getFieldValue("mat_nr")).intValue();
|
| 178 | Integer materialNumber = Integer.valueOf(materialWithoutTextures ? -1 * matNr - 1 : matNr);
|
| 179 | List<Integer> indexList = meshesMap.get(materialNumber);
|
| 180 | if (indexList == null) {
|
| 181 | indexList = new ArrayList<Integer>();
|
| 182 | meshesMap.put(materialNumber, indexList);
|
| 183 | }
|
| 184 |
|
| 185 | // attaching image to texture (face can have UV's and image whlie its material may have no texture attached)
|
| 186 | if (pImage != null && pImage.isNotNull() && !materialNumberToTexture.containsKey(materialNumber)) {
|
| 187 | Texture texture = textureHelper.getTextureFromImage(pImage.fetchData(blenderContext.getInputStream()).get(0),
|
| 188 | blenderContext);
|
| 189 | if (texture != null) {
|
| 190 | materialNumberToTexture.put(materialNumber, texture);
|
| 191 | }
|
| 192 | }
|
| 193 |
|
| 194 | int v1 = ((Number) mFace.getFieldValue("v1")).intValue();
|
| 195 | int v2 = ((Number) mFace.getFieldValue("v2")).intValue();
|
| 196 | int v3 = ((Number) mFace.getFieldValue("v3")).intValue();
|
| 197 | int v4 = ((Number) mFace.getFieldValue("v4")).intValue();
|
| 198 |
|
| 199 | Vector3f n = FastMath.computeNormal(vertices[v1], vertices[v2], vertices[v3]);
|
| 200 | this.addNormal(n, normalMap, smooth, vertices[v1], vertices[v2], vertices[v3]);
|
| 201 | normalList.add(normalMap.get(vertices[v1]));
|
| 202 | normalList.add(normalMap.get(vertices[v2]));
|
| 203 | normalList.add(normalMap.get(vertices[v3]));
|
| 204 |
|
| 205 | this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
|
| 206 | indexList.add(vertexList.size());
|
| 207 | vertexList.add(vertices[v1]);
|
| 208 |
|
| 209 | this.appendVertexReference(v2, vertexList.size(), vertexReferenceMap);
|
| 210 | indexList.add(vertexList.size());
|
| 211 | vertexList.add(vertices[v2]);
|
| 212 |
|
| 213 | this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
|
| 214 | indexList.add(vertexList.size());
|
| 215 | vertexList.add(vertices[v3]);
|
| 216 |
|
| 217 | if (v4 > 0) {
|
| 218 | if (uvs != null) {
|
| 219 | uvCoordinates.add(new Vector2f(uvs.get(0, 0).floatValue(), uvs.get(0, 1).floatValue()));
|
| 220 | uvCoordinates.add(new Vector2f(uvs.get(2, 0).floatValue(), uvs.get(2, 1).floatValue()));
|
| 221 | uvCoordinates.add(new Vector2f(uvs.get(3, 0).floatValue(), uvs.get(3, 1).floatValue()));
|
| 222 | }
|
| 223 | this.appendVertexReference(v1, vertexList.size(), vertexReferenceMap);
|
| 224 | indexList.add(vertexList.size());
|
| 225 | vertexList.add(vertices[v1]);
|
| 226 |
|
| 227 | this.appendVertexReference(v3, vertexList.size(), vertexReferenceMap);
|
| 228 | indexList.add(vertexList.size());
|
| 229 | vertexList.add(vertices[v3]);
|
| 230 |
|
| 231 | this.appendVertexReference(v4, vertexList.size(), vertexReferenceMap);
|
| 232 | indexList.add(vertexList.size());
|
| 233 | vertexList.add(vertices[v4]);
|
| 234 |
|
| 235 | this.addNormal(n, normalMap, smooth, vertices[v4]);
|
| 236 | normalList.add(normalMap.get(vertices[v1]));
|
| 237 | normalList.add(normalMap.get(vertices[v3]));
|
| 238 | normalList.add(normalMap.get(vertices[v4]));
|
| 239 |
|
| 240 | if (verticesColors != null) {
|
| 241 | verticesColors.add(vertexColorIndex + 3, verticesColors.get(vertexColorIndex));
|
| 242 | verticesColors.add(vertexColorIndex + 4, verticesColors.get(vertexColorIndex + 2));
|
| 243 | }
|
| 244 | vertexColorIndex += 6;
|
| 245 | } else {
|
| 246 | if (verticesColors != null) {
|
| 247 | verticesColors.remove(vertexColorIndex + 3);
|
| 248 | vertexColorIndex += 3;
|
| 249 | }
|
| 250 | }
|
| 251 | }
|
| 252 | meshContext.setVertexList(vertexList);
|
| 253 | meshContext.setVertexReferenceMap(vertexReferenceMap);
|
| 254 |
|
| 255 | Vector3f[] normals = normalList.toArray(new Vector3f[normalList.size()]);
|
| 256 |
|
| 257 | // reading vertices groups (from the parent)
|
| 258 | Structure parent = blenderContext.peekParent();
|
| 259 | Structure defbase = (Structure) parent.getFieldValue("defbase");
|
| 260 | List<Structure> defs = defbase.evaluateListBase(blenderContext);
|
| 261 | String[] verticesGroups = new String[defs.size()];
|
| 262 | int defIndex = 0;
|
| 263 | for (Structure def : defs) {
|
| 264 | verticesGroups[defIndex++] = def.getFieldValue("name").toString();
|
| 265 | }
|
| 266 |
|
| 267 | // reading materials
|
| 268 | MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
| 269 | Material[] materials = null;
|
| 270 | Material[] nonTexturedMaterials = null;
|
| 271 | if ((blenderContext.getBlenderKey().getFeaturesToLoad() & FeaturesToLoad.MATERIALS) != 0) {
|
| 272 | materials = materialHelper.getMaterials(structure, blenderContext);
|
| 273 | nonTexturedMaterials = materials == null ? null : new Material[materials.length];// fill it when needed
|
| 274 | }
|
| 275 |
|
| 276 | // creating the result meshes
|
| 277 | geometries = new ArrayList<Geometry>(meshesMap.size());
|
| 278 |
|
| 279 | VertexBuffer verticesBuffer = new VertexBuffer(Type.Position);
|
| 280 | verticesBuffer.setupData(Usage.Stream, 3, Format.Float,
|
| 281 | BufferUtils.createFloatBuffer(vertexList.toArray(new Vector3f[vertexList.size()])));
|
| 282 |
|
| 283 | // initial vertex position (used with animation)
|
| 284 | VertexBuffer verticesBind = new VertexBuffer(Type.BindPosePosition);
|
| 285 | verticesBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(verticesBuffer.getData()));
|
| 286 |
|
| 287 | VertexBuffer normalsBuffer = new VertexBuffer(Type.Normal);
|
| 288 | normalsBuffer.setupData(Usage.Stream, 3, Format.Float, BufferUtils.createFloatBuffer(normals));
|
| 289 |
|
| 290 | // initial normals position (used with animation)
|
| 291 | VertexBuffer normalsBind = new VertexBuffer(Type.BindPoseNormal);
|
| 292 | normalsBind.setupData(Usage.CpuOnly, 3, Format.Float, BufferUtils.clone(normalsBuffer.getData()));
|
| 293 |
|
| 294 | VertexBuffer uvCoordsBuffer = null;
|
| 295 | if (uvCoordinates != null) {
|
| 296 | uvCoordsBuffer = new VertexBuffer(Type.TexCoord);
|
| 297 | uvCoordsBuffer.setupData(Usage.Static, 2, Format.Float,
|
| 298 | BufferUtils.createFloatBuffer(uvCoordinates.toArray(new Vector2f[uvCoordinates.size()])));
|
| 299 | }
|
| 300 |
|
| 301 | //reading custom properties
|
| 302 | Properties properties = this.loadProperties(structure, blenderContext);
|
| 303 |
|
| 304 | // generating meshes
|
| 305 | //FloatBuffer verticesColorsBuffer = this.createFloatBuffer(verticesColors);
|
| 306 | ByteBuffer verticesColorsBuffer = createByteBuffer(verticesColors);
|
Scott Barta | a6b4465 | 2012-03-09 13:52:20 -0800 | [diff] [blame] | 307 | verticesAmount = vertexList.size();
|
Scott Barta | 59b2e68 | 2012-03-01 12:35:35 -0800 | [diff] [blame] | 308 | for (Entry<Integer, List<Integer>> meshEntry : meshesMap.entrySet()) {
|
| 309 | Mesh mesh = new Mesh();
|
| 310 |
|
| 311 | // creating vertices indices for this mesh
|
| 312 | List<Integer> indexList = meshEntry.getValue();
|
Scott Barta | a6b4465 | 2012-03-09 13:52:20 -0800 | [diff] [blame] | 313 | if(verticesAmount <= Short.MAX_VALUE) {
|
Scott Barta | 59b2e68 | 2012-03-01 12:35:35 -0800 | [diff] [blame] | 314 | short[] indices = new short[indexList.size()];
|
| 315 | for (int i = 0; i < indexList.size(); ++i) {
|
| 316 | indices[i] = indexList.get(i).shortValue();
|
| 317 | }
|
| 318 | mesh.setBuffer(Type.Index, 1, indices);
|
| 319 | } else {
|
| 320 | int[] indices = new int[indexList.size()];
|
| 321 | for (int i = 0; i < indexList.size(); ++i) {
|
| 322 | indices[i] = indexList.get(i).intValue();
|
| 323 | }
|
| 324 | mesh.setBuffer(Type.Index, 1, indices);
|
| 325 | }
|
| 326 |
|
| 327 | mesh.setBuffer(verticesBuffer);
|
| 328 | mesh.setBuffer(verticesBind);
|
| 329 |
|
| 330 | // setting vertices colors
|
| 331 | if (verticesColorsBuffer != null) {
|
| 332 | mesh.setBuffer(Type.Color, 4, verticesColorsBuffer);
|
| 333 | mesh.getBuffer(Type.Color).setNormalized(true);
|
| 334 | }
|
| 335 |
|
| 336 | // setting faces' normals
|
| 337 | mesh.setBuffer(normalsBuffer);
|
| 338 | mesh.setBuffer(normalsBind);
|
| 339 |
|
| 340 | // creating the result
|
| 341 | Geometry geometry = new Geometry(name + (geometries.size() + 1), mesh);
|
| 342 | if (materials != null) {
|
| 343 | int materialNumber = meshEntry.getKey().intValue();
|
| 344 | Material material;
|
| 345 | if (materialNumber >= 0) {
|
| 346 | material = materials[materialNumber];
|
| 347 | if (materialNumberToTexture.containsKey(Integer.valueOf(materialNumber))) {
|
| 348 | if (material.getMaterialDef().getAssetName().contains("Lighting")) {
|
| 349 | if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_DIFFUSE)) {
|
| 350 | material = material.clone();
|
| 351 | material.setTexture(MaterialHelper.TEXTURE_TYPE_DIFFUSE,
|
| 352 | materialNumberToTexture.get(Integer.valueOf(materialNumber)));
|
| 353 | }
|
| 354 | } else {
|
| 355 | if (!materialHelper.hasTexture(material, MaterialHelper.TEXTURE_TYPE_COLOR)) {
|
| 356 | material = material.clone();
|
| 357 | material.setTexture(MaterialHelper.TEXTURE_TYPE_COLOR,
|
| 358 | materialNumberToTexture.get(Integer.valueOf(materialNumber)));
|
| 359 | }
|
| 360 | }
|
| 361 | }
|
| 362 | } else {
|
| 363 | materialNumber = -1 * (materialNumber + 1);
|
| 364 | if (nonTexturedMaterials[materialNumber] == null) {
|
| 365 | nonTexturedMaterials[materialNumber] = materialHelper.getNonTexturedMaterial(materials[materialNumber],
|
| 366 | TextureHelper.TEX_IMAGE);
|
| 367 | }
|
| 368 | material = nonTexturedMaterials[materialNumber];
|
| 369 | }
|
| 370 | geometry.setMaterial(material);
|
| 371 | if (material.isTransparent()) {
|
| 372 | geometry.setQueueBucket(Bucket.Transparent);
|
| 373 | }
|
| 374 | } else {
|
| 375 | geometry.setMaterial(blenderContext.getDefaultMaterial());
|
| 376 | }
|
| 377 | if (properties != null && properties.getValue() != null) {
|
| 378 | geometry.setUserData("properties", properties);
|
| 379 | }
|
| 380 | geometries.add(geometry);
|
| 381 | }
|
| 382 |
|
| 383 | //applying uvCoordinates for all the meshes
|
| 384 | if (uvCoordsBuffer != null) {
|
| 385 | for (Geometry geom : geometries) {
|
| 386 | geom.getMesh().setBuffer(uvCoordsBuffer);
|
| 387 | }
|
| 388 | } else {
|
| 389 | Map<Material, List<Geometry>> materialMap = new HashMap<Material, List<Geometry>>();
|
| 390 | for (Geometry geom : geometries) {
|
| 391 | Material material = geom.getMaterial();
|
| 392 | List<Geometry> geomsWithCommonMaterial = materialMap.get(material);
|
| 393 | if (geomsWithCommonMaterial == null) {
|
| 394 | geomsWithCommonMaterial = new ArrayList<Geometry>();
|
| 395 | materialMap.put(material, geomsWithCommonMaterial);
|
| 396 | }
|
| 397 | geomsWithCommonMaterial.add(geom);
|
| 398 |
|
| 399 | }
|
| 400 | for (Entry<Material, List<Geometry>> entry : materialMap.entrySet()) {
|
| 401 | MaterialContext materialContext = blenderContext.getMaterialContext(entry.getKey());
|
| 402 | if (materialContext != null && materialContext.getTexturesCount() > 0) {
|
| 403 | VertexBuffer coords = UVCoordinatesGenerator.generateUVCoordinates(materialContext.getUvCoordinatesType(),
|
| 404 | materialContext.getProjectionType(), materialContext.getTextureDimension(),
|
| 405 | materialContext.getProjection(0), entry.getValue());
|
| 406 | //setting the coordinates inside the mesh context
|
| 407 | for (Geometry geometry : entry.getValue()) {
|
| 408 | meshContext.addUVCoordinates(geometry, coords);
|
| 409 | }
|
| 410 | }
|
| 411 | }
|
| 412 | }
|
| 413 |
|
| 414 | // if there are multiple materials used, extract the shared
|
| 415 | // vertex data
|
| 416 | if (geometries.size() > 1){
|
| 417 | // extract from itself
|
| 418 | for (Geometry geom : geometries){
|
| 419 | geom.getMesh().extractVertexData(geom.getMesh());
|
| 420 | }
|
| 421 | }
|
| 422 |
|
| 423 | blenderContext.addLoadedFeatures(structure.getOldMemoryAddress(), structure.getName(), structure, geometries);
|
| 424 | blenderContext.setMeshContext(structure.getOldMemoryAddress(), meshContext);
|
| 425 | return geometries;
|
| 426 | }
|
| 427 |
|
| 428 | /**
|
| 429 | * This method adds a normal to a normals' map. This map is used to merge normals of a vertor that should be rendered smooth.
|
| 430 | *
|
| 431 | * @param normalToAdd
|
| 432 | * a normal to be added
|
| 433 | * @param normalMap
|
| 434 | * merges normals of faces that will be rendered smooth; the key is the vertex and the value - its normal vector
|
| 435 | * @param smooth
|
| 436 | * the variable that indicates wheather to merge normals (creating the smooth mesh) or not
|
| 437 | * @param vertices
|
| 438 | * a list of vertices read from the blender file
|
| 439 | */
|
| 440 | public void addNormal(Vector3f normalToAdd, Map<Vector3f, Vector3f> normalMap, boolean smooth, Vector3f... vertices) {
|
| 441 | for (Vector3f v : vertices) {
|
| 442 | Vector3f n = normalMap.get(v);
|
| 443 | if (!smooth || n == null) {
|
| 444 | normalMap.put(v, normalToAdd.clone());
|
| 445 | } else {
|
| 446 | n.addLocal(normalToAdd).normalizeLocal();
|
| 447 | }
|
| 448 | }
|
| 449 | }
|
| 450 |
|
| 451 | /**
|
| 452 | * This method fills the vertex reference map. The vertices are loaded once and referenced many times in the model. This map is created
|
| 453 | * to tell where the basic vertices are referenced in the result vertex lists. The key of the map is the basic vertex index, and its key
|
| 454 | * - the reference indices list.
|
| 455 | *
|
| 456 | * @param basicVertexIndex
|
| 457 | * the index of the vertex from its basic table
|
| 458 | * @param resultIndex
|
| 459 | * the index of the vertex in its result vertex list
|
| 460 | * @param vertexReferenceMap
|
| 461 | * the reference map
|
| 462 | */
|
| 463 | protected void appendVertexReference(int basicVertexIndex, int resultIndex, Map<Integer, List<Integer>> vertexReferenceMap) {
|
| 464 | List<Integer> referenceList = vertexReferenceMap.get(Integer.valueOf(basicVertexIndex));
|
| 465 | if (referenceList == null) {
|
| 466 | referenceList = new ArrayList<Integer>();
|
| 467 | vertexReferenceMap.put(Integer.valueOf(basicVertexIndex), referenceList);
|
| 468 | }
|
| 469 | referenceList.add(Integer.valueOf(resultIndex));
|
| 470 | }
|
| 471 |
|
| 472 | /**
|
| 473 | * This method returns the vertices colors. Each vertex is stored in byte[4] array.
|
| 474 | *
|
| 475 | * @param meshStructure
|
| 476 | * the structure containing the mesh data
|
| 477 | * @param blenderContext
|
| 478 | * the blender context
|
| 479 | * @return a list of vertices colors, each color belongs to a single vertex
|
| 480 | * @throws BlenderFileException
|
| 481 | * this exception is thrown when the blend file structure is somehow invalid or corrupted
|
| 482 | */
|
| 483 | public List<byte[]> getVerticesColors(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
|
| 484 | Pointer pMCol = (Pointer) meshStructure.getFieldValue("mcol");
|
| 485 | List<byte[]> verticesColors = null;
|
| 486 | List<Structure> mCol = null;
|
| 487 | if (pMCol.isNotNull()) {
|
| 488 | verticesColors = new LinkedList<byte[]>();
|
| 489 | mCol = pMCol.fetchData(blenderContext.getInputStream());
|
| 490 | for (Structure color : mCol) {
|
| 491 | byte r = ((Number)color.getFieldValue("r")).byteValue();
|
| 492 | byte g = ((Number)color.getFieldValue("g")).byteValue();
|
| 493 | byte b = ((Number)color.getFieldValue("b")).byteValue();
|
| 494 | byte a = ((Number)color.getFieldValue("a")).byteValue();
|
| 495 | verticesColors.add(new byte[]{b, g, r, a});
|
| 496 | }
|
| 497 | }
|
| 498 | return verticesColors;
|
| 499 | }
|
| 500 |
|
| 501 | /**
|
| 502 | * This method returns the vertices.
|
| 503 | *
|
| 504 | * @param meshStructure
|
| 505 | * the structure containing the mesh data
|
| 506 | * @param blenderContext
|
| 507 | * the blender context
|
| 508 | * @return a list of vertices colors, each color belongs to a single vertex
|
| 509 | * @throws BlenderFileException
|
| 510 | * this exception is thrown when the blend file structure is somehow invalid or corrupted
|
| 511 | */
|
| 512 | @SuppressWarnings("unchecked")
|
| 513 | private Vector3f[] getVertices(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
|
| 514 | int verticesAmount = ((Number) meshStructure.getFieldValue("totvert")).intValue();
|
| 515 | Vector3f[] vertices = new Vector3f[verticesAmount];
|
| 516 | if (verticesAmount == 0) {
|
| 517 | return vertices;
|
| 518 | }
|
| 519 |
|
| 520 | Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
|
| 521 | List<Structure> mVerts = pMVert.fetchData(blenderContext.getInputStream());
|
| 522 | if(this.fixUpAxis) {
|
| 523 | for (int i = 0; i < verticesAmount; ++i) {
|
| 524 | DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
|
| 525 | vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
|
| 526 | }
|
| 527 | } else {
|
| 528 | for (int i = 0; i < verticesAmount; ++i) {
|
| 529 | DynamicArray<Number> coordinates = (DynamicArray<Number>) mVerts.get(i).getFieldValue("co");
|
| 530 | vertices[i] = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
|
| 531 | }
|
| 532 | }
|
| 533 | return vertices;
|
| 534 | }
|
| 535 |
|
| 536 | @Override
|
| 537 | public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) {
|
| 538 | return true;
|
| 539 | }
|
| 540 | }
|