/**************************************************************************\ * * This file is part of the Coin 3D visualization library. * Copyright (C) 1998-2007 by Systems in Motion. All rights reserved. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * ("GPL") version 2 as published by the Free Software Foundation. * See the file LICENSE.GPL at the root directory of this source * distribution for additional information about the GNU GPL. * * For using Coin with software that can not be combined with the GNU * GPL, and for taking advantage of the additional benefits of our * support services, please contact Systems in Motion about acquiring * a Coin Professional Edition License. * * See http://www.coin3d.org/ for more information. * * Systems in Motion, Postboks 1283, Pirsenteret, 7462 Trondheim, NORWAY. * http://www.sim.no/ sales@sim.no coin-support@coin3d.org * \**************************************************************************/ #ifdef HAVE_CONFIG_H #include #endif // HAVE_CONFIG_H #ifdef HAVE_3DS_IMPORT_CAPABILITIES // // 3DS File Loader for Open Inventor // // developed by PC John (peciva@fit.vutbr.cz) // // // Comments about 3ds files: Structure of the 3ds files is well known // (http://www.cyberloonies.com/3dsftk.html). However, it is often hard to // understand what is the informations in 3ds file about and how to // interpret them. For example, texture coordinates are not always // represented by OpenGL texture coordinates, and I can't find out // what is their meaning. // // // All loaded models are centered around [0,0,0] and normalized to // size 10 by default. // // If loading fails during reading 3ds file, file pointer position is // undefined. // // By default only error messages are generated. COIN_DEBUG_3DS environment // variable can be used to specify amount of debug messages: // 0 .. only error messages (default) // 1 .. warnings that usually concerns data parsed from the 3ds file // 2 .. print basic 3ds file structure info // 3 .. print everything // // // TODO list: // // - incomplete texture implementation - in 3ds files there is possible to // make materials with about 20 textures (diffuse color texture, specular // texture, bump-map texture,...) There is not a support for them in the // Inventor yet. // // - Texture coordinates are not always loaded right, because 3ds uses // many strange mapping modes. Deeper understanding of 3ds will be needed // to fix this. // // - Backface culling functionality is disabled because it does not work // right on some models. It looks like some models are CLOCKWISE and other // COUNTERCLOCKWISE. // // - unimplemented lights // // - per-vertex normals generation // // - investigate the color of the default material objects // // - ?environment? (ambient light, fog,...) // // - ?emissiveColor? (maybe MAT_SELF_ILLUM and MAT_SELF_ILPCT chunks) // // - some animations? // // - public API to control the loading // // #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "SoStream.h" #define DISABLE_BACKFACE_CULLING // File Header Chunks #define M3DMAGIC 0x4D4D #define M3D_VERSION 0x0002 #define MLIBMAGIC 0x3DAA #define CMAGIC 0xC23D // Major Section Chunks #define MDATA 0x3D3D #define KFDATA 0xB000 // Viewport Control Chunks #define VIEWPORT_LAYOUT 0x7001 #define VIEWPORT_DATA 0x7011 #define VIEWPORT_DATA_3 0x7012 #define VIEWPORT_SIZE 0x7020 // MDATA Section Chunks // Common Chunks #define COLOR_F 0x0010 #define COLOR_24 0x0011 #define LIN_COLOR_24 0x0012 #define LIN_COLOR_F 0x0013 #define INT_PERCENTAGE 0x0030 #define FLOAT_PERCENTAGE 0x0031 // Section Settings Chunks #define MESH_VERSION 0x3D3E #define MASTER_SCALE 0x0100 #define LO_SHADOW_BIAS 0x1400 #define HI_SHADOW_BIAS 0x1410 #define SHADOW_MAP_SIZE 0x1420 #define SHADOW_SAMPLES 0x1430 #define SHADOW_RANGE 0x1440 #define SHADOW_FILTER 0x1450 #define RAY_BIAS 0x1460 #define O_CONSTS 0x1500 #define AMBIENT_LIGHT 0x2100 // Background Settings Chunks #define BIT_MAP 0x1100 #define SOLID_BGND 0x1200 #define V_GRADIENT 0x1300 #define USE_BIT_MAP 0x1101 #define USE_SOLID_BGND 0x1201 #define USE_V_GRADIENT 0x1301 // Atmosphere Settings Chunks #define FOG 0x2200 #define FOG_BGND 0x2210 #define LAYER_FOG 0x2302 #define DISTANCE_CUE 0x2300 #define DCUE_BGND 0x2310 #define USE_FOG 0x2201 #define USE_LAYER_FOG 0x2303 #define USE_DISTANCE_CUE 0x2301 // Viewport Chunks #define DEFAULT_VIEW 0x3000 #define VIEW_TOP 0x3010 #define VIEW_BOTTOM 0x3020 #define VIEW_LEFT 0x3030 #define VIEW_RIGHT 0x3040 #define VIEW_FRONT 0x3050 #define VIEW_BACK 0x3060 #define VIEW_USER 0x3070 #define VIEW_CAMERA 0x3080 // Materials Chunks #define MAT_ENTRY 0xAFFF #define MAT_NAME 0xA000 #define MAT_AMBIENT 0xA010 #define MAT_DIFFUSE 0xA020 #define MAT_SPECULAR 0xA030 #define MAT_SHININESS 0xA040 #define MAT_SHIN2PCT 0xA041 #define MAT_TRANSPARENCY 0xA050 #define MAT_XPFALL 0xA052 #define MAT_USE_XPFALL 0xA240 #define MAT_REFBLUR 0xA053 #define MAT_SHADING 0xA100 #define MAT_USE_REFBLUR 0xA250 #define MAT_SELF_ILLUM 0xA080 #define MAT_TWO_SIDE 0xA081 #define MAT_DECAL 0xA082 #define MAT_ADDITIVE 0xA083 #define MAT_WIRE 0xA085 #define MAT_FACEMAP 0xA088 #define MAT_PHONGSOFT 0xA08C #define MAT_WIREABS 0xA08E #define MAT_WIRESIZE 0xA087 #define MAT_TEXMAP 0xA200 #define MAT_SXP_TEXT_DATA 0xA320 #define MAT_TEXMASK 0xA3EH #define MAT_SXP_TEXTMASK_DATA 0xA32A #define MAT_TEX2MAP 0xA33A #define MAT_SXP_TEXT2_DATA 0xA321 #define MAT_TEX2MASK 0xA340H #define MAT_SXP_TEXT2MASK_DATA 0xA32C #define MAT_OPACMAP 0xA210 #define MAT_SXP_OPAC_DATA 0xA322 #define MAT_OPACMASK 0xA342 #define MAT_SXP_OPACMASK_DATA 0xA32E #define MAT_BUMPMAP 0xA230 #define MAT_SXP_BUMP_DATA 0xA324 #define MAT_BUMPMASK 0xA344 #define MAT_SXP_BUMPMASK_DATA 0xA330 #define MAT_SPECMAP 0xA204 #define MAT_SXP_SPEC_DATA 0xA325 #define MAT_SPECMASK 0xA348 #define MAT_SXP_SPECMASK_DATA 0xA332 #define MAT_SHINMAP 0xA33C #define MAT_SXP_SHIN_DATA 0xA326 #define MAT_SHINMASK 0xA346 #define MAT_SXP_SHINMASK_DATA 0xA334 #define MAT_SELFIMAP 0xA33D #define MAT_SXP_SELFI_DATA 0xA328 #define MAT_SELFIMASK 0xA34A #define MAT_SXP_SELFIMASK_DATA 0xA336 #define MAT_REFLMAP 0xA220 #define MAT_REFLMASK 0xA34C #define MAT_SXP_REFLMASK_DATA 0xA338 #define MAT_ACUBIC 0xA310 #define MAT_MAPNAME 0xA300 #define MAT_MAP_TILING 0xA351 #define MAT_MAT_TEXBLUR 0xA353 #define MAT_MAP_USCALE 0xA354 #define MAT_MAP_VSCALE 0xA356 #define MAT_MAP_UOFFSET 0xA358 #define MAT_MAP_VOFFSET 0xA35A #define MAT_MAP_ANG 0xA35C #define MAT_MAP_COL1 0xA360 #define MAT_MAP_COL2 0xA362 #define MAT_MAP_RCOL 0xA364 #define MAT_MAP_GCOL 0xA366 #define MAT_MAP_BCOL 0xA368 // Object Chunks #define NAMED_OBJECT 0x4000 #define N_TRI_OBJECT 0x4100 #define POINT_ARRAY 0x4110 #define POINT_FLAG_ARRAY 0x4111 #define FACE_ARRAY 0x4120 #define MSH_MAT_GROUP 0x4130 #define SMOOTH_GROUP 0x4150 #define MSH_BOXMAP 0x4190 #define TEX_VERTS 0x4140 #define MESH_MATRIX 0x4160 #define MESH_COLOR 0x4165 #define MESH_TEXTURE_INFO 0x4170 #define PROC_NAME 0x4181 #define PROC_DATA 0x4182 #define N_DIRECT_LIGHT 0x4600 #define DL_OFF 0x4620 #define DL_OUTER_RANGE 0x465A #define DL_INNER_RANGE 0x4659 #define DL_MULTIPLIER 0x465B #define DL_EXCLUDE 0x4654 #define DL_ATTENUATE 0x4625 #define DL_SPOTLIGHT 0x4610 #define DL_SPOT_ROLL 0x4656 #define DL_SHADOWED 0x4630 #define DL_LOCAL_SHADOW2 0x4641 #define DL_SEE_CONE 0x4650 #define DL_SPOT_RECTANGULAR 0x4651 #define DL_SPOT_ASPECT 0x4657 #define DL_SPOT_PROJECTOR 0x4653 #define DL_SPOT_OVERSHOOT 0x4652 #define DL_RAY_BIAS 0x4658 #define DL_RAYSHAD 0x4627 #define N_CAMERA 0x4700 #define CAM_SEE_CONE 0x4710 #define CAM_RANGES 0x4720 #define OBJ_HIDDEN 0x4010 #define OBJ_VIS_LOFTER 0x4011 #define OBJ_DOESNT_CAST 0x4012 #define OBJ_DONT_RCVSHADOW 0x4017 #define OBJ_MATTE 0x4013 #define OBJ_FAST 0x4014 #define OBJ_PROCEDURAL 0x4015 #define OBJ_FROZEN 0x4016 struct tagFace; struct tagFaceGroup; struct tagMaterial; struct tagContext; typedef struct { SbVec3f point; SbVec2f texturePoint; SbList faceList; SbVec3f getNormal(tagContext *con, uint16_t myIndex) const; } Vertex; typedef struct tagFace { uint16_t v1,v2,v3; uint16_t flags; tagFaceGroup *faceGroup; uint32_t e12,e23,e31; SbBool isDegenerated; SbVec3f getNormal(tagContext *con) const; float getAngle(tagContext *con, uint16_t vertexIndex) const; SbVec3f getWeightedNormal(tagContext *con, uint16_t vertexIndex) const; void init(tagContext *con, uint16_t a, uint16_t b, uint16_t c, uint16_t f); } Face; typedef struct tagFaceGroup { tagMaterial *mat; SbList faceList; uint16_t numDegFaces; SbBool hasTexture2(tagContext *con); SoTexture2* getSoTexture2(tagContext *con); SbBool hasTexture2Transform(tagContext *con); SoTexture2Transform* getSoTexture2Transform(tagContext *con); SoMaterial* getSoMaterial(tagContext *con); SoNormal* createSoNormal(tagContext *con); SoCoordinate3* createSoCoordinate3_n(tagContext *con); SoTextureCoordinate2* createSoTextureCoordinate2_n(tagContext *con); SoTriangleStripSet* createSoTriStripSet_n(tagContext *con); SoIndexedTriangleStripSet* createSoIndexedTriStripSet_i(tagContext *con); } FaceGroup; // FIXME mortene: don't use "namespace" namespace DefaultFaceGroup { static tagMaterial* getMaterial(tagContext *con); static SbBool isEmpty(tagContext *con); static SoMaterial* getSoMaterial(tagContext *con); static SoNormal* createSoNormal(tagContext *con); static SoCoordinate3* createSoCoordinate3_n(tagContext *con); static SoTriangleStripSet* createSoTriStripSet_n(tagContext *con); static SoIndexedTriangleStripSet* createSoIndexedTriStripSet_i(tagContext *con); }; typedef struct { SbList faceList; } Edge; typedef struct tagMaterial { SbString name; SbColor ambient; SbColor diffuse; SbColor specular; float shininess; float transparency; SbString textureName; float uscale; float vscale; float uoffset; float voffset; SbBool twoSided; SoMaterial *matCache; SoTexture2 *texture2Cache; SoTexture2Transform *texture2TransformCache; void updateSoMaterial(int index, SoMaterial *m); SoMaterial* getSoMaterial(tagContext *con); SbBool hasTexture2(tagContext *con); SoTexture2* getSoTexture2(tagContext *con); SbBool hasTexture2Transform(tagContext *con); SoTexture2Transform* getSoTexture2Transform(tagContext *con); tagMaterial() : matCache(NULL), texture2Cache(NULL), texture2TransformCache(NULL) {} ~tagMaterial() { if (matCache) matCache->unref(); if (texture2Cache) texture2Cache->unref(); if (texture2TransformCache) texture2TransformCache->unref(); } } Material; typedef struct tagContext { // flags "What to load" int appendNormals; SbBool loadMaterials; SbBool loadTextures; SbBool loadObjNames; SbBool useIndexedTriSet; SbBool centerModel; // basic loading stuff SoStream &s; size_t stopPos; SoSeparator *root; SoSeparator *cObj; SbBool minMaxValid; float minX,maxX; float minY,maxY; float minZ,maxZ; char objectName[11]; // inconsistence with documentation, // object name must be 11 chars (3ds doc says 10) int totalVertices; int totalFaces; // material stuff SbList faceGroupList; SbList matList; Material defaultMat; Material *cMat; SbColor cColor; float cColorFloat; SbBool textureCoordsFound; // geometry stuff Vertex *vertexList; uint16_t numVertices; Face *faceList; uint16_t numFaces; uint16_t numDefaultDegFaces; Edge *edgeList; uint32_t numEdges; // scene graph generator stuff SoTexture2 *genCurrentTexture; SoMaterial *genCurrentMaterial; SoTexture2Transform *genCurrentTexTransform; int genTwoSided; // multiple-time used nodes SoTexture2 *genEmptyTexture; SoTexture2Transform *genEmptyTexTransform; SoShapeHints *genOneSidedHints; SoShapeHints *genTwoSidedHints; SoCoordinate3* createSoCoordinate3_i(tagContext *con) const; SoTextureCoordinate2* createSoTextureCoordinate2_i(tagContext *con) const; SoTexture2* genGetEmptyTexture(); SoTexture2Transform* genGetEmptyTexTransform(); SoShapeHints* genGetOneSidedHints(); SoShapeHints* genGetTwoSidedHints(); tagContext(SoStream &stream) : s(stream), root(NULL), cObj(NULL), totalVertices(0), totalFaces(0), vertexList(NULL), faceList(NULL), genEmptyTexture(NULL), genEmptyTexTransform(NULL), genOneSidedHints(NULL), genTwoSidedHints(NULL) {} ~tagContext() { for (int i=matList.getLength()-1; i>=1; i--) delete matList[i]; if (genEmptyTexture) genEmptyTexture->unref(); if (genEmptyTexTransform) genEmptyTexTransform->unref(); if (genOneSidedHints) genOneSidedHints->unref(); if (genTwoSidedHints) genTwoSidedHints->unref(); assert(!root && !cObj && !vertexList && !faceList && "You forgot to free some memory."); } } Context; #define CHUNK_DECL(_name_) static void _name_(Context *con) CHUNK_DECL(SkipChunk); CHUNK_DECL(LoadNamedObject); CHUNK_DECL(LoadNTriObject); CHUNK_DECL(LoadPointArray); CHUNK_DECL(LoadFaceArray); CHUNK_DECL(LoadMshMatGroup); CHUNK_DECL(LoadTexVerts); CHUNK_DECL(LoadMatEntry); CHUNK_DECL(LoadMatName); CHUNK_DECL(LoadMatAmbient); CHUNK_DECL(LoadMatDiffuse); CHUNK_DECL(LoadMatSpecular); CHUNK_DECL(LoadShininess); CHUNK_DECL(LoadTransparency); CHUNK_DECL(LoadMatTwoSide); CHUNK_DECL(LoadColor24); CHUNK_DECL(LoadLinColor24); CHUNK_DECL(LoadIntPercentage); CHUNK_DECL(LoadFloatPercentage); CHUNK_DECL(LoadM3DMagic); CHUNK_DECL(LoadM3DVersion); CHUNK_DECL(LoadMData); CHUNK_DECL(LoadMeshVersion); CHUNK_DECL(LoadTexMap); CHUNK_DECL(LoadMapName); CHUNK_DECL(LoadMapUScale); CHUNK_DECL(LoadMapVScale); CHUNK_DECL(LoadMapUOffset); CHUNK_DECL(LoadMapVOffset); static int coin_debug_3ds(); static SbBool read3dsFile(SoStream *in, SoSeparator *&root, int appendNormals, float creaseAngle, SbBool loadMaterials, SbBool loadTextures, SbBool loadObjNames, SbBool indexedTriSet, SbBool centerModel, float modelSize) { // read the stream header uint16_t header; *in >> header; if (header != M3DMAGIC) { SoDebugError::post("read3dsFile", "Bad 3ds stream: invalid header."); return FALSE; } // prepare Context structure Context con(*in); con.stopPos = 0; con.root = new SoSeparator; con.root->ref(); con.minMaxValid = FALSE; // customize loader if (appendNormals >= 2) appendNormals = 1; // per-vertex normals are // not currently supported con.appendNormals = appendNormals; con.loadMaterials = loadMaterials; con.loadTextures = loadTextures; con.loadObjNames = loadObjNames; con.useIndexedTriSet = indexedTriSet; con.centerModel = centerModel; // initialize materials and prepare default one con.matList.append(&con.defaultMat); // FIXME: the values for the default material are guessed one (maybe completely wrong) con.defaultMat.ambient = SbColor(0.6f, 0.6f, 0.6f); con.defaultMat.diffuse = SbColor(0.8f, 0.8f, 0.8f); con.defaultMat.specular = SbColor(0.f, 0.f, 0.f); con.defaultMat.shininess = 0.f; con.defaultMat.transparency = 0.f; con.defaultMat.twoSided = TRUE; // FIXME: is default material double sided? con.defaultMat.matCache = new SoMaterial; con.defaultMat.matCache->ref(); con.defaultMat.updateSoMaterial(0, con.defaultMat.matCache); con.cMat = 0; // build base scene graph #ifdef DISABLE_BACKFACE_CULLING // put default shape hints into the scene; // Backface culling is set to off and two sided lighting is enabled. SoShapeHints *sh = new SoShapeHints; sh->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; sh->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; con.root->addChild(sh); #endif // center model and scale it SoMatrixTransform *matrix = NULL; if (con.centerModel || modelSize != 0.f) { matrix = new SoMatrixTransform; con.root->addChild(matrix); } if (con.loadMaterials) { // material binding SoMaterialBinding *mb = new SoMaterialBinding; mb->value.setValue(SoMaterialBinding::OVERALL); con.root->addChild(mb); } if (con.appendNormals) { // normal binding SoNormalBinding *nb = new SoNormalBinding; if (con.appendNormals == 1) nb->value.setValue(SoNormalBinding::PER_FACE); else nb->value.setValue(SoNormalBinding::PER_VERTEX); con.root->addChild(nb); } if (con.loadTextures) { SoTextureCoordinateBinding *tb = new SoTextureCoordinateBinding; if (con.useIndexedTriSet) tb->value.setValue(SoTextureCoordinateBinding::PER_VERTEX_INDEXED); else tb->value.setValue(SoTextureCoordinateBinding::PER_VERTEX); con.root->addChild(tb); } // read the stream LoadM3DMagic(&con); // handle errors if (con.s.isBad()) { con.root->unref(); con.root = NULL; SoDebugError::post("read3dsFile", "3ds loading failed."); return FALSE; } if (con.centerModel || modelSize != 0.f) { SbMatrix m; // center model if (con.centerModel) m.setTranslate(SbVec3f(-(con.maxX+con.minX)/2.f, -(con.maxY+con.minY)/2.f, -(con.maxZ+con.minZ)/2.f)); else m.makeIdentity(); // set model size if (modelSize != 0.f) { SbMatrix m2; float max = SbMax(SbMax(con.maxX-con.minX, con.maxY-con.minY), con.maxZ-con.minZ); m2.setScale(modelSize/max); m.multRight(m2); } matrix->matrix.setValue(m); } // return root con.root->unrefNoDelete(); root = con.root; con.root = NULL; // debug info if (coin_debug_3ds() >= 2) SoDebugError::postInfo("read3dsFile", "File loading ok. Loaded %i vertices and %i faces.", con.totalVertices, con.totalFaces); return TRUE; } #define CHUNK(_name_) CHUNK_DECL(_name_) #define HEADER \ if (con->s.isBad()) \ return; \ \ uint32_t size; \ con->s >> size; \ uint32_t cpos = (uint32_t)con->s.getPos(); \ uint32_t stopPos = cpos + size - 6; #define READ_SUBCHUNKS(_subChunkSwitch_) \ while (con->s.getPos() < stopPos) { \ uint16_t chid; \ con->s >> chid; \ if (con->s.isBad()) \ break; \ \ switch (chid) { \ _subChunkSwitch_; \ default: \ SkipChunk(con); \ }; \ } CHUNK(SkipChunk) { HEADER; // move on the position of the next chunk con->s.setPos(stopPos); } CHUNK(LoadM3DMagic) { HEADER; con->stopPos = con->s.getPos() + size - 6; if (coin_debug_3ds() >= 2) SoDebugError::postInfo("LoadM3DMagic", "Loading 3ds stream (stream size: %i).", size); READ_SUBCHUNKS( case M3D_VERSION: LoadM3DVersion(con); break; case MDATA: LoadMData(con); break; case KFDATA: SkipChunk(con); break; ) } CHUNK(LoadM3DVersion) { HEADER; int32_t version; con->s >> version; if (version != 3 && coin_debug_3ds() >= 1) SoDebugError::postWarning("LoadM3DVersion", "Non-standard 3ds format version: %i.", version); } CHUNK(LoadMData) { HEADER; READ_SUBCHUNKS( case MESH_VERSION: LoadMeshVersion(con); break; case MAT_ENTRY: LoadMatEntry(con); break; case NAMED_OBJECT: LoadNamedObject(con); break; ) } CHUNK(LoadMeshVersion) { HEADER; int32_t version; con->s >> version; if (coin_debug_3ds() >= 3) SoDebugError::postInfo("LoadMeshVersion", "The 3ds file version: %i.", version); } CHUNK(LoadNamedObject) { HEADER; assert(!con->cObj && "Forgot to free the current object."); // read object name #if 0 // objectName moved to global structure Context char objectName[11]; // inconsistence with documentation, // object name must be 11 chars (3ds doc says 10) con->s.readZString(objectName, 11); #else con->s.readZString(con->objectName, 11); #endif READ_SUBCHUNKS( case N_TRI_OBJECT: LoadNTriObject(con); break; ) if (con->cObj) { // set object name if (con->loadObjNames && strlen(con->objectName) > 0) con->cObj->setName(con->objectName); // add cObj to the main scene graph if (con->cObj->getNumChildren() > 0) con->root->addChild(con->cObj); con->cObj->unref(); con->cObj = NULL; } } CHUNK(LoadNTriObject) { HEADER; assert(con->faceGroupList.getLength() == 0); con->numVertices = 0; con->numFaces = 0; con->numDefaultDegFaces = 0; con->textureCoordsFound = FALSE; READ_SUBCHUNKS( case POINT_ARRAY: LoadPointArray(con); break; case POINT_FLAG_ARRAY: SkipChunk(con); break; case FACE_ARRAY: LoadFaceArray(con); break; case MSH_MAT_GROUP: LoadMshMatGroup(con); break; case TEX_VERTS: LoadTexVerts(con); break; case MESH_MATRIX: SkipChunk(con); break; case MESH_COLOR: SkipChunk(con); break; case MESH_TEXTURE_INFO: SkipChunk(con); break; case PROC_NAME: SkipChunk(con); break; case PROC_DATA: SkipChunk(con); break; ) // debug info if (coin_debug_3ds() >= 3) SoDebugError::postInfo("LoadNTriObject", "Object %s parsed - vertices: %i, faces %i.", &con->objectName, con->numVertices, con->numFaces); con->totalVertices += con->numVertices; con->totalFaces += con->numFaces; // create object separator con->cObj = new SoSeparator; con->cObj->ref(); con->genCurrentTexture = NULL; con->genCurrentTexTransform = NULL; con->genCurrentMaterial = NULL; con->genTwoSided = -1; // create coordinates (in indexed mode) if (con->useIndexedTriSet) { // coordinates con->cObj->addChild(con->createSoCoordinate3_i(con)); // texture coordinates if (con->textureCoordsFound && con->loadTextures) con->cObj->addChild(con->createSoTextureCoordinate2_i(con)); } // create "default material" scene if (!DefaultFaceGroup::isEmpty(con)) { // default material has not a texture => switch it off #if 0 // non-optimized version if (con->loadTextures) con->cObj->addChild(new SoTexture2); #else if (con->loadTextures) { SoTexture2 *t = con->genGetEmptyTexture(); if (t != con->genCurrentTexture) { con->genCurrentTexture = t; con->cObj->addChild(t); } SoTexture2Transform *tt = con->genGetEmptyTexTransform(); if (tt != con->genCurrentTexTransform) { con->genCurrentTexTransform = tt; con->cObj->addChild(tt); } } #endif // materials #if 0 // non-optimized version if (con->loadMaterials) con->cObj->addChild(DefaultFaceGroup::getSoMaterial(con)); #else if (con->loadMaterials) { SoMaterial *m = DefaultFaceGroup::getSoMaterial(con); if (m != con->genCurrentMaterial) { con->genCurrentMaterial = m; con->cObj->addChild(con->genCurrentMaterial); } } #endif // normals if (con->appendNormals) con->cObj->addChild(DefaultFaceGroup::createSoNormal(con)); #ifndef DISABLE_BACKFACE_CULLING // single x double faces SbBool matTwoSided = DefaultFaceGroup::getMaterial(con)->twoSided; if (con->genTwoSided == -1 || (matTwoSided && (con->genTwoSided == 0)) || (!matTwoSided && (con->genTwoSided == 1))) { con->genTwoSided = (DefaultFaceGroup::getMaterial(con)->twoSided ? 1 : 0); con->cObj->addChild((con->genTwoSided == 1) ? con->genGetTwoSidedHints() : con->genGetOneSidedHints()); } #endif // load default material geometry if (con->useIndexedTriSet) { // indexed triStripSet con->cObj->addChild(DefaultFaceGroup::createSoIndexedTriStripSet_i(con)); } else { // coordinates con->cObj->addChild(DefaultFaceGroup::createSoCoordinate3_n(con)); // triStripSet con->cObj->addChild(DefaultFaceGroup::createSoTriStripSet_n(con)); } } // create non-default materials scene for (int i=0; ifaceGroupList.getLength(); i++) { FaceGroup *fg = con->faceGroupList[i]; // materials #if 0 // non-optimized version if (con->loadMaterials) con->cObj->addChild(fg->getSoMaterial(con)); #else if (con->loadMaterials) { SoMaterial *m = fg->getSoMaterial(con); if (m != con->genCurrentMaterial) { con->genCurrentMaterial = m; con->cObj->addChild(m); } } #endif // normals if (con->appendNormals) con->cObj->addChild(fg->createSoNormal(con)); // textures - optimized code if (con->loadTextures) { if (fg->hasTexture2(con)) { SoTexture2 *t = fg->getSoTexture2(con); if (t != con->genCurrentTexture) { con->genCurrentTexture = t; con->cObj->addChild(t); } } else { SoTexture2 *t = con->genGetEmptyTexture(); if (t != con->genCurrentTexture) { con->genCurrentTexture = t; con->cObj->addChild(t); } } // texture transform - optimized code if (fg->hasTexture2Transform(con)) { SoTexture2Transform *tt = fg->getSoTexture2Transform(con); if (tt != con->genCurrentTexTransform) { con->genCurrentTexTransform = tt; con->cObj->addChild(tt); } } else { SoTexture2Transform *tt = con->genGetEmptyTexTransform(); if (tt != con->genCurrentTexTransform) { con->genCurrentTexTransform = tt; con->cObj->addChild(tt); } } } #ifndef DISABLE_BACKFACE_CULLING // single x double faces if (con->genTwoSided == -1 || (fg->mat->twoSided && (con->genTwoSided == 0)) || (!fg->mat->twoSided && (con->genTwoSided == 1))) { con->genTwoSided = (fg->mat->twoSided ? 1 : 0); con->cObj->addChild((con->genTwoSided == 1) ? con->genGetTwoSidedHints() : con->genGetOneSidedHints()); } #endif if (con->useIndexedTriSet) { // indexed triStripSet con->cObj->addChild(fg->createSoIndexedTriStripSet_i(con)); } else { // coordinates con->cObj->addChild(fg->createSoCoordinate3_n(con)); // textures if (con->loadTextures && fg->hasTexture2(con)) con->cObj->addChild(fg->createSoTextureCoordinate2_n(con)); // triStripSet con->cObj->addChild(fg->createSoTriStripSet_n(con)); } } // clean up memory delete[] con->vertexList; con->vertexList = NULL; delete[] con->faceList; con->faceList = NULL; for (int j=con->faceGroupList.getLength()-1; j>=0; j--) { delete con->faceGroupList[j]; con->faceGroupList.removeFast(j); } } CHUNK(LoadPointArray) { HEADER; // number of vertices uint16_t num; con->s >> num; // alloc memory for Vertices assert(con->vertexList == NULL && "Forgot to free memory."); con->vertexList = new Vertex[num]; con->numVertices = num; // read points float x,y,z; for (int i=0; is >> x; con->s >> y; con->s >> z; con->vertexList[i].point = SbVec3f(x,z,-y); // 3ds has different //coordinate system. Z is up, and Y goes into the scene. } } CHUNK(LoadFaceArray) { HEADER; // number of faces uint16_t num; con->s >> num; // alloc memory for Faces assert(con->faceList == NULL && "Forgot to free memory."); con->faceList = new Face[num]; con->numFaces = num; // alloc memory for edges if (con->appendNormals == 2) { con->numEdges = 0; con->edgeList = new Edge[num*3]; } // make sure vertices are present yet if (num > 0) { if (con->vertexList == NULL) { assert(FALSE && "Vertex list not present."); con->s.setBadBit(); return; } } // read Faces uint16_t a,b,c; uint16_t flags; for (int i=0; is >> a; con->s >> b; con->s >> c; con->s >> flags; // STUB: decode flags (edge visibility and texture // wrapping, but first get idea what's the flags meaning) if (flags != 7 && coin_debug_3ds() >= 2) SoDebugError::postWarning("LoadFaceArray", "Non-standard face flags: %x, investigate it.\n", flags); con->faceList[i].init(con, a,c,b,flags); // we have to swap two // indices (b<=>c); It looks like 3ds uses clockwise // vertex ordering and we need it to be counter-clockwise. if (!con->minMaxValid) { con->minMaxValid = TRUE; con->minX = con->maxX = con->vertexList[a].point[0]; con->minY = con->maxY = con->vertexList[a].point[1]; con->minZ = con->maxZ = con->vertexList[a].point[2]; } #define PROCESS_VERTEX(_abc_, _xyz_, _index_) \ if (con->min##_xyz_ > con->vertexList[_abc_].point[_index_]) \ con->min##_xyz_ = con->vertexList[_abc_].point[_index_]; \ else \ if (con->max##_xyz_ < con->vertexList[_abc_].point[_index_]) \ con->max##_xyz_ = con->vertexList[_abc_].point[_index_] PROCESS_VERTEX(a, X, 0); PROCESS_VERTEX(a, Y, 1); PROCESS_VERTEX(a, Z, 2); PROCESS_VERTEX(b, X, 0); PROCESS_VERTEX(b, Y, 1); PROCESS_VERTEX(b, Z, 2); PROCESS_VERTEX(c, X, 0); PROCESS_VERTEX(c, Y, 1); PROCESS_VERTEX(c, Z, 2); #undef PROCESS_VERTEX } // report degenerated faces if (con->numDefaultDegFaces > 0 && coin_debug_3ds() >= 1) SoDebugError::postWarning("LoadFaceArray", "There are %i degenerated faces in the object named \"%s\" - removing them.", con->numDefaultDegFaces, &con->objectName); READ_SUBCHUNKS( case MSH_MAT_GROUP: LoadMshMatGroup(con); break; case SMOOTH_GROUP: SkipChunk(con); break; case MSH_BOXMAP: SkipChunk(con); break; ) } CHUNK(LoadMshMatGroup) { HEADER; if (!con->loadMaterials && !con->loadTextures) { // move on the position of the next chunk con->s.setPos(stopPos); return; } // material name char materialName[16]; con->s.readZString(materialName, 16); int matIndex; for (matIndex=1; matIndexmatList.getLength(); matIndex++) { if (strcmp(con->matList[matIndex]->name.getString(), materialName) == 0) break; } if (matIndex == con->matList.getLength()) { assert(FALSE && "Wrong material name in the file."); con->s.setBadBit(); return; } // create FaceGroup FaceGroup *mm = new FaceGroup; mm->mat = con->matList[matIndex]; con->faceGroupList.append(mm); mm->numDegFaces = 0; // number of faces uint16_t num; con->s >> num; // make sure faces are present yet if (num > 0) { if (con->faceList == NULL) { assert(FALSE && "Face list not present."); con->s.setBadBit(); return; } } // face indexes uint16_t faceMatIndex; for (int i=0; is >> faceMatIndex; if (faceMatIndex < con->numFaces) { assert(con->faceList[faceMatIndex].faceGroup == NULL && "3ds file error: Two materials on one face."); con->faceList[faceMatIndex].faceGroup = mm; mm->faceList.append(&con->faceList[faceMatIndex]); if (con->faceList[faceMatIndex].isDegenerated) { mm->numDegFaces++; con->numDefaultDegFaces--; } } else { assert(FALSE && "Wrong face material index."); con->s.setBadBit(); return; } } } CHUNK(LoadTexVerts) { HEADER; con->textureCoordsFound = TRUE; // number of faces uint16_t num; con->s >> num; // make sure vertices are present yet if (num > 0) { if (con->vertexList == NULL) { assert(FALSE && "Vertex list not present."); con->s.setBadBit(); return; } } // texture coordinates float u; float v; for (int i=0; is >> u; con->s >> v; con->vertexList[i].texturePoint = SbVec2f(u,v); } } CHUNK(LoadMatEntry) { HEADER; if (!con->loadMaterials && !con->loadTextures) { // move on the position of the next chunk con->s.setPos(stopPos); return; } assert(con->cMat == NULL); int index = con->matList.getLength(); con->cMat = new Material; con->matList.append(con->cMat); // default values con->cMat->name = ""; con->cMat->ambient = SbColor(0.f, 0.f, 0.f); con->cMat->diffuse = SbColor(0.f, 0.f, 0.f); con->cMat->specular = SbColor(0.f, 0.f, 0.f); con->cMat->shininess = 0.f; con->cMat->transparency = 0.f; con->cMat->twoSided = FALSE; READ_SUBCHUNKS( case MAT_NAME: LoadMatName(con); break; case MAT_AMBIENT: LoadMatAmbient(con); break; case MAT_DIFFUSE: LoadMatDiffuse(con); break; case MAT_SPECULAR: LoadMatSpecular(con); break; case MAT_SHININESS: LoadShininess(con); break; case MAT_TRANSPARENCY: LoadTransparency(con); break; case MAT_TWO_SIDE: LoadMatTwoSide(con); break; case MAT_TEXMAP: LoadTexMap(con); break; ) // create new SoMaterial con->cMat->matCache = new SoMaterial; con->cMat->matCache->ref(); con->cMat->updateSoMaterial(0, con->cMat->matCache); con->cMat = NULL; } CHUNK(LoadMatName) { HEADER; char materialName[16]; con->s.readZString(materialName, 16); con->cMat->name = materialName; } CHUNK(LoadMatAmbient) { HEADER; READ_SUBCHUNKS( case COLOR_24: LoadColor24(con); break; case LIN_COLOR_24: LoadLinColor24(con); break; ) con->cMat->ambient = con->cColor; } CHUNK(LoadMatDiffuse) { HEADER; READ_SUBCHUNKS( case COLOR_24: LoadColor24(con); break; case LIN_COLOR_24: LoadLinColor24(con); break; ) con->cMat->diffuse = con->cColor; } CHUNK(LoadMatSpecular) { HEADER; READ_SUBCHUNKS( case COLOR_24: LoadColor24(con); break; case LIN_COLOR_24: LoadLinColor24(con); break; ) con->cMat->specular = con->cColor; } CHUNK(LoadShininess) { HEADER; con->cColorFloat = 0.f; READ_SUBCHUNKS( case INT_PERCENTAGE: LoadIntPercentage(con); break; case FLOAT_PERCENTAGE: LoadFloatPercentage(con); break; ) con->cMat->shininess = con->cColorFloat; } CHUNK(LoadMatTwoSide) { HEADER; con->cMat->twoSided = TRUE; } CHUNK(LoadTransparency) { HEADER; con->cColorFloat = 0.f; READ_SUBCHUNKS( case INT_PERCENTAGE: LoadIntPercentage(con); break; case FLOAT_PERCENTAGE: LoadFloatPercentage(con); break; ) con->cMat->transparency = con->cColorFloat; } CHUNK(LoadTexMap) { HEADER; if (!con->loadTextures) { // move on the position of the next chunk con->s.setPos(stopPos); return; } con->cMat->uscale = con->cMat->vscale = 1.f; con->cMat->uoffset = con->cMat->voffset = 0.f; READ_SUBCHUNKS( case INT_PERCENTAGE: LoadIntPercentage(con); break; case FLOAT_PERCENTAGE: LoadFloatPercentage(con); break; case MAT_MAPNAME: LoadMapName(con); break; case MAT_MAP_USCALE: LoadMapUScale(con); break; case MAT_MAP_VSCALE: LoadMapVScale(con); break; case MAT_MAP_UOFFSET: LoadMapUOffset(con); break; case MAT_MAP_VOFFSET: LoadMapVOffset(con); break; ) if (con->cMat->textureName.getLength() > 0) { SoTexture2 *t = new SoTexture2; t->ref(); t->filename.setValue(con->cMat->textureName); t->model.setValue(SoTexture2::DECAL); con->cMat->texture2Cache = t; } if (con->cMat->uscale != 1.f || con->cMat->vscale != 1.f || con->cMat->uoffset != 0.f || con->cMat->voffset != 0.f) { SoTexture2Transform *tt = new SoTexture2Transform; tt->ref(); tt->scaleFactor.setValue(SbVec2f(con->cMat->uscale, con->cMat->vscale)); tt->translation.setValue(SbVec2f(con->cMat->uoffset, con->cMat->voffset)); con->cMat->texture2TransformCache = tt; } } CHUNK(LoadMapName) { HEADER; char textureName[13]; con->s.readZString(textureName, 13); con->cMat->textureName = textureName; } CHUNK(LoadMapUScale) { HEADER; con->s >> con->cMat->uscale; } CHUNK(LoadMapVScale) { HEADER; con->s >> con->cMat->vscale; } CHUNK(LoadMapUOffset) { HEADER; con->s >> con->cMat->uoffset; } CHUNK(LoadMapVOffset) { HEADER; con->s >> con->cMat->voffset; } CHUNK(LoadColor24) { HEADER; uint8_t r,g,b; con->s >> r; con->s >> g; con->s >> b; con->cColor = SbColor(float(r)/255.f, float(g)/255.f, float(b)/255.f); } CHUNK(LoadLinColor24) { HEADER; uint8_t r,g,b; con->s >> r; con->s >> g; con->s >> b; con->cColor = SbColor(float(r)/255.f, float(g)/255.f, float(b)/255.f); } CHUNK(LoadIntPercentage) { HEADER; int16_t i; con->s >> i; con->cColorFloat = float(i)/100.f; } CHUNK(LoadFloatPercentage) { HEADER; con->s >> (con->cColorFloat); } SbVec3f Vertex::getNormal(tagContext *con, uint16_t myIndex) const { int c = this->faceList.getLength(); SbVec3f normal(0.f,0.f,0.f); for (int i=0; ifaceList[i]->getWeightedNormal(con, myIndex); // ok to have a null vector here, it probably just means that we // have an empty triangle. (void) normal.normalize(); return normal; } SbVec3f Face::getNormal(tagContext *con) const { SbPlane plane(con->vertexList[v1].point, con->vertexList[v2].point, con->vertexList[v3].point); return plane.getNormal(); } float Face::getAngle(tagContext *con, uint16_t vertexIndex) const { int i1, i2; if (v1 == vertexIndex) { i1 = v2; i2 = v3; } else { i1 = v1; if (v2 == vertexIndex) i2 = v3; else { assert(v3 == vertexIndex); i2 = v2; } } SbVec3f vec1(con->vertexList[i1].point - con->vertexList[vertexIndex].point); SbVec3f vec2(con->vertexList[i2].point - con->vertexList[vertexIndex].point); SbRotation rot(vec1, vec2); float value; rot.getValue(vec1, value); return value; } SbVec3f Face::getWeightedNormal(tagContext *con, uint16_t vertexIndex) const { return getNormal(con) * getAngle(con, vertexIndex); } void Face::init(tagContext *con, uint16_t a, uint16_t b, uint16_t c, uint16_t f) { v1=a; v2=b; v3=c; flags=f; faceGroup = NULL; isDegenerated = (con->vertexList[v2].point-con->vertexList[v1].point).cross( con->vertexList[v3].point - con->vertexList[v1].point).sqrLength() == 0.f; if (isDegenerated) con->numDefaultDegFaces++; int n,i; Face *face; if (con->appendNormals == 2) { e12 = e23 = e31 = 0xffffffff; #define VERT_CODE(_nc_, _ns1_, _ns2_) \ n = con->vertexList[v##_nc_].faceList.getLength(); \ for (i=0; ivertexList[v##_nc_].faceList[i]; \ if (v##_ns1_ == face->v##_ns1_) { \ e##_nc_##_ns1_ = face->e##_nc_##_ns1_; \ con->edgeList[face->e##_nc_##_ns1_].faceList.append(this); \ } \ if (v##_ns2_ == face->v##_ns1_) { \ e##_ns2_##_nc_ = face->e##_nc_##_ns1_; \ con->edgeList[face->e##_nc_##_ns1_].faceList.append(this); \ } \ if (v##_ns1_ == face->v##_ns2_) { \ e##_nc_##_ns1_ = face->e##_ns2_##_nc_; \ con->edgeList[face->e##_ns2_##_nc_].faceList.append(this); \ } \ if (v##_ns2_ == face->v##_ns2_) { \ e##_ns2_##_nc_ = face->e##_ns2_##_nc_; \ con->edgeList[face->e##_ns2_##_nc_].faceList.append(this); \ } \ } \ con->vertexList[v##_nc_].faceList.append(this); VERT_CODE(1,2,3); VERT_CODE(2,3,1); VERT_CODE(3,1,2); #undef VERT_CODE if (e12 == 0xffffffff) e12 = con->numEdges++; if (e23 == 0xffffffff) e23 = con->numEdges++; if (e31 == 0xffffffff) e31 = con->numEdges++; } } SbBool FaceGroup::hasTexture2(tagContext *con) { return mat->hasTexture2(con); } SoTexture2* FaceGroup::getSoTexture2(tagContext *con) { return mat->getSoTexture2(con); } SbBool FaceGroup::hasTexture2Transform(tagContext *con) { return mat->hasTexture2Transform(con); } SoTexture2Transform* FaceGroup::getSoTexture2Transform(tagContext *con) { return mat->getSoTexture2Transform(con); } SoMaterial* FaceGroup::getSoMaterial(tagContext *con) { return mat->getSoMaterial(con); } SoNormal* FaceGroup::createSoNormal(tagContext *con) { SoNormal *normals = new SoNormal; int num = faceList.getLength(); if (con->appendNormals == 1) { normals->vector.setNum(num-numDegFaces); SbVec3f *v = normals->vector.startEditing(); for (int i=0; iisDegenerated) *(v++) = f->getNormal(con); } normals->vector.finishEditing(); } else { /* FIXME: This is incomplete implementation of per-vertex normal generator. normals->vector.setNum(num*3); SbVec3f *v = normals->vector.startEditing(); SbPlane p1, p2; for (int i=0; iedgeList[f->e12]; int fnum = e->faceList.getLength(); int j; for (j=0; i threshold) { // smooth towards this face vertnormal += normal; } } } *(v++) = con->vertexList[f->v1].getNormal(con, f->v1); *(v++) = con->vertexList[f->v2].getNormal(con, f->v2); *(v++) = con->vertexList[f->v3].getNormal(con, f->v3); } normals->vector.finishEditing();*/ } return normals; } SoCoordinate3* FaceGroup::createSoCoordinate3_n(tagContext *con) { assert(!con->useIndexedTriSet && "Improper use."); SoCoordinate3 *coords = new SoCoordinate3; int num = faceList.getLength(); coords->point.setNum((num-numDegFaces)*3); SbVec3f *c = coords->point.startEditing(); for (int i=0; iisDegenerated) { *(c++) = con->vertexList[f->v1].point; *(c++) = con->vertexList[f->v2].point; *(c++) = con->vertexList[f->v3].point; } } coords->point.finishEditing(); return coords; } SoTextureCoordinate2* FaceGroup::createSoTextureCoordinate2_n(tagContext *con) { assert(!con->useIndexedTriSet && "Improper use."); SoTextureCoordinate2 *tCoords = new SoTextureCoordinate2; int num = faceList.getLength(); tCoords->point.setNum((num-numDegFaces)*3); SbVec2f *c = tCoords->point.startEditing(); for (int i=0; iisDegenerated) { *(c++) = con->vertexList[f->v1].texturePoint; *(c++) = con->vertexList[f->v2].texturePoint; *(c++) = con->vertexList[f->v3].texturePoint; } } tCoords->point.finishEditing(); return tCoords; } SoTriangleStripSet* FaceGroup::createSoTriStripSet_n(tagContext *con) { assert(!con->useIndexedTriSet && "Improper use."); SoTriangleStripSet *triSet = new SoTriangleStripSet; int num = faceList.getLength() - numDegFaces; triSet->numVertices.setNum(num); int32_t *n = triSet->numVertices.startEditing(); for (int i=0; inumVertices.finishEditing(); return triSet; } SoIndexedTriangleStripSet* FaceGroup::createSoIndexedTriStripSet_i(tagContext *con) { assert(con->useIndexedTriSet && "Improper use."); SoIndexedTriangleStripSet *triSet = new SoIndexedTriangleStripSet; int num = faceList.getLength(); int i; // coords triSet->coordIndex.setNum((num-numDegFaces)*4); int32_t *c = triSet->coordIndex.startEditing(); for (i=0; iisDegenerated) { *(c++) = f->v1; *(c++) = f->v2; *(c++) = f->v3; *(c++) = SO_END_STRIP_INDEX; } } triSet->coordIndex.finishEditing(); // texture if (mat->hasTexture2(con) && con->loadTextures) { triSet->textureCoordIndex.setNum((num-numDegFaces)*4); int32_t *tc = triSet->textureCoordIndex.startEditing(); for (i=0; iisDegenerated) { *(tc++) = f->v1; *(tc++) = f->v2; *(tc++) = f->v3; *(tc++) = SO_END_STRIP_INDEX; } } triSet->textureCoordIndex.finishEditing(); } return triSet; } Material* DefaultFaceGroup::getMaterial(tagContext *con) { return &con->defaultMat; } SbBool DefaultFaceGroup::isEmpty(Context *con) { int num = con->numFaces; for (int i=0; ifaceList[i].faceGroup == NULL) return FALSE; return TRUE; } SoMaterial* DefaultFaceGroup::getSoMaterial(tagContext *con) { return getMaterial(con)->getSoMaterial(con); } SoNormal* DefaultFaceGroup::createSoNormal(tagContext *con) { SoNormal *normals = new SoNormal; int num = con->numFaces; int j = 0; if (con->appendNormals == 1) { normals->vector.setNum(num - con->numDefaultDegFaces); SbVec3f *v = normals->vector.startEditing(); for (int i=0; ifaceList[i]; if (f->faceGroup == NULL && !f->isDegenerated) { *(v++) = f->getNormal(con); j++; } } normals->vector.finishEditing(); normals->vector.setNum(j); } else { assert(FALSE); } return normals; } SoCoordinate3* DefaultFaceGroup::createSoCoordinate3_n(tagContext *con) { assert(!con->useIndexedTriSet && "Improper use."); SoCoordinate3 *coords = new SoCoordinate3; int num = con->numFaces; coords->point.setNum((num - con->numDefaultDegFaces) * 3); SbVec3f *c = coords->point.startEditing(); int j = 0; for (int i=0; ifaceList[i]; if (f->faceGroup == NULL && !f->isDegenerated) { *(c++) = con->vertexList[f->v1].point; *(c++) = con->vertexList[f->v2].point; *(c++) = con->vertexList[f->v3].point; j += 3; } } coords->point.finishEditing(); coords->point.setNum(j); return coords; } SoTriangleStripSet* DefaultFaceGroup::createSoTriStripSet_n(tagContext *con) { assert(!con->useIndexedTriSet && "Improper use."); SoTriangleStripSet *triSet = new SoTriangleStripSet; int num = con->numFaces - con->numDefaultDegFaces; int i; int j = 0; for (i=0; ifaceList[i]; if (f->faceGroup == NULL) j++; } j -= con->numDefaultDegFaces; triSet->numVertices.setNum(j); int32_t *n = triSet->numVertices.startEditing(); for (i=0; inumVertices.finishEditing(); return triSet; } SoIndexedTriangleStripSet* DefaultFaceGroup::createSoIndexedTriStripSet_i(tagContext *con) { assert(con->useIndexedTriSet && "Improper use."); SoIndexedTriangleStripSet *triSet = new SoIndexedTriangleStripSet; int num = con->numFaces; int i; int j = 0; for (i=0; ifaceList[i]; if (f->faceGroup == NULL) j++; } j -= con->numDefaultDegFaces; // coords triSet->coordIndex.setNum(j*4); int32_t *c = triSet->coordIndex.startEditing(); for (i=0; ifaceList[i]; if (f->faceGroup == NULL && !f->isDegenerated) { *(c++) = f->v1; *(c++) = f->v2; *(c++) = f->v3; *(c++) = SO_END_STRIP_INDEX; } } triSet->coordIndex.finishEditing(); return triSet; } void Material::updateSoMaterial(int index, SoMaterial *m) { m->ambientColor.set1Value(index, ambient); m->diffuseColor.set1Value(index, diffuse); m->specularColor.set1Value(index, specular); m->emissiveColor.set1Value(index, SbColor(0.f,0.f,0.f)); m->shininess.set1Value(index, shininess); m->transparency.set1Value(index, transparency); } SoMaterial* Material::getSoMaterial(Context *con) { return matCache; } SbBool Material::hasTexture2(tagContext *con) { return (texture2Cache != NULL); } SoTexture2* Material::getSoTexture2(tagContext *con) { return texture2Cache; } SbBool Material::hasTexture2Transform(tagContext *con) { return (texture2TransformCache != NULL); } SoTexture2Transform* Material::getSoTexture2Transform(tagContext *con) { return texture2TransformCache; } SoCoordinate3* Context::createSoCoordinate3_i(Context *con) const { assert(con->useIndexedTriSet && "Improper use."); SoCoordinate3 *coords = new SoCoordinate3; coords->point.setNum(con->numVertices); SbVec3f *c = coords->point.startEditing(); for (int i=0; inumVertices; i++) c[i] = con->vertexList[i].point; coords->point.finishEditing(); return coords; } SoTextureCoordinate2* Context::createSoTextureCoordinate2_i(tagContext *con) const { assert(con->useIndexedTriSet && "Improper use."); SoTextureCoordinate2 *tCoords = new SoTextureCoordinate2; tCoords->point.setNum(con->numVertices); SbVec2f *c = tCoords->point.startEditing(); for (int i=0; inumVertices; i++) c[i] = con->vertexList[i].texturePoint; tCoords->point.finishEditing(); return tCoords; } SoTexture2* Context::genGetEmptyTexture() { if (!genEmptyTexture) { genEmptyTexture = new SoTexture2; genEmptyTexture->ref(); } return genEmptyTexture; } SoTexture2Transform* Context::genGetEmptyTexTransform() { if (!genEmptyTexTransform) { genEmptyTexTransform = new SoTexture2Transform; genEmptyTexTransform->ref(); } return genEmptyTexTransform; } SoShapeHints* Context::genGetOneSidedHints() { if (!genOneSidedHints) { genOneSidedHints = new SoShapeHints; genOneSidedHints->ref(); // backface culling on, one-sided lighting genOneSidedHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; genOneSidedHints->shapeType = SoShapeHints::SOLID; } return genOneSidedHints; } SoShapeHints* Context::genGetTwoSidedHints() { if (!genTwoSidedHints) { genTwoSidedHints = new SoShapeHints; genTwoSidedHints->ref(); // backface culling off, two-sided lighting genTwoSidedHints->vertexOrdering = SoShapeHints::COUNTERCLOCKWISE; genTwoSidedHints->shapeType = SoShapeHints::UNKNOWN_SHAPE_TYPE; } return genTwoSidedHints; } /* Return value of COIN_DEBUG_3DS environment variable. */ static int coin_debug_3ds() { static int d = -1; if (d == -1) { const char * val = coin_getenv("COIN_DEBUG_3DS"); d = val ? atoi(val) : 0; } return d; } // ************************************************************************* // This is the only interface exposed to code outside this file. SbBool coin_3ds_read_file(SoInput *in, SoSeparator *&root, int appendNormals, float creaseAngle, SbBool loadMaterials, SbBool loadTextures, SbBool loadObjNames, SbBool indexedTriSet, SbBool centerModel, float modelSize) { SoStream s; s.setBinary(TRUE); s.setEndianOrdering(SoStream::LITTLE_ENDIAN_STREAM); s.wrapSoInput(in); return read3dsFile(&s, root, appendNormals, creaseAngle, loadMaterials, loadTextures, loadObjNames, indexedTriSet, centerModel, modelSize); } // ************************************************************************* #endif // HAVE_3DS_IMPORT_CAPABILITIES