// ********************************************************************************************************* void CDisplayerVisualEntity::drawBBox(const NLMISC::CMatrix &modelMatrix, const NLMISC::CAABBox &bbox, NLMISC::CRGBA colOverZ, NLMISC::CRGBA colUnderZ) { //H_AUTO(R2_CDisplayerVisualEntity_drawBBox) //nlwarning("bbox = (%f, %f, %f) - (%f, %f, %f), color = (%d, %d, %d, %d)", bbox.getMin().x, bbox.getMin().y, bbox.getMin().z, // bbox.getMax().x, bbox.getMax().y, bbox.getMax().z, (int) colOverZ.R, (int) colOverZ.G, (int) colOverZ.B, (int) colOverZ.A); // for z-precision, work with camera at (0, 0, 0) static volatile bool fixMatrixPos = false; CMatrix oldViewMatrix = Driver->getViewMatrix(); Driver->setModelMatrix(modelMatrix); CMatrix viewMat = oldViewMatrix; if (fixMatrixPos) { viewMat.setPos(CVector::Null); } Driver->setViewMatrix(viewMat); // draw below zbuffer UMaterial::ZFunc oldZfunc = GenericMat.getZFunc(); bool oldZWrite = GenericMat.getZWrite(); GenericMat.setZFunc(UMaterial::greater); GenericMat.setZWrite(false); ::drawBox(bbox.getMin(), bbox.getMax(), colUnderZ ); GenericMat.setZFunc(oldZfunc); GenericMat.setZWrite(oldZWrite); ::drawBox(bbox.getMin(), bbox.getMax(), colOverZ); Driver->setViewMatrix(oldViewMatrix); }
// *************************************************************************** void CVisualCollisionMesh::CStaticGrid::create(uint nbQuads, uint nbElts, const NLMISC::CAABBox &gridBBox) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ nlassert(nbQuads>0 && isPowerOf2(nbQuads)); // init the grid _GridSize= nbQuads; _GridSizePower= getPowerOf2(nbQuads); _Grid.resize(_GridSize*_GridSize); // start with 0 elt in each case memset(_Grid.getPtr(), 0, _Grid.size() * sizeof(CCase)); // init the Elt Build _EltBuild.resize(nbElts); // total size is 0 _GridDataSize= 0; // bbox init _GridPos= gridBBox.getMin(); _GridScale= gridBBox.getSize(); _GridScale.x= _GridSize / _GridScale.x; _GridScale.y= _GridSize / _GridScale.y; // reset intersection data _ItSession= 0; }
// *************************************************************************** void CQuadGridClipClusterQTreeNode::insertModel(const NLMISC::CAABBox &worldBBox, uint distSetup, CTransformShape *model) { // if leaf node, insert the model in the list if( LeafNode ) { if(Empty) { Empty= false; BBox= worldBBox; } else { // extend the bbox with 2 corners of the incoming bbox (sufficient for an AABBox). BBox.extend( worldBBox.getCenter() + worldBBox.getHalfSize() ); BBox.extend( worldBBox.getCenter() - worldBBox.getHalfSize() ); } // insert in list ListNode.insertModel(distSetup, model); } // else, recurs insert in branch else { // choose what son according to pivot. CQuadGridClipClusterQTreeNode *selectSon; if( worldBBox.getCenter().y<PivotBBox.getCenter().y ) { if( worldBBox.getCenter().x<PivotBBox.getCenter().x ) selectSon= Sons[NL3D_QCC_LEFT_DOWN]; else selectSon= Sons[NL3D_QCC_RIGHT_DOWN]; } else { if( worldBBox.getCenter().x<PivotBBox.getCenter().x ) selectSon= Sons[NL3D_QCC_LEFT_UP]; else selectSon= Sons[NL3D_QCC_RIGHT_UP]; } // insert in this cluster selectSon->insertModel(worldBBox, distSetup, model); // extend my boox according to this son cluster. if(Empty) { Empty= false; BBox= selectSon->BBox; } else { // extend the bbox with 2 corners of the son bbox (sufficient for an AABBox). BBox.extend( selectSon->BBox.getCenter() + selectSon->BBox.getHalfSize() ); BBox.extend( selectSon->BBox.getCenter() - selectSon->BBox.getHalfSize() ); } } // update bboxExt BBoxExt= BBox; }
// ********************************************************************************************************* NLMISC::CAABBox CDisplayerVisualEntity::getSelectBox() const { //H_AUTO(R2_NLMISC_CAABBox ) if (!_Entity) { if (_PlaceHolder) { return _PlaceHolder->getSelectBox(); } return CDisplayerVisual::getSelectBox(); } if (getSelectionType() == ISelectableObject::LocalSelectBox || getSelectionType() == ISelectableObject::GroundProjected ) { if (_Entity->isAsyncLoading()) { NLMISC::CAABBox result; result.setCenter(CVector::Null); result.setHalfSize(CVector::Null); return result; } return getEditor().getLocalSelectBox(*_Entity); } else { return getEditor().getSelectBox(*_Entity); } }
void CParticleSystemPage::updatePrecomputedBBoxParams() { NLMISC::CAABBox b; _Node->getPSPointer()->computeBBox(b); _ui.xDoubleSpinBox->setValue(b.getHalfSize().x); _ui.yDoubleSpinBox->setValue(b.getHalfSize().y); _ui.zDoubleSpinBox->setValue(b.getHalfSize().z); }
void CParticleSystemPage::decBbox() { NLMISC::CAABBox b; _Node->getPSPointer()->computeBBox(b); b.setHalfSize(0.9f * b.getHalfSize()); _Node->getPSPointer()->setPrecomputedBBox(b); updatePrecomputedBBoxParams(); }
// ********************************************************************************************************* void CDisplayerVisualShape::drawBBox(NLMISC::CRGBA color) const { //H_AUTO(R2_CDisplayerVisualShape_drawBBox) if (getRotateInProgress()) return; // no drawn while drawing (bbox moved one frame too late, must solve this) NLMISC::CAABBox bbox; _Instance.getShapeAABBox(bbox); Driver->setModelMatrix(_BBoxMatrix); ::drawBox(bbox.getMin(), bbox.getMax(), color); }
// ********************************************************************************************************* NLMISC::CAABBox CDisplayerVisualShape::getSelectBox() const { //H_AUTO(R2_CDisplayerVisualShape_getSelectBox) if (_Instance.empty()) return CDisplayerVisual::getSelectBox(); // TODO nico : cache the bbox NLMISC::CAABBox bbox; _Instance.getShapeAABBox(bbox); bbox.setMinMax(_Scale * bbox.getMin(), _Scale * bbox.getMax()); return bbox; }
void CParticleSystemPage::setZBbox(double value) { NLMISC::CAABBox b; _Node->getPSPointer()->computeBBox(b); NLMISC::CVector h; h.x = b.getHalfSize().x; h.y = b.getHalfSize().y; h.z = value; b.setHalfSize(h); _Node->getPSPointer()->setPrecomputedBBox(b); }
// *************************************************************************** void CTransformShape::getAABBox(NLMISC::CAABBox &bbox) const { if(Shape) { Shape->getAABBox(bbox); } else { bbox.setCenter(CVector::Null); bbox.setHalfSize(CVector::Null); } }
// *************************************************************************** uint CVisualCollisionMesh::CStaticGrid::select(const NLMISC::CAABBox &bbox, std::vector<uint16> &res) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ // increment the intersection session _ItSession++; // enlarge the result array as needed if(res.size()<_Sessions.size()) res.resize(_Sessions.size()); // the number of selected element uint numSel= 0; // compute the 2D bbox CVector minp= bbox.getMin() - _GridPos; CVector maxp= bbox.getMax() - _GridPos; sint xmin= (sint)floorf(minp.x*_GridScale.x); sint ymin= (sint)floorf(minp.y*_GridScale.y); sint xmax= (sint)ceilf(maxp.x*_GridScale.x); sint ymax= (sint)ceilf(maxp.y*_GridScale.y); clamp(xmin, 0, (sint)_GridSize-1); clamp(ymin, 0, (sint)_GridSize-1); clamp(xmax, xmin+1, (sint)_GridSize); clamp(ymax, ymin+1, (sint)_GridSize); // for each case touched, increment NumElts for(uint y=ymin;y<(uint)ymax;y++) { for(uint x=xmin;x<(uint)xmax;x++) { CCase &gcase= _Grid[(y<<_GridSizePower)+x]; // for each element in this case for(uint i= gcase.Start;i<gcase.Start + gcase.NumElts;i++) { uint elt= _GridData[i]; // if not alread inserted in the dest if(_Sessions[elt]!=_ItSession) { // mark as intersected _Sessions[elt]= _ItSession; // append res[numSel++]= elt; } } } } // return the number of selected elements return numSel; }
///=========================================================================== void CParticleSystemShape::getAABBox(NLMISC::CAABBox &bbox) const { if (!_UsePrecomputedBBox) { bbox.setCenter(NLMISC::CVector::Null); bbox.setHalfSize(NLMISC::CVector(1, 1, 1)); } else { bbox = _PrecomputedBBox; } }
// ********************************************************************************************************* bool CDisplayerVisualEntity::isInProjection(const NLMISC::CVector2f &pos) const { //H_AUTO(R2_CDisplayerVisualEntity_isInProjection) if (getActualSelectionDisplayMode() == CircleSelection) return false; nlassert(getSelectionType() == LocalSelectBox || getSelectionType() == GroundProjected); CVector localPos = getInvertedMatrix() * pos; NLMISC::CAABBox selectBox = getSelectBox(); CVector pmin = selectBox.getMin(); CVector pmax = selectBox.getMax(); if (localPos.x < pmin.x || localPos.x > pmax.x || localPos.y < pmin.y || localPos.y > pmax.y) return false; return true; }
// ********************************************************************************************************* void CDisplayerVisual::evalIconInScenePos(NLMISC::CVector &dest) const { //H_AUTO(R2_CDisplayerVisual_evalIconInScenePos) NLMISC::CAABBox selectBox = getSelectBox(); float radius = std::max(selectBox.getHalfSize().x, selectBox.getHalfSize().y); // use middle front of bbox for icon pos NLMISC::CVector result = getWorldPos().asVector() - radius * MainCam.getMatrix().getJ() + selectBox.getHalfSize().z * CVector::K; static volatile bool wantAssert = true; if (!isValidDouble(result.x) || !isValidDouble(result.y) ||!isValidDouble(result.z)) { nlassert(!wantAssert); return; } dest = result; }
// *************************************************************************** void CVisualCollisionMesh::CStaticGrid::add(uint16 id, const NLMISC::CAABBox &bbox) { /* *********************************************** * WARNING: This Class/Method must be thread-safe (ctor/dtor/serial): no static access for instance * It can be loaded/called through CAsyncFileManager for instance * ***********************************************/ CVector minp= bbox.getMin() - _GridPos; CVector maxp= bbox.getMax() - _GridPos; // compute the 2D bbox sint xmin= (sint)floorf(minp.x*_GridScale.x); sint ymin= (sint)floorf(minp.y*_GridScale.y); sint xmax= (sint)ceilf(maxp.x*_GridScale.x); sint ymax= (sint)ceilf(maxp.y*_GridScale.y); clamp(xmin, 0, (sint)_GridSize-1); clamp(ymin, 0, (sint)_GridSize-1); clamp(xmax, xmin+1, (sint)_GridSize); clamp(ymax, ymin+1, (sint)_GridSize); // set in the elt build _EltBuild[id].X0= xmin; _EltBuild[id].Y0= ymin; _EltBuild[id].X1= xmax; _EltBuild[id].Y1= ymax; // for each case touched, increment NumElts for(uint y=ymin;y<(uint)ymax;y++) { for(uint x=xmin;x<(uint)xmax;x++) { _Grid[(y<<_GridSizePower)+x].NumElts++; _GridDataSize++; } } }
//============================================ void CWaveMakerShape::getAABBox(NLMISC::CAABBox &bbox) const { // its just a point bbox.setCenter(NLMISC::CVector::Null); bbox.setHalfSize(NLMISC::CVector::Null); }
///========================================================= int main (int argc, char* argv[]) { // Filter addSearchPath NLMISC::createDebug(); InfoLog->addNegativeFilter ("adding the path"); TShapeMap shapeMap; // Check number of args if (argc<5) { // Help message printf ("zone_dependencies [properties.cfg] [firstZone.zone] [lastzone.zone] [output_dependencies.cfg]\n"); } else { NL3D::registerSerial3d(); // Light direction CVector lightDirection; // Config file handler try { // Read the properties file CConfigFile properties; // Load and parse the properties file properties.load (argv[1]); // Get the light direction CConfigFile::CVar &sun_direction = properties.getVar ("sun_direction"); lightDirection.set (sun_direction.asFloat(0), sun_direction.asFloat(1), sun_direction.asFloat(2)); lightDirection.normalize(); // Get the search pathes CConfigFile::CVar &search_pathes = properties.getVar ("search_pathes"); uint path; for (path = 0; path < (uint)search_pathes.size(); path++) { // Add to search path CPath::addSearchPath (search_pathes.asString(path)); } /* CConfigFile::CVar &ig_path = properties.getVar ("ig_path"); NLMISC::CPath::addSearchPath(ig_path.asString(), true, true); CConfigFile::CVar &shapes_path = properties.getVar ("shapes_path"); NLMISC::CPath::addSearchPath(shapes_path.asString(), true, true); */ CConfigFile::CVar &compute_dependencies_with_igs = properties.getVar ("compute_dependencies_with_igs"); bool computeDependenciesWithIgs = compute_dependencies_with_igs.asInt() != 0; // Get the file extension string ext=getExt (argv[2]); // Get the file directory string dir=getDir (argv[2]); // Get output extension string outExt=getExt (argv[4]); // Get output directory string outDir=getDir (argv[4]); // Get the first and last name string firstName=getName (argv[2]); string lastName=getName (argv[3]); // Get the coordinates uint16 firstX, firstY; uint16 lastX, lastY; if (getZoneCoordByName (firstName.c_str(), firstX, firstY)) { // Last zone if (getZoneCoordByName (lastName.c_str(), lastX, lastY)) { // Take care if (lastX<firstX) { uint16 tmp=firstX; firstX=lastX; lastX=firstX; } if (lastY<firstY) { uint16 tmp=firstY; firstY=lastY; lastY=firstY; } // Min z float minZ=FLT_MAX; // Make a quad grid CQuadGrid<CZoneDescriptorBB> quadGrid; quadGrid.create (256, 100); // The dependencies list vector< CZoneDependencies > dependencies; dependencies.resize ((lastX-firstX+1)*(lastY-firstY+1)); // Ryzom specific: build bbox for villages TString2LightingBBox villagesBBox; computeIGBBoxFromContinent(properties, shapeMap, villagesBBox); // Insert each zone in the quad tree sint y, x; for (y=firstY; y<=lastY; y++) for (x=firstX; x<=lastX; x++) { // Progress progress ("Build bounding boxes", (float)(x+y*lastX)/(float)(lastX*lastY)); // Make a zone file name string zoneName; getZoneNameByCoord (x, y, zoneName); // Open the file CIFile file; if (file.open (dir+zoneName+ext)) { // The zone CZone zone; try { // Serial the zone file.serial (zone); /// get bbox from the ig of this zone CLightingBBox igBBox; if (computeDependenciesWithIgs) { computeZoneIGBBox(zoneName.c_str(), igBBox, shapeMap, villagesBBox); } // Create a zone descriptor NLMISC::CAABBox zoneBox; zoneBox.setCenter(zone.getZoneBB().getCenter()); zoneBox.setHalfSize(zone.getZoneBB().getHalfSize()); CLightingBBox zoneLBox; zoneLBox.OccludingBox = zoneLBox.ReceivingBox = zoneBox; // can't be void zoneLBox.makeUnion(igBBox); nlassert(!zoneLBox.ReceivingBox.IsVoid); // CZoneDescriptorBB zoneDesc; zoneDesc.X=x; zoneDesc.Y=y; zoneDesc.BBox=zoneLBox.ReceivingBox.Box; // if (!zoneLBox.OccludingBox.IsVoid) { quadGrid.insert (zoneLBox.ReceivingBox.Box.getMin(), zoneLBox.ReceivingBox.Box.getMax(), zoneDesc); } // Insert in the dependencies // Index uint index=(x-firstX)+(y-firstY)*(lastX-firstX+1); // Loaded dependencies[index].Loaded=true; dependencies[index].X=x; dependencies[index].Y=y; dependencies[index].BBox=zoneLBox.OccludingBox.Box; // ZMin float newZ=zoneLBox.ReceivingBox.Box.getMin().z; if (newZ<minZ) minZ=newZ; } catch (Exception& e) { // Error handling nlwarning ("ERROR in file %s, %s", (dir+zoneName+ext).c_str(), e.what ()); } } } // Now select each zone in others and make a depencies list for (y=firstY; y<=lastY; y++) for (x=firstX; x<=lastX; x++) { // Progress progress ("Compute dependencies", (float)(x+y*lastX)/(float)(lastX*lastY)); // Index uint index=(x-firstX)+(y-firstY)*(lastX-firstX+1); // Loaded ? if (dependencies[index].Loaded) { // Min max vectors CVector vMin (dependencies[index].BBox.getMin()); CVector vMax (dependencies[index].BBox.getMax()); // Make a corner array CVector corners[4] = { CVector (vMin.x, vMin.y, vMax.z), CVector (vMax.x, vMin.y, vMax.z), CVector (vMax.x, vMax.y, vMax.z), CVector (vMin.x, vMax.y, vMax.z) }; // Extended bbox CAABBox bBox=dependencies[index].BBox.getAABBox(); // For each corner uint corner; for (corner=0; corner<4; corner++) { // Target position CVector target; if (lightDirection.z!=0) { // Not horizontal target target=corners[corner]+(lightDirection*((minZ-corners[corner].z)/lightDirection.z)); } else { // Horizontal target, select 500 meters around. target=(500*lightDirection)+corners[corner]; } // Extend the bbox bBox.extend (target); } // Clear quad tree selection quadGrid.clearSelection (); // Select quadGrid.select (bBox.getMin(), bBox.getMax()); // Check selection CQuadGrid<CZoneDescriptorBB>::CIterator it=quadGrid.begin(); while (it!=quadGrid.end()) { // Index uint targetIndex=((*it).X-firstX)+((*it).Y-firstY)*(lastX-firstX+1); // Not the same if (targetIndex!=index) { // Target min z float targetMinZ=dependencies[targetIndex].BBox.getMin().z; if (targetMinZ<vMax.z) { // Min z inf to max z ? // Target optimized bbox CAABBox bBoxOptimized=dependencies[index].BBox.getAABBox(); // For each corner for (corner=0; corner<4; corner++) { // Target position CVector target; if (lightDirection.z!=0) { // Not horizontal target target=corners[corner]+(lightDirection*((targetMinZ-corners[corner].z) /lightDirection.z)); } else { // Horizontal target, select 500 meters around. target=(500*lightDirection)+corners[corner]; } // Extend the bbox bBoxOptimized.extend (target); } // Check it more presisly //if ((*it).BBox.intersect (bBoxOptimized)) if ((*it).BBox.intersect (bBox)) { // Insert in the set dependencies[targetIndex].Dependences.insert (CZoneDependenciesValue (x, y)); } } } // Next selected it++; } } } // For each zone for (y=firstY; y<=lastY; y++) for (x=firstX; x<=lastX; x++) { // Progress progress ("Save depend files", (float)(x+y*lastX)/(float)(lastX*lastY)); // Index uint index=(x-firstX)+(y-firstY)*(lastX-firstX+1); // Loaded ? if (dependencies[index].Loaded) { // Make a file name string outputFileName; getZoneNameByCoord(x, y, outputFileName); outputFileName=outDir+outputFileName+outExt; // Write the dependencies file FILE *outputFile; if ((outputFile=fopen (toLower (outputFileName).c_str(), "w"))) { // Add a dependency entry fprintf (outputFile, "dependencies =\n{\n"); // Add dependent zones set<CZoneDependenciesValue>::iterator ite=dependencies[index].Dependences.begin(); while (ite!=dependencies[index].Dependences.end()) { // Name of the dependent zone std::string zoneName; getZoneNameByCoord(ite->first, ite->second, zoneName); // Write it string message="\t\""+zoneName+"\""; fprintf (outputFile, "%s", toLower (message).c_str()); // Next ite; ite++; if (ite!=dependencies[index].Dependences.end()) fprintf (outputFile, ",\n"); } // Close the variable fprintf (outputFile, "\n};\n\n"); } else { nlwarning ("ERROR can't open %s for writing.\n", outputFileName.c_str()); } // Close the file fclose (outputFile); } } } else { // Not valid nlwarning ("ERROR %s is not a valid zone name.\n", lastName.c_str()); } } else { // Not valid nlwarning ("ERROR %s is not a valid zone name.\n", firstName.c_str()); } } catch (Exception &ee) { nlwarning ("ERROR %s\n", ee.what()); } } return 0; }
//*************************************************************************************************************** void CFlareShape::getAABBox(NLMISC::CAABBox &bbox) const { // the flare himself is a point bbox.setCenter(CVector::Null); bbox.setHalfSize(CVector::Null); }
CInstanceGroup* CExportNel::buildInstanceGroup(const vector<INode*>& vectNode, vector<INode*>& resultInstanceNode, TimeValue tvTime) { // Extract from the node the name, the transformations and the parent CInstanceGroup::TInstanceArray aIGArray; uint32 i, nNumIG; uint32 j,k,m; aIGArray.empty (); resultInstanceNode.empty (); aIGArray.resize (vectNode.size()); resultInstanceNode.resize (vectNode.size()); int nNbInstance = 0; for (i = 0; i < vectNode.size(); ++i) { INode *pNode = vectNode[i]; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 0) // If not an accelerator if (!RPO::isZone (*pNode, tvTime)) if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime)) { ++nNbInstance; } } // Check integrity of the hierarchy and set the parents std::vector<INode*>::const_iterator it = vectNode.begin(); nNumIG = 0; for (i = 0; i < (sint)vectNode.size(); ++i, ++it) { INode *pNode = *it; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 0) // If not an accelerator if (!RPO::isZone( *pNode, tvTime )) if (CExportNel::isMesh( *pNode, tvTime ) || CExportNel::isDummy(*pNode, tvTime)) { aIGArray[nNumIG].DontAddToScene = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_DONT_ADD_TO_SCENE, 0)?true:false; aIGArray[nNumIG].InstanceName = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_INSTANCE_NAME, ""); resultInstanceNode[nNumIG] = pNode; if (aIGArray[nNumIG].InstanceName == "") // no instance name was set, takes the node name instead { aIGArray[nNumIG].InstanceName = pNode->GetName(); } // Visible? always true, but if special flag for camera collision sint appDataCameraCol= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_CAMERA_COLLISION_MESH_GENERATION, 0); aIGArray[nNumIG].Visible= appDataCameraCol!=3; INode *pParent = pNode->GetParentNode(); // Set the DontCastShadow flag. aIGArray[nNumIG].DontCastShadow= pNode->CastShadows()==0; // Set the Special DontCastShadow flag. aIGArray[nNumIG].DontCastShadowForInterior= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_LIGHT_DONT_CAST_SHADOW_INTERIOR, BST_UNCHECKED)?true:false; aIGArray[nNumIG].DontCastShadowForExterior= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_LIGHT_DONT_CAST_SHADOW_EXTERIOR, BST_UNCHECKED)?true:false; // Is the pNode has the root node for parent ? if( pParent->IsRootNode() == 0 ) { // Look if the parent is in the selection int nNumIG2 = 0; for (j = 0; j < vectNode.size(); ++j) { INode *pNode2 = vectNode[j]; int nAccelType2 = CExportNel::getScriptAppData (pNode2, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType2&3) == 0) // If not an accelerator if (!RPO::isZone( *pNode2, tvTime )) if (CExportNel::isMesh( *pNode2, tvTime )) { if (pNode2 == pParent) break; ++nNumIG2; } } if (nNumIG2 == nNbInstance) { // The parent is not selected ! link to root aIGArray[nNumIG].nParent = -1; } else { aIGArray[nNumIG].nParent = nNumIG2; } } else { aIGArray[nNumIG].nParent = -1; } ++nNumIG; } } aIGArray.resize( nNumIG ); resultInstanceNode.resize( nNumIG ); // Build the array of node vGlobalPos = CVector(0,0,0); nNumIG = 0; it = vectNode.begin(); for (i = 0; i < (sint)vectNode.size(); ++i, ++it) { INode *pNode = *it; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 0) // If not an accelerator if (!RPO::isZone (*pNode, tvTime)) if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime)) { CVector vScaleTemp; CQuat qRotTemp; CVector vPosTemp; // Get Nel Name for the object. aIGArray[nNumIG].Name= CExportNel::getNelObjectName(*pNode); //Get the local transformation matrix Matrix3 nodeTM = pNode->GetNodeTM(0); INode *pParent = pNode->GetParentNode(); Matrix3 parentTM = pParent->GetNodeTM(0); Matrix3 localTM = nodeTM*Inverse(parentTM); // Extract transformations CExportNel::decompMatrix (vScaleTemp, qRotTemp, vPosTemp, localTM); aIGArray[nNumIG].Rot = qRotTemp; aIGArray[nNumIG].Pos = vPosTemp; aIGArray[nNumIG].Scale = vScaleTemp; vGlobalPos += vPosTemp; ++nNumIG; } } // todo Make this work (precision): /* vGlobalPos = vGlobalPos / nNumIG; for (i = 0; i < nNumIG; ++i) aIGArray[i].Pos -= vGlobalPos; */ vGlobalPos = CVector(0,0,0); // Temporary !!! // Accelerator Portal/Cluster part //================= // Creation of all the clusters vector<CCluster> vClusters; it = vectNode.begin(); for (i = 0; i < (sint)vectNode.size(); ++i, ++it) { INode *pNode = *it; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, NEL3D_APPDATA_ACCEL_DEFAULT); bool bFatherVisible = nAccelType&NEL3D_APPDATA_ACCEL_FATHER_VISIBLE?true:false; bool bVisibleFromFather = nAccelType&NEL3D_APPDATA_ACCEL_VISIBLE_FROM_FATHER?true:false; bool bAudibleLikeVisible = (nAccelType&NEL3D_APPDATA_ACCEL_AUDIBLE_NOT_LIKE_VISIBLE)?false:true; bool bFatherAudible = bAudibleLikeVisible ? bFatherVisible : nAccelType&NEL3D_APPDATA_ACCEL_FATHER_AUDIBLE?true:false; bool bAudibleFromFather = bAudibleLikeVisible ? bVisibleFromFather : nAccelType&NEL3D_APPDATA_ACCEL_AUDIBLE_FROM_FATHER?true:false; if ((nAccelType&NEL3D_APPDATA_ACCEL_TYPE) == NEL3D_APPDATA_ACCEL_CLUSTER) // If cluster if (!RPO::isZone (*pNode, tvTime)) if (CExportNel::isMesh(*pNode, tvTime)) { CCluster clusterTemp; std::string temp; temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_SOUND_GROUP, "no sound"); clusterTemp.setSoundGroup(temp != "no sound" ? temp : ""); temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_ENV_FX, "no fx"); clusterTemp.setEnvironmentFx(temp != "no fx" ? temp : ""); CMesh::CMeshBuild *pMB; CMeshBase::CMeshBaseBuild *pMBB; pMB = createMeshBuild (*pNode, tvTime, pMBB); convertToWorldCoordinate( pMB, pMBB ); for (j = 0; j < pMB->Faces.size(); ++j) { if (!clusterTemp.makeVolume (pMB->Vertices[pMB->Faces[j].Corner[0].Vertex], pMB->Vertices[pMB->Faces[j].Corner[1].Vertex], pMB->Vertices[pMB->Faces[j].Corner[2].Vertex]) ) { // ERROR : The volume is not convex !!! char tam[256]; sprintf(tam,"ERROR: The cluster %s is not convex.",vectNode[i]->GetName()); //MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR); nlwarning(tam); } } clusterTemp.FatherVisible = bFatherVisible; clusterTemp.VisibleFromFather = bVisibleFromFather; clusterTemp.FatherAudible = bFatherAudible; clusterTemp.AudibleFromFather = bAudibleFromFather; clusterTemp.Name = pNode->GetName(); vClusters.push_back (clusterTemp); delete pMB; delete pMBB; } } // Creation of all the portals vector<CPortal> vPortals; it = vectNode.begin(); for (i = 0; i < (sint)vectNode.size(); ++i, ++it) { INode *pNode = *it; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 1) // If Portal if (!RPO::isZone (*pNode, tvTime)) if (CExportNel::isMesh(*pNode, tvTime)) { CPortal portalTemp; std::string temp; temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_OCC_MODEL, "no occlusion"); portalTemp.setOcclusionModel(temp != "no occlusion" ? temp : ""); temp = CExportNel::getScriptAppData(pNode, NEL3D_APPDATA_OPEN_OCC_MODEL, "no occlusion"); portalTemp.setOpenOcclusionModel(temp != "no occlusion" ? temp : ""); CMesh::CMeshBuild *pMB; CMeshBase::CMeshBaseBuild *pMBB; pMB = createMeshBuild (*pNode, tvTime, pMBB); convertToWorldCoordinate( pMB, pMBB ); vector<sint32> poly; vector<bool> facechecked; facechecked.resize (pMB->Faces.size()); for (j = 0; j < pMB->Faces.size(); ++j) facechecked[j] = false; poly.push_back(pMB->Faces[0].Corner[0].Vertex); poly.push_back(pMB->Faces[0].Corner[1].Vertex); poly.push_back(pMB->Faces[0].Corner[2].Vertex); facechecked[0] = true; for (j = 0; j < pMB->Faces.size(); ++j) if (!facechecked[j]) { bool found = false; for(k = 0; k < 3; ++k) { for(m = 0; m < poly.size(); ++m) { if ((pMB->Faces[j].Corner[k].Vertex == poly[m]) && (pMB->Faces[j].Corner[(k+1)%3].Vertex == poly[(m+1)%poly.size()])) { found = true; break; } if ((pMB->Faces[j].Corner[(k+1)%3].Vertex == poly[m]) && (pMB->Faces[j].Corner[k].Vertex == poly[(m+1)%poly.size()])) { found = true; break; } } if (found) break; } if (found) { // insert an empty space in poly between m and m+1 poly.resize (poly.size()+1); for (uint32 a = poly.size()-2; a > m; --a) poly[a+1] = poly[a]; poly[m+1] = pMB->Faces[j].Corner[(k+2)%3].Vertex; facechecked[j] = true; j = 0; } } vector<CVector> polyv; polyv.resize (poly.size()); for (j = 0; j < poly.size(); ++j) polyv[j] = pMB->Vertices[poly[j]]; if (!portalTemp.setPoly (polyv)) { // ERROR : Poly not convex, or set of vertices not plane char tam[256]; sprintf(tam,"ERROR: The portal %s is not convex.",vectNode[i]->GetName()); //MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR); nlwarning(tam); } if (nAccelType&16) // is dynamic portal ? { string InstanceName = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_INSTANCE_NAME, ""); if (!InstanceName.empty()) portalTemp.setName (InstanceName); else portalTemp.setName (string(pNode->GetName())); } // Check if portal has 2 cluster int nNbCluster = 0; for (j = 0; j < vClusters.size(); ++j) { bool bPortalInCluster = true; for (k = 0; k < polyv.size(); ++k) if (!vClusters[j].isIn (polyv[k]) ) { bPortalInCluster = false; break; } if (bPortalInCluster) ++nNbCluster; } if (nNbCluster != 2) { // ERROR char tam[256]; sprintf(tam,"ERROR: The portal %s has not 2 clusters but %d",vectNode[i]->GetName(), nNbCluster); //MessageBox(NULL,tam,"Error",MB_OK|MB_ICONERROR); nlwarning(tam); } vPortals.push_back (portalTemp); delete pMB; delete pMBB; } } // Link instance to clusters (an instance has a list of clusters) nNumIG = 0; it = vectNode.begin(); for (i = 0; i < (sint)vectNode.size(); ++i, ++it) { INode *pNode = *it; int nAccelType = CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_ACCEL, 32); if ((nAccelType&3) == 0) // If not an accelerator if (!RPO::isZone (*pNode, tvTime)) if (CExportNel::isMesh (*pNode, tvTime) || CExportNel::isDummy(*pNode, tvTime)) { if (nAccelType&32) // Is the flag clusterize set ? { // Test against all clusters // The list of vertices used to test against cluster std::vector<NLMISC::CVector> *testVertices; std::vector<NLMISC::CVector> FXVertices; // Used only if the obj is a fx. It contains the corners of the bbox. bool buildMeshBBox = true; /** If it is a mesh, we build its bbox and transform in world * If it is a FX, we read its bbox from its shape * If we can't read it, we use the bbox of the fx helper in max */ Object *obj = pNode->EvalWorldState(tvTime).obj; // Check if there is an object if (obj) { Class_ID clid = obj->ClassID(); // is the object a particle system ? if (clid.PartA() == NEL_PARTICLE_SYSTEM_CLASS_ID) { // build the shape from the file name std::string objName = CExportNel::getNelObjectName(*pNode); if (!objName.empty()) { NL3D::CShapeStream ss; NLMISC::CIFile iF; if (iF.open(objName.c_str())) { try { iF.serial(ss); NL3D::CParticleSystemShape *pss = dynamic_cast<NL3D::CParticleSystemShape *>(ss.getShapePointer()); if (!pss) { nlwarning("ERROR: Node %s shape is not a FX", CExportNel::getName(*pNode).c_str()); } else { NLMISC::CAABBox bbox; pss->getAABBox(bbox); // transform in world Matrix3 xForm = pNode->GetNodeTM(tvTime); NLMISC::CMatrix nelXForm; CExportNel::convertMatrix(nelXForm, xForm); bbox = NLMISC::CAABBox::transformAABBox(nelXForm, bbox); // store vertices of the bbox in the list FXVertices.reserve(8); for(uint k = 0; k < 8; ++k) { FXVertices.push_back(CVector(((k & 1) ? 1 : -1) * bbox.getHalfSize().x + bbox.getCenter().x, ((k & 2) ? 1 : -1) * bbox.getHalfSize().y + bbox.getCenter().y, ((k & 4) ? 1 : -1) * bbox.getHalfSize().z + bbox.getCenter().z)); } // testVertices = &FXVertices; buildMeshBBox = false; } delete ss.getShapePointer(); } catch (NLMISC::Exception &e) { nlwarning(e.what()); } } if (buildMeshBBox) { nlwarning("ERROR: Can't get bbox of a particle system from its shape, using helper bbox instead"); } } } } CMesh::CMeshBuild *pMB = NULL; CMeshBase::CMeshBaseBuild *pMBB = NULL; if (buildMeshBBox) { pMB = createMeshBuild (*pNode, tvTime, pMBB); convertToWorldCoordinate( pMB, pMBB ); testVertices = &pMB->Vertices; } for(k = 0; k < vClusters.size(); ++k) { bool bMeshInCluster = false; for(j = 0; j < testVertices->size(); ++j) { if (vClusters[k].isIn ((*testVertices)[j])) { bMeshInCluster = true; break; } } if (bMeshInCluster) { aIGArray[nNumIG].Clusters.push_back (k); } } // debug purpose : to remove if (vClusters.size() > 0) if (aIGArray[nNumIG].Clusters.size() == 0) { char tam[256]; sprintf(tam,"ERROR: Object %s is not attached to any cluster\nbut his flag clusterize is set", pNode->GetName()); //MessageBox(NULL, tam, "Warning", MB_OK); nlwarning(tam); } // debug purpose : to remove delete pMB; delete pMBB; } ++nNumIG; } // debug purpose : to remove /* if ((nAccelType&3) == 0) // If not an accelerator if (!(nAccelType&32)) { char tam[256]; sprintf(tam,"Object %s is not clusterized", pNode->GetName()); MessageBox(NULL, tam, "Info", MB_OK); } */ // debug purpose : to remove } // PointLight part //================= bool sunLightEnabled= false; sint nNumPointLight = 0; vector<CPointLightNamed> pointLights; pointLights.resize(vectNode.size()); // For all nodes for (i = 0; i < (sint)vectNode.size(); ++i) { INode *pNode = vectNode[i]; SLightBuild sLightBuild; // If it is a Max Light. if ( sLightBuild.canConvertFromMaxLight(pNode, tvTime) ) { // And if this light is checked to realtime export int nRTExport= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_EXPORT_REALTIME_LIGHT, BST_CHECKED); if(nRTExport == BST_CHECKED) { // get Max Light info. sLightBuild.convertFromMaxLight(pNode, tvTime); // Skip if LightDir if(sLightBuild.Type != SLightBuild::LightDir) { // Fill PointLight Info. NL3D::CPointLightNamed &plNamed= pointLights[nNumPointLight]; // Position plNamed.setPosition(sLightBuild.Position); // Attenuation plNamed.setupAttenuation(sLightBuild.rRadiusMin, sLightBuild.rRadiusMax); // Colors // Ensure A=255 for localAmbient to work. NLMISC::CRGBA ambient= sLightBuild.Ambient; ambient.A= 255; plNamed.setDefaultAmbient(ambient); plNamed.setAmbient(ambient); plNamed.setDefaultDiffuse(sLightBuild.Diffuse); plNamed.setDiffuse(sLightBuild.Diffuse); plNamed.setDefaultSpecular(sLightBuild.Specular); plNamed.setSpecular(sLightBuild.Specular); // GroupName. plNamed.AnimatedLight = sLightBuild.AnimatedLight; plNamed.LightGroup = sLightBuild.LightGroup; // Which light type?? if(sLightBuild.bAmbientOnly || sLightBuild.Type== SLightBuild::LightAmbient) { plNamed.setType(CPointLight::AmbientLight); // Special ambient info int nRTAmbAdd= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_REALTIME_AMBIENT_ADD_SUN, BST_UNCHECKED); plNamed.setAddAmbientWithSun(nRTAmbAdd==BST_CHECKED); } else if(sLightBuild.Type== SLightBuild::LightPoint) { plNamed.setType(CPointLight::PointLight); } else if(sLightBuild.Type== SLightBuild::LightSpot) { plNamed.setType(CPointLight::SpotLight); // Export Spot infos. plNamed.setupSpotDirection(sLightBuild.Direction); plNamed.setupSpotAngle(sLightBuild.rHotspot, sLightBuild.rFallof); } else { // What??? nlstop; } // inc Size ++nNumPointLight; } } // if this light is a directionnal and checked to export as Sun Light int nExportSun= CExportNel::getScriptAppData (pNode, NEL3D_APPDATA_EXPORT_AS_SUN_LIGHT, BST_UNCHECKED); if(nExportSun== BST_CHECKED) { // get Max Light info. sLightBuild.convertFromMaxLight(pNode, tvTime); // Skip if not dirLight. if(sLightBuild.Type == SLightBuild::LightDir) sunLightEnabled= true; } } } // Good size pointLights.resize(nNumPointLight); // Build the ig //================= CInstanceGroup* pIG = new CInstanceGroup; // Link portals and clusters and create meta cluster if one pIG->build (vGlobalPos, aIGArray, vClusters, vPortals, pointLights); // IG touched by sun ?? pIG->enableRealTimeSunContribution(sunLightEnabled); return pIG; }