com.jme3.scene.plugins.blender.meshes.MeshHelper Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jme3-blender Show documentation
Show all versions of jme3-blender Show documentation
jMonkeyEngine is a 3D game engine for adventurous Java developers
The newest version!
/*
* Copyright (c) 2009-2019 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 com.jme3.scene.plugins.blender.meshes;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.jme3.material.Material;
import com.jme3.math.ColorRGBA;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
import com.jme3.scene.plugins.blender.BlenderContext;
import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
import com.jme3.scene.plugins.blender.file.BlenderFileException;
import com.jme3.scene.plugins.blender.file.DynamicArray;
import com.jme3.scene.plugins.blender.file.Pointer;
import com.jme3.scene.plugins.blender.file.Structure;
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
import com.jme3.scene.plugins.blender.objects.Properties;
/**
* A class that is used in mesh calculations.
*
* @author Marcin Roguski (Kaelthas)
*/
public class MeshHelper extends AbstractBlenderHelper {
private static final Logger LOGGER = Logger.getLogger(MeshHelper.class.getName());
/** A type of UV data layer in traditional faced mesh (triangles or quads). */
public static final int UV_DATA_LAYER_TYPE_FMESH = 5;
/** A type of UV data layer in bmesh type. */
public static final int UV_DATA_LAYER_TYPE_BMESH = 16;
/** A material used for single lines and points. */
private Material blackUnshadedMaterial;
/**
* This constructor parses the given blender version and stores the result. Some functionalities may differ in different blender
* versions.
*
* @param blenderVersion
* the version read from the blend file
* @param blenderContext
* the blender context
*/
public MeshHelper(String blenderVersion, BlenderContext blenderContext) {
super(blenderVersion, blenderContext);
}
/**
* Converts the mesh structure into temporal mesh.
* The temporal mesh is stored in blender context and here always a clone is being returned because the mesh might
* be modified by modifiers.
*
* @param meshStructure
* the mesh structure
* @param blenderContext
* the blender context
* @return temporal mesh read from the given structure
* @throws BlenderFileException
* an exception is thrown when problems with reading blend file occur
*/
public TemporalMesh toTemporalMesh(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading temporal mesh named: {0}.", meshStructure.getName());
TemporalMesh temporalMesh = (TemporalMesh) blenderContext.getLoadedFeature(meshStructure.getOldMemoryAddress(), LoadedDataType.TEMPORAL_MESH);
if (temporalMesh != null) {
LOGGER.fine("The mesh is already loaded. Returning its clone.");
return temporalMesh.clone();
}
if ("ID".equals(meshStructure.getType())) {
LOGGER.fine("Loading mesh from external blend file.");
return (TemporalMesh) this.loadLibrary(meshStructure);
}
String name = meshStructure.getName();
LOGGER.log(Level.FINE, "Reading mesh: {0}.", name);
temporalMesh = new TemporalMesh(meshStructure, blenderContext);
LOGGER.fine("Loading materials.");
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
temporalMesh.setMaterials(materialHelper.getMaterials(meshStructure, blenderContext));
LOGGER.fine("Reading custom properties.");
Properties properties = this.loadProperties(meshStructure, blenderContext);
temporalMesh.setProperties(properties);
blenderContext.addLoadedFeatures(meshStructure.getOldMemoryAddress(), LoadedDataType.STRUCTURE, meshStructure);
blenderContext.addLoadedFeatures(meshStructure.getOldMemoryAddress(), LoadedDataType.TEMPORAL_MESH, temporalMesh);
return temporalMesh.clone();
}
/**
* Tells if the given mesh structure supports BMesh.
*
* @param meshStructure
* the mesh structure
* @return true if BMesh is supported and false otherwise
*/
public boolean isBMeshCompatible(Structure meshStructure) {
Pointer pMLoop = (Pointer) meshStructure.getFieldValue("mloop");
Pointer pMPoly = (Pointer) meshStructure.getFieldValue("mpoly");
return pMLoop != null && pMPoly != null && pMLoop.isNotNull() && pMPoly.isNotNull();
}
/**
* This method returns the vertices: a list of vertex positions and a list
* of vertex normals.
*
* @param meshStructure
* the structure containing the mesh data
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
*/
@SuppressWarnings("unchecked")
public void loadVerticesAndNormals(Structure meshStructure, List vertices, List normals) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading vertices and normals from mesh: {0}.", meshStructure.getName());
int count = ((Number) meshStructure.getFieldValue("totvert")).intValue();
if (count > 0) {
Pointer pMVert = (Pointer) meshStructure.getFieldValue("mvert");
List mVerts = pMVert.fetchData();
Vector3f co = null, no = null;
if (fixUpAxis) {
for (int i = 0; i < count; ++i) {
DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co");
co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(2).floatValue(), -coordinates.get(1).floatValue());
vertices.add(co);
DynamicArray norm = (DynamicArray) mVerts.get(i).getFieldValue("no");
no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f, -norm.get(1).shortValue() / 32767.0f);
normals.add(no);
}
} else {
for (int i = 0; i < count; ++i) {
DynamicArray coordinates = (DynamicArray) mVerts.get(i).getFieldValue("co");
co = new Vector3f(coordinates.get(0).floatValue(), coordinates.get(1).floatValue(), coordinates.get(2).floatValue());
vertices.add(co);
DynamicArray norm = (DynamicArray) mVerts.get(i).getFieldValue("no");
no = new Vector3f(norm.get(0).shortValue() / 32767.0f, norm.get(1).shortValue() / 32767.0f, norm.get(2).shortValue() / 32767.0f);
normals.add(no);
}
}
}
LOGGER.log(Level.FINE, "Loaded {0} vertices and normals.", vertices.size());
}
/**
* This method returns the vertices colors. Each vertex is stored in byte[4] array.
*
* @param meshStructure
* the structure containing the mesh data
* @param blenderContext
* the blender context
* @return a list of vertices colors, each color belongs to a single vertex or empty list of colors are not specified
* @throws BlenderFileException
* this exception is thrown when the blend file structure is somehow invalid or corrupted
*/
public List loadVerticesColors(Structure meshStructure, BlenderContext blenderContext) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading vertices colors from mesh: {0}.", meshStructure.getName());
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
Pointer pMCol = (Pointer) meshStructure.getFieldValue(meshHelper.isBMeshCompatible(meshStructure) ? "mloopcol" : "mcol");
List verticesColors = new ArrayList();
// it was likely a bug in blender untill version 2.63 (the blue and red factors were misplaced in their structure)
// so we need to put them right
boolean useBGRA = blenderContext.getBlenderVersion() < 263;
if (pMCol.isNotNull()) {
List mCol = pMCol.fetchData();
for (Structure color : mCol) {
byte r = ((Number) color.getFieldValue("r")).byteValue();
byte g = ((Number) color.getFieldValue("g")).byteValue();
byte b = ((Number) color.getFieldValue("b")).byteValue();
byte a = ((Number) color.getFieldValue("a")).byteValue();
verticesColors.add(useBGRA ? new byte[] { b, g, r, a } : new byte[] { r, g, b, a });
}
}
return verticesColors;
}
/**
* The method loads the UV coordinates. The result is a map where the key is the user's UV set name and the values are UV coordinates.
* But depending on the mesh type (triangle/quads or bmesh) the lists in the map have different meaning.
* For bmesh they are enlisted just like they are stored in the blend file (in loops).
* For traditional faces every 4 UV's should be assigned for a single face.
* @param meshStructure
* the mesh structure
* @return a map that sorts UV coordinates between different UV sets
* @throws BlenderFileException
* an exception is thrown when problems with blend file occur
*/
@SuppressWarnings("unchecked")
public LinkedHashMap> loadUVCoordinates(Structure meshStructure) throws BlenderFileException {
LOGGER.log(Level.FINE, "Loading UV coordinates from mesh: {0}.", meshStructure.getName());
LinkedHashMap> result = new LinkedHashMap>();
if (this.isBMeshCompatible(meshStructure)) {
// in this case the UV's are assigned to vertices (an array is the same length as the vertex array)
Structure loopData = (Structure) meshStructure.getFieldValue("ldata");
Pointer pLoopDataLayers = (Pointer) loopData.getFieldValue("layers");
List loopDataLayers = pLoopDataLayers.fetchData();
for (Structure structure : loopDataLayers) {
Pointer p = (Pointer) structure.getFieldValue("data");
if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_BMESH) {
String uvSetName = structure.getFieldValue("name").toString();
List uvsStructures = p.fetchData();
List uvs = new ArrayList(uvsStructures.size());
for (Structure uvStructure : uvsStructures) {
DynamicArray loopUVS = (DynamicArray) uvStructure.getFieldValue("uv");
uvs.add(new Vector2f(loopUVS.get(0).floatValue(), loopUVS.get(1).floatValue()));
}
result.put(uvSetName, uvs);
}
}
} else {
// in this case UV's are assigned to faces (the array has the same length as the faces count)
Structure facesData = (Structure) meshStructure.getFieldValue("fdata");
Pointer pFacesDataLayers = (Pointer) facesData.getFieldValue("layers");
if (pFacesDataLayers.isNotNull()) {
List facesDataLayers = pFacesDataLayers.fetchData();
for (Structure structure : facesDataLayers) {
Pointer p = (Pointer) structure.getFieldValue("data");
if (p.isNotNull() && ((Number) structure.getFieldValue("type")).intValue() == MeshHelper.UV_DATA_LAYER_TYPE_FMESH) {
String uvSetName = structure.getFieldValue("name").toString();
List uvsStructures = p.fetchData();
List uvs = new ArrayList(uvsStructures.size());
for (Structure uvStructure : uvsStructures) {
DynamicArray mFaceUVs = (DynamicArray) uvStructure.getFieldValue("uv");
uvs.add(new Vector2f(mFaceUVs.get(0).floatValue(), mFaceUVs.get(1).floatValue()));
uvs.add(new Vector2f(mFaceUVs.get(2).floatValue(), mFaceUVs.get(3).floatValue()));
uvs.add(new Vector2f(mFaceUVs.get(4).floatValue(), mFaceUVs.get(5).floatValue()));
uvs.add(new Vector2f(mFaceUVs.get(6).floatValue(), mFaceUVs.get(7).floatValue()));
}
result.put(uvSetName, uvs);
}
}
}
}
return result;
}
/**
* Loads all vertices groups.
* @param meshStructure
* the mesh structure
* @return a list of vertex groups for every vertex in the mesh
* @throws BlenderFileException
* an exception is thrown when problems with blend file occur
*/
public List
© 2015 - 2024 Weber Informatics LLC | Privacy Policy