1 package com.jme3.scene.plugins.blender.curves; 2 3 import com.jme3.material.Material; 4 import com.jme3.material.RenderState.FaceCullMode; 5 import com.jme3.math.Spline.SplineType; 6 import com.jme3.math.*; 7 import com.jme3.scene.Geometry; 8 import com.jme3.scene.Mesh; 9 import com.jme3.scene.VertexBuffer.Type; 10 import com.jme3.scene.plugins.blender.AbstractBlenderHelper; 11 import com.jme3.scene.plugins.blender.BlenderContext; 12 import com.jme3.scene.plugins.blender.exceptions.BlenderFileException; 13 import com.jme3.scene.plugins.blender.file.*; 14 import com.jme3.scene.plugins.blender.materials.MaterialHelper; 15 import com.jme3.scene.plugins.blender.meshes.MeshHelper; 16 import com.jme3.scene.plugins.blender.objects.Properties; 17 import com.jme3.scene.shape.Curve; 18 import com.jme3.scene.shape.Surface; 19 import com.jme3.util.BufferUtils; 20 import java.nio.FloatBuffer; 21 import java.nio.IntBuffer; 22 import java.util.ArrayList; 23 import java.util.HashMap; 24 import java.util.List; 25 import java.util.Map; 26 import java.util.Map.Entry; 27 import java.util.logging.Logger; 28 29 /** 30 * A class that is used in mesh calculations. 31 * @author Marcin Roguski 32 */ 33 public class CurvesHelper extends AbstractBlenderHelper { 34 35 private static final Logger LOGGER = Logger.getLogger(CurvesHelper.class.getName()); 36 /** Minimum basis U function degree for NURBS curves and surfaces. */ 37 protected int minimumBasisUFunctionDegree = 4; 38 /** Minimum basis V function degree for NURBS curves and surfaces. */ 39 protected int minimumBasisVFunctionDegree = 4; 40 41 /** 42 * This constructor parses the given blender version and stores the result. Some functionalities may differ in 43 * different blender versions. 44 * @param blenderVersion 45 * the version read from the blend file 46 * @param fixUpAxis 47 * a variable that indicates if the Y asxis is the UP axis or not 48 */ 49 public CurvesHelper(String blenderVersion, boolean fixUpAxis) { 50 super(blenderVersion, fixUpAxis); 51 } 52 53 /** 54 * This method converts given curve structure into a list of geometries representing the curve. The list is used here because on object 55 * can have several separate curves. 56 * @param curveStructure 57 * the curve structure 58 * @param blenderContext 59 * the blender context 60 * @return a list of geometries repreenting a single curve object 61 * @throws BlenderFileException 62 */ 63 public List<Geometry> toCurve(Structure curveStructure, BlenderContext blenderContext) throws BlenderFileException { 64 String name = curveStructure.getName(); 65 int flag = ((Number) curveStructure.getFieldValue("flag")).intValue(); 66 boolean is3D = (flag & 0x01) != 0; 67 boolean isFront = (flag & 0x02) != 0 && !is3D; 68 boolean isBack = (flag & 0x04) != 0 && !is3D; 69 if (isFront) { 70 LOGGER.warning("No front face in curve implemented yet!");//TODO: implement front face 71 } 72 if (isBack) { 73 LOGGER.warning("No back face in curve implemented yet!");//TODO: implement back face 74 } 75 76 //reading nurbs (and sorting them by material) 77 List<Structure> nurbStructures = ((Structure) curveStructure.getFieldValue("nurb")).evaluateListBase(blenderContext); 78 Map<Number, List<Structure>> nurbs = new HashMap<Number, List<Structure>>(); 79 for (Structure nurb : nurbStructures) { 80 Number matNumber = (Number) nurb.getFieldValue("mat_nr"); 81 List<Structure> nurbList = nurbs.get(matNumber); 82 if (nurbList == null) { 83 nurbList = new ArrayList<Structure>(); 84 nurbs.put(matNumber, nurbList); 85 } 86 nurbList.add(nurb); 87 } 88 89 //getting materials 90 MaterialHelper materialHelper = blenderContext.getHelper(MaterialHelper.class); 91 Material[] materials = materialHelper.getMaterials(curveStructure, blenderContext); 92 if (materials == null) { 93 materials = new Material[]{blenderContext.getDefaultMaterial().clone()}; 94 } 95 for (Material material : materials) { 96 material.getAdditionalRenderState().setFaceCullMode(FaceCullMode.Off); 97 } 98 99 //getting or creating bevel object 100 List<Geometry> bevelObject = null; 101 Pointer pBevelObject = (Pointer) curveStructure.getFieldValue("bevobj"); 102 if (pBevelObject.isNotNull()) { 103 Pointer pBevelStructure = (Pointer) pBevelObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data"); 104 Structure bevelStructure = pBevelStructure.fetchData(blenderContext.getInputStream()).get(0); 105 bevelObject = this.toCurve(bevelStructure, blenderContext); 106 } else { 107 int bevResol = ((Number) curveStructure.getFieldValue("bevresol")).intValue(); 108 float extrude = ((Number) curveStructure.getFieldValue("ext1")).floatValue(); 109 float bevelDepth = ((Number) curveStructure.getFieldValue("ext2")).floatValue(); 110 if (bevelDepth > 0.0f) { 111 float handlerLength = bevelDepth / 2.0f; 112 113 List<Vector3f> conrtolPoints = new ArrayList<Vector3f>(extrude > 0.0f ? 19 : 13); 114 conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0)); 115 conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength + extrude, 0)); 116 117 conrtolPoints.add(new Vector3f(-handlerLength, bevelDepth + extrude, 0)); 118 conrtolPoints.add(new Vector3f(0, bevelDepth + extrude, 0)); 119 conrtolPoints.add(new Vector3f(handlerLength, bevelDepth + extrude, 0)); 120 121 conrtolPoints.add(new Vector3f(bevelDepth, extrude + handlerLength, 0)); 122 conrtolPoints.add(new Vector3f(bevelDepth, extrude, 0)); 123 conrtolPoints.add(new Vector3f(bevelDepth, extrude - handlerLength, 0)); 124 125 if (extrude > 0.0f) { 126 conrtolPoints.add(new Vector3f(bevelDepth, -extrude + handlerLength, 0)); 127 conrtolPoints.add(new Vector3f(bevelDepth, -extrude, 0)); 128 conrtolPoints.add(new Vector3f(bevelDepth, -extrude - handlerLength, 0)); 129 } 130 131 conrtolPoints.add(new Vector3f(handlerLength, -bevelDepth - extrude, 0)); 132 conrtolPoints.add(new Vector3f(0, -bevelDepth - extrude, 0)); 133 conrtolPoints.add(new Vector3f(-handlerLength, -bevelDepth - extrude, 0)); 134 135 conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength - extrude, 0)); 136 conrtolPoints.add(new Vector3f(-bevelDepth, -extrude, 0)); 137 138 if (extrude > 0.0f) { 139 conrtolPoints.add(new Vector3f(-bevelDepth, handlerLength - extrude, 0)); 140 141 conrtolPoints.add(new Vector3f(-bevelDepth, -handlerLength + extrude, 0)); 142 conrtolPoints.add(new Vector3f(-bevelDepth, extrude, 0)); 143 } 144 145 Spline bevelSpline = new Spline(SplineType.Bezier, conrtolPoints, 0, false); 146 Curve bevelCurve = new Curve(bevelSpline, bevResol); 147 bevelObject = new ArrayList<Geometry>(1); 148 bevelObject.add(new Geometry("", bevelCurve)); 149 } else if (extrude > 0.0f) { 150 Spline bevelSpline = new Spline(SplineType.Linear, new Vector3f[]{ 151 new Vector3f(0, extrude, 0), new Vector3f(0, -extrude, 0) 152 }, 1, false); 153 Curve bevelCurve = new Curve(bevelSpline, bevResol); 154 bevelObject = new ArrayList<Geometry>(1); 155 bevelObject.add(new Geometry("", bevelCurve)); 156 } 157 } 158 159 //getting taper object 160 Curve taperObject = null; 161 Pointer pTaperObject = (Pointer) curveStructure.getFieldValue("taperobj"); 162 if (bevelObject != null && pTaperObject.isNotNull()) { 163 Pointer pTaperStructure = (Pointer) pTaperObject.fetchData(blenderContext.getInputStream()).get(0).getFieldValue("data"); 164 Structure taperStructure = pTaperStructure.fetchData(blenderContext.getInputStream()).get(0); 165 taperObject = this.loadTaperObject(taperStructure, blenderContext); 166 } 167 168 Vector3f loc = this.getLoc(curveStructure); 169 //creating the result curves 170 List<Geometry> result = new ArrayList<Geometry>(nurbs.size()); 171 for (Entry<Number, List<Structure>> nurbEntry : nurbs.entrySet()) { 172 for (Structure nurb : nurbEntry.getValue()) { 173 int type = ((Number) nurb.getFieldValue("type")).intValue(); 174 List<Geometry> nurbGeoms = null; 175 if ((type & 0x01) != 0) {//Bezier curve 176 nurbGeoms = this.loadBezierCurve(loc, nurb, bevelObject, taperObject, blenderContext); 177 } else if ((type & 0x04) != 0) {//NURBS 178 nurbGeoms = this.loadNurb(loc, nurb, bevelObject, taperObject, blenderContext); 179 } 180 if (nurbGeoms != null) {//setting the name and assigning materials 181 for (Geometry nurbGeom : nurbGeoms) { 182 nurbGeom.setMaterial(materials[nurbEntry.getKey().intValue()]); 183 nurbGeom.setName(name); 184 result.add(nurbGeom); 185 } 186 } 187 } 188 } 189 190 //reading custom properties 191 Properties properties = this.loadProperties(curveStructure, blenderContext); 192 if(properties != null && properties.getValue() != null) { 193 for(Geometry geom : result) { 194 geom.setUserData("properties", properties); 195 } 196 } 197 198 return result; 199 } 200 201 /** 202 * This method loads the bezier curve. 203 * @param loc 204 * the translation of the curve 205 * @param nurb 206 * the nurb structure 207 * @param bevelObject 208 * the bevel object 209 * @param taperObject 210 * the taper object 211 * @param blenderContext 212 * the blender context 213 * @return a list of geometries representing the curves 214 * @throws BlenderFileException 215 * an exception is thrown when there are problems with the blender file 216 */ 217 protected List<Geometry> loadBezierCurve(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, 218 BlenderContext blenderContext) throws BlenderFileException { 219 Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); 220 List<Geometry> result = new ArrayList<Geometry>(); 221 if (pBezierTriple.isNotNull()) { 222 boolean smooth = (((Number) nurb.getFlatFieldValue("flag")).intValue() & 0x01) != 0; 223 int resolution = ((Number) nurb.getFieldValue("resolu")).intValue(); 224 boolean cyclic = (((Number) nurb.getFieldValue("flagu")).intValue() & 0x01) != 0; 225 226 //creating the curve object 227 BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3); 228 List<Vector3f> controlPoints = bezierCurve.getControlPoints(); 229 if (cyclic) { 230 //copy the first three points at the end 231 for (int i = 0; i < 3; ++i) { 232 controlPoints.add(controlPoints.get(i)); 233 } 234 } 235 //removing the first and last handles 236 controlPoints.remove(0); 237 controlPoints.remove(controlPoints.size() - 1); 238 239 //creating curve 240 Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); 241 Curve curve = new Curve(spline, resolution); 242 if (bevelObject == null) {//creating a normal curve 243 Geometry curveGeometry = new Geometry(null, curve); 244 result.add(curveGeometry); 245 //TODO: use front and back flags; surface excluding algorithm for bezier circles should be added 246 } else {//creating curve with bevel and taper shape 247 result = this.applyBevelAndTaper(curve, bevelObject, taperObject, smooth, blenderContext); 248 } 249 } 250 return result; 251 } 252 253 /** 254 * This method loads the NURBS curve or surface. 255 * @param loc 256 * object's location 257 * @param nurb 258 * the NURBS data structure 259 * @param bevelObject 260 * the bevel object to be applied 261 * @param taperObject 262 * the taper object to be applied 263 * @param blenderContext 264 * the blender context 265 * @return a list of geometries that represents the loaded NURBS curve or surface 266 * @throws BlenderFileException 267 * an exception is throw when problems with blender loaded data occurs 268 */ 269 @SuppressWarnings("unchecked") 270 protected List<Geometry> loadNurb(Vector3f loc, Structure nurb, List<Geometry> bevelObject, Curve taperObject, 271 BlenderContext blenderContext) throws BlenderFileException { 272 //loading the knots 273 List<Float>[] knots = new List[2]; 274 Pointer[] pKnots = new Pointer[]{(Pointer) nurb.getFieldValue("knotsu"), (Pointer) nurb.getFieldValue("knotsv")}; 275 for (int i = 0; i < knots.length; ++i) { 276 if (pKnots[i].isNotNull()) { 277 FileBlockHeader fileBlockHeader = blenderContext.getFileBlock(pKnots[i].getOldMemoryAddress()); 278 BlenderInputStream blenderInputStream = blenderContext.getInputStream(); 279 blenderInputStream.setPosition(fileBlockHeader.getBlockPosition()); 280 int knotsAmount = fileBlockHeader.getCount() * fileBlockHeader.getSize() / 4; 281 knots[i] = new ArrayList<Float>(knotsAmount); 282 for (int j = 0; j < knotsAmount; ++j) { 283 knots[i].add(Float.valueOf(blenderInputStream.readFloat())); 284 } 285 } 286 } 287 288 //loading the flags and orders (basis functions degrees) 289 int flagU = ((Number) nurb.getFieldValue("flagu")).intValue(); 290 int flagV = ((Number) nurb.getFieldValue("flagv")).intValue(); 291 int orderU = ((Number) nurb.getFieldValue("orderu")).intValue(); 292 int orderV = ((Number) nurb.getFieldValue("orderv")).intValue(); 293 294 //loading control points and their weights 295 int pntsU = ((Number) nurb.getFieldValue("pntsu")).intValue(); 296 int pntsV = ((Number) nurb.getFieldValue("pntsv")).intValue(); 297 List<Structure> bPoints = ((Pointer) nurb.getFieldValue("bp")).fetchData(blenderContext.getInputStream()); 298 List<List<Vector4f>> controlPoints = new ArrayList<List<Vector4f>>(pntsV); 299 for (int i = 0; i < pntsV; ++i) { 300 List<Vector4f> uControlPoints = new ArrayList<Vector4f>(pntsU); 301 for (int j = 0; j < pntsU; ++j) { 302 DynamicArray<Float> vec = (DynamicArray<Float>) bPoints.get(j + i * pntsU).getFieldValue("vec"); 303 if (fixUpAxis) { 304 uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(2).floatValue(), -vec.get(1).floatValue(), vec.get(3).floatValue())); 305 } else { 306 uControlPoints.add(new Vector4f(vec.get(0).floatValue(), vec.get(1).floatValue(), vec.get(2).floatValue(), vec.get(3).floatValue())); 307 } 308 } 309 if ((flagU & 0x01) != 0) { 310 for (int k = 0; k < orderU - 1; ++k) { 311 uControlPoints.add(uControlPoints.get(k)); 312 } 313 } 314 controlPoints.add(uControlPoints); 315 } 316 if ((flagV & 0x01) != 0) { 317 for (int k = 0; k < orderV - 1; ++k) { 318 controlPoints.add(controlPoints.get(k)); 319 } 320 } 321 322 int resolu = ((Number) nurb.getFieldValue("resolu")).intValue() + 1; 323 List<Geometry> result; 324 if (knots[1] == null) {//creating the curve 325 Spline nurbSpline = new Spline(controlPoints.get(0), knots[0]); 326 Curve nurbCurve = new Curve(nurbSpline, resolu); 327 if (bevelObject != null) { 328 result = this.applyBevelAndTaper(nurbCurve, bevelObject, taperObject, true, blenderContext);//TODO: smooth 329 } else { 330 result = new ArrayList<Geometry>(1); 331 Geometry nurbGeometry = new Geometry("", nurbCurve); 332 result.add(nurbGeometry); 333 } 334 } else {//creating the nurb surface 335 int resolv = ((Number) nurb.getFieldValue("resolv")).intValue() + 1; 336 Surface nurbSurface = Surface.createNurbsSurface(controlPoints, knots, resolu, resolv, orderU, orderV); 337 Geometry nurbGeometry = new Geometry("", nurbSurface); 338 result = new ArrayList<Geometry>(1); 339 result.add(nurbGeometry); 340 } 341 return result; 342 } 343 344 /** 345 * This method returns the taper scale that should be applied to the object. 346 * @param taperPoints 347 * the taper points 348 * @param taperLength 349 * the taper curve length 350 * @param percent 351 * the percent of way along the whole taper curve 352 * @param store 353 * the vector where the result will be stored 354 */ 355 protected float getTaperScale(float[] taperPoints, float taperLength, float percent) { 356 float length = taperLength * percent; 357 float currentLength = 0; 358 Vector3f p = new Vector3f(); 359 int i; 360 for (i = 0; i < taperPoints.length - 6 && currentLength < length; i += 3) { 361 p.set(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]); 362 p.subtractLocal(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5]); 363 currentLength += p.length(); 364 } 365 currentLength -= p.length(); 366 float leftLength = length - currentLength; 367 float percentOnSegment = p.length() == 0 ? 0 : leftLength / p.length(); 368 Vector3f store = FastMath.interpolateLinear(percentOnSegment, 369 new Vector3f(taperPoints[i], taperPoints[i + 1], taperPoints[i + 2]), 370 new Vector3f(taperPoints[i + 3], taperPoints[i + 4], taperPoints[i + 5])); 371 return store.y; 372 } 373 374 /** 375 * This method applies bevel and taper objects to the curve. 376 * @param curve 377 * the curve we apply the objects to 378 * @param bevelObject 379 * the bevel object 380 * @param taperObject 381 * the taper object 382 * @param smooth 383 * the smooth flag 384 * @param blenderContext 385 * the blender context 386 * @return a list of geometries representing the beveled and/or tapered curve 387 */ 388 protected List<Geometry> applyBevelAndTaper(Curve curve, List<Geometry> bevelObject, Curve taperObject, 389 boolean smooth, BlenderContext blenderContext) { 390 float[] curvePoints = BufferUtils.getFloatArray(curve.getFloatBuffer(Type.Position)); 391 MeshHelper meshHelper = blenderContext.getHelper(MeshHelper.class); 392 float curveLength = curve.getLength(); 393 //TODO: use the smooth var 394 395 //taper data 396 float[] taperPoints = null; 397 float taperLength = 0; 398 if (taperObject != null) { 399 taperPoints = BufferUtils.getFloatArray(taperObject.getFloatBuffer(Type.Position)); 400 taperLength = taperObject.getLength(); 401 } 402 403 //several objects can be allocated only once 404 Vector3f p = new Vector3f(); 405 Vector3f z = new Vector3f(0, 0, 1); 406 Vector3f negativeY = new Vector3f(0, -1, 0); 407 Matrix4f m = new Matrix4f(); 408 float lengthAlongCurve = 0, taperScale = 1.0f; 409 Quaternion planeRotation = new Quaternion(); 410 Quaternion zRotation = new Quaternion(); 411 float[] temp = new float[]{0, 0, 0, 1}; 412 Map<Vector3f, Vector3f> normalMap = new HashMap<Vector3f, Vector3f>();//normalMap merges normals of faces that will be rendered smooth 413 414 FloatBuffer[] vertexBuffers = new FloatBuffer[bevelObject.size()]; 415 FloatBuffer[] normalBuffers = new FloatBuffer[bevelObject.size()]; 416 IntBuffer[] indexBuffers = new IntBuffer[bevelObject.size()]; 417 for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) { 418 Mesh mesh = bevelObject.get(geomIndex).getMesh(); 419 FloatBuffer positions = mesh.getFloatBuffer(Type.Position); 420 float[] vertices = BufferUtils.getFloatArray(positions); 421 422 for (int i = 0; i < curvePoints.length; i += 3) { 423 p.set(curvePoints[i], curvePoints[i + 1], curvePoints[i + 2]); 424 Vector3f v; 425 if (i == 0) { 426 v = new Vector3f(curvePoints[3] - p.x, curvePoints[4] - p.y, curvePoints[5] - p.z); 427 } else if (i + 3 >= curvePoints.length) { 428 v = new Vector3f(p.x - curvePoints[i - 3], p.y - curvePoints[i - 2], p.z - curvePoints[i - 1]); 429 lengthAlongCurve += v.length(); 430 } else { 431 v = new Vector3f(curvePoints[i + 3] - curvePoints[i - 3], 432 curvePoints[i + 4] - curvePoints[i - 2], 433 curvePoints[i + 5] - curvePoints[i - 1]); 434 lengthAlongCurve += new Vector3f(curvePoints[i + 3] - p.x, curvePoints[i + 4] - p.y, curvePoints[i + 5] - p.z).length(); 435 } 436 v.normalizeLocal(); 437 438 float angle = FastMath.acos(v.dot(z)); 439 v.crossLocal(z).normalizeLocal();//v is the rotation axis now 440 planeRotation.fromAngleAxis(angle, v); 441 442 Vector3f zAxisRotationVector = negativeY.cross(v).normalizeLocal(); 443 float zAxisRotationAngle = FastMath.acos(negativeY.dot(v)); 444 zRotation.fromAngleAxis(zAxisRotationAngle, zAxisRotationVector); 445 446 //point transformation matrix 447 if (taperPoints != null) { 448 taperScale = this.getTaperScale(taperPoints, taperLength, lengthAlongCurve / curveLength); 449 } 450 m.set(Matrix4f.IDENTITY); 451 m.setRotationQuaternion(planeRotation.multLocal(zRotation)); 452 m.setTranslation(p); 453 454 //these vertices need to be thrown on XY plane 455 //and moved to the origin of [p1.x, p1.y] on the plane 456 Vector3f[] verts = new Vector3f[vertices.length / 3]; 457 for (int j = 0; j < verts.length; ++j) { 458 temp[0] = vertices[j * 3] * taperScale; 459 temp[1] = vertices[j * 3 + 1] * taperScale; 460 temp[2] = 0; 461 m.mult(temp);//the result is stored in the array 462 if (fixUpAxis) {//TODO: not the other way ??? 463 verts[j] = new Vector3f(temp[0], temp[1], temp[2]); 464 } else { 465 verts[j] = new Vector3f(temp[0], temp[2], -temp[1]); 466 } 467 } 468 if (vertexBuffers[geomIndex] == null) { 469 vertexBuffers[geomIndex] = BufferUtils.createFloatBuffer(verts.length * curvePoints.length); 470 } 471 FloatBuffer buffer = BufferUtils.createFloatBuffer(verts); 472 vertexBuffers[geomIndex].put(buffer); 473 474 //adding indexes 475 IntBuffer indexBuffer = indexBuffers[geomIndex]; 476 if (indexBuffer == null) { 477 //the amount of faces in the final mesh is the amount of edges in the bevel curve 478 //(which is less by 1 than its number of vertices) 479 //multiplied by 2 (because each edge has two faces assigned on both sides) 480 //and multiplied by the amount of bevel curve repeats which is equal to the amount of vertices on the target curve 481 //finally we need to subtract the bevel edges amount 2 times because the border edges have only one face attached 482 //and at last multiply everything by 3 because each face needs 3 indexes to be described 483 int bevelCurveEdgesAmount = verts.length - 1; 484 indexBuffer = BufferUtils.createIntBuffer(((bevelCurveEdgesAmount << 1) * curvePoints.length - bevelCurveEdgesAmount << 1) * 3); 485 indexBuffers[geomIndex] = indexBuffer; 486 } 487 int pointOffset = i / 3 * verts.length; 488 if (i + 3 < curvePoints.length) { 489 for (int index = 0; index < verts.length - 1; ++index) { 490 indexBuffer.put(index + pointOffset); 491 indexBuffer.put(index + pointOffset + 1); 492 indexBuffer.put(verts.length + index + pointOffset); 493 indexBuffer.put(verts.length + index + pointOffset); 494 indexBuffer.put(index + pointOffset + 1); 495 indexBuffer.put(verts.length + index + pointOffset + 1); 496 } 497 } 498 } 499 } 500 501 //calculating the normals 502 for (int geomIndex = 0; geomIndex < bevelObject.size(); ++geomIndex) { 503 Vector3f[] allVerts = BufferUtils.getVector3Array(vertexBuffers[geomIndex]); 504 int[] allIndices = BufferUtils.getIntArray(indexBuffers[geomIndex]); 505 for (int i = 0; i < allIndices.length - 3; i += 3) { 506 Vector3f n = FastMath.computeNormal(allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]); 507 meshHelper.addNormal(n, normalMap, smooth, allVerts[allIndices[i]], allVerts[allIndices[i + 1]], allVerts[allIndices[i + 2]]); 508 } 509 if (normalBuffers[geomIndex] == null) { 510 normalBuffers[geomIndex] = BufferUtils.createFloatBuffer(allVerts.length * 3); 511 } 512 for (Vector3f v : allVerts) { 513 Vector3f n = normalMap.get(v); 514 normalBuffers[geomIndex].put(n.x); 515 normalBuffers[geomIndex].put(n.y); 516 normalBuffers[geomIndex].put(n.z); 517 } 518 } 519 520 List<Geometry> result = new ArrayList<Geometry>(vertexBuffers.length); 521 Float oneReferenceToCurveLength = new Float(curveLength);//its important for array modifier to use one reference here 522 for (int i = 0; i < vertexBuffers.length; ++i) { 523 Mesh mesh = new Mesh(); 524 mesh.setBuffer(Type.Position, 3, vertexBuffers[i]); 525 mesh.setBuffer(Type.Index, 3, indexBuffers[i]); 526 mesh.setBuffer(Type.Normal, 3, normalBuffers[i]); 527 Geometry g = new Geometry("g" + i, mesh); 528 g.setUserData("curveLength", oneReferenceToCurveLength); 529 g.updateModelBound(); 530 result.add(g); 531 } 532 533 return result; 534 } 535 536 /** 537 * This method loads the taper object. 538 * @param taperStructure 539 * the taper structure 540 * @param blenderContext 541 * the blender context 542 * @return the taper object 543 * @throws BlenderFileException 544 */ 545 protected Curve loadTaperObject(Structure taperStructure, BlenderContext blenderContext) throws BlenderFileException { 546 //reading nurbs 547 List<Structure> nurbStructures = ((Structure) taperStructure.getFieldValue("nurb")).evaluateListBase(blenderContext); 548 for (Structure nurb : nurbStructures) { 549 Pointer pBezierTriple = (Pointer) nurb.getFieldValue("bezt"); 550 if (pBezierTriple.isNotNull()) { 551 //creating the curve object 552 BezierCurve bezierCurve = new BezierCurve(0, pBezierTriple.fetchData(blenderContext.getInputStream()), 3); 553 List<Vector3f> controlPoints = bezierCurve.getControlPoints(); 554 //removing the first and last handles 555 controlPoints.remove(0); 556 controlPoints.remove(controlPoints.size() - 1); 557 558 //return the first taper curve that has more than 3 control points 559 if (controlPoints.size() > 3) { 560 Spline spline = new Spline(SplineType.Bezier, controlPoints, 0, false); 561 int resolution = ((Number) taperStructure.getFieldValue("resolu")).intValue(); 562 return new Curve(spline, resolution); 563 } 564 } 565 } 566 return null; 567 } 568 569 /** 570 * This method returns the translation of the curve. The UP axis is taken into account here. 571 * @param curveStructure 572 * the curve structure 573 * @return curve translation 574 */ 575 @SuppressWarnings("unchecked") 576 protected Vector3f getLoc(Structure curveStructure) { 577 DynamicArray<Number> locArray = (DynamicArray<Number>) curveStructure.getFieldValue("loc"); 578 if (fixUpAxis) { 579 return new Vector3f(locArray.get(0).floatValue(), locArray.get(1).floatValue(), -locArray.get(2).floatValue()); 580 } else { 581 return new Vector3f(locArray.get(0).floatValue(), locArray.get(2).floatValue(), locArray.get(1).floatValue()); 582 } 583 } 584 585 @Override 586 public boolean shouldBeLoaded(Structure structure, BlenderContext blenderContext) { 587 return true; 588 } 589 }