allows to apply modifiers on them.experimental
parent
6ec86fe919
commit
c90e50bb2e
@ -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; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue