1 /* 2 * $RCSfile: J3dLwoParser.java,v $ 3 * 4 * Copyright (c) 2007 Sun Microsystems, Inc. All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * - Redistribution of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 13 * - Redistribution in binary form must reproduce the above copyright 14 * notice, this list of conditions and the following disclaimer in 15 * the documentation and/or other materials provided with the 16 * distribution. 17 * 18 * Neither the name of Sun Microsystems, Inc. or the names of 19 * contributors may be used to endorse or promote products derived 20 * from this software without specific prior written permission. 21 * 22 * This software is provided "AS IS," without a warranty of any 23 * kind. ALL EXPRESS OR IMPLIED CONDITIONS, REPRESENTATIONS AND 24 * WARRANTIES, INCLUDING ANY IMPLIED WARRANTY OF MERCHANTABILITY, 25 * FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT, ARE HEREBY 26 * EXCLUDED. SUN MICROSYSTEMS, INC. ("SUN") AND ITS LICENSORS SHALL 27 * NOT BE LIABLE FOR ANY DAMAGES SUFFERED BY LICENSEE AS A RESULT OF 28 * USING, MODIFYING OR DISTRIBUTING THIS SOFTWARE OR ITS 29 * DERIVATIVES. IN NO EVENT WILL SUN OR ITS LICENSORS BE LIABLE FOR 30 * ANY LOST REVENUE, PROFIT OR DATA, OR FOR DIRECT, INDIRECT, SPECIAL, 31 * CONSEQUENTIAL, INCIDENTAL OR PUNITIVE DAMAGES, HOWEVER CAUSED AND 32 * REGARDLESS OF THE THEORY OF LIABILITY, ARISING OUT OF THE USE OF OR 33 * INABILITY TO USE THIS SOFTWARE, EVEN IF SUN HAS BEEN ADVISED OF THE 34 * POSSIBILITY OF SUCH DAMAGES. 35 * 36 * You acknowledge that this software is not designed, licensed or 37 * intended for use in the design, construction, operation or 38 * maintenance of any nuclear facility. 39 * 40 * $Revision: 1.4 $ 41 * $Date: 2007/02/09 17:20:07 $ 42 * $State: Exp $ 43 */ 44 45 package com.sun.j3d.loaders.lw3d; 46 47 import java.awt.Component; 48 import java.awt.Image; 49 import java.util.Enumeration; 50 import java.util.Vector; 51 import com.sun.j3d.utils.geometry.GeometryInfo; 52 import com.sun.j3d.utils.geometry.NormalGenerator; 53 import com.sun.j3d.utils.geometry.Stripifier; 54 import com.sun.j3d.utils.image.TextureLoader; 55 import com.sun.j3d.loaders.IncorrectFormatException; 56 import java.io.FileNotFoundException; 57 58 import javax.media.j3d.*; 59 import javax.vecmath.*; 60 61 import java.net.*; 62 63 64 /** 65 * This class is responsible for turning Lightwave geometry data into 66 * Java3D geometry. It is a subclass of LwoObject and calls that 67 * superclass when first instantiated to parse the binary file and turn it 68 * into intermediate data structures. J3dLwoParser then goes through 69 * those data structures and turns them into Java3D objects. For each 70 * ShapeHolder object created by the parent class, this class retrieves 71 * the geometry data and associated surface data, creates the 72 * appropriate Geometry object (usually IndexedTriangleFanArray, 73 * unless the object is points or lines), including calculating normals for 74 * the polygons and sets up the Appearance according to the surface 75 * parameters (including calculating texture coordinates if necessary). 76 */ 77 78 class J3dLwoParser extends LwoParser { 79 80 float normalCoordsArray[]; 81 int normalIndicesArray[]; 82 Shape3D objectShape; 83 Color3f color, diffuseColor, specularColor, emissiveColor; 84 float shininess; 85 Vector objectShapeList = new Vector(); 86 87 /** 88 * Constructor: Calls LwoObject to parse file and create data structures 89 */ J3dLwoParser(String fileName, int debugVals)90 J3dLwoParser(String fileName, 91 int debugVals) throws FileNotFoundException { 92 super(fileName, debugVals); 93 } 94 J3dLwoParser(URL url, int debugVals)95 J3dLwoParser(URL url, int debugVals) 96 throws FileNotFoundException { 97 super(url, debugVals); 98 } 99 getSurf(int length)100 void getSurf(int length) throws FileNotFoundException { 101 super.getSurf(length); 102 } 103 104 105 /** 106 * Turns LwoObject's data structures (created from the binary geometry 107 * file) into Java3d objects 108 */ createJava3dGeometry()109 void createJava3dGeometry() throws IncorrectFormatException { 110 111 GeometryArray object; 112 LwoTexture texture; 113 114 for (Enumeration e = shapeList.elements(); 115 e.hasMoreElements() ;) { 116 int vertexFormat = javax.media.j3d.GeometryArray.COORDINATES; 117 ShapeHolder shape = (ShapeHolder)e.nextElement(); 118 debugOutputLn(LINE_TRACE, "about to create Arrays for Shape"); 119 debugOutputLn(VALUES, "shape = " + shape); 120 shape.createArrays(true); 121 int vertexCount = shape.coordsArray.length/3; 122 int indexCount = 0; 123 if (shape.facetIndices != null) 124 indexCount = shape.facetIndices.length; 125 debugOutputLn(VALUES, "numSurf = " + shape.numSurf); 126 // Find the right surface. Note: surfaces are indexed by 127 // name. So take this surf number, look up the name of that 128 // surface in surfaceNameList, then take that name and 129 // find the matching LwoSurface 130 String surfName = 131 (String)surfNameList.elementAt(shape.numSurf - 1); 132 LwoSurface surf = null; 133 for (int surfNum = 0; 134 surfNum < surfaceList.size(); 135 ++surfNum) { 136 LwoSurface tempSurf = 137 (LwoSurface)surfaceList.elementAt(surfNum); 138 String tempSurfName = tempSurf.surfName; 139 if (surfName.equals(tempSurfName)) { 140 surf = tempSurf; 141 break; 142 } 143 } 144 if (surf == null) { 145 throw new IncorrectFormatException( 146 "bad surf for surfnum/name = " + shape.numSurf + ", " + 147 surfName); 148 } 149 debugOutputLn(VALUES, "surf = " + surf); 150 151 // Get the LwoTexture object (if any) for the surface 152 texture = surf.getTexture(); 153 154 Appearance appearance = new Appearance(); 155 if (shape.facetSizes[0] == 1) { 156 // This case happens if the objects are points 157 // Note that points are colored, not lit 158 object = new 159 javax.media.j3d.PointArray(vertexCount, vertexFormat); 160 object.setCoordinates(0, shape.coordsArray); 161 ColoringAttributes colorAtt = 162 new ColoringAttributes(surf.getColor(), 163 ColoringAttributes.FASTEST); 164 PointAttributes pointStyle = new PointAttributes(); 165 pointStyle.setPointSize(1); 166 167 appearance.setColoringAttributes(colorAtt); 168 appearance.setPointAttributes(pointStyle); 169 } 170 else if (shape.facetSizes[0] == 2) { 171 // This case happens if the objects are lines 172 // Note that lines are colored, not lit 173 debugOutputLn(LINE_TRACE, "Creating IndexedLineArray"); 174 object = new javax.media.j3d.LineArray(vertexCount, 175 vertexFormat); 176 object.setCoordinates(0, shape.coordsArray); 177 ColoringAttributes colorAtt = 178 new ColoringAttributes(surf.getColor(), 179 ColoringAttributes.FASTEST); 180 appearance.setColoringAttributes(colorAtt); 181 } 182 else { 183 // This is the case for any polygonal objects 184 debugOutputLn(LINE_TRACE, "Creating IndexedTriFanArray"); 185 // create triFanArray 186 vertexFormat |= javax.media.j3d.GeometryArray.NORMALS; 187 188 debugOutputLn(LINE_TRACE, "about to process vertices/indices, facetIndices = " + 189 shape.facetIndices); 190 if (shape.facetIndices != null) { 191 float[] textureCoords = null; 192 int[] textureIndices = null; 193 194 debugOutputLn(LINE_TRACE, "setting vertexCount, normind = " + shape.normalIndices); 195 // If these are null we're going direct (non-indexed) 196 debugOutputLn(LINE_TRACE, "vtxcount, format, indcount = " + 197 vertexCount + ", " + vertexFormat + 198 ", " + indexCount); 199 if (texture != null) { 200 // There's a texture here - need to create the appropriate arrays 201 // and calculate texture coordinates for the object 202 vertexFormat |= GeometryArray.TEXTURE_COORDINATE_2; 203 textureCoords = new float[vertexCount * 2]; 204 textureIndices = new int[shape.facetIndices.length]; 205 calculateTextureCoords(texture, shape.coordsArray, 206 shape.facetIndices, 207 textureCoords, textureIndices); 208 debugOutputLn(LINE_TRACE, "textureCoords:"); 209 debugOutputLn(LINE_TRACE, "texture Coords, Indices.length = " + textureCoords.length + ", " + textureIndices.length); 210 } 211 debugOutputLn(LINE_TRACE, "about to create GeometryInfo"); 212 213 // Use the GeometryInfo utility to calculate smooth normals 214 GeometryInfo gi = 215 new GeometryInfo(GeometryInfo.TRIANGLE_FAN_ARRAY); 216 gi.setCoordinates(shape.coordsArray); 217 gi.setCoordinateIndices(shape.facetIndices); 218 gi.setStripCounts(shape.facetSizes); 219 if (texture != null) { 220 gi.setTextureCoordinateParams(1, 2); 221 gi.setTextureCoordinates(0, textureCoords); 222 gi.setTextureCoordinateIndices(0, textureIndices); 223 } 224 gi.recomputeIndices(); 225 NormalGenerator ng = 226 new NormalGenerator(surf.getCreaseAngle()); 227 ng.generateNormals(gi); 228 Stripifier st = new Stripifier(); 229 st.stripify(gi); 230 object = gi.getGeometryArray(true, true, false); 231 debugOutputLn(LINE_TRACE, "done."); 232 } 233 else { 234 // This case is called if LwoObject did not create facet 235 // indices. This code is not currently used because facet 236 // indices are always created for polygonal objects 237 debugOutputLn(LINE_TRACE, 238 "about to create trifanarray with " + 239 "vertexCount, facetSizes.len = " + 240 vertexCount + ", " + 241 shape.facetSizes.length); 242 object = new 243 javax.media.j3d.TriangleFanArray(vertexCount, 244 vertexFormat, 245 shape.facetSizes); 246 object.setCoordinates(0, shape.coordsArray); 247 object.setNormals(0, shape.normalCoords); 248 debugOutputLn(VALUES, "passed in normalCoords, length = " + 249 shape.normalCoords.length); 250 } 251 debugOutputLn(LINE_TRACE, "created fan array"); 252 253 // Setup Appearance given the surface parameters 254 Material material = new Material(surf.getColor(), 255 surf.getEmissiveColor(), 256 surf.getDiffuseColor(), 257 surf.getSpecularColor(), 258 surf.getShininess()); 259 material.setLightingEnable(true); 260 appearance.setMaterial(material); 261 if (surf.getTransparency() != 0f) { 262 TransparencyAttributes ta = new TransparencyAttributes(); 263 ta.setTransparency(surf.getTransparency()); 264 ta.setTransparencyMode(ta.BLENDED); 265 appearance.setTransparencyAttributes(ta); 266 } 267 if (texture != null) { 268 debugOutputLn(LINE_TRACE, "texture != null, enable texturing"); 269 Texture tex = texture.getTexture(); 270 tex.setEnable(true); 271 appearance.setTexture(tex); 272 TextureAttributes ta = new TextureAttributes(); 273 if (texture.getType().equals("DTEX")) 274 ta.setTextureMode(TextureAttributes.MODULATE); 275 else if (texture.getType().equals("CTEX")) 276 ta.setTextureMode(TextureAttributes.DECAL); 277 appearance.setTextureAttributes(ta); 278 } 279 else { 280 debugOutputLn(LINE_TRACE, "texture == null, no texture to use"); 281 } 282 } 283 debugOutputLn(LINE_TRACE, "done creating object"); 284 285 // This does gc 286 shape.nullify(); 287 288 objectShape = new Shape3D(object); 289 290 // Combine the appearance and geometry 291 objectShape.setAppearance(appearance); 292 objectShapeList.addElement(objectShape); 293 } 294 } 295 296 /** 297 * Calculate texture coordinates for the geometry given the texture 298 * map properties specified in the LwoTexture object 299 */ calculateTextureCoords(LwoTexture texture, float verts[], int indices[], float[] textureCoords, int[] textureIndices)300 void calculateTextureCoords(LwoTexture texture, 301 float verts[], int indices[], 302 float[] textureCoords, int[] textureIndices) { 303 304 /* 305 the actual math in these coord calculations comes directly from 306 Newtek - they posted sample code to help compute tex coords based 307 on the type of mapping: 308 309 Here are some simplified code fragments showing how LightWave 310 computes UV coordinates from X, Y, and Z. If the resulting 311 UV coordinates are not in the range from 0 to 1, the 312 appropriate integer should be added to them to bring them into 313 that range (the fract function should have accomplished 314 this by subtracting the floor of each number from itself). 315 Then they can be multiplied by the width and height (in pixels) 316 of an image map to determine which pixel to look up. The 317 texture size, center, and tiling parameters are taken right 318 off the texture control panel. 319 320 321 x -= xTextureCenter; 322 y -= yTextureCenter; 323 z -= zTextureCenter; 324 if (textureType == TT_PLANAR) { 325 s = (textureAxis == TA_X) ? z / zTextureSize + .5 : 326 x / xTextureSize + .5; 327 t = (textureAxis == TA_Y) ? -z / zTextureSize + .5 : 328 -y / yTextureSize + .5; 329 u = fract(s); 330 v = fract(t); 331 } 332 else if (type == TT_CYLINDRICAL) { 333 if (textureAxis == TA_X) { 334 xyztoh(z,x,-y,&lon); 335 t = -x / xTextureSize + .5; 336 } 337 else if (textureAxis == TA_Y) { 338 xyztoh(-x,y,z,&lon); 339 t = -y / yTextureSize + .5; 340 } 341 else { 342 xyztoh(-x,z,-y,&lon); 343 t = -z / zTextureSize + .5; 344 } 345 lon = 1.0 - lon / TWOPI; 346 if (widthTiling != 1.0) 347 lon = fract(lon) * widthTiling; 348 u = fract(lon); 349 v = fract(t); 350 } 351 else if (type == TT_SPHERICAL) { 352 if (textureAxis == TA_X) 353 xyztohp(z,x,-y,&lon,&lat); 354 else if (textureAxis == TA_Y) 355 xyztohp(-x,y,z,&lon,&lat); 356 else 357 xyztohp(-x,z,-y,&lon,&lat); 358 lon = 1.0 - lon / TWOPI; 359 lat = .5 - lat / PI; 360 if (widthTiling != 1.0) 361 lon = fract(lon) * widthTiling; 362 if (heightTiling != 1.0) 363 lat = fract(lat) * heightTiling; 364 u = fract(lon); 365 v = fract(lat); 366 } 367 368 support functions: 369 370 void xyztoh(float x,float y,float z,float *h) 371 { 372 if (x == 0.0 && z == 0.0) 373 *h = 0.0; 374 else { 375 if (z == 0.0) 376 *h = (x < 0.0) ? HALFPI : -HALFPI; 377 else if (z < 0.0) 378 *h = -atan(x / z) + PI; 379 else 380 *h = -atan(x / z); 381 } 382 } 383 384 void xyztohp(float x,float y,float z,float *h,float *p) 385 { 386 if (x == 0.0 && z == 0.0) { 387 *h = 0.0; 388 if (y != 0.0) 389 *p = (y < 0.0) ? -HALFPI : HALFPI; 390 else 391 *p = 0.0; 392 } 393 else { 394 if (z == 0.0) 395 *h = (x < 0.0) ? HALFPI : -HALFPI; 396 else if (z < 0.0) 397 *h = -atan(x / z) + PI; 398 else 399 *h = -atan(x / z); 400 x = sqrt(x * x + z * z); 401 if (x == 0.0) 402 *p = (y < 0.0) ? -HALFPI : HALFPI; 403 else 404 *p = atan(y / x); 405 } 406 } 407 */ 408 409 debugOutputLn(TRACE, "calculateTextureCoords()"); 410 // Compute texture coord stuff 411 float sx = 0, sz = 0, ty = 0, tz = 0; 412 double s, t; 413 int textureAxis = texture.getTextureAxis(); 414 Vector3f textureSize = texture.getTextureSize(); 415 Vector3f textureCenter = texture.getTextureCenter(); 416 417 String mappingType = texture.getMappingType(); 418 if (mappingType.startsWith("Cylindrical")) 419 calculateCylindricalTextureCoords(textureAxis, textureSize, 420 textureCenter, textureCoords, 421 textureIndices, 422 verts, indices); 423 else if (mappingType.startsWith("Spherical")) 424 calculateSphericalTextureCoords(textureAxis, 425 textureCenter, textureCoords, 426 textureIndices, 427 verts, indices); 428 else if (mappingType.startsWith("Planar")) 429 calculatePlanarTextureCoords(textureAxis, textureSize, 430 textureCenter, textureCoords, 431 textureIndices, 432 verts, indices); 433 434 } 435 436 /** See the comments in calculateTextureCoordinates*/ xyztoh(float x,float y,float z)437 double xyztoh(float x,float y,float z) { 438 if (x == 0.0 && z == 0.0) 439 return 0.0; 440 else { 441 if (z == 0.0) 442 return (x < 0.0) ? Math.PI/2.0 : -Math.PI/2.0; 443 else if (z < 0.0) 444 return -Math.atan(x / z) + Math.PI; 445 else 446 return -Math.atan(x / z); 447 } 448 } 449 450 /** See the comments in calculateTextureCoordinates*/ xyztop(float x,float y,float z)451 double xyztop(float x,float y,float z) { 452 double p; 453 454 if (x == 0.0 && z == 0.0) { 455 if (y != 0.0) 456 p = (y < 0.0) ? -Math.PI/2 : Math.PI/2; 457 else 458 p = 0.0; 459 } 460 else { 461 x = (float)Math.sqrt(x * x + z * z); 462 if (x == 0.0) 463 p = (y < 0.0) ? -Math.PI/2 : Math.PI/2; 464 else 465 p = Math.atan(y / x); 466 } 467 return p; 468 } 469 470 471 /** See the comments in calculateTextureCoordinates*/ calculateSphericalTextureCoords(int textureAxis, Vector3f textureCenter, float textureCoords[], int textureIndices[], float verts[], int indices[])472 void calculateSphericalTextureCoords(int textureAxis, 473 Vector3f textureCenter, 474 float textureCoords[], 475 int textureIndices[], 476 float verts[], int indices[]) { 477 debugOutputLn(TRACE, "calculateSphericalTextureCoords"); 478 double s, t; 479 480 481 for (int i = 0; i < indices.length; ++i) { 482 float x = verts[3*indices[i]] - textureCenter.x; 483 float y = verts[3*indices[i]+1] - textureCenter.y; 484 float z = -(verts[3*indices[i]+2] + textureCenter.z); 485 if (textureAxis == 1){ // X Axis 486 s = xyztoh(z, x, -y); 487 t = xyztop(z,x,-y); 488 } 489 else if (textureAxis == 2) { // Y Axis 490 s = xyztoh(-x,y,z); 491 t = xyztop(-x,y,z); 492 } 493 else { // Z Axis 494 s = xyztoh(-x,z,-y); 495 t = xyztop(-x,z,-y); 496 } 497 s = 1.0 - s / (2*Math.PI); 498 t = -(.5 - t / Math.PI); 499 textureCoords[indices[i]*2] = (float)s; 500 textureCoords[indices[i]*2 + 1] = (float)t; 501 textureIndices[i] = indices[i]; 502 } 503 } 504 505 /** See the comments in calculateTextureCoordinates*/ calculateCylindricalTextureCoords(int textureAxis, Vector3f textureSize, Vector3f textureCenter, float textureCoords[], int textureIndices[], float verts[], int indices[])506 void calculateCylindricalTextureCoords(int textureAxis, 507 Vector3f textureSize, 508 Vector3f textureCenter, 509 float textureCoords[], 510 int textureIndices[], 511 float verts[], int indices[]) { 512 debugOutputLn(TRACE, "calculateCylindricalTextureCoords"); 513 debugOutputLn(VALUES, "axis, size, center, tc, ti, v, i = " + 514 textureAxis + ", " + 515 textureSize + ", " + 516 textureCenter + ", " + 517 textureCoords + ", " + 518 textureIndices + ", " + 519 verts + ", " + 520 indices); 521 double s, t; 522 523 debugOutputLn(VALUES, "Cyl Texture Coords:"); 524 for (int i = 0; i < indices.length; ++i) { 525 float x = verts[3*indices[i]] - textureCenter.x; 526 float y = verts[3*indices[i]+1] - textureCenter.y; 527 float z = -(verts[3*indices[i]+2] + textureCenter.z); 528 // Negate z value because we invert geom z's to swap handedness 529 if (textureAxis == 1) { // X axis 530 s = xyztoh(z,x,-y); 531 t = x / textureSize.x + .5; 532 } 533 else if (textureAxis == 2) { // Y Axis 534 s = xyztoh(-x,y,z); 535 t = y / textureSize.y + .5; 536 } 537 else { 538 s = xyztoh(-x,z,-y); 539 t = z / textureSize.z + .5; 540 } 541 s = 1.0 - s / (2*Math.PI); 542 textureCoords[indices[i]*2] = (float)s; 543 textureCoords[indices[i]*2 + 1] = (float)t; 544 textureIndices[i] = indices[i]; 545 debugOutputLn(VALUES, "x, y, z = " + 546 x + ", " + y + ", " + z + " " + 547 "s, t = " + s + ", " + t); 548 } 549 } 550 551 /** See the comments in calculateTextureCoordinates*/ calculatePlanarTextureCoords(int textureAxis, Vector3f textureSize, Vector3f textureCenter, float textureCoords[], int textureIndices[], float verts[], int indices[])552 void calculatePlanarTextureCoords(int textureAxis, Vector3f textureSize, 553 Vector3f textureCenter, 554 float textureCoords[], 555 int textureIndices[], 556 float verts[], int indices[]) { 557 debugOutputLn(TRACE, "calculatePlanarTextureCoords"); 558 debugOutputLn(VALUES, "size, center, axis = " + 559 textureSize + textureCenter + ", " + textureAxis); 560 float sx = 0, sz = 0, ty = 0, tz = 0; 561 double s, t; 562 563 if (textureAxis == 1) { // X Axis 564 sz = -1.0f / textureSize.z; // Negate because we negate z in geom 565 ty = 1.0f / textureSize.y; 566 } 567 else if (textureAxis == 2) { // Y Axis 568 sx = 1.0f / textureSize.x; 569 tz = -1.0f / textureSize.z; // Negate because we negate z in geom 570 } 571 else { // Z Axis 572 sx = 1.0f / textureSize.x; 573 ty = 1.0f / textureSize.y; 574 } 575 576 debugOutputLn(VALUES, "Planar Texture Coords:"); 577 for (int i = 0; i < indices.length; ++i) { 578 float x = verts[3*indices[i]] - textureCenter.x; 579 float y = verts[3*indices[i]+1] - textureCenter.y; 580 float z = verts[3*indices[i]+2] + textureCenter.z; 581 s = x*sx + z*sz + .5; 582 t = y*ty + z*tz + .5; 583 textureCoords[indices[i]*2] = (float)s; 584 textureCoords[indices[i]*2 + 1] = (float)t; 585 textureIndices[i] = indices[i]; 586 debugOutputLn(VALUES, "x, y, z = " + 587 x + ", " + y + ", " + z + " " + 588 "s, t = " + s + ", " + t); 589 } 590 } 591 592 getJava3dShape()593 Shape3D getJava3dShape() { 594 return objectShape; 595 } 596 getJava3dShapeList()597 Vector getJava3dShapeList() { 598 return objectShapeList; 599 } 600 601 } 602