Feature: added support for mask modifier.
This commit is contained in:
parent
b39772c401
commit
e18ffccf8a
@ -23,6 +23,7 @@ import com.jme3.scene.plugins.blender.objects.ObjectHelper;
|
||||
*/
|
||||
public class BoneContext {
|
||||
// the flags of the bone
|
||||
public static final int SELECTED = 0x0001;
|
||||
public static final int CONNECTED_TO_PARENT = 0x0010;
|
||||
public static final int DEFORM = 0x1000;
|
||||
|
||||
|
@ -11,6 +11,7 @@ import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
|
||||
|
||||
/**
|
||||
* A class that represents a single edge between two vertices.
|
||||
@ -82,10 +83,17 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
* Shifts indexes by a given amount.
|
||||
* @param shift
|
||||
* how much the indexes should be shifted
|
||||
* @param predicate
|
||||
* the predicate that verifies which indexes should be shifted; if null then all will be shifted
|
||||
*/
|
||||
public void shiftIndexes(int shift) {
|
||||
index1 += shift;
|
||||
index2 += shift;
|
||||
public void shiftIndexes(int shift, IndexPredicate predicate) {
|
||||
if (predicate == null) {
|
||||
index1 += shift;
|
||||
index2 += shift;
|
||||
} else {
|
||||
index1 += predicate.execute(index1) ? shift : 0;
|
||||
index2 += predicate.execute(index2) ? shift : 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -116,12 +116,19 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
}
|
||||
return indexes.get(indexPosition);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the original indexes of the face
|
||||
*/
|
||||
public IndexesLoop getIndexes() {
|
||||
return indexes;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all indexes
|
||||
* @return current indexes of the face (if it is already triangulated then more than one index group will be in the result list)
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public List<List<Integer>> getIndexes() {
|
||||
public List<List<Integer>> getCurrentIndexes() {
|
||||
if(triangulatedFaces == null) {
|
||||
return Arrays.asList(indexes.getAll());
|
||||
}
|
||||
@ -178,25 +185,6 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
return detachedFaces;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method returns the position of the given index in the indexes loop.
|
||||
* @param index
|
||||
* the index whose position will be queried
|
||||
* @return position of the given index or -1 if such index is not in the index loop
|
||||
*/
|
||||
public int indexOf(Integer index) {
|
||||
return indexes.indexOf(index);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method shifts all indexes by a given value.
|
||||
* @param shift
|
||||
* the value to shift all indexes
|
||||
*/
|
||||
public void shiftIndexes(int shift) {
|
||||
indexes.shiftIndexes(shift);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the temporal mesh for the face. The given mesh cannot be null.
|
||||
* @param temporalMesh
|
||||
@ -273,7 +261,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
Face face = facesToTriangulate.remove(0);
|
||||
int previousIndex1 = -1, previousIndex2 = -1, previousIndex3 = -1;
|
||||
while (face.vertexCount() > 0) {
|
||||
indexes[0] = face.getIndex(0);
|
||||
indexes[0] = face.getIndex(0);
|
||||
indexes[1] = face.findClosestVertex(indexes[0], -1);
|
||||
indexes[2] = face.findClosestVertex(indexes[0], indexes[1]);
|
||||
|
||||
@ -394,7 +382,7 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
// with the one creaded by vertices: [index1 - 1, index1, index2]
|
||||
// if the latter is greater than it means that the edge is outside the face
|
||||
// IMPORTANT: we assume that all vertices are in one plane (this should be ensured before creating the Face)
|
||||
int indexOfIndex1 = this.indexOf(index1);
|
||||
int indexOfIndex1 = indexes.indexOf(index1);
|
||||
int indexMinus1 = this.getIndex(indexOfIndex1 - 1);// indexOfIndex1 == 0 ? indexes.get(indexes.size() - 1) : indexes.get(indexOfIndex1 - 1);
|
||||
int indexPlus1 = this.getIndex(indexOfIndex1 + 1);// indexOfIndex1 == indexes.size() - 1 ? 0 : indexes.get(indexOfIndex1 + 1);
|
||||
|
||||
@ -525,6 +513,6 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
|
||||
@Override
|
||||
public int compare(Integer index1, Integer index2) {
|
||||
return this.indexOf(index1) - this.indexOf(index2);
|
||||
return indexes.indexOf(index1) - indexes.indexOf(index2);
|
||||
}
|
||||
}
|
||||
|
@ -18,10 +18,17 @@ import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class IndexesLoop implements Comparator<Integer>, Iterable<Integer> {
|
||||
public static final IndexPredicate INDEX_PREDICATE_USE_ALL = new IndexPredicate() {
|
||||
@Override
|
||||
public boolean execute(Integer index) {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
/** The indexes. */
|
||||
private List<Integer> nodes;
|
||||
/** The edges of the indexes graph. The key is the 'from' index and 'value' is - 'to' index. */
|
||||
private Map<Integer, List<Integer>> edges = new HashMap<Integer, List<Integer>>();
|
||||
private Map<Integer, List<Integer>> edges = new HashMap<Integer, List<Integer>>();
|
||||
|
||||
/**
|
||||
* The constructor uses the given nodes in their give order. Each neighbour indexes will form an edge.
|
||||
@ -124,18 +131,23 @@ public class IndexesLoop implements Comparator<Integer>, Iterable<Integer> {
|
||||
* The method shifts all indexes by a given value.
|
||||
* @param shift
|
||||
* the value to shift all indexes
|
||||
* @param predicate
|
||||
* the predicate that verifies which indexes should be shifted; if null then all will be shifted
|
||||
*/
|
||||
public void shiftIndexes(int shift) {
|
||||
public void shiftIndexes(int shift, IndexPredicate predicate) {
|
||||
if (predicate == null) {
|
||||
predicate = INDEX_PREDICATE_USE_ALL;
|
||||
}
|
||||
List<Integer> nodes = new ArrayList<Integer>(this.nodes.size());
|
||||
for (Integer node : this.nodes) {
|
||||
nodes.add(node + shift);
|
||||
nodes.add(node + (predicate.execute(node) ? shift : 0));
|
||||
}
|
||||
|
||||
Map<Integer, List<Integer>> edges = new HashMap<Integer, List<Integer>>();
|
||||
for (Entry<Integer, List<Integer>> entry : this.edges.entrySet()) {
|
||||
List<Integer> neighbours = new ArrayList<Integer>(entry.getValue().size());
|
||||
for (Integer neighbour : entry.getValue()) {
|
||||
neighbours.add(neighbour + shift);
|
||||
neighbours.add(neighbour + (predicate.execute(neighbour) ? shift : 0));
|
||||
}
|
||||
edges.put(entry.getKey() + shift, neighbours);
|
||||
}
|
||||
@ -264,4 +276,8 @@ public class IndexesLoop implements Comparator<Integer>, Iterable<Integer> {
|
||||
public Iterator<Integer> iterator() {
|
||||
return nodes.iterator();
|
||||
}
|
||||
|
||||
public static interface IndexPredicate {
|
||||
boolean execute(Integer index);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,7 @@ import java.util.logging.Logger;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
|
||||
|
||||
/**
|
||||
* A class that represents a single point on the scene that is not a part of an edge.
|
||||
@ -47,9 +48,13 @@ import com.jme3.scene.plugins.blender.file.Structure;
|
||||
* The method shifts the index by a given value.
|
||||
* @param shift
|
||||
* the value to shift the index
|
||||
* @param predicate
|
||||
* the predicate that verifies which indexes should be shifted; if null then all will be shifted
|
||||
*/
|
||||
public void shiftIndexes(int shift) {
|
||||
index += shift;
|
||||
public void shiftIndexes(int shift, IndexPredicate predicate) {
|
||||
if (predicate == null || predicate.execute(index)) {
|
||||
index += shift;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -4,12 +4,15 @@ import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -29,6 +32,7 @@ import com.jme3.scene.plugins.blender.BlenderContext.LoadedDataType;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.meshes.IndexesLoop.IndexPredicate;
|
||||
import com.jme3.scene.plugins.blender.meshes.MeshBuffers.BoneBuffersData;
|
||||
import com.jme3.scene.plugins.blender.modifiers.Modifier;
|
||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
@ -222,14 +226,14 @@ public class TemporalMesh extends Geometry {
|
||||
int shift = vertices.size();
|
||||
if (shift > 0) {
|
||||
for (Face face : mesh.faces) {
|
||||
face.shiftIndexes(shift);
|
||||
face.getIndexes().shiftIndexes(shift, null);
|
||||
face.setTemporalMesh(this);
|
||||
}
|
||||
for (Edge edge : mesh.edges) {
|
||||
edge.shiftIndexes(shift);
|
||||
edge.shiftIndexes(shift, null);
|
||||
}
|
||||
for (Point point : mesh.points) {
|
||||
point.shiftIndexes(shift);
|
||||
point.shiftIndexes(shift, null);
|
||||
}
|
||||
}
|
||||
|
||||
@ -320,6 +324,94 @@ public class TemporalMesh extends Geometry {
|
||||
return normals.get(i);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the vertex groups at the given vertex index.
|
||||
* @param i
|
||||
* the vertex groups for vertex with a given index
|
||||
* @return the vertex groups at the given vertex index
|
||||
*/
|
||||
public Map<String, Float> getVertexGroups(int i) {
|
||||
return vertexGroups.size() > i ? vertexGroups.get(i) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return a collection of vertex group names for this mesh
|
||||
*/
|
||||
public Collection<String> getVertexGroupNames() {
|
||||
Set<String> result = new HashSet<String>();
|
||||
for (Map<String, Float> groups : vertexGroups) {
|
||||
result.addAll(groups.keySet());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all vertices from the mesh.
|
||||
*/
|
||||
public void clear() {
|
||||
vertices.clear();
|
||||
normals.clear();
|
||||
vertexGroups.clear();
|
||||
verticesColors.clear();
|
||||
faces.clear();
|
||||
edges.clear();
|
||||
points.clear();
|
||||
}
|
||||
|
||||
/**
|
||||
* Every face, edge and point that contains
|
||||
* the vertex will be removed.
|
||||
* @param index
|
||||
* the index of a vertex to be removed
|
||||
* @throws IndexOutOfBoundsException
|
||||
* thrown when given index is negative or beyond the count of vertices
|
||||
*/
|
||||
public void removeVertexAt(final int index) {
|
||||
if (index < 0 || index >= vertices.size()) {
|
||||
throw new IndexOutOfBoundsException("The given index is out of bounds: " + index);
|
||||
}
|
||||
|
||||
vertices.remove(index);
|
||||
normals.remove(index);
|
||||
if(vertexGroups.size() > 0) {
|
||||
vertexGroups.remove(index);
|
||||
}
|
||||
if(verticesColors.size() > 0) {
|
||||
verticesColors.remove(index);
|
||||
}
|
||||
|
||||
IndexPredicate shiftPredicate = new IndexPredicate() {
|
||||
@Override
|
||||
public boolean execute(Integer i) {
|
||||
return i > index;
|
||||
}
|
||||
};
|
||||
for (int i = faces.size() - 1; i >= 0; --i) {
|
||||
Face face = faces.get(i);
|
||||
if (face.getIndexes().indexOf(index) >= 0) {
|
||||
faces.remove(i);
|
||||
} else {
|
||||
face.getIndexes().shiftIndexes(-1, shiftPredicate);
|
||||
}
|
||||
}
|
||||
for (int i = edges.size() - 1; i >= 0; --i) {
|
||||
Edge edge = edges.get(i);
|
||||
if (edge.getFirstIndex() == index || edge.getSecondIndex() == index) {
|
||||
edges.remove(i);
|
||||
} else {
|
||||
edge.shiftIndexes(-1, shiftPredicate);
|
||||
}
|
||||
}
|
||||
for (int i = points.size() - 1; i >= 0; --i) {
|
||||
Point point = points.get(i);
|
||||
if (point.getIndex() == index) {
|
||||
points.remove(i);
|
||||
} else {
|
||||
point.shiftIndexes(-1, shiftPredicate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Flips the order of the mesh's indexes.
|
||||
*/
|
||||
@ -397,10 +489,10 @@ public class TemporalMesh extends Geometry {
|
||||
faceMeshes.put(face.getMaterialNumber(), meshBuffers);
|
||||
}
|
||||
|
||||
List<List<Integer>> triangulatedIndexes = face.getIndexes();
|
||||
List<List<Integer>> triangulatedIndexes = face.getCurrentIndexes();
|
||||
List<byte[]> vertexColors = face.getVertexColors();
|
||||
|
||||
for(List<Integer> indexes : triangulatedIndexes) {
|
||||
|
||||
for (List<Integer> indexes : triangulatedIndexes) {
|
||||
assert indexes.size() == 3 : "The mesh has not been properly triangulated!";
|
||||
boneBuffers.clear();
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
@ -408,7 +500,7 @@ public class TemporalMesh extends Geometry {
|
||||
tempVerts[i] = vertices.get(vertIndex);
|
||||
tempNormals[i] = normals.get(vertIndex);
|
||||
tempVertColors[i] = vertexColors != null ? vertexColors.get(i) : null;
|
||||
|
||||
|
||||
if (boneIndexes.size() > 0) {
|
||||
Map<Float, Integer> boneBuffersForVertex = new HashMap<Float, Integer>();
|
||||
Map<String, Float> vertexGroupsForVertex = vertexGroups.get(vertIndex);
|
||||
@ -420,7 +512,7 @@ public class TemporalMesh extends Geometry {
|
||||
boneBuffers.add(boneBuffersForVertex);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
meshBuffers.append(face.isSmooth(), tempVerts, tempNormals, face.getUvSets(), tempVertColors, boneBuffers);
|
||||
}
|
||||
}
|
||||
@ -495,7 +587,7 @@ public class TemporalMesh extends Geometry {
|
||||
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);
|
||||
while (edges.size() > 0) {
|
||||
@ -568,7 +660,7 @@ public class TemporalMesh extends Geometry {
|
||||
protected void preparePointsGeometry(List<Geometry> result, MeshHelper meshHelper) {
|
||||
if (points.size() > 0) {
|
||||
LOGGER.fine("Preparing point geometries.");
|
||||
|
||||
|
||||
MeshBuffers pointBuffers = new MeshBuffers(0);
|
||||
for (Point point : points) {
|
||||
pointBuffers.append(vertices.get(point.getIndex()), normals.get(point.getIndex()));
|
||||
|
@ -0,0 +1,138 @@
|
||||
package com.jme3.scene.plugins.blender.modifiers;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.scene.Node;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.animations.BoneContext;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
||||
|
||||
/**
|
||||
* This modifier allows to use mask modifier on the object.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
/* package */class MaskModifier extends Modifier {
|
||||
private static final Logger LOGGER = Logger.getLogger(MaskModifier.class.getName());
|
||||
|
||||
private static final int FLAG_INVERT_MASK = 0x01;
|
||||
|
||||
private static final int MODE_VERTEX_GROUP = 0;
|
||||
private static final int MODE_ARMATURE = 1;
|
||||
|
||||
private Pointer pArmatureObject;
|
||||
private String vertexGroupName;
|
||||
private boolean invertMask;
|
||||
|
||||
public MaskModifier(Structure modifierStructure, BlenderContext blenderContext) {
|
||||
if (this.validate(modifierStructure, blenderContext)) {
|
||||
int flag = ((Number) modifierStructure.getFieldValue("flag")).intValue();
|
||||
invertMask = (flag & FLAG_INVERT_MASK) != 0;
|
||||
|
||||
int mode = ((Number) modifierStructure.getFieldValue("mode")).intValue();
|
||||
if (mode == MODE_VERTEX_GROUP) {
|
||||
vertexGroupName = modifierStructure.getFieldValue("vgroup").toString();
|
||||
if (vertexGroupName != null && vertexGroupName.length() == 0) {
|
||||
vertexGroupName = null;
|
||||
}
|
||||
} else if (mode == MODE_ARMATURE) {
|
||||
pArmatureObject = (Pointer) modifierStructure.getFieldValue("ob_arm");
|
||||
} else {
|
||||
LOGGER.log(Level.SEVERE, "Unknown mode type: {0}. Cannot apply modifier: {1}.", new Object[] { mode, modifierStructure.getName() });
|
||||
invalid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void apply(Node node, BlenderContext blenderContext) {
|
||||
if (invalid) {
|
||||
LOGGER.log(Level.WARNING, "Mirror modifier is invalid! Cannot be applied to: {0}", node.getName());
|
||||
} else {
|
||||
TemporalMesh temporalMesh = this.getTemporalMesh(node);
|
||||
if (temporalMesh != null) {
|
||||
List<String> vertexGroupsToRemove = new ArrayList<String>();
|
||||
if (vertexGroupName != null) {
|
||||
vertexGroupsToRemove.add(vertexGroupName);
|
||||
} else if (pArmatureObject != null && pArmatureObject.isNotNull()) {
|
||||
try {
|
||||
Structure armatureObject = pArmatureObject.fetchData().get(0);
|
||||
|
||||
Structure armatureStructure = ((Pointer) armatureObject.getFieldValue("data")).fetchData().get(0);
|
||||
List<Structure> bonebase = ((Structure) armatureStructure.getFieldValue("bonebase")).evaluateListBase();
|
||||
vertexGroupsToRemove.addAll(this.readBoneNames(bonebase));
|
||||
} catch (BlenderFileException e) {
|
||||
LOGGER.log(Level.SEVERE, "Cannot load armature object for the mask modifier. Cause: {0}", e.getLocalizedMessage());
|
||||
LOGGER.log(Level.SEVERE, "Mask modifier will NOT be applied to node named: {0}", node.getName());
|
||||
}
|
||||
} else {
|
||||
// if the mesh has no vertex groups then remove all verts
|
||||
// if the mesh has at least one vertex group - then do nothing
|
||||
// I have no idea why we should do that, but blender works this way
|
||||
Collection<String> vertexGroupNames = temporalMesh.getVertexGroupNames();
|
||||
if (vertexGroupNames.size() == 0 && !invertMask || vertexGroupNames.size() > 0 && invertMask) {
|
||||
temporalMesh.clear();
|
||||
}
|
||||
}
|
||||
|
||||
if (vertexGroupsToRemove.size() > 0) {
|
||||
List<Integer> vertsToBeRemoved = new ArrayList<Integer>();
|
||||
for (int i = 0; i < temporalMesh.getVertexCount(); ++i) {
|
||||
Map<String, Float> vertexGroups = temporalMesh.getVertexGroups(i);
|
||||
boolean hasVertexGroup = false;
|
||||
if(vertexGroups != null) {
|
||||
for (String groupName : vertexGroupsToRemove) {
|
||||
Float weight = vertexGroups.get(groupName);
|
||||
if (weight != null && weight > 0) {
|
||||
hasVertexGroup = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!hasVertexGroup && !invertMask || hasVertexGroup && invertMask) {
|
||||
vertsToBeRemoved.add(i);
|
||||
}
|
||||
}
|
||||
|
||||
Collections.reverse(vertsToBeRemoved);
|
||||
for (Integer vertexIndex : vertsToBeRemoved) {
|
||||
temporalMesh.removeVertexAt(vertexIndex);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LOGGER.log(Level.WARNING, "Cannot find temporal mesh for node: {0}. The modifier will NOT be applied!", node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the names of the bones from the given bone base.
|
||||
* @param boneBase
|
||||
* the list of bone base structures
|
||||
* @return a list of bones' names
|
||||
* @throws BlenderFileException
|
||||
* is thrown if problems with reading the child bones' bases occur
|
||||
*/
|
||||
private List<String> readBoneNames(List<Structure> boneBase) throws BlenderFileException {
|
||||
List<String> result = new ArrayList<String>();
|
||||
for (Structure boneStructure : boneBase) {
|
||||
int flag = ((Number) boneStructure.getFieldValue("flag")).intValue();
|
||||
if ((flag & BoneContext.SELECTED) != 0) {
|
||||
result.add(boneStructure.getFieldValue("name").toString());
|
||||
}
|
||||
List<Structure> childbase = ((Structure) boneStructure.getFieldValue("childbase")).evaluateListBase();
|
||||
result.addAll(this.readBoneNames(childbase));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user