use of com.jme3.scene.plugins.blender.objects.Properties in project jmonkeyengine by jMonkeyEngine.
the class ObjectHelper method toObject.
/**
* This method reads the given structure and createn an object that
* represents the data.
*
* @param objectStructure
* the object's structure
* @param blenderContext
* the blender context
* @return blener's object representation or null if its type is excluded from loading
* @throws BlenderFileException
* an exception is thrown when the given data is inapropriate
*/
public Object toObject(Structure objectStructure, BlenderContext blenderContext) throws BlenderFileException {
Object loadedResult = blenderContext.getLoadedFeature(objectStructure.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (loadedResult != null) {
return loadedResult;
}
LOGGER.fine("Loading blender object.");
if ("ID".equals(objectStructure.getType())) {
Node object = (Node) this.loadLibrary(objectStructure);
if (object.getParent() != null) {
LOGGER.log(Level.FINEST, "Detaching object {0}, loaded from external file, from its parent.", object);
object.getParent().detachChild(object);
}
return object;
}
int type = ((Number) objectStructure.getFieldValue("type")).intValue();
ObjectType objectType = ObjectType.valueOf(type);
LOGGER.log(Level.FINE, "Type of the object: {0}.", objectType);
int lay = ((Number) objectStructure.getFieldValue("lay")).intValue();
if ((lay & blenderContext.getBlenderKey().getLayersToLoad()) == 0) {
LOGGER.fine("The layer this object is located in is not included in loading.");
return null;
}
blenderContext.pushParent(objectStructure);
String name = objectStructure.getName();
LOGGER.log(Level.FINE, "Loading obejct: {0}", name);
int restrictflag = ((Number) objectStructure.getFieldValue("restrictflag")).intValue();
boolean visible = (restrictflag & 0x01) != 0;
Pointer pParent = (Pointer) objectStructure.getFieldValue("parent");
Object parent = blenderContext.getLoadedFeature(pParent.getOldMemoryAddress(), LoadedDataType.FEATURE);
if (parent == null && pParent.isNotNull()) {
Structure parentStructure = pParent.fetchData().get(0);
parent = this.toObject(parentStructure, blenderContext);
}
Transform t = this.getTransformation(objectStructure, blenderContext);
LOGGER.log(Level.FINE, "Importing object of type: {0}", objectType);
Node result = null;
try {
switch(objectType) {
case LATTICE:
case METABALL:
case TEXT:
case WAVE:
LOGGER.log(Level.WARNING, "{0} type is not supported but the node will be returned in order to keep parent - child relationship.", objectType);
case EMPTY:
case ARMATURE:
// need to use an empty node to properly create
// parent-children relationships between nodes
result = new Node(name);
break;
case MESH:
result = new Node(name);
MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class);
Pointer pMesh = (Pointer) objectStructure.getFieldValue("data");
List<Structure> meshesArray = pMesh.fetchData();
TemporalMesh temporalMesh = meshHelper.toTemporalMesh(meshesArray.get(0), blenderContext);
if (temporalMesh != null) {
result.attachChild(temporalMesh);
}
break;
case SURF:
case CURVE:
result = new Node(name);
Pointer pCurve = (Pointer) objectStructure.getFieldValue("data");
if (pCurve.isNotNull()) {
CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
Structure curveData = pCurve.fetchData().get(0);
TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext);
if (curvesTemporalMesh != null) {
result.attachChild(curvesTemporalMesh);
}
}
break;
case LAMP:
Pointer pLamp = (Pointer) objectStructure.getFieldValue("data");
if (pLamp.isNotNull()) {
LightHelper lightHelper = blenderContext.getHelper(LightHelper.class);
List<Structure> lampsArray = pLamp.fetchData();
Light light = lightHelper.toLight(lampsArray.get(0), blenderContext);
if (light == null) {
// probably some light type is not supported, just create a node so that we can maintain child-parent relationship for nodes
result = new Node(name);
} else {
result = new LightNode(name, light);
}
}
break;
case CAMERA:
Pointer pCamera = (Pointer) objectStructure.getFieldValue("data");
if (pCamera.isNotNull()) {
CameraHelper cameraHelper = blenderContext.getHelper(CameraHelper.class);
List<Structure> camerasArray = pCamera.fetchData();
Camera camera = cameraHelper.toCamera(camerasArray.get(0), blenderContext);
if (camera == null) {
// just create a node so that we can maintain child-parent relationship for nodes
result = new Node(name);
} else {
result = new CameraNode(name, camera);
}
}
break;
default:
LOGGER.log(Level.WARNING, "Unsupported object type: {0}", type);
}
if (result != null) {
LOGGER.fine("Storing loaded feature in blender context and applying markers (those will be removed before the final result is released).");
Long oma = objectStructure.getOldMemoryAddress();
blenderContext.addLoadedFeatures(oma, LoadedDataType.STRUCTURE, objectStructure);
blenderContext.addLoadedFeatures(oma, LoadedDataType.FEATURE, result);
blenderContext.addMarker(OMA_MARKER, result, objectStructure.getOldMemoryAddress());
if (objectType == ObjectType.ARMATURE) {
blenderContext.addMarker(ARMATURE_NODE_MARKER, result, Boolean.TRUE);
}
result.setLocalTransform(t);
result.setCullHint(visible ? CullHint.Always : CullHint.Inherit);
if (parent instanceof Node) {
((Node) parent).attachChild(result);
}
LOGGER.fine("Reading and applying object's modifiers.");
ModifierHelper modifierHelper = blenderContext.getHelper(ModifierHelper.class);
Collection<Modifier> modifiers = modifierHelper.readModifiers(objectStructure, blenderContext);
for (Modifier modifier : modifiers) {
modifier.apply(result, blenderContext);
}
if (result.getChildren() != null && result.getChildren().size() > 0) {
if (result.getChildren().size() == 1 && result.getChild(0) instanceof TemporalMesh) {
LOGGER.fine("Converting temporal mesh into jme geometries.");
((TemporalMesh) result.getChild(0)).toGeometries();
}
LOGGER.fine("Applying proper scale to the geometries.");
for (Spatial child : result.getChildren()) {
if (child instanceof Geometry) {
this.flipMeshIfRequired((Geometry) child, child.getWorldScale());
}
}
}
// I prefer do compute bounding box here than read it from the file
result.updateModelBound();
LOGGER.fine("Applying animations to the object if such are defined.");
AnimationHelper animationHelper = blenderContext.getHelper(AnimationHelper.class);
animationHelper.applyAnimations(result, blenderContext.getBlenderKey().getAnimationMatchMethod());
LOGGER.fine("Loading constraints connected with this object.");
ConstraintHelper constraintHelper = blenderContext.getHelper(ConstraintHelper.class);
constraintHelper.loadConstraints(objectStructure, blenderContext);
LOGGER.fine("Loading custom properties.");
if (blenderContext.getBlenderKey().isLoadObjectProperties()) {
Properties properties = this.loadProperties(objectStructure, blenderContext);
// each value and set it to Spatial
if (properties != null && properties.getValue() != null) {
this.applyProperties(result, properties);
}
}
}
} finally {
blenderContext.popParent();
}
return result;
}
use of com.jme3.scene.plugins.blender.objects.Properties in project jmonkeyengine by jMonkeyEngine.
the class Properties method load.
/**
* This method loads the property from the belnder file.
* @param idPropertyStructure
* the ID structure constining the property
* @param blenderContext
* the blender context
* @throws BlenderFileException
* an exception is thrown when the belnder file is somehow invalid
*/
public void load(Structure idPropertyStructure, BlenderContext blenderContext) throws BlenderFileException {
name = idPropertyStructure.getFieldValue("name").toString();
if (name == null || name.length() == 0) {
name = DEFAULT_NAME;
}
subType = ((Number) idPropertyStructure.getFieldValue("subtype")).intValue();
type = ((Number) idPropertyStructure.getFieldValue("type")).intValue();
// reading the data
Structure data = (Structure) idPropertyStructure.getFieldValue("data");
int len = ((Number) idPropertyStructure.getFieldValue("len")).intValue();
switch(type) {
case IDP_STRING:
{
Pointer pointer = (Pointer) data.getFieldValue("pointer");
BlenderInputStream bis = blenderContext.getInputStream();
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
bis.setPosition(dataFileBlock.getBlockPosition());
value = bis.readString();
break;
}
case IDP_INT:
int intValue = ((Number) data.getFieldValue("val")).intValue();
value = Integer.valueOf(intValue);
break;
case IDP_FLOAT:
int floatValue = ((Number) data.getFieldValue("val")).intValue();
value = Float.valueOf(Float.intBitsToFloat(floatValue));
break;
case IDP_ARRAY:
{
Pointer pointer = (Pointer) data.getFieldValue("pointer");
BlenderInputStream bis = blenderContext.getInputStream();
FileBlockHeader dataFileBlock = blenderContext.getFileBlock(pointer.getOldMemoryAddress());
bis.setPosition(dataFileBlock.getBlockPosition());
int elementAmount = dataFileBlock.getSize();
switch(subType) {
case IDP_INT:
elementAmount /= 4;
int[] intList = new int[elementAmount];
for (int i = 0; i < elementAmount; ++i) {
intList[i] = bis.readInt();
}
value = intList;
break;
case IDP_FLOAT:
elementAmount /= 4;
float[] floatList = new float[elementAmount];
for (int i = 0; i < elementAmount; ++i) {
floatList[i] = bis.readFloat();
}
value = floatList;
break;
case IDP_DOUBLE:
elementAmount /= 8;
double[] doubleList = new double[elementAmount];
for (int i = 0; i < elementAmount; ++i) {
doubleList[i] = bis.readDouble();
}
value = doubleList;
break;
default:
throw new IllegalStateException("Invalid array subtype: " + subType);
}
}
case IDP_GROUP:
Structure group = (Structure) data.getFieldValue("group");
List<Structure> dataList = group.evaluateListBase();
List<Properties> subProperties = new ArrayList<Properties>(len);
for (Structure d : dataList) {
Properties properties = new Properties();
properties.load(d, blenderContext);
subProperties.add(properties);
}
value = subProperties;
break;
case IDP_DOUBLE:
int doublePart1 = ((Number) data.getFieldValue("val")).intValue();
int doublePart2 = ((Number) data.getFieldValue("val2")).intValue();
long doubleVal = (long) doublePart2 << 32 | doublePart1;
value = Double.valueOf(Double.longBitsToDouble(doubleVal));
break;
case IDP_IDPARRAY:
{
Pointer pointer = (Pointer) data.getFieldValue("pointer");
List<Structure> arrays = pointer.fetchData();
List<Object> result = new ArrayList<Object>(arrays.size());
Properties temp = new Properties();
for (Structure array : arrays) {
temp.load(array, blenderContext);
result.add(temp.value);
}
value = result;
break;
}
case IDP_NUMTYPES:
throw new UnsupportedOperationException();
// return null;
default:
throw new IllegalStateException("Unknown custom property type: " + type);
}
this.completeLoading();
}
use of com.jme3.scene.plugins.blender.objects.Properties in project jmonkeyengine by jMonkeyEngine.
the class TemporalMesh method prepareLinesGeometry.
/**
* The method creates geometries from lines.
* @param result
* the list where new geometries will be appended
* @param meshHelper
* the mesh helper
*/
protected void prepareLinesGeometry(List<Geometry> result, MeshHelper meshHelper) {
if (edges.size() > 0) {
LOGGER.fine("Preparing lines geometries.");
List<List<Integer>> separateEdges = new ArrayList<List<Integer>>();
List<Edge> edges = new ArrayList<Edge>(this.edges.size());
for (Edge edge : this.edges) {
if (!edge.isInFace()) {
edges.add(edge);
}
}
while (edges.size() > 0) {
boolean edgeAppended = false;
int edgeIndex = 0;
for (List<Integer> list : separateEdges) {
for (edgeIndex = 0; edgeIndex < edges.size() && !edgeAppended; ++edgeIndex) {
Edge edge = edges.get(edgeIndex);
if (list.get(0).equals(edge.getFirstIndex())) {
list.add(0, edge.getSecondIndex());
--edgeIndex;
edgeAppended = true;
} else if (list.get(0).equals(edge.getSecondIndex())) {
list.add(0, edge.getFirstIndex());
--edgeIndex;
edgeAppended = true;
} else if (list.get(list.size() - 1).equals(edge.getFirstIndex())) {
list.add(edge.getSecondIndex());
--edgeIndex;
edgeAppended = true;
} else if (list.get(list.size() - 1).equals(edge.getSecondIndex())) {
list.add(edge.getFirstIndex());
--edgeIndex;
edgeAppended = true;
}
}
if (edgeAppended) {
break;
}
}
Edge edge = edges.remove(edgeAppended ? edgeIndex : 0);
if (!edgeAppended) {
separateEdges.add(new ArrayList<Integer>(Arrays.asList(edge.getFirstIndex(), edge.getSecondIndex())));
}
}
for (List<Integer> list : separateEdges) {
MeshBuffers meshBuffers = new MeshBuffers(0);
for (int index : list) {
meshBuffers.append(vertices.get(index), normals.get(index));
}
Mesh mesh = new Mesh();
mesh.setLineWidth(blenderContext.getBlenderKey().getLinesWidth());
mesh.setMode(Mode.LineStrip);
if (meshBuffers.isShortIndexBuffer()) {
mesh.setBuffer(Type.Index, 1, (ShortBuffer) meshBuffers.getIndexBuffer());
} else {
mesh.setBuffer(Type.Index, 1, (IntBuffer) meshBuffers.getIndexBuffer());
}
mesh.setBuffer(meshBuffers.getPositionsBuffer());
mesh.setBuffer(meshBuffers.getNormalsBuffer());
Geometry geometry = new Geometry(meshStructure.getName() + (result.size() + 1), mesh);
geometry.setMaterial(meshHelper.getBlackUnshadedMaterial(blenderContext));
if (properties != null && properties.getValue() != null) {
meshHelper.applyProperties(geometry, properties);
}
result.add(geometry);
}
}
}
use of com.jme3.scene.plugins.blender.objects.Properties in project jmonkeyengine by jMonkeyEngine.
the class TemporalMesh method prepareFacesGeometry.
/**
* The method creates geometries from faces.
* @param result
* the list where new geometries will be appended
* @param meshHelper
* the mesh helper
*/
protected void prepareFacesGeometry(List<Geometry> result, MeshHelper meshHelper) {
LOGGER.fine("Preparing faces geometries.");
this.triangulate();
Vector3f[] tempVerts = new Vector3f[3];
Vector3f[] tempNormals = new Vector3f[3];
byte[][] tempVertColors = new byte[3][];
List<Map<Float, Integer>> boneBuffers = new ArrayList<Map<Float, Integer>>(3);
LOGGER.log(Level.FINE, "Appending {0} faces to mesh buffers.", faces.size());
Map<Integer, MeshBuffers> faceMeshes = new HashMap<Integer, MeshBuffers>();
for (Face face : faces) {
MeshBuffers meshBuffers = faceMeshes.get(face.getMaterialNumber());
if (meshBuffers == null) {
meshBuffers = new MeshBuffers(face.getMaterialNumber());
faceMeshes.put(face.getMaterialNumber(), meshBuffers);
}
List<List<Integer>> triangulatedIndexes = face.getCurrentIndexes();
List<byte[]> vertexColors = face.getVertexColors();
for (List<Integer> indexes : triangulatedIndexes) {
assert indexes.size() == 3 : "The mesh has not been properly triangulated!";
Vector3f normal = null;
if (!face.isSmooth()) {
normal = FastMath.computeNormal(vertices.get(indexes.get(0)), vertices.get(indexes.get(1)), vertices.get(indexes.get(2)));
}
boneBuffers.clear();
for (int i = 0; i < 3; ++i) {
int vertIndex = indexes.get(i);
tempVerts[i] = vertices.get(vertIndex);
tempNormals[i] = normal != null ? normal : normals.get(vertIndex);
tempVertColors[i] = vertexColors != null ? vertexColors.get(face.getIndexes().indexOf(vertIndex)) : null;
if (boneIndexes.size() > 0 && vertexGroups.size() > 0) {
Map<Float, Integer> boneBuffersForVertex = new HashMap<Float, Integer>();
Map<String, Float> vertexGroupsForVertex = vertexGroups.get(vertIndex);
for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
if (vertexGroupsForVertex.containsKey(entry.getKey())) {
float weight = vertexGroupsForVertex.get(entry.getKey());
if (weight > MINIMUM_BONE_WEIGHT) {
// only values of weight greater than MINIMUM_BONE_WEIGHT are used
// if all non zero weights were used, and they were samm enough, problems with normalisation would occur
// because adding a very small value to 1.0 will give 1.0
// so in order to avoid such errors, which can cause severe animation artifacts we need to use some minimum weight value
boneBuffersForVertex.put(weight, entry.getValue());
}
}
}
if (boneBuffersForVertex.size() == 0) {
// attach the vertex to zero-indexed bone so that it does not collapse to (0, 0, 0)
boneBuffersForVertex.put(1.0f, 0);
}
boneBuffers.add(boneBuffersForVertex);
}
}
Map<String, List<Vector2f>> uvs = meshHelper.selectUVSubset(face, indexes.toArray(new Integer[indexes.size()]));
meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, uvs, tempVertColors, boneBuffers);
}
}
LOGGER.fine("Converting mesh buffers to geometries.");
Map<Geometry, MeshBuffers> geometryToBuffersMap = new HashMap<Geometry, MeshBuffers>();
for (Entry<Integer, MeshBuffers> entry : faceMeshes.entrySet()) {
MeshBuffers meshBuffers = entry.getValue();
Mesh mesh = new Mesh();
if (meshBuffers.isShortIndexBuffer()) {
mesh.setBuffer(Type.Index, 1, (ShortBuffer) meshBuffers.getIndexBuffer());
} else {
mesh.setBuffer(Type.Index, 1, (IntBuffer) meshBuffers.getIndexBuffer());
}
mesh.setBuffer(meshBuffers.getPositionsBuffer());
mesh.setBuffer(meshBuffers.getNormalsBuffer());
if (meshBuffers.areVertexColorsUsed()) {
mesh.setBuffer(Type.Color, 4, meshBuffers.getVertexColorsBuffer());
mesh.getBuffer(Type.Color).setNormalized(true);
}
BoneBuffersData boneBuffersData = meshBuffers.getBoneBuffers();
if (boneBuffersData != null) {
mesh.setMaxNumWeights(boneBuffersData.maximumWeightsPerVertex);
mesh.setBuffer(boneBuffersData.verticesWeights);
mesh.setBuffer(boneBuffersData.verticesWeightsIndices);
LOGGER.fine("Generating bind pose and normal buffers.");
mesh.generateBindPose(true);
// change the usage type of vertex and normal buffers from Static to Stream
mesh.getBuffer(Type.Position).setUsage(Usage.Stream);
mesh.getBuffer(Type.Normal).setUsage(Usage.Stream);
// creating empty buffers for HW skinning; the buffers will be setup if ever used
VertexBuffer verticesWeightsHW = new VertexBuffer(Type.HWBoneWeight);
VertexBuffer verticesWeightsIndicesHW = new VertexBuffer(Type.HWBoneIndex);
mesh.setBuffer(verticesWeightsHW);
mesh.setBuffer(verticesWeightsIndicesHW);
}
Geometry geometry = new Geometry(name + (result.size() + 1), mesh);
if (properties != null && properties.getValue() != null) {
meshHelper.applyProperties(geometry, properties);
}
result.add(geometry);
geometryToBuffersMap.put(geometry, meshBuffers);
}
LOGGER.fine("Applying materials to geometries.");
for (Entry<Geometry, MeshBuffers> entry : geometryToBuffersMap.entrySet()) {
int materialIndex = entry.getValue().getMaterialIndex();
Geometry geometry = entry.getKey();
if (materialIndex >= 0 && materials != null && materials.length > materialIndex && materials[materialIndex] != null) {
materials[materialIndex].applyMaterial(geometry, meshStructure.getOldMemoryAddress(), entry.getValue().getUvCoords(), blenderContext);
} else {
Material defaultMaterial = blenderContext.getDefaultMaterial().clone();
defaultMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
geometry.setMaterial(defaultMaterial);
}
}
}
use of com.jme3.scene.plugins.blender.objects.Properties in project jmonkeyengine by jMonkeyEngine.
the class TemporalMesh method clone.
@Override
public TemporalMesh clone() {
try {
TemporalMesh result = new TemporalMesh(meshStructure, blenderContext, false);
result.name = name;
for (Vector3f v : vertices) {
result.vertices.add(v.clone());
}
for (Vector3f n : normals) {
result.normals.add(n.clone());
}
for (Map<String, Float> group : vertexGroups) {
result.vertexGroups.add(new HashMap<String, Float>(group));
}
for (byte[] vertColor : verticesColors) {
result.verticesColors.add(vertColor.clone());
}
result.materials = materials;
result.properties = properties;
result.boneIndexes.putAll(boneIndexes);
result.postMeshCreationModifiers.addAll(postMeshCreationModifiers);
for (Face face : faces) {
result.faces.add(face.clone());
}
for (Edge edge : edges) {
result.edges.add(edge.clone());
}
for (Point point : points) {
result.points.add(point.clone());
}
result.rebuildIndexesMappings();
return result;
} catch (BlenderFileException e) {
LOGGER.log(Level.SEVERE, "Error while cloning the temporal mesh: {0}. Returning null.", e.getLocalizedMessage());
}
return null;
}
Aggregations