Merge branch 'master' of https://github.com/jMonkeyEngine/jmonkeyengine
This commit is contained in:
commit
ef6882aeda
@ -28,8 +28,12 @@ public class BezierCurve {
|
||||
/** Array that stores a radius for each bezier triple. */
|
||||
private double[] radiuses;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension) {
|
||||
this(type, bezTriples, dimension, false);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public BezierCurve(final int type, final List<Structure> bezTriples, final int dimension, boolean fixUpAxis) {
|
||||
if (dimension != 2 && dimension != 3) {
|
||||
throw new IllegalArgumentException("The dimension of the curve should be 2 or 3!");
|
||||
}
|
||||
@ -45,7 +49,12 @@ public class BezierCurve {
|
||||
DynamicArray<Number> vec = (DynamicArray<Number>) bezTriple.getFieldValue("vec");
|
||||
for (j = 0; j < 3; ++j) {
|
||||
for (k = 0; k < dimension; ++k) {
|
||||
bezierPoints[i][j][k] = vec.get(j, k).floatValue();
|
||||
bezierPoints[i][j][k] = vec.get(j, k).doubleValue();
|
||||
}
|
||||
if (fixUpAxis && dimension == 3) {
|
||||
double temp = bezierPoints[i][j][2];
|
||||
bezierPoints[i][j][2] = -bezierPoints[i][j][1];
|
||||
bezierPoints[i][j][1] = temp;
|
||||
}
|
||||
}
|
||||
radiuses[i++] = ((Number) bezTriple.getFieldValue("radius")).floatValue();
|
||||
@ -114,9 +123,9 @@ public class BezierCurve {
|
||||
public List<Vector3f> getControlPoints() {
|
||||
List<Vector3f> controlPoints = new ArrayList<Vector3f>(bezierPoints.length * 3);
|
||||
for (int i = 0; i < bezierPoints.length; ++i) {
|
||||
controlPoints.add(new Vector3f((float)bezierPoints[i][0][0], (float)bezierPoints[i][0][1], (float)bezierPoints[i][0][2]));
|
||||
controlPoints.add(new Vector3f((float)bezierPoints[i][1][0], (float)bezierPoints[i][1][1], (float)bezierPoints[i][1][2]));
|
||||
controlPoints.add(new Vector3f((float)bezierPoints[i][2][0], (float)bezierPoints[i][2][1], (float)bezierPoints[i][2][2]));
|
||||
controlPoints.add(new Vector3f((float) bezierPoints[i][0][0], (float) bezierPoints[i][0][1], (float) bezierPoints[i][0][2]));
|
||||
controlPoints.add(new Vector3f((float) bezierPoints[i][1][0], (float) bezierPoints[i][1][1], (float) bezierPoints[i][1][2]));
|
||||
controlPoints.add(new Vector3f((float) bezierPoints[i][2][0], (float) bezierPoints[i][2][1], (float) bezierPoints[i][2][2]));
|
||||
}
|
||||
return controlPoints;
|
||||
}
|
||||
|
@ -31,44 +31,16 @@
|
||||
*/
|
||||
package com.jme3.scene.plugins.blender.curves;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.nio.IntBuffer;
|
||||
import java.nio.ShortBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Matrix4f;
|
||||
import com.jme3.math.Quaternion;
|
||||
import com.jme3.math.Spline;
|
||||
import com.jme3.math.Spline.SplineType;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.scene.Geometry;
|
||||
import com.jme3.scene.Mesh;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.mesh.IndexBuffer;
|
||||
import com.jme3.scene.plugins.blender.AbstractBlenderHelper;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||
import com.jme3.scene.plugins.blender.objects.Properties;
|
||||
import com.jme3.scene.shape.Curve;
|
||||
import com.jme3.scene.shape.Surface;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* A class that is used in mesh calculations.
|
||||
@ -95,586 +67,17 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
super(blenderVersion, blenderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object
|
||||
* can have several separate curves.
|
||||
* @param curveStructure
|
||||
* the curve structure
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return a list of geometries repreenting a single curve object
|
||||
* @throws BlenderFileException
|
||||
*/
|
||||
public List<Geometry> toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
String name = curveStructure.getName();
|
||||
int flag = ((Number) curveStructure.getFieldValue("flag")).intValue();
|
||||
boolean is3D = (flag & 0x01) != 0;
|
||||
boolean isFront = (flag & 0x02) != 0 && !is3D;
|
||||
boolean isBack = (flag & 0x04) != 0 && !is3D;
|
||||
if (isFront) {
|
||||
LOGGER.warning("No front face in curve implemented yet!");// TODO: implement front face
|
||||
}
|
||||
if (isBack) {
|
||||
LOGGER.warning("No back face in curve implemented yet!");// TODO: implement back face
|
||||
}
|
||||
public CurvesTemporalMesh toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
CurvesTemporalMesh result = new CurvesTemporalMesh(curveStructure, blenderContext);
|
||||
|
||||
// reading nurbs (and sorting them by material)
|
||||
List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase();
|
||||
Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
|
||||
for (Structure nurb : nurbStructures) {
|
||||
Number matNumber = (Number) nurb.getFieldValue("mat_nr");
|
||||
List<Structure> nurbList = nurbs.get(matNumber);
|
||||
if (nurbList == null) {
|
||||
nurbList = new ArrayList<Structure>();
|
||||
nurbs.put(matNumber, nurbList);
|
||||
}
|
||||
nurbList.add(nurb);
|
||||
}
|
||||
|
||||
// getting materials
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
MaterialContext[] materialContexts = materialHelper.getMaterials(curveStructure, blenderContext);
|
||||
Material defaultMaterial = null;
|
||||
if (materialContexts != null) {
|
||||
for (MaterialContext materialContext : materialContexts) {
|
||||
materialContext.setFaceCullMode(FaceCullMode.Off);
|
||||
}
|
||||
} else {
|
||||
defaultMaterial = blenderContext.getDefaultMaterial().clone();
|
||||
defaultMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
|
||||
}
|
||||
|
||||
// getting or creating bevel object
|
||||
List<Geometry> bevelObject = null;
|
||||
Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");
|
||||
if (pBevelObject.isNotNull()) {
|
||||
Pointer pBevelStructure = (Pointer) pBevelObject.fetchData().get(0).getFieldValue("data");
|
||||
Structure bevelStructure = pBevelStructure.fetchData().get(0);
|
||||
bevelObject = this.toCurve(bevelStructure, blenderContext);
|
||||
} else {
|
||||
int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue();
|
||||
float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue();
|
||||
float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue();
|
||||
if (bevelDepth > 0.0f) {
|
||||
float handlerLength = bevelDepth / 2.0f;
|
||||
|
||||
List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13);
|
||||
if (extrude > 0.0f) {
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, extrude));
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, -handlerLength + extrude));
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, handlerLength - extrude));
|
||||
}
|
||||
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, -extrude));
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, -handlerLength - extrude));
|
||||
|
||||
conrtolPoints.add(new Vector3f(-handlerLength, 0, -bevelDepth - extrude));
|
||||
conrtolPoints.add(new Vector3f(0, 0, -bevelDepth - extrude));
|
||||
conrtolPoints.add(new Vector3f(handlerLength, 0, -bevelDepth - extrude));
|
||||
|
||||
if (extrude > 0.0f) {
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude - handlerLength));
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude));
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, -extrude + handlerLength));
|
||||
}
|
||||
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude - handlerLength));
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude));
|
||||
conrtolPoints.add(new Vector3f(bevelDepth, 0, extrude + handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(handlerLength, 0, bevelDepth + extrude));
|
||||
conrtolPoints.add(new Vector3f(0, 0, bevelDepth + extrude));
|
||||
conrtolPoints.add(new Vector3f(-handlerLength, 0, bevelDepth + extrude));
|
||||
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, handlerLength + extrude));
|
||||
conrtolPoints.add(new Vector3f(-bevelDepth, 0, extrude));
|
||||
|
||||
Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false);
|
||||
Curve bevelCurve = new Curve(bevelSpline, bevResol);
|
||||
bevelObject = new ArrayList<Geometry>(1);
|
||||
bevelObject.add(new Geometry("", bevelCurve));
|
||||
} else if (extrude > 0.0f) {
|
||||
Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[] { new Vector3f(0, 0, -extrude), new Vector3f(0, 0, extrude) }, 1, false);
|
||||
Curve bevelCurve = new Curve(bevelSpline, bevResol);
|
||||
bevelObject = new ArrayList<Geometry>(1);
|
||||
bevelObject.add(new Geometry("", bevelCurve));
|
||||
}
|
||||
}
|
||||
|
||||
// getting taper object
|
||||
Spline taperObject = null;
|
||||
Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");
|
||||
if (bevelObject != null && pTaperObject.isNotNull()) {
|
||||
Pointer pTaperStructure = (Pointer) pTaperObject.fetchData().get(0).getFieldValue("data");
|
||||
Structure taperStructure = pTaperStructure.fetchData().get(0);
|
||||
taperObject = this.loadTaperObject(taperStructure);
|
||||
}
|
||||
|
||||
Vector3f loc = this.getLoc(curveStructure);
|
||||
// creating the result curves
|
||||
List<Geometry> result = new ArrayList<Geometry>(nurbs.size());
|
||||
for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
|
||||
for (Structure nurb : nurbEntry.getValue()) {
|
||||
int type = ((Number) nurb.getFieldValue("type")).intValue();
|
||||
List<Geometry> nurbGeoms = null;
|
||||
if ((type & 0x01) != 0) {// Bezier curve
|
||||
nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, blenderContext);
|
||||
} else if ((type & 0x04) != 0) {// NURBS
|
||||
nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, blenderContext);
|
||||
}
|
||||
if (nurbGeoms != null) {// setting the name and assigning materials
|
||||
for (Geometry nurbGeom : nurbGeoms) {
|
||||
if (materialContexts != null) {
|
||||
materialContexts[nurbEntry.getKey().intValue()].applyMaterial(nurbGeom, curveStructure.getOldMemoryAddress(), null, blenderContext);
|
||||
} else {
|
||||
nurbGeom.setMaterial(defaultMaterial);
|
||||
}
|
||||
nurbGeom.setName(name);
|
||||
result.add(nurbGeom);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// reading custom properties
|
||||
if (blenderContext.getBlenderKey().isLoadObjectProperties() && result.size() > 0) {
|
||||
Properties properties = this.loadProperties(curveStructure, blenderContext);
|
||||
// the loaded property is a group property, so we need to get each value and set it to every geometry of the curve
|
||||
if (properties != null && properties.getValue() != null) {
|
||||
for(Geometry geom : result) {
|
||||
this.applyProperties(geom, properties);
|
||||
}
|
||||
}
|
||||
if (blenderContext.getBlenderKey().isLoadObjectProperties()) {
|
||||
LOGGER.fine("Reading custom properties.");
|
||||
result.setProperties(this.loadProperties(curveStructure, blenderContext));
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the bezier curve.
|
||||
* @param loc
|
||||
* the translation of the curve
|
||||
* @param nurb
|
||||
* the nurb structure
|
||||
* @param bevelObject
|
||||
* the bevel object
|
||||
* @param taperObject
|
||||
* the taper object
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return a list of geometries representing the curves
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when there are problems with the blender file
|
||||
*/
|
||||
protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Spline taperObject, BlenderContext blenderContext) throws BlenderFileException {
|
||||
Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
|
||||
List<Geometry> result = new ArrayList<Geometry>();
|
||||
if (pBezierTriple.isNotNull()) {
|
||||
boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0;
|
||||
int resolution = ((Number) nurb.getFieldValue("resolu")).intValue();
|
||||
boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0;
|
||||
|
||||
// creating the curve object
|
||||
BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(), 3);
|
||||
List<Vector3f> controlPoints = bezierCurve.getControlPoints();
|
||||
if (fixUpAxis) {
|
||||
for (Vector3f v : controlPoints) {
|
||||
float y = v.y;
|
||||
v.y = v.z;
|
||||
v.z = -y;
|
||||
}
|
||||
}
|
||||
|
||||
if (bevelObject != null && taperObject == null) {// create taper object using the scales of the bezier triple
|
||||
int triplesCount = controlPoints.size() / 3;
|
||||
List<Vector3f> taperControlPoints = new ArrayList<Vector3f>(triplesCount);
|
||||
for (int i = 0; i < triplesCount; ++i) {
|
||||
taperControlPoints.add(new Vector3f(controlPoints.get(i * 3 + 1).x, (float)bezierCurve.getRadius(i), 0));
|
||||
}
|
||||
taperObject = new Spline(SplineType.Linear, taperControlPoints, 0, false);
|
||||
}
|
||||
|
||||
if (cyclic) {
|
||||
// copy the first three points at the end
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
controlPoints.add(controlPoints.get(i));
|
||||
}
|
||||
}
|
||||
// removing the first and last handles
|
||||
controlPoints.remove(0);
|
||||
controlPoints.remove(controlPoints.size() - 1);
|
||||
|
||||
// creating curve
|
||||
Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false);
|
||||
Curve curve = new Curve(spline, resolution);
|
||||
if (bevelObject == null) {// creating a normal curve
|
||||
Geometry curveGeometry = new Geometry(null, curve);
|
||||
result.add(curveGeometry);
|
||||
// TODO: use front and back flags; surface excluding algorithm for bezier circles should be added
|
||||
} else {// creating curve with bevel and taper shape
|
||||
result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, blenderContext);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the NURBS curve or surface.
|
||||
* @param loc
|
||||
* object's location
|
||||
* @param nurb
|
||||
* the NURBS data structure
|
||||
* @param bevelObject
|
||||
* the bevel object to be applied
|
||||
* @param taperObject
|
||||
* the taper object to be applied
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return a list of geometries that represents the loaded NURBS curve or surface
|
||||
* @throws BlenderFileException
|
||||
* an exception is throw when problems with blender loaded data occurs
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Spline taperObject, BlenderContext blenderContext) throws BlenderFileException {
|
||||
// loading the knots
|
||||
List<Float>[] knots = new List[2];
|
||||
Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") };
|
||||
for (int i = 0; i < knots.length; ++i) {
|
||||
if (pKnots[i].isNotNull()) {
|
||||
FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress());
|
||||
BlenderInputStream blenderInputStream = blenderContext.getInputStream();
|
||||
blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());
|
||||
int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;
|
||||
knots[i] = new ArrayList<Float>(knotsAmount);
|
||||
for (int j = 0; j < knotsAmount; ++j) {
|
||||
knots[i].add(Float.valueOf(blenderInputStream.readFloat()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loading the flags and orders (basis functions degrees)
|
||||
int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();
|
||||
int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();
|
||||
int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();
|
||||
int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();
|
||||
|
||||
// loading control points and their weights
|
||||
int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();
|
||||
int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();
|
||||
List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData();
|
||||
List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
|
||||
for (int i = 0; i < pntsV; ++i) {
|
||||
List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
|
||||
for (int j = 0; j < pntsU; ++j) {
|
||||
DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");
|
||||
if (fixUpAxis) {
|
||||
uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));
|
||||
} else {
|
||||
uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));
|
||||
}
|
||||
}
|
||||
if ((flagU & 0x01) != 0) {
|
||||
for (int k = 0; k < orderU - 1; ++k) {
|
||||
uControlPoints.add(uControlPoints.get(k));
|
||||
}
|
||||
}
|
||||
controlPoints.add(uControlPoints);
|
||||
}
|
||||
if ((flagV & 0x01) != 0) {
|
||||
for (int k = 0; k < orderV - 1; ++k) {
|
||||
controlPoints.add(controlPoints.get(k));
|
||||
}
|
||||
}
|
||||
|
||||
int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1;
|
||||
List<Geometry> result;
|
||||
if (knots[1] == null) {// creating the curve
|
||||
Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]);
|
||||
Curve nurbCurve = new Curve(nurbSpline, resolu);
|
||||
if (bevelObject != null) {
|
||||
result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, blenderContext);// TODO: smooth
|
||||
} else {
|
||||
result = new ArrayList<Geometry>(1);
|
||||
Geometry nurbGeometry = new Geometry("", nurbCurve);
|
||||
result.add(nurbGeometry);
|
||||
}
|
||||
} else {// creating the nurb surface
|
||||
int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1;
|
||||
Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV);
|
||||
Geometry nurbGeometry = new Geometry("", nurbSurface);
|
||||
result = new ArrayList<Geometry>(1);
|
||||
result.add(nurbGeometry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method computes the taper scale on the given point on the curve.
|
||||
*
|
||||
* @param taper
|
||||
* the taper object that defines the scale
|
||||
* @param percent
|
||||
* the percent of the 'road' along the curve
|
||||
* @return scale on the pointed place along the curve
|
||||
*/
|
||||
protected float getTaperScale(Spline taper, float percent) {
|
||||
if (taper == null) {
|
||||
return 1;// return scale = 1 if no taper is applied
|
||||
}
|
||||
percent = FastMath.clamp(percent, 0, 1);
|
||||
List<Float> segmentLengths = taper.getSegmentsLength();
|
||||
float percentLength = taper.getTotalLength() * percent;
|
||||
float partLength = 0;
|
||||
int i;
|
||||
for (i = 0; i < segmentLengths.size(); ++i) {
|
||||
partLength += segmentLengths.get(i);
|
||||
if (partLength > percentLength) {
|
||||
partLength -= segmentLengths.get(i);
|
||||
percentLength -= partLength;
|
||||
percent = percentLength / segmentLengths.get(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
// do not cross the line :)
|
||||
if (percent >= 1) {
|
||||
percent = 1;
|
||||
--i;
|
||||
}
|
||||
if (taper.getType() == SplineType.Bezier) {
|
||||
i *= 3;
|
||||
}
|
||||
return taper.interpolate(percent, i, null).y;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies bevel and taper objects to the curve.
|
||||
* @param curve
|
||||
* the curve we apply the objects to
|
||||
* @param bevelObject
|
||||
* the bevel object
|
||||
* @param taperObject
|
||||
* the taper object
|
||||
* @param smooth
|
||||
* the smooth flag
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return a list of geometries representing the beveled and/or tapered curve
|
||||
*/
|
||||
protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Spline taperObject, boolean smooth, BlenderContext blenderContext) {
|
||||
Vector3f[] curvePoints = BufferUtils.getVector3Array(curve.getFloatBuffer(Type.Position));
|
||||
Vector3f subtractResult = new Vector3f();
|
||||
float curveLength = curve.getLength();
|
||||
|
||||
FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()];
|
||||
FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()];
|
||||
IndexBuffer[] indexBuffers = new IndexBuffer[bevelObject.size()];
|
||||
for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) {
|
||||
Mesh mesh = bevelObject.get(geomIndex).getMesh();
|
||||
Vector3f[] positions = BufferUtils.getVector3Array(mesh.getFloatBuffer(Type.Position));
|
||||
Vector3f[] bevelPoints = this.transformToFirstLineOfBevelPoints(positions, curvePoints[0], curvePoints[1]);
|
||||
|
||||
List<Vector3f[]> bevels = new ArrayList<Vector3f[]>(curvePoints.length);
|
||||
bevels.add(bevelPoints);
|
||||
|
||||
vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(bevelPoints.length * 3 * curvePoints.length * (smooth ? 1 : 6));
|
||||
for (int i = 1; i < curvePoints.length - 1; ++i) {
|
||||
bevelPoints = this.transformBevel(bevelPoints, curvePoints[i - 1], curvePoints[i], curvePoints[i + 1]);
|
||||
bevels.add(bevelPoints);
|
||||
}
|
||||
bevelPoints = this.transformBevel(bevelPoints, curvePoints[curvePoints.length - 2], curvePoints[curvePoints.length - 1], null);
|
||||
bevels.add(bevelPoints);
|
||||
|
||||
if (bevels.size() > 2) {
|
||||
// changing the first and last bevel so that they are parallel to their neighbours (blender works this way)
|
||||
// notice this implicates that the distances of every corresponding point in th two bevels must be identical and
|
||||
// equal to the distance between the points on curve that define the bevel position
|
||||
// so instead doing complicated rotations on each point we will simply properly translate each of them
|
||||
|
||||
int[][] pointIndexes = new int[][] { { 0, 1 }, { curvePoints.length - 1, curvePoints.length - 2 } };
|
||||
for (int[] indexes : pointIndexes) {
|
||||
float distance = curvePoints[indexes[1]].subtract(curvePoints[indexes[0]], subtractResult).length();
|
||||
Vector3f[] bevel = bevels.get(indexes[0]);
|
||||
Vector3f[] nextBevel = bevels.get(indexes[1]);
|
||||
for (int i = 0; i < bevel.length; ++i) {
|
||||
float d = bevel[i].subtract(nextBevel[i], subtractResult).length();
|
||||
subtractResult.normalizeLocal().multLocal(distance - d);
|
||||
bevel[i].addLocal(subtractResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// apply scales to the bevels
|
||||
float lengthAlongCurve = 0;
|
||||
for (int i = 0; i < curvePoints.length; ++i) {
|
||||
if (i > 0) {
|
||||
lengthAlongCurve += curvePoints[i].subtract(curvePoints[i - 1], subtractResult).length();
|
||||
}
|
||||
float taperScale = this.getTaperScale(taperObject, i == 0 ? 0 : lengthAlongCurve / curveLength);
|
||||
this.applyScale(bevels.get(i), curvePoints[i], taperScale);
|
||||
}
|
||||
|
||||
if (smooth) {// add everything to the buffer
|
||||
for (Vector3f[] bevel : bevels) {
|
||||
for (Vector3f d : bevel) {
|
||||
vertexBuffers[geomIndex].put(d.x);
|
||||
vertexBuffers[geomIndex].put(d.y);
|
||||
vertexBuffers[geomIndex].put(d.z);
|
||||
}
|
||||
}
|
||||
} else {// add vertices to the buffer duplicating them so that every vertex belongs only to a single triangle
|
||||
for (int i = 0; i < curvePoints.length - 1; ++i) {
|
||||
for (int j = 0; j < bevelPoints.length - 1; ++j) {
|
||||
// first triangle
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j].z);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
|
||||
|
||||
// second triangle
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i)[j + 1].z);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j + 1].z);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].x);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].y);
|
||||
vertexBuffers[geomIndex].put(bevels.get(i + 1)[j].z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
indexBuffers[geomIndex] = this.generateIndexes(bevelPoints.length, curvePoints.length, smooth);
|
||||
normalBuffers[geomIndex] = this.generateNormals(indexBuffers[geomIndex], vertexBuffers[geomIndex], smooth);
|
||||
}
|
||||
|
||||
// creating and returning the result
|
||||
List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length);
|
||||
Float oneReferenceToCurveLength = new Float(curveLength);// its important for array modifier to use one reference here
|
||||
for (int i = 0; i < vertexBuffers.length; ++i) {
|
||||
Mesh mesh = new Mesh();
|
||||
mesh.setBuffer(Type.Position, 3, vertexBuffers[i]);
|
||||
if (indexBuffers[i].getBuffer() instanceof IntBuffer) {
|
||||
mesh.setBuffer(Type.Index, 3, (IntBuffer) indexBuffers[i].getBuffer());
|
||||
} else {
|
||||
mesh.setBuffer(Type.Index, 3, (ShortBuffer) indexBuffers[i].getBuffer());
|
||||
}
|
||||
mesh.setBuffer(Type.Normal, 3, normalBuffers[i]);
|
||||
Geometry g = new Geometry("g" + i, mesh);
|
||||
g.setUserData("curveLength", oneReferenceToCurveLength);
|
||||
g.updateModelBound();
|
||||
result.add(g);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* the method applies scale for the given bevel points. The points table is
|
||||
* being modified so expect ypur result there.
|
||||
*
|
||||
* @param points
|
||||
* the bevel points
|
||||
* @param centerPoint
|
||||
* the center point of the bevel
|
||||
* @param scale
|
||||
* the scale to be applied
|
||||
*/
|
||||
private void applyScale(Vector3f[] points, Vector3f centerPoint, float scale) {
|
||||
Vector3f taperScaleVector = new Vector3f();
|
||||
for (Vector3f p : points) {
|
||||
taperScaleVector.set(centerPoint).subtractLocal(p).multLocal(1 - scale);
|
||||
p.addLocal(taperScaleVector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method generates normal buffer for the created mesh of the curve.
|
||||
*
|
||||
* @param indexes
|
||||
* the indexes of the mesh points
|
||||
* @param points
|
||||
* the mesh's points
|
||||
* @param smooth
|
||||
* the flag indicating if the result is to be smooth or solid
|
||||
* @return normals buffer for the mesh
|
||||
*/
|
||||
private FloatBuffer generateNormals(IndexBuffer indexes, FloatBuffer points, boolean smooth) {
|
||||
Map<Integer, Vector3f> normalMap = new TreeMap<Integer, Vector3f>();
|
||||
Vector3f[] allVerts = BufferUtils.getVector3Array(points);
|
||||
|
||||
for (int i = 0; i < indexes.size(); i += 3) {
|
||||
int index1 = indexes.get(i);
|
||||
int index2 = indexes.get(i + 1);
|
||||
int index3 = indexes.get(i + 2);
|
||||
|
||||
Vector3f n = FastMath.computeNormal(allVerts[index1], allVerts[index2], allVerts[index3]);
|
||||
this.addNormal(n, normalMap, smooth, index1, index2, index3);
|
||||
}
|
||||
|
||||
FloatBuffer normals = BufferUtils.createFloatBuffer(normalMap.size() * 3);
|
||||
for (Entry<Integer, Vector3f> entry : normalMap.entrySet()) {
|
||||
normals.put(entry.getValue().x);
|
||||
normals.put(entry.getValue().y);
|
||||
normals.put(entry.getValue().z);
|
||||
}
|
||||
return normals;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of faces in the final mesh is the amount of edges in the bevel
|
||||
* curve (which is less by 1 than its number of vertices) multiplied by 2
|
||||
* (because each edge has two faces assigned on both sides) and multiplied
|
||||
* by the amount of bevel curve repeats which is equal to the amount of
|
||||
* vertices on the target curve finally we need to subtract the bevel edges
|
||||
* amount 2 times because the border edges have only one face attached and
|
||||
* at last multiply everything by 3 because each face needs 3 indexes to be
|
||||
* described
|
||||
*
|
||||
* @param bevelShapeVertexCount
|
||||
* amount of points in bevel shape
|
||||
* @param bevelRepeats
|
||||
* amount of bevel shapes along the curve
|
||||
* @param smooth
|
||||
* the smooth flag
|
||||
* @return index buffer for the mesh
|
||||
*/
|
||||
private IndexBuffer generateIndexes(int bevelShapeVertexCount, int bevelRepeats, boolean smooth) {
|
||||
int putIndex = 0;
|
||||
if (smooth) {
|
||||
int indexBufferSize = (bevelRepeats - 1) * (bevelShapeVertexCount - 1) * 6;
|
||||
IndexBuffer result = IndexBuffer.createIndexBuffer(indexBufferSize, indexBufferSize);
|
||||
|
||||
for (int i = 0; i < bevelRepeats - 1; ++i) {
|
||||
for (int j = 0; j < bevelShapeVertexCount - 1; ++j) {
|
||||
result.put(putIndex++, i * bevelShapeVertexCount + j);
|
||||
result.put(putIndex++, i * bevelShapeVertexCount + j + 1);
|
||||
result.put(putIndex++, (i + 1) * bevelShapeVertexCount + j);
|
||||
|
||||
result.put(putIndex++, i * bevelShapeVertexCount + j + 1);
|
||||
result.put(putIndex++, (i + 1) * bevelShapeVertexCount + j + 1);
|
||||
result.put(putIndex++, (i + 1) * bevelShapeVertexCount + j);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
// every pair of bevel vertices belongs to two triangles
|
||||
// we have the same amount of pairs as the amount of vertices in bevel
|
||||
// so the amount of triangles is: bevelShapeVertexCount * 2 * (bevelRepeats - 1)
|
||||
// and this gives the amount of vertices in non smooth shape as below ...
|
||||
int indexBufferSize = bevelShapeVertexCount * bevelRepeats * 6;// 6 = 2 * 3 where 2 is stated above and 3 is the count of vertices for each triangle
|
||||
IndexBuffer result = IndexBuffer.createIndexBuffer(indexBufferSize, indexBufferSize);
|
||||
for (int i = 0; i < indexBufferSize; ++i) {
|
||||
result.put(putIndex++, i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method transforms the bevel along the curve.
|
||||
*
|
||||
@ -689,7 +92,7 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
* next curve point
|
||||
* @return points of transformed bevel
|
||||
*/
|
||||
private Vector3f[] transformBevel(Vector3f[] bevel, Vector3f prevPos, Vector3f currPos, Vector3f nextPos) {
|
||||
protected Vector3f[] transformBevel(Vector3f[] bevel, Vector3f prevPos, Vector3f currPos, Vector3f nextPos) {
|
||||
bevel = bevel.clone();
|
||||
|
||||
// currPos and directionVector define the line in 3D space
|
||||
@ -736,103 +139,21 @@ public class CurvesHelper extends AbstractBlenderHelper {
|
||||
* the second curve's point
|
||||
* @return points of transformed bevel
|
||||
*/
|
||||
private Vector3f[] transformToFirstLineOfBevelPoints(Vector3f[] startingLinePoints, Vector3f firstCurvePoint, Vector3f secondCurvePoint) {
|
||||
protected Vector3f[] transformToFirstLineOfBevelPoints(Vector3f[] startingLinePoints, Vector3f firstCurvePoint, Vector3f secondCurvePoint) {
|
||||
Vector3f planeNormal = secondCurvePoint.subtract(firstCurvePoint).normalizeLocal();
|
||||
|
||||
float angle = FastMath.acos(planeNormal.dot(Vector3f.UNIT_Y));
|
||||
planeNormal.crossLocal(Vector3f.UNIT_Y).normalizeLocal();// planeNormal is the rotation axis now
|
||||
Quaternion pointRotation = new Quaternion();
|
||||
pointRotation.fromAngleAxis(angle, planeNormal);
|
||||
|
||||
float angle = FastMath.acos(planeNormal.dot(Vector3f.UNIT_X));
|
||||
Vector3f rotationVector = Vector3f.UNIT_X.cross(planeNormal).normalizeLocal();
|
||||
|
||||
Matrix4f m = new Matrix4f();
|
||||
m.setRotationQuaternion(pointRotation);
|
||||
m.setRotationQuaternion(new Quaternion().fromAngleAxis(angle, rotationVector));
|
||||
m.setTranslation(firstCurvePoint);
|
||||
|
||||
float[] temp = new float[] { 0, 0, 0, 1 };
|
||||
Vector3f temp = new Vector3f();
|
||||
Vector3f[] verts = new Vector3f[startingLinePoints.length];
|
||||
for (int j = 0; j < verts.length; ++j) {
|
||||
temp[0] = startingLinePoints[j].x;
|
||||
temp[1] = startingLinePoints[j].y;
|
||||
temp[2] = startingLinePoints[j].z;
|
||||
temp = m.mult(temp);// the result is stored in the array
|
||||
if (fixUpAxis) {
|
||||
verts[j] = new Vector3f(temp[0], -temp[2], temp[1]);
|
||||
} else {
|
||||
verts[j] = new Vector3f(temp[0], temp[1], temp[2]);
|
||||
}
|
||||
for (int i = 0; i < verts.length; ++i) {
|
||||
verts[i] = m.mult(startingLinePoints[i], temp).clone();
|
||||
}
|
||||
return verts;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method adds a normal to the given map. Depending in the smooth factor
|
||||
* it is either merged with the revious normal or not.
|
||||
*
|
||||
* @param normalToAdd
|
||||
* the normal vector to be added
|
||||
* @param normalMap
|
||||
* the normal map where we add vectors
|
||||
* @param smooth
|
||||
* the smooth flag
|
||||
* @param indexes
|
||||
* the indexes of the normals
|
||||
*/
|
||||
private void addNormal(Vector3f normalToAdd, Map<Integer, Vector3f> normalMap, boolean smooth, int... indexes) {
|
||||
for (int index : indexes) {
|
||||
Vector3f n = normalMap.get(index);
|
||||
if (!smooth || n == null) {
|
||||
normalMap.put(index, normalToAdd.clone());
|
||||
} else {
|
||||
n.addLocal(normalToAdd).normalizeLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the taper object.
|
||||
*
|
||||
* @param taperStructure
|
||||
* the taper structure
|
||||
* @return the taper object
|
||||
* @throws BlenderFileException
|
||||
*/
|
||||
protected Spline loadTaperObject(Structure taperStructure) throws BlenderFileException {
|
||||
// reading nurbs
|
||||
List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase();
|
||||
for (Structure nurb : nurbStructures) {
|
||||
Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt");
|
||||
if (pBezierTriple.isNotNull()) {
|
||||
// creating the curve object
|
||||
BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(), 3);
|
||||
List<Vector3f> controlPoints = bezierCurve.getControlPoints();
|
||||
// removing the first and last handles
|
||||
controlPoints.remove(0);
|
||||
controlPoints.remove(controlPoints.size() - 1);
|
||||
|
||||
// return the first taper curve that has more than 3 control points
|
||||
if (controlPoints.size() > 3) {
|
||||
return new Spline(SplineType.Bezier, controlPoints, 0, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the translation of the curve. The UP axis is taken
|
||||
* into account here.
|
||||
*
|
||||
* @param curveStructure
|
||||
* the curve structure
|
||||
* @return curve translation
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Vector3f getLoc(Structure curveStructure) {
|
||||
DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc");
|
||||
if (fixUpAxis) {
|
||||
return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue());
|
||||
} else {
|
||||
return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue());
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,890 @@
|
||||
package com.jme3.scene.plugins.blender.curves;
|
||||
|
||||
import java.nio.FloatBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Map.Entry;
|
||||
import java.util.TreeMap;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Spline;
|
||||
import com.jme3.math.Spline.SplineType;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.math.Vector4f;
|
||||
import com.jme3.scene.VertexBuffer.Type;
|
||||
import com.jme3.scene.mesh.IndexBuffer;
|
||||
import com.jme3.scene.plugins.blender.BlenderContext;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderFileException;
|
||||
import com.jme3.scene.plugins.blender.file.BlenderInputStream;
|
||||
import com.jme3.scene.plugins.blender.file.DynamicArray;
|
||||
import com.jme3.scene.plugins.blender.file.FileBlockHeader;
|
||||
import com.jme3.scene.plugins.blender.file.Pointer;
|
||||
import com.jme3.scene.plugins.blender.file.Structure;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialContext;
|
||||
import com.jme3.scene.plugins.blender.materials.MaterialHelper;
|
||||
import com.jme3.scene.plugins.blender.meshes.Edge;
|
||||
import com.jme3.scene.plugins.blender.meshes.Face;
|
||||
import com.jme3.scene.plugins.blender.meshes.TemporalMesh;
|
||||
import com.jme3.scene.shape.Curve;
|
||||
import com.jme3.scene.shape.Surface;
|
||||
import com.jme3.util.BufferUtils;
|
||||
|
||||
/**
|
||||
* A temporal mesh for curves and surfaces. It works in similar way as TemporalMesh for meshes.
|
||||
* It prepares all neccessary lines and faces and allows to apply modifiers just like in regular temporal mesh.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public class CurvesTemporalMesh extends TemporalMesh {
|
||||
private static final Logger LOGGER = Logger.getLogger(CurvesTemporalMesh.class.getName());
|
||||
|
||||
private static final int TYPE_BEZIER = 0x0001;
|
||||
private static final int TYPE_NURBS = 0x0004;
|
||||
|
||||
private static final int FLAG_3D = 0x0001;
|
||||
private static final int FLAG_FRONT = 0x0002;
|
||||
private static final int FLAG_BACK = 0x0004;
|
||||
private static final int FLAG_FILL_CAPS = 0x4000;
|
||||
|
||||
private static final int FLAG_SMOOTH = 0x0001;
|
||||
|
||||
protected CurvesHelper curvesHelper;
|
||||
protected boolean is2D;
|
||||
protected boolean isFront;
|
||||
protected boolean isBack;
|
||||
protected boolean fillCaps;
|
||||
protected float bevelStart;
|
||||
protected float bevelEnd;
|
||||
protected List<BezierLine> beziers = new ArrayList<BezierLine>();
|
||||
protected CurvesTemporalMesh bevelObject;
|
||||
protected CurvesTemporalMesh taperObject;
|
||||
/** The scale that is used if the curve is a bevel or taper curve. */
|
||||
protected Vector3f scale = new Vector3f(1, 1, 1);
|
||||
|
||||
/**
|
||||
* The constructor creates an empty temporal mesh.
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @throws BlenderFileException
|
||||
* this will never be thrown here
|
||||
*/
|
||||
protected CurvesTemporalMesh(BlenderContext blenderContext) throws BlenderFileException {
|
||||
super(null, blenderContext, false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the temporal mesh from the given curve structure. The mesh can be either curve or surface.
|
||||
* @param curveStructure
|
||||
* the structure that contains the curve/surface data
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
public CurvesTemporalMesh(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException {
|
||||
this(curveStructure, new Vector3f(1, 1, 1), true, blenderContext);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads the temporal mesh from the given curve structure. The mesh can be either curve or surface.
|
||||
* @param curveStructure
|
||||
* the structure that contains the curve/surface data
|
||||
* @param scale
|
||||
* the scale used if the current curve is used as a bevel curve
|
||||
* @param loadBevelAndTaper indicates if bevel and taper should be loaded (this is not needed for curves that are loaded to be used as bevel and taper)
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private CurvesTemporalMesh(Structure curveStructure, Vector3f scale, boolean loadBevelAndTaper, BlenderContext blenderContext) throws BlenderFileException {
|
||||
super(curveStructure, blenderContext, false);
|
||||
name = curveStructure.getName();
|
||||
curvesHelper = blenderContext.getHelper(CurvesHelper.class);
|
||||
this.scale = scale;
|
||||
|
||||
int flag = ((Number) curveStructure.getFieldValue("flag")).intValue();
|
||||
is2D = (flag & FLAG_3D) == 0;
|
||||
if (is2D) {
|
||||
// TODO: add support for 3D flag
|
||||
LOGGER.warning("2D flag not yet supported for curves!");
|
||||
}
|
||||
isFront = (flag & FLAG_FRONT) != 0;
|
||||
isBack = (flag & FLAG_BACK) != 0;
|
||||
fillCaps = (flag & FLAG_FILL_CAPS) != 0;
|
||||
bevelStart = ((Number) curveStructure.getFieldValue("bevfac1", 0)).floatValue();
|
||||
bevelEnd = ((Number) curveStructure.getFieldValue("bevfac2", 1)).floatValue();
|
||||
if (bevelStart > bevelEnd) {
|
||||
float temp = bevelStart;
|
||||
bevelStart = bevelEnd;
|
||||
bevelEnd = temp;
|
||||
}
|
||||
|
||||
LOGGER.fine("Reading nurbs (and sorting them by material).");
|
||||
Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>();
|
||||
List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase();
|
||||
for (Structure nurb : nurbStructures) {
|
||||
Number matNumber = (Number) nurb.getFieldValue("mat_nr");
|
||||
List<Structure> nurbList = nurbs.get(matNumber);
|
||||
if (nurbList == null) {
|
||||
nurbList = new ArrayList<Structure>();
|
||||
nurbs.put(matNumber, nurbList);
|
||||
}
|
||||
nurbList.add(nurb);
|
||||
}
|
||||
|
||||
LOGGER.fine("Getting materials.");
|
||||
MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class);
|
||||
materials = materialHelper.getMaterials(curveStructure, blenderContext);
|
||||
if (materials != null) {
|
||||
for (MaterialContext materialContext : materials) {
|
||||
materialContext.setFaceCullMode(FaceCullMode.Off);
|
||||
}
|
||||
}
|
||||
|
||||
LOGGER.fine("Getting or creating bevel object.");
|
||||
bevelObject = loadBevelAndTaper ? this.loadBevelObject(curveStructure) : null;
|
||||
|
||||
LOGGER.fine("Getting taper object.");
|
||||
Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj");
|
||||
if (bevelObject != null && pTaperObject.isNotNull()) {
|
||||
Structure taperObjectStructure = pTaperObject.fetchData().get(0);
|
||||
DynamicArray<Number> scaleArray = (DynamicArray<Number>) taperObjectStructure.getFieldValue("size");
|
||||
scale = blenderContext.getBlenderKey().isFixUpAxis() ? new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()) : new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(2).floatValue(), scaleArray.get(1).floatValue());
|
||||
Pointer pTaperStructure = (Pointer) taperObjectStructure.getFieldValue("data");
|
||||
Structure taperStructure = pTaperStructure.fetchData().get(0);
|
||||
taperObject = new CurvesTemporalMesh(taperStructure, blenderContext);
|
||||
}
|
||||
|
||||
LOGGER.fine("Creating the result curves.");
|
||||
for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) {
|
||||
for (Structure nurb : nurbEntry.getValue()) {
|
||||
int type = ((Number) nurb.getFieldValue("type")).intValue();
|
||||
if ((type & TYPE_BEZIER) != 0) {
|
||||
this.loadBezierCurve(nurb, nurbEntry.getKey().intValue());
|
||||
} else if ((type & TYPE_NURBS) != 0) {
|
||||
this.loadNurbSurface(nurb, nurbEntry.getKey().intValue());
|
||||
} else {
|
||||
throw new BlenderFileException("Unknown curve type: " + type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (bevelObject != null && beziers.size() > 0) {
|
||||
this.append(this.applyBevelAndTaper(this, bevelObject, taperObject, blenderContext));
|
||||
} else {
|
||||
int originalVerticesAmount = vertices.size();
|
||||
for (BezierLine bezierLine : beziers) {
|
||||
vertices.add(bezierLine.vertices[0]);
|
||||
Vector3f v = bezierLine.vertices[1].subtract(bezierLine.vertices[0]).normalizeLocal();
|
||||
float temp = v.x;
|
||||
v.x = -v.y;
|
||||
v.y = temp;
|
||||
v.z = 0;
|
||||
normals.add(v);// this will be smoothed in the next iteration
|
||||
|
||||
for (int i = 1; i < bezierLine.vertices.length; ++i) {
|
||||
vertices.add(bezierLine.vertices[i]);
|
||||
edges.add(new Edge(originalVerticesAmount + i - 1, originalVerticesAmount + i, 0, false, this));
|
||||
|
||||
// generating normal for vertex at 'i'
|
||||
v = bezierLine.vertices[i].subtract(bezierLine.vertices[i - 1]).normalizeLocal();
|
||||
temp = v.x;
|
||||
v.x = -v.y;
|
||||
v.y = temp;
|
||||
v.z = 0;
|
||||
|
||||
// make the previous normal smooth
|
||||
normals.get(i - 1).addLocal(v).multLocal(0.5f).normalizeLocal();
|
||||
normals.add(v);// this will be smoothed in the next iteration
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method computes the value of a point at the certain relational distance from its beggining.
|
||||
* @param alongRatio
|
||||
* the relative distance along the curve; should be a value between 0 and 1 inclusive;
|
||||
* if the value exceeds the boundaries it is truncated to them
|
||||
* @return computed value along the curve
|
||||
*/
|
||||
private Vector3f getValueAlongCurve(float alongRatio) {
|
||||
alongRatio = FastMath.clamp(alongRatio, 0, 1);
|
||||
Vector3f result = new Vector3f();
|
||||
float probeLength = this.getLength() * alongRatio, length = 0;
|
||||
for (BezierLine bezier : beziers) {
|
||||
float edgeLength = bezier.getLength();
|
||||
if (length + edgeLength >= probeLength) {
|
||||
float ratioAlongEdge = (probeLength - length) / edgeLength;
|
||||
return bezier.getValueAlongCurve(ratioAlongEdge);
|
||||
}
|
||||
length += edgeLength;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of the curve
|
||||
*/
|
||||
private float getLength() {
|
||||
float result = 0;
|
||||
for (BezierLine bezier : beziers) {
|
||||
result += bezier.getLength();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The methods loads the bezier curve from the given structure.
|
||||
* @param nurbStructure
|
||||
* the structure containing a single curve definition
|
||||
* @param materialIndex
|
||||
* the index of this segment's material
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
private void loadBezierCurve(Structure nurbStructure, int materialIndex) throws BlenderFileException {
|
||||
Pointer pBezierTriple = (Pointer) nurbStructure.getFieldValue("bezt");
|
||||
if (pBezierTriple.isNotNull()) {
|
||||
int resolution = ((Number) nurbStructure.getFieldValue("resolu")).intValue();
|
||||
boolean cyclic = (((Number) nurbStructure.getFieldValue("flagu")).intValue() & 0x01) != 0;
|
||||
boolean smooth = (((Number) nurbStructure.getFieldValue("flag")).intValue() & FLAG_SMOOTH) != 0;
|
||||
|
||||
// creating the curve object
|
||||
BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(), 3, blenderContext.getBlenderKey().isFixUpAxis());
|
||||
List<Vector3f> controlPoints = bezierCurve.getControlPoints();
|
||||
|
||||
if (cyclic) {
|
||||
// copy the first three points at the end
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
controlPoints.add(controlPoints.get(i));
|
||||
}
|
||||
}
|
||||
// removing the first and last handles
|
||||
controlPoints.remove(0);
|
||||
controlPoints.remove(controlPoints.size() - 1);
|
||||
|
||||
// creating curve
|
||||
Curve curve = new Curve(new Spline(SplineType.Bezier, controlPoints, 0, false), resolution);
|
||||
|
||||
FloatBuffer vertsBuffer = (FloatBuffer) curve.getBuffer(Type.Position).getData();
|
||||
beziers.add(new BezierLine(BufferUtils.getVector3Array(vertsBuffer), materialIndex, smooth, cyclic));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This method loads the NURBS curve or surface.
|
||||
* @param nurb
|
||||
* the NURBS data structure
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private void loadNurbSurface(Structure nurb, int materialIndex) throws BlenderFileException {
|
||||
// loading the knots
|
||||
List<Float>[] knots = new List[2];
|
||||
Pointer[] pKnots = new Pointer[] { (Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv") };
|
||||
for (int i = 0; i < knots.length; ++i) {
|
||||
if (pKnots[i].isNotNull()) {
|
||||
FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress());
|
||||
BlenderInputStream blenderInputStream = blenderContext.getInputStream();
|
||||
blenderInputStream.setPosition(fileBlockHeader.getBlockPosition());
|
||||
int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4;
|
||||
knots[i] = new ArrayList<Float>(knotsAmount);
|
||||
for (int j = 0; j < knotsAmount; ++j) {
|
||||
knots[i].add(Float.valueOf(blenderInputStream.readFloat()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// loading the flags and orders (basis functions degrees)
|
||||
int flag = ((Number) nurb.getFieldValue("flag")).intValue();
|
||||
boolean smooth = (flag & FLAG_SMOOTH) != 0;
|
||||
int flagU = ((Number) nurb.getFieldValue("flagu")).intValue();
|
||||
int flagV = ((Number) nurb.getFieldValue("flagv")).intValue();
|
||||
int orderU = ((Number) nurb.getFieldValue("orderu")).intValue();
|
||||
int orderV = ((Number) nurb.getFieldValue("orderv")).intValue();
|
||||
|
||||
// loading control points and their weights
|
||||
int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue();
|
||||
int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue();
|
||||
List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData();
|
||||
List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV);
|
||||
for (int i = 0; i < pntsV; ++i) {
|
||||
List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU);
|
||||
for (int j = 0; j < pntsU; ++j) {
|
||||
DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec");
|
||||
if (blenderContext.getBlenderKey().isFixUpAxis()) {
|
||||
uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue()));
|
||||
} else {
|
||||
uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue()));
|
||||
}
|
||||
}
|
||||
if ((flagU & 0x01) != 0) {
|
||||
for (int k = 0; k < orderU - 1; ++k) {
|
||||
uControlPoints.add(uControlPoints.get(k));
|
||||
}
|
||||
}
|
||||
controlPoints.add(uControlPoints);
|
||||
}
|
||||
if ((flagV & 0x01) != 0) {
|
||||
for (int k = 0; k < orderV - 1; ++k) {
|
||||
controlPoints.add(controlPoints.get(k));
|
||||
}
|
||||
}
|
||||
|
||||
int originalVerticesAmount = vertices.size();
|
||||
int resolu = ((Number) nurb.getFieldValue("resolu")).intValue();
|
||||
if (knots[1] == null) {// creating the NURB curve
|
||||
Curve curve = new Curve(new Spline(controlPoints.get(0), knots[0]), resolu);
|
||||
FloatBuffer vertsBuffer = (FloatBuffer) curve.getBuffer(Type.Position).getData();
|
||||
beziers.add(new BezierLine(BufferUtils.getVector3Array(vertsBuffer), materialIndex, smooth, false));
|
||||
} else {// creating the NURB surface
|
||||
int resolv = ((Number) nurb.getFieldValue("resolv")).intValue();
|
||||
int uSegments = resolu * controlPoints.get(0).size() - 1;
|
||||
int vSegments = resolv * controlPoints.size() - 1;
|
||||
Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, uSegments, vSegments, orderU, orderV, smooth);
|
||||
|
||||
FloatBuffer vertsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Position).getData();
|
||||
vertices.addAll(Arrays.asList(BufferUtils.getVector3Array(vertsBuffer)));
|
||||
FloatBuffer normalsBuffer = (FloatBuffer) nurbSurface.getBuffer(Type.Normal).getData();
|
||||
normals.addAll(Arrays.asList(BufferUtils.getVector3Array(normalsBuffer)));
|
||||
|
||||
IndexBuffer indexBuffer = nurbSurface.getIndexBuffer();
|
||||
for (int i = 0; i < indexBuffer.size(); i += 3) {
|
||||
int index1 = indexBuffer.get(i) + originalVerticesAmount;
|
||||
int index2 = indexBuffer.get(i + 1) + originalVerticesAmount;
|
||||
int index3 = indexBuffer.get(i + 2) + originalVerticesAmount;
|
||||
faces.add(new Face(new Integer[] { index1, index2, index3 }, smooth, materialIndex, null, null, this));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The method loads the bevel object that sould be applied to curve. It can either be another curve or a generated one
|
||||
* based on the bevel generating parameters in blender.
|
||||
* @param curveStructure
|
||||
* the structure with the curve's data (the curve being loaded, NOT the bevel curve)
|
||||
* @return the curve's bevel object
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
private CurvesTemporalMesh loadBevelObject(Structure curveStructure) throws BlenderFileException {
|
||||
CurvesTemporalMesh bevelObject = null;
|
||||
Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj");
|
||||
boolean cyclic = false;
|
||||
if (pBevelObject.isNotNull()) {
|
||||
Structure bevelObjectStructure = pBevelObject.fetchData().get(0);
|
||||
DynamicArray<Number> scaleArray = (DynamicArray<Number>) bevelObjectStructure.getFieldValue("size");
|
||||
Vector3f scale = blenderContext.getBlenderKey().isFixUpAxis() ? new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(1).floatValue(), scaleArray.get(2).floatValue()) : new Vector3f(scaleArray.get(0).floatValue(), scaleArray.get(2).floatValue(), scaleArray.get(1).floatValue());
|
||||
Pointer pBevelStructure = (Pointer) bevelObjectStructure.getFieldValue("data");
|
||||
Structure bevelStructure = pBevelStructure.fetchData().get(0);
|
||||
bevelObject = new CurvesTemporalMesh(bevelStructure, scale, false, blenderContext);
|
||||
|
||||
// transforming the bezier lines from plane XZ to plane YZ
|
||||
for (BezierLine bl : bevelObject.beziers) {
|
||||
for (Vector3f v : bl.vertices) {
|
||||
// casting the bezier curve orthogonally on the plane XZ (making Y = 0) and then moving the plane XZ to ZY in a way that:
|
||||
// -Z => +Y and +X => +Z and +Y => +X (but because casting would make Y = 0, then we simply set X = 0)
|
||||
v.y = -v.z;
|
||||
v.z = v.x;
|
||||
v.x = 0;
|
||||
}
|
||||
|
||||
// bevel curves should not have repeated the first vertex at the end when they are cyclic (this is handled differently)
|
||||
if (bl.isCyclic()) {
|
||||
bl.removeLastVertex();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fillCaps = false;// this option is inactive in blender when there is no bevel object applied
|
||||
int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue();
|
||||
float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue();
|
||||
float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue();
|
||||
float offset = ((Number) curveStructure.getFieldValue("offset", 0)).floatValue();
|
||||
if (offset != 0) {
|
||||
// TODO: add support for offset parameter
|
||||
LOGGER.warning("Offset parameter not yet supported.");
|
||||
}
|
||||
Curve bevelCurve = null;
|
||||
if (bevelDepth > 0.0f) {
|
||||
float handlerLength = bevelDepth / 2.0f;
|
||||
cyclic = !isFront && !isBack;
|
||||
List<Vector3f> conrtolPoints = new ArrayList<Vector3f>();
|
||||
|
||||
// blenders from 2.49 to 2.52 did not pay attention to fron and back faces
|
||||
// so in order to draw the scene exactly as it is in different blender versions the blender version is checked here
|
||||
// when neither fron and back face is selected all version behave the same and draw full bevel around the curve
|
||||
if (cyclic || blenderContext.getBlenderVersion() < 253) {
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, -handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, -bevelDepth));
|
||||
|
||||
if (extrude > 0) {
|
||||
conrtolPoints.add(new Vector3f(0, extrude - handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude + handlerLength, -bevelDepth));
|
||||
}
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, -handlerLength));
|
||||
conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, 0));
|
||||
|
||||
if (cyclic) {
|
||||
conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, extrude + handlerLength, bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude, bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude - handlerLength, bevelDepth));
|
||||
|
||||
if (extrude > 0) {
|
||||
conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude, bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, bevelDepth));
|
||||
}
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, handlerLength));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0));
|
||||
}
|
||||
} else {
|
||||
if (extrude > 0) {
|
||||
if (isBack) {
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, 0));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - bevelDepth, -handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -extrude - handlerLength, -bevelDepth));
|
||||
}
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -extrude, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, -extrude + handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude - handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, extrude, -bevelDepth));
|
||||
|
||||
if (isFront) {
|
||||
conrtolPoints.add(new Vector3f(0, extrude + handlerLength, -bevelDepth));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, -handlerLength));
|
||||
conrtolPoints.add(new Vector3f(0, extrude + bevelDepth, 0));
|
||||
}
|
||||
} else {
|
||||
if (isFront && isBack) {
|
||||
conrtolPoints.add(new Vector3f(0, -bevelDepth, 0));
|
||||
conrtolPoints.add(new Vector3f(0, -bevelDepth, -handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, 0, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, handlerLength, -bevelDepth));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, bevelDepth, -handlerLength));
|
||||
conrtolPoints.add(new Vector3f(0, bevelDepth, 0));
|
||||
} else {
|
||||
if (isBack) {
|
||||
conrtolPoints.add(new Vector3f(0, -bevelDepth, 0));
|
||||
conrtolPoints.add(new Vector3f(0, -bevelDepth, -handlerLength));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, -handlerLength, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, 0, -bevelDepth));
|
||||
} else {
|
||||
conrtolPoints.add(new Vector3f(0, 0, -bevelDepth));
|
||||
conrtolPoints.add(new Vector3f(0, handlerLength, -bevelDepth));
|
||||
|
||||
conrtolPoints.add(new Vector3f(0, bevelDepth, -handlerLength));
|
||||
conrtolPoints.add(new Vector3f(0, bevelDepth, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bevelCurve = new Curve(new Spline(SplineType.Bezier, conrtolPoints, 0, false), bevResol);
|
||||
} else if (extrude > 0.0f) {
|
||||
Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[] { new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0) }, 1, false);
|
||||
bevelCurve = new Curve(bevelSpline, bevResol);
|
||||
}
|
||||
if (bevelCurve != null) {
|
||||
bevelObject = new CurvesTemporalMesh(blenderContext);
|
||||
FloatBuffer vertsBuffer = (FloatBuffer) bevelCurve.getBuffer(Type.Position).getData();
|
||||
Vector3f[] verts = BufferUtils.getVector3Array(vertsBuffer);
|
||||
if (cyclic) {// get rid of the last vertex which is identical to the first one
|
||||
verts = Arrays.copyOf(verts, verts.length - 1);
|
||||
}
|
||||
bevelObject.beziers.add(new BezierLine(verts, 0, false, cyclic));
|
||||
}
|
||||
}
|
||||
return bevelObject;
|
||||
}
|
||||
|
||||
private List<BezierLine> getScaledBeziers() {
|
||||
if (scale.equals(Vector3f.UNIT_XYZ)) {
|
||||
return beziers;
|
||||
}
|
||||
List<BezierLine> result = new ArrayList<BezierLine>();
|
||||
for (BezierLine bezierLine : beziers) {
|
||||
result.add(bezierLine.scale(scale));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method applies bevel and taper objects to the curve.
|
||||
* @param curve
|
||||
* the curve we apply the objects to
|
||||
* @param bevelObject
|
||||
* the bevel object
|
||||
* @param taperObject
|
||||
* the taper object
|
||||
* @param blenderContext
|
||||
* the blender context
|
||||
* @return a list of geometries representing the beveled and/or tapered curve
|
||||
* @throws BlenderFileException
|
||||
* an exception is thrown when problems with reading occur
|
||||
*/
|
||||
private CurvesTemporalMesh applyBevelAndTaper(CurvesTemporalMesh curve, CurvesTemporalMesh bevelObject, CurvesTemporalMesh taperObject, BlenderContext blenderContext) throws BlenderFileException {
|
||||
List<BezierLine> bevelBezierLines = bevelObject.getScaledBeziers();
|
||||
List<BezierLine> curveLines = curve.beziers;
|
||||
if (bevelBezierLines.size() == 0 || curveLines.size() == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
CurvesTemporalMesh result = new CurvesTemporalMesh(blenderContext);
|
||||
for (BezierLine curveLine : curveLines) {
|
||||
Vector3f[] curveLineVertices = curveLine.getVertices(bevelStart, bevelEnd);
|
||||
|
||||
for (BezierLine bevelBezierLine : bevelBezierLines) {
|
||||
CurvesTemporalMesh partResult = new CurvesTemporalMesh(blenderContext);
|
||||
|
||||
Vector3f[] bevelLineVertices = bevelBezierLine.getVertices();
|
||||
List<Vector3f[]> bevels = new ArrayList<Vector3f[]>();
|
||||
|
||||
Vector3f[] bevelPoints = curvesHelper.transformToFirstLineOfBevelPoints(bevelLineVertices, curveLineVertices[0], curveLineVertices[1]);
|
||||
bevels.add(bevelPoints);
|
||||
for (int i = 1; i < curveLineVertices.length - 1; ++i) {
|
||||
bevelPoints = curvesHelper.transformBevel(bevelPoints, curveLineVertices[i - 1], curveLineVertices[i], curveLineVertices[i + 1]);
|
||||
bevels.add(bevelPoints);
|
||||
}
|
||||
bevelPoints = curvesHelper.transformBevel(bevelPoints, curveLineVertices[curveLineVertices.length - 2], curveLineVertices[curveLineVertices.length - 1], null);
|
||||
bevels.add(bevelPoints);
|
||||
|
||||
Vector3f subtractResult = new Vector3f();
|
||||
if (bevels.size() > 2) {
|
||||
// changing the first and last bevel so that they are parallel to their neighbours (blender works this way)
|
||||
// notice this implicates that the distances of every corresponding point in the two bevels must be identical and
|
||||
// equal to the distance between the points on curve that define the bevel position
|
||||
// so instead doing complicated rotations on each point we will simply properly translate each of them
|
||||
int[][] pointIndexes = new int[][] { { 0, 1 }, { curveLineVertices.length - 1, curveLineVertices.length - 2 } };
|
||||
for (int[] indexes : pointIndexes) {
|
||||
float distance = curveLineVertices[indexes[1]].subtract(curveLineVertices[indexes[0]], subtractResult).length();
|
||||
Vector3f[] bevel = bevels.get(indexes[0]);
|
||||
Vector3f[] nextBevel = bevels.get(indexes[1]);
|
||||
for (int i = 0; i < bevel.length; ++i) {
|
||||
float d = bevel[i].subtract(nextBevel[i], subtractResult).length();
|
||||
subtractResult.normalizeLocal().multLocal(distance - d);
|
||||
bevel[i].addLocal(subtractResult);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (taperObject != null) {
|
||||
float curveLength = curveLine.getLength(), lengthAlongCurve = bevelStart;
|
||||
for (int i = 0; i < curveLineVertices.length; ++i) {
|
||||
if (i > 0) {
|
||||
lengthAlongCurve += curveLineVertices[i].subtract(curveLineVertices[i - 1], subtractResult).length();
|
||||
}
|
||||
float taperScale = -taperObject.getValueAlongCurve(lengthAlongCurve / curveLength).z * taperObject.scale.z;
|
||||
if (taperScale != 1) {
|
||||
this.applyScale(bevels.get(i), curveLineVertices[i], taperScale);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// adding vertices to the part result
|
||||
for (Vector3f[] bevel : bevels) {
|
||||
for (Vector3f d : bevel) {
|
||||
partResult.getVertices().add(d);
|
||||
}
|
||||
}
|
||||
|
||||
// preparing faces for the part result (each face is a quad)
|
||||
int bevelVertCount = bevelPoints.length;
|
||||
for (int i = 0; i < bevels.size() - 1; ++i) {
|
||||
for (int j = 0; j < bevelVertCount - 1; ++j) {
|
||||
Integer[] indexes = new Integer[] { i * bevelVertCount + j + 1, (i + 1) * bevelVertCount + j + 1, (i + 1) * bevelVertCount + j, i * bevelVertCount + j };
|
||||
partResult.getFaces().add(new Face(indexes, curveLine.isSmooth(), curveLine.getMaterialNumber(), null, null, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[0], indexes[1], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[1], indexes[2], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[2], indexes[3], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[3], indexes[0], 0, true, partResult));
|
||||
}
|
||||
if (bevelBezierLine.isCyclic()) {
|
||||
int j = bevelVertCount - 1;
|
||||
Integer[] indexes = new Integer[] { i * bevelVertCount, (i + 1) * bevelVertCount, (i + 1) * bevelVertCount + j, i * bevelVertCount + j };
|
||||
partResult.getFaces().add(new Face(indexes, curveLine.isSmooth(), curveLine.getMaterialNumber(), null, null, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[0], indexes[1], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[1], indexes[2], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[2], indexes[3], 0, true, partResult));
|
||||
partResult.getEdges().add(new Edge(indexes[3], indexes[0], 0, true, partResult));
|
||||
}
|
||||
}
|
||||
|
||||
partResult.generateNormals();
|
||||
|
||||
if (fillCaps) {// caps in blender behave as if they weren't affected by the smooth factor
|
||||
// START CAP
|
||||
Vector3f[] cap = bevels.get(0);
|
||||
List<Integer> capIndexes = new ArrayList<Integer>(cap.length);
|
||||
Vector3f capNormal = curveLineVertices[0].subtract(curveLineVertices[1]).normalizeLocal();
|
||||
for (int i = 0; i < cap.length; ++i) {
|
||||
capIndexes.add(partResult.getVertices().size());
|
||||
partResult.getVertices().add(cap[i]);
|
||||
partResult.getNormals().add(capNormal);
|
||||
}
|
||||
Collections.reverse(capIndexes);// the indexes ned to be reversed for the face to have fron face outside the beveled line
|
||||
partResult.getFaces().add(new Face(capIndexes.toArray(new Integer[capIndexes.size()]), false, curveLine.getMaterialNumber(), null, null, partResult));
|
||||
for (int i = 1; i < capIndexes.size(); ++i) {
|
||||
partResult.getEdges().add(new Edge(capIndexes.get(i - 1), capIndexes.get(i), 0, true, partResult));
|
||||
}
|
||||
|
||||
// END CAP
|
||||
cap = bevels.get(bevels.size() - 1);
|
||||
capIndexes.clear();
|
||||
capNormal = curveLineVertices[curveLineVertices.length - 1].subtract(curveLineVertices[curveLineVertices.length - 2]).normalizeLocal();
|
||||
for (int i = 0; i < cap.length; ++i) {
|
||||
capIndexes.add(partResult.getVertices().size());
|
||||
partResult.getVertices().add(cap[i]);
|
||||
partResult.getNormals().add(capNormal);
|
||||
}
|
||||
partResult.getFaces().add(new Face(capIndexes.toArray(new Integer[capIndexes.size()]), false, curveLine.getMaterialNumber(), null, null, partResult));
|
||||
for (int i = 1; i < capIndexes.size(); ++i) {
|
||||
partResult.getEdges().add(new Edge(capIndexes.get(i - 1), capIndexes.get(i), 0, true, partResult));
|
||||
}
|
||||
}
|
||||
|
||||
result.append(partResult);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* The method generates normals for the curve. If any normals were already stored they are discarded.
|
||||
*/
|
||||
private void generateNormals() {
|
||||
Map<Integer, Vector3f> normalMap = new TreeMap<Integer, Vector3f>();
|
||||
for (Face face : faces) {
|
||||
// the first 3 verts are enough here (all faces are triangles except for the caps, but those are fully flat anyway)
|
||||
int index1 = face.getIndexes().get(0);
|
||||
int index2 = face.getIndexes().get(1);
|
||||
int index3 = face.getIndexes().get(2);
|
||||
|
||||
Vector3f n = FastMath.computeNormal(vertices.get(index1), vertices.get(index2), vertices.get(index3));
|
||||
for (int index : face.getIndexes()) {
|
||||
Vector3f normal = normalMap.get(index);
|
||||
if (normal == null) {
|
||||
normalMap.put(index, n.clone());
|
||||
} else {
|
||||
normal.addLocal(n).normalizeLocal();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
normals.clear();
|
||||
Collections.addAll(normals, new Vector3f[normalMap.size()]);
|
||||
for (Entry<Integer, Vector3f> entry : normalMap.entrySet()) {
|
||||
normals.set(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* the method applies scale for the given bevel points. The points table is
|
||||
* being modified so expect ypur result there.
|
||||
*
|
||||
* @param points
|
||||
* the bevel points
|
||||
* @param centerPoint
|
||||
* the center point of the bevel
|
||||
* @param scale
|
||||
* the scale to be applied
|
||||
*/
|
||||
private void applyScale(Vector3f[] points, Vector3f centerPoint, float scale) {
|
||||
Vector3f taperScaleVector = new Vector3f();
|
||||
for (Vector3f p : points) {
|
||||
taperScaleVector.set(centerPoint).subtractLocal(p).multLocal(1 - scale);
|
||||
p.addLocal(taperScaleVector);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A helper class that represents a single bezier line. It consists of Edge's and allows to
|
||||
* get a subline of a lentgh of the line.
|
||||
*
|
||||
* @author Marcin Roguski (Kaelthas)
|
||||
*/
|
||||
public static class BezierLine {
|
||||
/** The edges of the bezier line. */
|
||||
private Vector3f[] vertices;
|
||||
/** The material number of the line. */
|
||||
private int materialNumber;
|
||||
/** Indicates if the line is smooth of flat. */
|
||||
private boolean smooth;
|
||||
/** The length of the line. */
|
||||
private float length;
|
||||
/** Indicates if the current line is cyclic or not. */
|
||||
private boolean cyclic;
|
||||
|
||||
public BezierLine(Vector3f[] vertices, int materialNumber, boolean smooth, boolean cyclik) {
|
||||
this.vertices = vertices;
|
||||
this.materialNumber = materialNumber;
|
||||
this.smooth = smooth;
|
||||
cyclic = cyclik;
|
||||
this.recomputeLength();
|
||||
}
|
||||
|
||||
public BezierLine scale(Vector3f scale) {
|
||||
BezierLine result = new BezierLine(vertices, materialNumber, smooth, cyclic);
|
||||
result.vertices = new Vector3f[vertices.length];
|
||||
for (int i = 0; i < vertices.length; ++i) {
|
||||
result.vertices[i] = vertices[i].mult(scale);
|
||||
}
|
||||
result.recomputeLength();
|
||||
return result;
|
||||
}
|
||||
|
||||
public void removeLastVertex() {
|
||||
Vector3f[] newVertices = new Vector3f[vertices.length - 1];
|
||||
for (int i = 0; i < vertices.length - 1; ++i) {
|
||||
newVertices[i] = vertices[i];
|
||||
}
|
||||
vertices = newVertices;
|
||||
this.recomputeLength();
|
||||
}
|
||||
|
||||
private void recomputeLength() {
|
||||
length = 0;
|
||||
for (int i = 1; i < vertices.length; ++i) {
|
||||
length += vertices[i - 1].distance(vertices[i]);
|
||||
}
|
||||
if (cyclic) {
|
||||
// if the first vertex is repeated at the end the distance will be = 0 so it won't affect the result, and if it is not repeated
|
||||
// then it is neccessary to add the length between the last and the first vertex
|
||||
length += vertices[vertices.length - 1].distance(vertices[0]);
|
||||
}
|
||||
}
|
||||
|
||||
public Vector3f[] getVertices() {
|
||||
return this.getVertices(0, 1);
|
||||
}
|
||||
|
||||
public Vector3f[] getVertices(float startSlice, float endSlice) {
|
||||
if (startSlice == 0 && endSlice == 1) {
|
||||
return vertices;
|
||||
}
|
||||
List<Vector3f> result = new ArrayList<Vector3f>();
|
||||
float length = this.getLength(), temp = 0;
|
||||
float startSliceLength = length * startSlice;
|
||||
float endSliceLength = length * endSlice;
|
||||
int index = 1;
|
||||
|
||||
if (startSlice > 0) {
|
||||
while (temp < startSliceLength) {
|
||||
Vector3f v1 = vertices[index - 1];
|
||||
Vector3f v2 = vertices[index++];
|
||||
float edgeLength = v1.distance(v2);
|
||||
temp += edgeLength;
|
||||
if (temp == startSliceLength) {
|
||||
result.add(v2);
|
||||
} else if (temp > startSliceLength) {
|
||||
result.add(v1.subtract(v2).normalizeLocal().multLocal(temp - startSliceLength).addLocal(v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (endSlice < 1) {
|
||||
if (index == vertices.length) {
|
||||
Vector3f v1 = vertices[vertices.length - 2];
|
||||
Vector3f v2 = vertices[vertices.length - 1];
|
||||
result.add(v1.subtract(v2).normalizeLocal().multLocal(length - endSliceLength).addLocal(v2));
|
||||
} else {
|
||||
for (int i = index; i < vertices.length && temp < endSliceLength; ++i) {
|
||||
Vector3f v1 = vertices[index - 1];
|
||||
Vector3f v2 = vertices[index++];
|
||||
temp += v1.distance(v2);
|
||||
if (temp == endSliceLength) {
|
||||
result.add(v2);
|
||||
} else if (temp > endSliceLength) {
|
||||
result.add(v1.subtract(v2).normalizeLocal().multLocal(temp - startSliceLength).addLocal(v2));
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result.addAll(Arrays.asList(Arrays.copyOfRange(vertices, index, vertices.length)));
|
||||
}
|
||||
|
||||
return result.toArray(new Vector3f[result.size()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* The method computes the value of a point at the certain relational distance from its beggining.
|
||||
* @param alongRatio
|
||||
* the relative distance along the curve; should be a value between 0 and 1 inclusive;
|
||||
* if the value exceeds the boundaries it is truncated to them
|
||||
* @return computed value along the curve
|
||||
*/
|
||||
public Vector3f getValueAlongCurve(float alongRatio) {
|
||||
alongRatio = FastMath.clamp(alongRatio, 0, 1);
|
||||
Vector3f result = new Vector3f();
|
||||
float probeLength = this.getLength() * alongRatio;
|
||||
float length = 0;
|
||||
for (int i = 1; i < vertices.length; ++i) {
|
||||
float edgeLength = vertices[i].distance(vertices[i - 1]);
|
||||
if (length + edgeLength > probeLength) {
|
||||
float ratioAlongEdge = (probeLength - length) / edgeLength;
|
||||
return FastMath.interpolateLinear(ratioAlongEdge, vertices[i - 1], vertices[i]);
|
||||
} else if (length + edgeLength == probeLength) {
|
||||
return vertices[i];
|
||||
}
|
||||
length += edgeLength;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the material number of this bezier line
|
||||
*/
|
||||
public int getMaterialNumber() {
|
||||
return materialNumber;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return indicates if the line is smooth of flat
|
||||
*/
|
||||
public boolean isSmooth() {
|
||||
return smooth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of this bezier line
|
||||
*/
|
||||
public float getLength() {
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return indicates if the current line is cyclic or not
|
||||
*/
|
||||
public boolean isCyclic() {
|
||||
return cyclic;
|
||||
}
|
||||
}
|
||||
}
|
@ -126,12 +126,24 @@ public class Structure implements Cloneable {
|
||||
* @return the value of the field or null if no field with a given name is found
|
||||
*/
|
||||
public Object getFieldValue(String fieldName) {
|
||||
return this.getFieldValue(fieldName, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* This method returns the value of the filed with a given name.
|
||||
* @param fieldName
|
||||
* the name of the field
|
||||
* @param defaultValue
|
||||
* the value that is being returned when no field of a given name is found
|
||||
* @return the value of the field or the given default value if no field with a given name is found
|
||||
*/
|
||||
public Object getFieldValue(String fieldName, Object defaultValue) {
|
||||
for (Field field : fields) {
|
||||
if (field.name.equalsIgnoreCase(fieldName)) {
|
||||
return field.value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -70,6 +70,20 @@ public class Edge {
|
||||
return index2;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the first vertex of the edge
|
||||
*/
|
||||
public Vector3f getFirstVertex() {
|
||||
return temporalMesh.getVertices().get(index1);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the second vertex of the edge
|
||||
*/
|
||||
public Vector3f getSecondVertex() {
|
||||
return temporalMesh.getVertices().get(index2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the index other than the given.
|
||||
* @param index
|
||||
@ -100,12 +114,25 @@ public class Edge {
|
||||
return inFace;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the length of the edge
|
||||
*/
|
||||
public float getLength() {
|
||||
return this.getFirstVertex().distance(this.getSecondVertex());
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the mesh this edge belongs to
|
||||
*/
|
||||
public TemporalMesh getTemporalMesh() {
|
||||
return temporalMesh;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the centroid of the edge
|
||||
*/
|
||||
public Vector3f computeCentroid() {
|
||||
List<Vector3f> vertices = temporalMesh.getVertices();
|
||||
return vertices.get(index1).add(vertices.get(index2)).divideLocal(2);
|
||||
return this.getFirstVertex().add(this.getSecondVertex()).divideLocal(2);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -161,10 +188,10 @@ public class Edge {
|
||||
* @return <b>true</b> if the edges cross and false otherwise
|
||||
*/
|
||||
public boolean cross(Edge edge) {
|
||||
Vector3f P1 = temporalMesh.getVertices().get(index1);
|
||||
Vector3f P2 = edge.temporalMesh.getVertices().get(edge.index1);
|
||||
Vector3f u = temporalMesh.getVertices().get(index2).subtract(P1);
|
||||
Vector3f v = edge.temporalMesh.getVertices().get(edge.index2).subtract(P2);
|
||||
Vector3f P1 = this.getFirstVertex();
|
||||
Vector3f P2 = edge.getFirstVertex();
|
||||
Vector3f u = this.getSecondVertex().subtract(P1);
|
||||
Vector3f v = edge.getSecondVertex().subtract(P2);
|
||||
float t2 = (u.x * (P2.y - P1.y) - u.y * (P2.x - P1.x)) / (u.y * v.x - u.x * v.y);
|
||||
float t1 = (P2.x - P1.x + v.x * t2) / u.x;
|
||||
Vector3f p1 = P1.add(u.mult(t1));
|
||||
@ -187,7 +214,7 @@ public class Edge {
|
||||
@Override
|
||||
public String toString() {
|
||||
String result = "Edge [" + index1 + ", " + index2 + "] {" + crease + "}";
|
||||
result += " (" + temporalMesh.getVertices().get(index1) + " -> " + temporalMesh.getVertices().get(index2) + ")";
|
||||
result += " (" + this.getFirstVertex() + " -> " + this.getSecondVertex() + ")";
|
||||
if (inFace) {
|
||||
result += "[F]";
|
||||
}
|
||||
|
@ -17,6 +17,9 @@ import java.util.logging.Logger;
|
||||
|
||||
import com.jme3.bounding.BoundingBox;
|
||||
import com.jme3.bounding.BoundingVolume;
|
||||
import com.jme3.material.Material;
|
||||
import com.jme3.material.RenderState.FaceCullMode;
|
||||
import com.jme3.math.FastMath;
|
||||
import com.jme3.math.Vector2f;
|
||||
import com.jme3.math.Vector3f;
|
||||
import com.jme3.scene.Geometry;
|
||||
@ -364,32 +367,34 @@ public class TemporalMesh extends Geometry {
|
||||
* the mesh to be appended
|
||||
*/
|
||||
public void append(TemporalMesh mesh) {
|
||||
// we need to shift the indexes in faces, lines and points
|
||||
int shift = vertices.size();
|
||||
if (shift > 0) {
|
||||
for (Face face : mesh.faces) {
|
||||
face.getIndexes().shiftIndexes(shift, null);
|
||||
face.setTemporalMesh(this);
|
||||
}
|
||||
for (Edge edge : mesh.edges) {
|
||||
edge.shiftIndexes(shift, null);
|
||||
}
|
||||
for (Point point : mesh.points) {
|
||||
point.shiftIndexes(shift, null);
|
||||
if (mesh != null) {
|
||||
// we need to shift the indexes in faces, lines and points
|
||||
int shift = vertices.size();
|
||||
if (shift > 0) {
|
||||
for (Face face : mesh.faces) {
|
||||
face.getIndexes().shiftIndexes(shift, null);
|
||||
face.setTemporalMesh(this);
|
||||
}
|
||||
for (Edge edge : mesh.edges) {
|
||||
edge.shiftIndexes(shift, null);
|
||||
}
|
||||
for (Point point : mesh.points) {
|
||||
point.shiftIndexes(shift, null);
|
||||
}
|
||||
}
|
||||
|
||||
faces.addAll(mesh.faces);
|
||||
edges.addAll(mesh.edges);
|
||||
points.addAll(mesh.points);
|
||||
|
||||
vertices.addAll(mesh.vertices);
|
||||
normals.addAll(mesh.normals);
|
||||
vertexGroups.addAll(mesh.vertexGroups);
|
||||
verticesColors.addAll(mesh.verticesColors);
|
||||
boneIndexes.putAll(mesh.boneIndexes);
|
||||
|
||||
this.rebuildIndexesMappings();
|
||||
}
|
||||
|
||||
faces.addAll(mesh.faces);
|
||||
edges.addAll(mesh.edges);
|
||||
points.addAll(mesh.points);
|
||||
|
||||
vertices.addAll(mesh.vertices);
|
||||
normals.addAll(mesh.normals);
|
||||
vertexGroups.addAll(mesh.vertexGroups);
|
||||
verticesColors.addAll(mesh.verticesColors);
|
||||
boneIndexes.putAll(mesh.boneIndexes);
|
||||
|
||||
this.rebuildIndexesMappings();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -506,11 +511,17 @@ public class TemporalMesh extends Geometry {
|
||||
|
||||
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] = normals.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) {
|
||||
@ -519,12 +530,12 @@ public class TemporalMesh extends Geometry {
|
||||
for (Entry<String, Integer> entry : boneIndexes.entrySet()) {
|
||||
if (vertexGroupsForVertex.containsKey(entry.getKey())) {
|
||||
float weight = vertexGroupsForVertex.get(entry.getKey());
|
||||
if(weight > 0) {// no need to use such weights
|
||||
if (weight > 0) {// no need to use such weights
|
||||
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)
|
||||
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);
|
||||
@ -591,7 +602,9 @@ public class TemporalMesh extends Geometry {
|
||||
if (materialIndex >= 0 && materials != null && materials.length > materialIndex && materials[materialIndex] != null) {
|
||||
materials[materialIndex].applyMaterial(geometry, meshStructure.getOldMemoryAddress(), entry.getValue().getUvCoords(), blenderContext);
|
||||
} else {
|
||||
geometry.setMaterial(blenderContext.getDefaultMaterial());
|
||||
Material defaultMaterial = blenderContext.getDefaultMaterial().clone();
|
||||
defaultMaterial.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off);
|
||||
geometry.setMaterial(defaultMaterial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -182,9 +182,9 @@ public class ObjectHelper extends AbstractBlenderHelper {
|
||||
if (pCurve.isNotNull()) {
|
||||
CurvesHelper curvesHelper = blenderContext.getHelper(CurvesHelper.class);
|
||||
Structure curveData = pCurve.fetchData().get(0);
|
||||
List<Geometry> curves = curvesHelper.toCurve(curveData, blenderContext);
|
||||
for (Geometry curve : curves) {
|
||||
result.attachChild(curve);
|
||||
TemporalMesh curvesTemporalMesh = curvesHelper.toCurve(curveData, blenderContext);
|
||||
if(curvesTemporalMesh != null) {
|
||||
result.attachChild(curvesTemporalMesh);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
@ -0,0 +1,91 @@
|
||||
/*
|
||||
* Copyright (c) 2009-2014 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.system;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* Allows extraction of platform specific binaries from classpath via build
|
||||
* systems.
|
||||
*
|
||||
* @author normenhansen
|
||||
*/
|
||||
public class ExtractNativeLibraries {
|
||||
|
||||
public static void main(String[] args) {
|
||||
if (args.length == 1) {
|
||||
if ("getjarexcludes".equals(args[0])) {
|
||||
File[] jarFiles = NativeLibraryLoader.getJarsWithNatives();
|
||||
for (int i = 0; i < jarFiles.length; i++) {
|
||||
File jarFile = jarFiles[i];
|
||||
System.out.print("**/*" + jarFile.getName());
|
||||
if (i != jarFiles.length - 1) {
|
||||
System.out.print(",");
|
||||
}
|
||||
}
|
||||
System.exit(0);
|
||||
}
|
||||
}
|
||||
if (args.length < 2) {
|
||||
System.err.println("Usage: ExtractNativeLibraries Platform ExtractionPath");
|
||||
System.err.println("Where 'Platform' is either Windows32, Windows64, Linux32, Linux64, MacOSX32 or MacOSX64");
|
||||
System.err.println("'ExtractionPath' is a folder to extract the binaries to.");
|
||||
System.err.println("You can also use ExtractNativeLibraries getjarexcludes to get a list of excludes for the jar files that contain binaries.");
|
||||
System.exit(1);
|
||||
}
|
||||
String path = args[1].replace('/', File.separatorChar);
|
||||
File folder = new File(path);
|
||||
try {
|
||||
if ("Windows32".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.Windows32, folder);
|
||||
} else if ("Windows64".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.Windows64, folder);
|
||||
} else if ("Linux32".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.Linux32, folder);
|
||||
} else if ("Linux64".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.Linux64, folder);
|
||||
} else if ("MacOSX32".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.MacOSX32, folder);
|
||||
} else if ("MacOSX64".equals(args[0])) {
|
||||
NativeLibraryLoader.extractNativeLibraries(Platform.MacOSX64, folder);
|
||||
} else {
|
||||
System.err.println("Please specify a platform, Windows32, Windows64, Linux32, Linux64, MacOSX32 or MacOSX64");
|
||||
System.exit(3);
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
Logger.getLogger(ExtractNativeLibraries.class.getName()).log(Level.SEVERE, null, ex);
|
||||
}
|
||||
}
|
||||
}
|
@ -385,9 +385,9 @@ public final class NativeLibraryLoader {
|
||||
String fileNameInJarWithoutExtension
|
||||
= fileNameInJar.substring(0, fileNameInJar.lastIndexOf("."));
|
||||
|
||||
if (platform.is64Bit() && !fileNameInJarWithoutExtension.endsWith("64")) {
|
||||
fileNameInJarWithoutExtension += "64";
|
||||
}
|
||||
// if (platform.is64Bit() && !fileNameInJarWithoutExtension.endsWith("64")) {
|
||||
// fileNameInJarWithoutExtension += "64";
|
||||
// }
|
||||
|
||||
String systemJniExtension;
|
||||
String dummyLib = mapLibraryName_emulated("", platform);
|
||||
@ -510,11 +510,11 @@ public final class NativeLibraryLoader {
|
||||
String fileNameInJarWithoutExtension
|
||||
= fileNameInJar.substring(0, fileNameInJar.lastIndexOf("."));
|
||||
|
||||
if (platform.is64Bit() && !fileNameInJarWithoutExtension.endsWith("64")) {
|
||||
// This is to avoid conflicts with 32-bit versions of the
|
||||
// same library when extracting.
|
||||
fileNameInJarWithoutExtension += "64";
|
||||
}
|
||||
// if (platform.is64Bit() && !fileNameInJarWithoutExtension.endsWith("64")) {
|
||||
// // This is to avoid conflicts with 32-bit versions of the
|
||||
// // same library when extracting.
|
||||
// fileNameInJarWithoutExtension += "64";
|
||||
// }
|
||||
|
||||
String systemJniExtension;
|
||||
String dummyLib = System.mapLibraryName("");
|
||||
|
@ -42,12 +42,9 @@ javac.classpath=\
|
||||
${libs.jme3-desktop.classpath}:\
|
||||
${libs.jme3-lwjgl.classpath}:\
|
||||
${libs.jme3-niftygui.classpath}:\
|
||||
${libs.jme3-jogl.classpath}:\
|
||||
${libs.jme3-effects.classpath}:\
|
||||
${libs.jme3-terrain.classpath}:\
|
||||
${libs.jme3-android.classpath}:\
|
||||
${libs.jme3-jbullet.classpath}:\
|
||||
${libs.jme3-ios.classpath}
|
||||
${libs.jme3-jbullet.classpath}
|
||||
# Space-separated list of extra javac options
|
||||
javac.compilerargs=
|
||||
javac.deprecation=false
|
||||
|
Binary file not shown.
@ -1,4 +1,4 @@
|
||||
<target name="-desktop-deployment" depends="-create-launcher-jvmargs, -test-platforms-enabled, -windows-x86-app, -windows-x64-app, -linux-x86-app, -linux-x64-app, -macosx-x64-app"/>
|
||||
<target name="-desktop-deployment" depends="-extract-native-binaries, -create-launcher-jvmargs, -test-platforms-enabled, -windows-x86-app, -windows-x64-app, -linux-x86-app, -linux-x64-app, -macosx-x64-app"/>
|
||||
|
||||
<target name="-windows-x86-app" if="is.windows-x86.app.enabled">
|
||||
<echo>Windows 32bit Application Creation</echo>
|
||||
@ -16,7 +16,8 @@
|
||||
<zipfileset file="resources/desktop-deployment/windows-x86/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
|
||||
<zipfileset file="resources/desktop-deployment/windows-x86/icon.ico" fullpath="${application.title}/${application.title}.ico"/>
|
||||
<zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
|
||||
<zipfileset dir="${dist.dir}/lib" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${build.dir}/natives/windows-x86" prefix="${application.title}/app" erroronmissingdir="false"/>
|
||||
</zip>
|
||||
<delete file="resources/desktop-deployment/windows-x86/_package.cfg"/>
|
||||
<antcall target="-package-windows-x86-jre"/>
|
||||
@ -50,7 +51,8 @@
|
||||
<zipfileset file="resources/desktop-deployment/windows-x64/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
|
||||
<zipfileset file="resources/desktop-deployment/windows-x64/icon.ico" fullpath="${application.title}/${application.title}.ico"/>
|
||||
<zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
|
||||
<zipfileset dir="${dist.dir}/lib" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${build.dir}/natives/windows-x64" prefix="${application.title}/app" erroronmissingdir="false"/>
|
||||
</zip>
|
||||
<delete file="resources/desktop-deployment/windows-x64/_package.cfg"/>
|
||||
<antcall target="-package-windows-x64-jre"/>
|
||||
@ -83,7 +85,8 @@
|
||||
<zipfileset file="resources/desktop-deployment/linux-x86/stub" filemode="755" fullpath="${application.title}/${application.title}"/>
|
||||
<zipfileset file="resources/desktop-deployment/linux-x86/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
|
||||
<zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
|
||||
<zipfileset dir="${dist.dir}/lib" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${build.dir}/natives/linux-x86" prefix="${application.title}/app" erroronmissingdir="false"/>
|
||||
</zip>
|
||||
<delete file="resources/desktop-deployment/linux-x86/_package.cfg"/>
|
||||
<antcall target="-package-linux-x86-jre"/>
|
||||
@ -117,7 +120,8 @@
|
||||
<zipfileset file="resources/desktop-deployment/linux-x64/stub" filemode="755" fullpath="${application.title}/${application.title}"/>
|
||||
<zipfileset file="resources/desktop-deployment/linux-x64/_package.cfg" fullpath="${application.title}/app/package.cfg"/>
|
||||
<zipfileset file="${dist.jar}" prefix="${application.title}/app"/>
|
||||
<zipfileset dir="${dist.dir}/lib" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}/app/lib"/>
|
||||
<zipfileset dir="${build.dir}/natives/linux-x64" prefix="${application.title}/app" erroronmissingdir="false"/>
|
||||
</zip>
|
||||
<delete file="resources/desktop-deployment/linux-x64/_package.cfg"/>
|
||||
<antcall target="-package-linux-x64-jre"/>
|
||||
@ -154,7 +158,8 @@
|
||||
<zipfileset file="resources/desktop-deployment/macosx-x64/_Info.plist" fullpath="${application.title}.app/Contents/Info.plist"/>
|
||||
<zipfileset file="resources/desktop-deployment/macosx-x64/icon.icns" fullpath="${application.title}.app/Contents/Resources/GenericApp.icns"/>
|
||||
<zipfileset file="${dist.jar}" prefix="${application.title}.app/Contents/Java"/>
|
||||
<zipfileset dir="${dist.dir}/lib" prefix="${application.title}.app/Contents/Java/lib"/>
|
||||
<zipfileset dir="${dist.dir}/lib" excludes="${deployment.jarexcludes}" prefix="${application.title}.app/Contents/Java/lib"/>
|
||||
<zipfileset dir="${build.dir}/natives/macosx-x64" prefix="${application.title}.app/Contents/Java" erroronmissingdir="false"/>
|
||||
</zip>
|
||||
<delete file="resources/desktop-deployment/macosx-x64/_Info.plist"/>
|
||||
<antcall target="-package-macosx-x64-jre"/>
|
||||
@ -244,4 +249,30 @@
|
||||
</tokenfilter>
|
||||
</filterchain>
|
||||
</loadresource>
|
||||
</target>
|
||||
|
||||
<target name="-extract-native-binaries">
|
||||
<java outputproperty="deployment.jarexcludes" dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="getjarexcludes"/>
|
||||
</java>
|
||||
<java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="Windows32"/>
|
||||
<arg value="${build.dir}/natives/windows-x86"/>
|
||||
</java>
|
||||
<java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="Windows64"/>
|
||||
<arg value="${build.dir}/natives/windows-x64"/>
|
||||
</java>
|
||||
<java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="Linux32"/>
|
||||
<arg value="${build.dir}/natives/linux-x86"/>
|
||||
</java>
|
||||
<java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="Linux64"/>
|
||||
<arg value="${build.dir}/natives/linux-x64"/>
|
||||
</java>
|
||||
<java dir="${basedir}" classname="com.jme3.system.ExtractNativeLibraries" fork="true" failonerror="false" classpath="${dist.jar}">
|
||||
<arg value="MacOSX64"/>
|
||||
<arg value="${build.dir}/natives/macosx-x64"/>
|
||||
</java>
|
||||
</target>
|
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user