void MeshAlg::computeBounds( const Array<Vector3>& vertexArray, AABox& box, Sphere& sphere) { Vector3 xmin, xmax, ymin, ymax, zmin, zmax; // FIRST PASS: find 6 minima/maxima points xmin.x = ymin.y = zmin.z = finf(); xmax.x = ymax.y = zmax.z = -finf(); for (int v = 0; v < vertexArray.size(); ++v) { const Vector3& vertex = vertexArray[v]; if (vertex.x < xmin.x) { xmin = vertex; } if (vertex.x > xmax.x) { xmax = vertex; } if (vertex.y < ymin.y) { ymin = vertex; } if (vertex.y > ymax.y) { ymax = vertex; } if (vertex.z < zmin.z) { zmin = vertex; } if (vertex.z > zmax.z) { zmax = vertex; } } // Set points dia1 & dia2 to the maximally separated pair Vector3 dia1 = xmin; Vector3 dia2 = xmax; { // Set xspan = distance between the 2 points xmin & xmax (squared) double xspan = (xmax - xmin).squaredMagnitude(); // Same for y & z spans double yspan = (ymax - ymin).squaredMagnitude(); double zspan = (zmax - zmin).squaredMagnitude(); double maxspan = xspan; if (yspan > maxspan) { maxspan = yspan; dia1 = ymin; dia2 = ymax; } if (zspan > maxspan) { maxspan = zspan; dia1 = zmin; dia2 = zmax; } } // dia1, dia2 is a diameter of initial sphere // calc initial center Vector3 center = (dia1 + dia2) / 2.0; // calculate initial radius^2 and radius Vector3 d = dia2 - sphere.center; double radSq = d.squaredMagnitude(); double rad = sqrt(radSq); // SECOND PASS: increment current sphere double old_to_p, old_to_new; for (int v = 0; v < vertexArray.size(); ++v) { const Vector3& vertex = vertexArray[v]; d = vertex - center; double old_to_p_sq = d.squaredMagnitude(); // do r^2 test first if (old_to_p_sq > radSq) { // this point is outside of current sphere old_to_p = sqrt(old_to_p_sq); // calc radius of new sphere rad = (rad + old_to_p) / 2.0; // for next r^2 compare radSq = rad * rad; old_to_new = old_to_p - rad; // calc center of new sphere center = (rad * center + old_to_new * vertex) / old_to_p; } } const Vector3 min(xmin.x, ymin.y, zmin.z); const Vector3 max(xmax.x, ymax.y, zmax.z); box = AABox(min, max); const float boxRadSq = (max - min).squaredMagnitude() * 0.25f; if (boxRadSq >= radSq){ if (isNaN(center.x) || ! isFinite(rad)) { sphere = Sphere(Vector3::zero(), finf()); } else { sphere = Sphere(center, rad); } } else { sphere = Sphere((max + min) * 0.5f, sqrt(boxRadSq)); } }
void VisibleEntity::onPose(Array<shared_ptr<Surface> >& surfaceArray) { // We have to pose in order to compute bounds that are used for selection in the editor // and collisions in simulation, so pose anyway if not visible, // but then roll back. debugAssert(isFinite(m_frame.translation.x)); debugAssert(! isNaN(m_frame.rotation[0][0])); const int oldLen = surfaceArray.size(); const bool boundsChangedSincePreviousFrame = poseModel(surfaceArray); // Compute bounds for objects that moved if (m_lastAABoxBounds.isEmpty() || boundsChangedSincePreviousFrame || (m_lastChangeTime > m_lastBoundsTime)) { m_lastSphereBounds = Sphere(m_frame.translation, 0); const CFrame& myFrameInverse = frame().inverse(); m_lastObjectSpaceAABoxBounds = AABox::empty(); m_lastBoxBoundArray.fastClear(); // Look at all surfaces produced for (int i = oldLen; i < surfaceArray.size(); ++i) { AABox b; Sphere s; const shared_ptr<Surface>& surf = surfaceArray[i]; // body to world transformation for the surface CoordinateFrame cframe; surf->getCoordinateFrame(cframe, false); debugAssertM(cframe.translation.x == cframe.translation.x, "NaN translation"); surf->getObjectSpaceBoundingSphere(s); s = cframe.toWorldSpace(s); m_lastSphereBounds.radius = max(m_lastSphereBounds.radius, (s.center - m_lastSphereBounds.center).length() + s.radius); // Take the entity's frame out of consideration, so that we get tight AA bounds // in the Entity's frame CFrame osFrame = myFrameInverse * cframe; surf->getObjectSpaceBoundingBox(b); m_lastBoxBoundArray.append(cframe.toWorldSpace(b)); const Box& temp = osFrame.toWorldSpace(b); m_lastObjectSpaceAABoxBounds.merge(temp); } // Box can't represent an empty box, so we make empty boxes into real boxes with zero volume here if (m_lastObjectSpaceAABoxBounds.isEmpty()) { m_lastObjectSpaceAABoxBounds = AABox(Point3::zero()); m_lastAABoxBounds = AABox(frame().translation); } m_lastBoxBounds = frame().toWorldSpace(m_lastObjectSpaceAABoxBounds); m_lastBoxBounds.getBounds(m_lastAABoxBounds); m_lastBoundsTime = System::time(); } if (! m_visible) { // Discard my surfaces if I'm invisible; they were only needed for bounds surfaceArray.resize(oldLen, false); } }
void MD2Model::Part::load(const std::string& filename, float resize) { resize *= 0.55f; // If models are being reloaded it is dangerous to trust the interpolation cache. interpolatedModel = NULL; alwaysAssertM(FileSystem::exists(filename), std::string("Can't find \"") + filename + "\""); setNormalTable(); // Clear out reset(); BinaryInput b(filename, G3D_LITTLE_ENDIAN); MD2ModelHeader header; header.deserialize(b); debugAssert(header.version == 8); debugAssert(header.numVertices <= 4096); keyFrame.resize(header.numFrames); Array<Vector3> frameMin; frameMin.resize(header.numFrames); Array<Vector3> frameMax; frameMax.resize(header.numFrames); Array<double> frameRad; frameRad.resize(header.numFrames); texCoordScale.x = 1.0f / header.skinWidth; texCoordScale.y = 1.0f / header.skinHeight; Vector3 min = Vector3::inf(); Vector3 max = -Vector3::inf(); double rad = 0; if (header.numVertices < 3) { Log::common()->printf("\n*****************\nWarning: \"%s\" is corrupted and is not being loaded.\n", filename.c_str()); return; } loadTextureFilenames(b, header.numSkins, header.offsetSkins); for (int f = 0; f < keyFrame.size(); ++f) { MD2Frame md2Frame; b.setPosition(header.offsetFrames + f * header.frameSize); md2Frame.deserialize(b); // Read the vertices for the frame keyFrame[f].vertexArray.resize(header.numVertices); keyFrame[f].normalArray.resize(header.numVertices); // Per-pose bounds Vector3 min_1 = Vector3::inf(); Vector3 max_1 = -Vector3::inf(); double rad_1 = 0; // Quake's axes are permuted and scaled double scale[3] = {-.07, .07, -.07}; int permute[3] = {2, 0, 1}; int v, i; for (v = 0; v < header.numVertices; ++v) { Vector3& vertex = keyFrame[f].vertexArray[v]; for (i = 0; i < 3; ++i) { vertex[permute[i]] = (b.readUInt8() * md2Frame.scale[i] + md2Frame.translate[i]) * float(scale[permute[i]]); } vertex *= resize; uint8 normalIndex = b.readUInt8(); debugAssertM(normalIndex < 162, "Illegal canonical normal index in file"); keyFrame[f].normalArray[v] = iClamp(normalIndex, 0, 161); min_1 = min_1.min(vertex); max_1 = max_1.max(vertex); if (vertex.squaredMagnitude() > rad_1) { rad_1 = vertex.squaredMagnitude(); } } frameMin[f] = min_1; frameMax[f] = max_1; frameRad[f] = sqrt(rad_1); min = min.min(min_1); max = max.max(max_1); if (rad_1 > rad) { rad = rad_1; } } // Compute per-animation bounds based on frame bounds for (int a = 0; a < JUMP; ++a) { const int first = animationTable[a].first; const int last = animationTable[a].last; if ((first < header.numFrames) && (last < header.numFrames)) { Vector3 min = frameMin[first]; Vector3 max = frameMax[first]; double rad = frameRad[first]; for (int i = first + 1; i <= last; ++i) { min = min.min(frameMin[i]); max = max.max(frameMax[i]); rad = G3D::max(rad, frameRad[i]); } animationBoundingBox[a] = AABox(min, max); // Sometimes the sphere bounding the box is tighter than the one we calculated. const float boxRadSq = (max-min).squaredMagnitude() * 0.25f; if (boxRadSq >= square(rad)) { animationBoundingSphere[a] = Sphere(Vector3::zero(), (float)rad); } else { animationBoundingSphere[a] = Sphere((max + min) * 0.5f, sqrt(boxRadSq)); } } else { // This animation is not supported by this model animationBoundingBox[a] = AABox(Vector3::zero(), Vector3::zero()); animationBoundingSphere[a] = Sphere(Vector3::zero(), 0); } } animationBoundingBox[JUMP] = animationBoundingBox[JUMP_DOWN]; animationBoundingSphere[JUMP] = animationBoundingSphere[JUMP_DOWN]; boundingBox = AABox(min, max); boundingSphere = Sphere(Vector3::zero(), (float)sqrt(rad)); // Load the texture coords Array<Vector2int16> fileTexCoords; fileTexCoords.resize(header.numTexCoords); b.setPosition(header.offsetTexCoords); for (int t = 0; t < fileTexCoords.size(); ++t) { fileTexCoords[t].x = b.readUInt16(); fileTexCoords[t].y = b.readUInt16(); } // The indices for the texture coords (which don't match the // vertex indices originally). indexArray.resize(header.numTriangles * 3); Array<Vector2int16> index_texCoordArray; index_texCoordArray.resize(indexArray.size()); // Read the triangles, reversing them to get triangle list order b.setPosition(header.offsetTriangles); for (int t = header.numTriangles - 1; t >= 0; --t) { for (int i = 2; i >= 0; --i) { indexArray[t * 3 + i] = b.readUInt16(); } for (int i = 2; i >= 0; --i) { index_texCoordArray[t * 3 + i] = fileTexCoords[b.readUInt16()]; } } computeTexCoords(index_texCoordArray); // Read the primitives { primitiveArray.clear(); b.setPosition(header.offsetGlCommands); int n = b.readInt32(); while (n != 0) { Primitive& primitive = primitiveArray.next(); if (n > 0) { primitive.type = PrimitiveType::TRIANGLE_STRIP; } else { primitive.type = PrimitiveType::TRIANGLE_FAN; n = -n; } primitive.pvertexArray.resize(n); Array<Primitive::PVertex>& pvertex = primitive.pvertexArray; for (int i = 0; i < pvertex.size(); ++i) { pvertex[i].texCoord.x = b.readFloat32(); pvertex[i].texCoord.y = b.readFloat32(); pvertex[i].index = b.readInt32(); } n = b.readInt32(); } } MeshAlg::computeAdjacency(keyFrame[0].vertexArray, indexArray, faceArray, edgeArray, vertexArray); weldedFaceArray = faceArray; weldedEdgeArray = edgeArray; weldedVertexArray = vertexArray; MeshAlg::weldAdjacency(keyFrame[0].vertexArray, weldedFaceArray, weldedEdgeArray, weldedVertexArray); numBoundaryEdges = MeshAlg::countBoundaryEdges(edgeArray); numWeldedBoundaryEdges = MeshAlg::countBoundaryEdges(weldedEdgeArray); shared_ptr<VertexBuffer> indexBuffer = VertexBuffer::create(indexArray.size() * sizeof(int), VertexBuffer::WRITE_ONCE); indexVAR = IndexStream(indexArray, indexBuffer); }
void LoadGameObjectModelList() { FILE* model_list_file = fopen((sWorld.GetDataPath() + "vmaps/" + VMAP::GAMEOBJECT_MODELS).c_str(), "rb"); if (!model_list_file) return; uint32 name_length, displayId; char buff[500]; while (!feof(model_list_file)) { fread(&displayId, sizeof(uint32), 1, model_list_file); fread(&name_length, sizeof(uint32), 1, model_list_file); if (name_length >= sizeof(buff)) { sLog.outDebug("File %s seems to be corrupted", VMAP::GAMEOBJECT_MODELS); break; } fread(&buff, sizeof(char), name_length, model_list_file); Vector3 v1, v2; fread(&v1, sizeof(Vector3), 1, model_list_file); fread(&v2, sizeof(Vector3), 1, model_list_file); model_list.insert(ModelList::value_type(displayId, GameobjectModelData(std::string(buff, name_length), AABox(v1, v2)))); } fclose(model_list_file); }
//================================================================= bool TileAssembler::convertRawFile(const std::string& pModelFilename) { bool success = true; std::string filename = iSrcDir; if (filename.length() >0) filename.append("/"); filename.append(pModelFilename); FILE *rf = fopen(filename.c_str(), "rb"); if (!rf) { printf("ERROR: Can't open model file in form: %s",pModelFilename.c_str()); printf("... or form: %s",filename.c_str() ); return false; } char ident[8]; int readOperation = 1; // temporary use defines to simplify read/check code (close file and return at fail) #define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \ fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++; #define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \ fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); } READ_OR_RETURN(&ident, 8); CMP_OR_RETURN(ident, "VMAP003"); // we have to read one int. This is needed during the export and we have to skip it here uint32 tempNVectors; READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors)); uint32 groups; uint32 RootWMOID; char blockId[5]; blockId[4] = 0; int blocksize; READ_OR_RETURN(&groups, sizeof(uint32)); READ_OR_RETURN(&RootWMOID, sizeof(uint32)); std::vector<GroupModel> groupsArray; for (uint32 g=0; g<groups; ++g) { std::vector<MeshTriangle> triangles; std::vector<Vector3> vertexArray; uint32 mogpflags, GroupWMOID; READ_OR_RETURN(&mogpflags, sizeof(uint32)); READ_OR_RETURN(&GroupWMOID, sizeof(uint32)); float bbox1[3], bbox2[3]; READ_OR_RETURN(bbox1, sizeof(float)*3); READ_OR_RETURN(bbox2, sizeof(float)*3); uint32 liquidflags; READ_OR_RETURN(&liquidflags, sizeof(uint32)); // will this ever be used? what is it good for anyway?? uint32 branches; READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "GRP "); READ_OR_RETURN(&blocksize, sizeof(int)); READ_OR_RETURN(&branches, sizeof(uint32)); for (uint32 b=0; b<branches; ++b) { uint32 indexes; // indexes for each branch (not used jet) READ_OR_RETURN(&indexes, sizeof(uint32)); } // ---- indexes READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "INDX"); READ_OR_RETURN(&blocksize, sizeof(int)); uint32 nindexes; READ_OR_RETURN(&nindexes, sizeof(uint32)); if (nindexes >0) { uint16 *indexarray = new uint16[nindexes]; READ_OR_RETURN(indexarray, nindexes*sizeof(uint16)); for (uint32 i=0; i<nindexes; i+=3) { triangles.push_back(MeshTriangle(indexarray[i], indexarray[i+1], indexarray[i+2])); } delete[] indexarray; } // ---- vectors READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "VERT"); READ_OR_RETURN(&blocksize, sizeof(int)); uint32 nvectors; READ_OR_RETURN(&nvectors, sizeof(uint32)); if (nvectors >0) { float *vectorarray = new float[nvectors*3]; READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3); for (uint32 i=0; i<nvectors; ++i) { vertexArray.push_back( Vector3(vectorarray + 3*i) ); } delete[] vectorarray; } // ----- liquid WmoLiquid *liquid = 0; if (liquidflags& 1) { WMOLiquidHeader hlq; READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "LIQU"); READ_OR_RETURN(&blocksize, sizeof(int)); READ_OR_RETURN(&hlq, sizeof(WMOLiquidHeader)); liquid = new WmoLiquid(hlq.xtiles, hlq.ytiles, Vector3(hlq.pos_x, hlq.pos_y, hlq.pos_z), hlq.type); uint32 size = hlq.xverts*hlq.yverts; READ_OR_RETURN(liquid->GetHeightStorage(), size*sizeof(float)); size = hlq.xtiles*hlq.ytiles; READ_OR_RETURN(liquid->GetFlagsStorage(), size); } groupsArray.push_back(GroupModel(mogpflags, GroupWMOID, AABox(Vector3(bbox1), Vector3(bbox2)))); groupsArray.back().setMeshData(vertexArray, triangles); groupsArray.back().setLiquidData(liquid); // drop of temporary use defines #undef READ_OR_RETURN #undef CMP_OR_RETURN } fclose(rf); // write WorldModel WorldModel model; model.setRootWmoID(RootWMOID); if (groupsArray.size()) { model.setGroupModels(groupsArray); success = model.writeFile(iDestDir + "/" + pModelFilename + ".vmo"); } //std::cout << "readRawFile2: '" << pModelFilename << "' tris: " << nElements << " nodes: " << nNodes << std::endl; return success; }
void TileAssembler::exportGameobjectModels() { FILE* model_list = fopen((iSrcDir + "/" + "temp_gameobject_models").c_str(), "rb"); if (!model_list) return; FILE* model_list_copy = fopen((iDestDir + "/" + GAMEOBJECT_MODELS).c_str(), "wb"); if (!model_list_copy) { fclose(model_list); return; } uint32 name_length, displayId; char buff[500]; while (true) { if (fread(&displayId, sizeof(uint32), 1, model_list) != 1) if (feof(model_list)) // EOF flag is only set after failed reading attempt break; if (fread(&name_length, sizeof(uint32), 1, model_list) != 1 || name_length >= sizeof(buff) || fread(&buff, sizeof(char), name_length, model_list) != name_length) { std::cout << "\nFile 'temp_gameobject_models' seems to be corrupted" << std::endl; break; } std::string model_name(buff, name_length); WorldModel_Raw raw_model; if (!raw_model.Read((iSrcDir + "/" + model_name).c_str()) ) continue; spawnedModelFiles.insert(model_name); AABox bounds; bool boundEmpty = true; for (uint32 g = 0; g < raw_model.groupsArray.size(); ++g) { std::vector<Vector3>& vertices = raw_model.groupsArray[g].vertexArray; uint32 nvectors = vertices.size(); for (uint32 i = 0; i < nvectors; ++i) { Vector3& v = vertices[i]; if (boundEmpty) bounds = AABox(v, v), boundEmpty = false; else bounds.merge(v); } } fwrite(&displayId, sizeof(uint32), 1, model_list_copy); fwrite(&name_length, sizeof(uint32), 1, model_list_copy); fwrite(&buff, sizeof(char), name_length, model_list_copy); fwrite(&bounds.low(), sizeof(Vector3), 1, model_list_copy); fwrite(&bounds.high(), sizeof(Vector3), 1, model_list_copy); } fclose(model_list); fclose(model_list_copy); }
AABox CinderGraphicItem::bounding_aabox() const { return AABox(position() + scale(), position() - scale()); }
bool TileAssembler::calculateTransformedBound(ModelSpawn &spawn) { std::string modelFilename = iSrcDir + "/" + spawn.name; ModelPosition modelPosition; modelPosition.iDir = spawn.iRot; modelPosition.iScale = spawn.iScale; modelPosition.init(); FILE *rf = fopen(modelFilename.c_str(), "rb"); if (!rf) { printf("ERROR: Can't open model file: %s\n", modelFilename.c_str()); return false; } AABox modelBound; bool boundEmpty=true; char ident[8]; int readOperation = 1; // temporary use defines to simplify read/check code (close file and return at fail) #define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \ fclose(rf); printf("readfail, op = %i\n", readOperation); return(false); }readOperation++; #define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \ fclose(rf); printf("cmpfail, %s!=%s\n", V, S);return(false); } READ_OR_RETURN(&ident, 8); CMP_OR_RETURN(ident, "VMAP003"); // we have to read one int. This is needed during the export and we have to skip it here uint32 tempNVectors; READ_OR_RETURN(&tempNVectors, sizeof(tempNVectors)); uint32 groups, wmoRootId; char blockId[5]; blockId[4] = 0; int blocksize; float *vectorarray = 0; READ_OR_RETURN(&groups, sizeof(uint32)); READ_OR_RETURN(&wmoRootId, sizeof(uint32)); if (groups != 1) printf("Warning: '%s' does not seem to be a M2 model!\n", modelFilename.c_str()); for (uint32 g=0; g<groups; ++g) // should be only one for M2 files... { fseek(rf, 3*sizeof(uint32) + 6*sizeof(float), SEEK_CUR); READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "GRP "); READ_OR_RETURN(&blocksize, sizeof(int)); fseek(rf, blocksize, SEEK_CUR); // ---- indexes READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "INDX"); READ_OR_RETURN(&blocksize, sizeof(int)); fseek(rf, blocksize, SEEK_CUR); // ---- vectors READ_OR_RETURN(&blockId, 4); CMP_OR_RETURN(blockId, "VERT"); READ_OR_RETURN(&blocksize, sizeof(int)); uint32 nvectors; READ_OR_RETURN(&nvectors, sizeof(uint32)); if (nvectors >0) { vectorarray = new float[nvectors*3]; READ_OR_RETURN(vectorarray, nvectors*sizeof(float)*3); } else { std::cout << "error: model '" << spawn.name << "' has no geometry!" << std::endl; return false; } for (uint32 i=0, indexNo=0; indexNo<nvectors; indexNo++, i+=3) { Vector3 v = Vector3(vectorarray[i+0], vectorarray[i+1], vectorarray[i+2]); v = modelPosition.transform(v); if (boundEmpty) modelBound = AABox(v, v), boundEmpty=false; else modelBound.merge(v); } delete[] vectorarray; // drop of temporary use defines #undef READ_OR_RETURN #undef CMP_OR_RETURN } spawn.iBound = modelBound + spawn.iPos; spawn.flags |= MOD_HAS_BOUND; fclose(rf); return true; }
void Capsule::getBounds(AABox& out) const { Vector3 min = p1.min(p2) - (Vector3(1, 1, 1) * _radius); Vector3 max = p1.max(p2) + (Vector3(1, 1, 1) * _radius); out = AABox(min, max); }
void Sphere::getBounds(AABox& out) const { Vector3 extent(radius, radius, radius); out = AABox(center - extent, center + extent); }
OctreeElement::AppendState EntityTreeElement::appendElementData(OctreePacketData* packetData, EncodeBitstreamParams& params) const { OctreeElement::AppendState appendElementState = OctreeElement::COMPLETED; // assume the best... // first, check the params.extraEncodeData to see if there's any partial re-encode data for this element OctreeElementExtraEncodeData* extraEncodeData = params.extraEncodeData; EntityTreeElementExtraEncodeData* entityTreeElementExtraEncodeData = NULL; bool hadElementExtraData = false; if (extraEncodeData && extraEncodeData->contains(this)) { entityTreeElementExtraEncodeData = static_cast<EntityTreeElementExtraEncodeData*>((*extraEncodeData)[this]); hadElementExtraData = true; } else { // if there wasn't one already, then create one entityTreeElementExtraEncodeData = new EntityTreeElementExtraEncodeData(); entityTreeElementExtraEncodeData->elementCompleted = !hasContent(); for (int i = 0; i < NUMBER_OF_CHILDREN; i++) { EntityTreeElementPointer child = getChildAtIndex(i); if (!child) { entityTreeElementExtraEncodeData->childCompleted[i] = true; // if no child exists, it is completed } else { if (child->hasEntities()) { entityTreeElementExtraEncodeData->childCompleted[i] = false; } else { // if the child doesn't have enities, it is completed entityTreeElementExtraEncodeData->childCompleted[i] = true; } } } forEachEntity([&](EntityItemPointer entity) { entityTreeElementExtraEncodeData->entities.insert(entity->getEntityItemID(), entity->getEntityProperties(params)); }); } //assert(extraEncodeData); //assert(extraEncodeData->contains(this)); //entityTreeElementExtraEncodeData = static_cast<EntityTreeElementExtraEncodeData*>((*extraEncodeData)[this]); LevelDetails elementLevel = packetData->startLevel(); // write our entities out... first determine which of the entities are in view based on our params uint16_t numberOfEntities = 0; uint16_t actualNumberOfEntities = 0; int numberOfEntitiesOffset = 0; withReadLock([&] { QVector<uint16_t> indexesOfEntitiesToInclude; // It's possible that our element has been previous completed. In this case we'll simply not include any of our // entities for encoding. This is needed because we encode the element data at the "parent" level, and so we // need to handle the case where our sibling elements need encoding but we don't. if (!entityTreeElementExtraEncodeData->elementCompleted) { for (uint16_t i = 0; i < _entityItems.size(); i++) { EntityItemPointer entity = _entityItems[i]; bool includeThisEntity = true; if (!params.forceSendScene && entity->getLastChangedOnServer() < params.lastViewFrustumSent) { includeThisEntity = false; } if (hadElementExtraData) { includeThisEntity = includeThisEntity && entityTreeElementExtraEncodeData->entities.contains(entity->getEntityItemID()); } if (includeThisEntity || params.recurseEverything) { // we want to use the maximum possible box for this, so that we don't have to worry about the nuance of // simulation changing what's visible. consider the case where the entity contains an angular velocity // the entity may not be in view and then in view a frame later, let the client side handle it's view // frustum culling on rendering. bool success; AACube entityCube = entity->getQueryAACube(success); if (!success || !params.viewFrustum.cubeIntersectsKeyhole(entityCube)) { includeThisEntity = false; // out of view, don't include it } else { // Check the size of the entity, it's possible that a "too small to see" entity is included in a // larger octree cell because of its position (for example if it crosses the boundary of a cell it // pops to the next higher cell. So we want to check to see that the entity is large enough to be seen // before we consider including it. success = true; // we can't cull a parent-entity by its dimensions because the child may be larger. we need to // avoid sending details about a child but not the parent. the parent's queryAACube should have // been adjusted to encompass the queryAACube of the child. AABox entityBounds = entity->hasChildren() ? AABox(entityCube) : entity->getAABox(success); if (!success) { // if this entity is a child of an avatar, the entity-server wont be able to determine its // AABox. If this happens, fall back to the queryAACube. entityBounds = AABox(entityCube); } auto renderAccuracy = calculateRenderAccuracy(params.viewFrustum.getPosition(), entityBounds, params.octreeElementSizeScale, params.boundaryLevelAdjust); if (renderAccuracy <= 0.0f) { includeThisEntity = false; // too small, don't include it #ifdef WANT_LOD_DEBUGGING qDebug() << "skipping entity - TOO SMALL - \n" << "......id:" << entity->getID() << "\n" << "....name:" << entity->getName() << "\n" << "..bounds:" << entityBounds << "\n" << "....cell:" << getAACube(); #endif } } } if (includeThisEntity) { #ifdef WANT_LOD_DEBUGGING qDebug() << "including entity - \n" << "......id:" << entity->getID() << "\n" << "....name:" << entity->getName() << "\n" << "....cell:" << getAACube(); #endif indexesOfEntitiesToInclude << i; numberOfEntities++; } else { // if the extra data included this entity, and we've decided to not include the entity, then // we can treat it as if it was completed. entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID()); } } } numberOfEntitiesOffset = packetData->getUncompressedByteOffset(); bool successAppendEntityCount = packetData->appendValue(numberOfEntities); if (successAppendEntityCount) { foreach(uint16_t i, indexesOfEntitiesToInclude) { EntityItemPointer entity = _entityItems[i]; LevelDetails entityLevel = packetData->startLevel(); OctreeElement::AppendState appendEntityState = entity->appendEntityData(packetData, params, entityTreeElementExtraEncodeData); // If none of this entity data was able to be appended, then discard it // and don't include it in our entity count if (appendEntityState == OctreeElement::NONE) { packetData->discardLevel(entityLevel); } else { // If either ALL or some of it got appended, then end the level (commit it) // and include the entity in our final count of entities packetData->endLevel(entityLevel); actualNumberOfEntities++; } // If the entity item got completely appended, then we can remove it from the extra encode data if (appendEntityState == OctreeElement::COMPLETED) { entityTreeElementExtraEncodeData->entities.remove(entity->getEntityItemID()); } // If any part of the entity items didn't fit, then the element is considered partial // NOTE: if the entity item didn't fit or only partially fit, then the entity item should have // added itself to the extra encode data. if (appendEntityState != OctreeElement::COMPLETED) { appendElementState = OctreeElement::PARTIAL; } } } else {
bool MeshDefFragment::unpack(WLDReader *s) { uint16_t vertexCount, texCoordsCount, normalCount, colorCount, polyCount; uint16_t vertexPieceCount, polyTexCount, vertexTexCount, scaleFactor; s->unpackField('I', &m_flags); s->unpackReference(&m_palette); s->unpackArray("R", 3, m_ref); s->unpackArray("f", 3, &m_center); s->unpackArray("I", 3, &m_param2); s->unpackField('f', &m_maxDist); // This is not always defined, we will calculate it later on. s->unpackArray("f", 3, &m_boundsAA.low); s->unpackArray("f", 3, &m_boundsAA.high); s->unpackFields("hhhhhhhhhh", &vertexCount, &texCoordsCount, &normalCount, &colorCount, &polyCount, &vertexPieceCount, &polyTexCount, &vertexTexCount, &m_size9, &scaleFactor); float scale = 1.0 / float(1 << scaleFactor); int16_t vertex[3], texCoord[2]; int8_t normal[3], color[4]; uint16_t polygon[4], vertexPiece[2], polyTex[2], vertexTex[2]; s->unpackStruct("hhh", vertex); vec3 scaledVertex = vec3(vertex[0] * scale, vertex[1] * scale, vertex[2] * scale); m_boundsAA = AABox(scaledVertex, scaledVertex); m_vertices.append(scaledVertex); for(uint16_t i = 1; i < vertexCount; i++) { s->unpackStruct("hhh", vertex); scaledVertex = vec3(vertex[0] * scale, vertex[1] * scale, vertex[2] * scale); m_boundsAA.extendTo(scaledVertex); m_vertices.append(scaledVertex); } for(uint16_t i = 0; i < texCoordsCount; i++) { s->unpackStruct("hh", texCoord); m_texCoords.append(vec2((texCoord[0] / 256.0), (texCoord[1] / 256.0))); } for(uint16_t i = 0; i < normalCount; i++) { s->unpackStruct("bbb", normal); m_normals.append(vec3(normal[0] / 127.0, normal[1] / 127.0, normal[2] / 127.0)); } for(uint16_t i = 0; i < colorCount; i++) { s->unpackStruct("BBBB", color); m_colors.append(qRgba(color[0], color[1], color[2], color[3])); } for(uint16_t i = 0; i < polyCount; i++) { s->unpackStruct("HHHH", polygon); m_polygonFlags.append(polygon[0]); m_indices.append(polygon[1]); m_indices.append(polygon[2]); m_indices.append(polygon[3]); } for(uint16_t i = 0; i < vertexPieceCount; i++) { s->unpackStruct("HH", vertexPiece); m_vertexPieces.append(vec2us(vertexPiece[0], vertexPiece[1])); } for(uint16_t i = 0; i < polyTexCount; i++) { s->unpackStruct("HH", polyTex); m_polygonsByTex.append(vec2us(polyTex[0], polyTex[1])); } for(uint16_t i = 0; i < vertexTexCount; i++) { s->unpackStruct("HH", vertexTex); m_verticesByTex.append(vec2us(vertexTex[0], vertexTex[1])); } return true; }
/* * Tries to load vmap and tilemap for a gridtile and creates a navmesh for it. * */ bool ModelContainerView::generateMoveMapForTile (int pMapId, int x, int y) { bool result = iVMapManager.loadMap (gVMapDataDir.c_str (), pMapId, x, y) == VMAP_LOAD_RESULT_OK; if (result == VMAP_LOAD_RESULT_OK) { //VMap loaded. Add data from vmap to global Triangle-Array parseVMap (pMapId, x, y); } // Add data from Height-Map to global Triangle-Array generateHeightMap(pMapId,x,y); // We will now add all triangles inside the given zone to the vectormap. // We could also do additional checks here. double x_max = (32-x)*SIZE_OF_GRIDS + 50; double y_max = (32-y)*SIZE_OF_GRIDS + 50; double x_min = x_max - SIZE_OF_GRIDS - 100; double y_min = y_max - SIZE_OF_GRIDS - 100; Vector3 low = Vector3(x_min,y_min,-inf()); Vector3 high = Vector3(x_max,y_max,inf()); AABox checkBox = AABox(low,high); AABox check; Triangle t; //each triangle has mangos format. for (int i = 0; i < globalTriangleArray.size(); i++) { t = globalTriangleArray[i]; t.getBounds(check); if (checkBox.contains(check)) { // Write it down in detour format. iGlobArray.append(t.vertex(0).y,t.vertex(0).z,t.vertex(0).x); iGlobArray.append(t.vertex(1).y,t.vertex(1).z,t.vertex(1).x); iGlobArray.append(t.vertex(2).y,t.vertex(2).z,t.vertex(2).x); } } if (iGlobArray.size() == 0) { printf("No models - check your mmap.datadir in your config"); return true; } if(gMakeObjFile) debugGenerateObjFile(); // create obj file for Recast Demo viewer //return true; float bmin[3], bmax[3]; /* * The format looks like this * Verticle = float[3] * Triangle = Verticle[3] * So there are * array.size() floats * that means there are * nverts = array.size()/3 Verticles * that means there are * ntris = nverts/3 */ //array/3 verticles const int nverts = iGlobArray.size()/3; // because 1 vert is 3 float. // -> vert = float[3] const float* verts = iGlobArray.getCArray(); rcCalcBounds(verts,nverts,bmin,bmax); // nverts/3 triangles // -> Triangle = vert[3] = float[9] int* tris = new int[nverts];// because 1 triangle is 3 verts for (int i = 0; i< nverts; i++) tris[i] = i; /* tris[i] = 1,2,3;4,5,6;7,8,9; * */ const int ntris = (nverts/3); rcConfig m_cfg; // // Step 1. Initialize build config. // // Init build configuration from GUI memset(&m_cfg, 0, sizeof(m_cfg)); // Change config settings here! m_cfg.cs = 0.3f; m_cfg.ch = 0.2f; m_cfg.walkableSlopeAngle = 50.0f; m_cfg.walkableHeight = 10; m_cfg.walkableClimb = 4; m_cfg.walkableRadius = 2; m_cfg.maxEdgeLen = (int)(12 / 0.3f); m_cfg.maxSimplificationError = 1.3f; m_cfg.minRegionSize = (int)rcSqr(50); m_cfg.mergeRegionSize = (int)rcSqr(20); m_cfg.maxVertsPerPoly = (int)6; m_cfg.detailSampleDist = 1.8f; m_cfg.detailSampleMaxError = 0.2f * 1; bool m_keepInterResults = false; printf("CellSize : %.2f\n",m_cfg.cs); printf("CellHeight : %.2f\n",m_cfg.ch); printf("WalkableSlope : %.2f\n",m_cfg.walkableSlopeAngle); printf("WalkableHeight : %i\n",m_cfg.walkableHeight); printf("walkableClimb : %i\n",m_cfg.walkableClimb); printf("walkableRadius : %i\n",m_cfg.walkableRadius); printf("maxEdgeLen : %i\n",m_cfg.maxEdgeLen); printf("maxSimplific.Er.: %.2f\n",m_cfg.maxSimplificationError); printf("minRegionSize : %i\n",m_cfg.minRegionSize); printf("mergedRegSize : %i\n",m_cfg.mergeRegionSize); printf("maxVertsPerPoly : %i\n",m_cfg.maxVertsPerPoly); printf("detailSampledist: %.2f\n",m_cfg.detailSampleDist); printf("det.Samp.max.err: %.2f\n",m_cfg.detailSampleMaxError); // Set the area where the navigation will be build. // Here the bounds of the input mesh are used, but the // area could be specified by an user defined box, etc. vcopy(m_cfg.bmin, bmin); vcopy(m_cfg.bmax, bmax); rcCalcGridSize(m_cfg.bmin, m_cfg.bmax, m_cfg.cs, &m_cfg.width, &m_cfg.height); // // Step 2. Rasterize input polygon soup. // // Allocate voxel heighfield where we rasterize our input data to. rcHeightfield* m_solid = new rcHeightfield; if (!m_solid) { printf("buildNavigation: Out of memory 'solid'.\n"); return false; } if (!rcCreateHeightfield(*m_solid, m_cfg.width, m_cfg.height, m_cfg.bmin, m_cfg.bmax, m_cfg.cs, m_cfg.ch)) { printf("buildNavigation: Could not create solid heightfield.\n"); return false; } // Allocate array that can hold triangle flags. // If you have multiple meshes you need to process, allocate // and array which can hold the max number of triangles you need to process. unsigned char* m_triflags = new unsigned char[ntris]; if (!m_triflags) { printf("buildNavigation: Out of memory 'triangleFlags' (%d).\n", ntris); return false; } // Find triangles which are walkable based on their slope and rasterize them. // If your input data is multiple meshes, you can transform them here, calculate // the flags for each of the meshes and rasterize them. memset(m_triflags, 0, ntris*sizeof(unsigned char)); rcMarkWalkableTriangles(m_cfg.walkableSlopeAngle, verts, nverts, tris, ntris, m_triflags); rcRasterizeTriangles(verts, nverts, tris, m_triflags, ntris, *m_solid, m_cfg.walkableClimb); // should delete [] verts? - probably not, this is just pointer to data in a G3D Array // should delete [] tris? if (!m_keepInterResults) { delete [] m_triflags; m_triflags = 0; } // // Step 3. Filter walkables surfaces. // // Once all geoemtry is rasterized, we do initial pass of filtering to // remove unwanted overhangs caused by the conservative rasterization // as well as filter spans where the character cannot possibly stand. rcFilterLowHangingWalkableObstacles(m_cfg.walkableClimb, *m_solid); rcFilterLedgeSpans(m_cfg.walkableHeight, m_cfg.walkableClimb, *m_solid); rcFilterWalkableLowHeightSpans(m_cfg.walkableHeight, *m_solid); // // Step 4. Partition walkable surface to simple regions. // // Compact the heightfield so that it is faster to handle from now on. // This will result more cache coherent data as well as the neighbours // between walkable cells will be calculated. rcCompactHeightfield* m_chf = new rcCompactHeightfield; if (!m_chf) { printf("buildNavigation: Out of memory 'chf'.\n"); return false; } if (!rcBuildCompactHeightfield(m_cfg.walkableHeight, m_cfg.walkableClimb, RC_WALKABLE, *m_solid, *m_chf)) { printf( "buildNavigation: Could not build compact data.\n"); return false; } if (!m_keepInterResults) { delete m_solid; m_solid = 0; } // Erode the walkable area by agent radius. if (!rcErodeArea(RC_WALKABLE_AREA, m_cfg.walkableRadius, *m_chf)) { printf("buildNavigation: Could not erode.\n"); return false; } // (Optional) Mark areas. //const ConvexVolume* vols = m_geom->getConvexVolumes(); //for (int i = 0; i < m_geom->getConvexVolumeCount(); ++i) // rcMarkConvexPolyArea(vols[i].verts, vols[i].nverts, vols[i].hmin, vols[i].hmax, (unsigned char)vols[i].area, *m_chf); // Prepare for region partitioning, by calculating distance field along the walkable surface. if (!rcBuildDistanceField(*m_chf)) { printf("buildNavigation: Could not build distance field.\n"); return false; } // Partition the walkable surface into simple regions without holes. if (!rcBuildRegions(*m_chf, m_cfg.borderSize, m_cfg.minRegionSize, m_cfg.mergeRegionSize)) { printf("buildNavigation: Could not build regions.\n"); } // // Step 5. Trace and simplify region contours. // // Create contours. rcContourSet* m_cset = new rcContourSet; if (!m_cset) { printf("buildNavigation: Out of memory 'cset'.\n"); return false; } if (!rcBuildContours(*m_chf, m_cfg.maxSimplificationError, m_cfg.maxEdgeLen, *m_cset)) { printf("buildNavigation: Could not create contours.\n"); return false; } // // Step 6. Build polygons mesh from contours. // // Build polygon navmesh from the contours. rcPolyMesh* m_pmesh = new rcPolyMesh; if (!m_pmesh) { printf("buildNavigation: Out of memory 'pmesh'.\n"); return false; } if (!rcBuildPolyMesh(*m_cset, m_cfg.maxVertsPerPoly, *m_pmesh)) { printf( "buildNavigation: Could not triangulate contours.\n"); return false; } // // Step 7. Create detail mesh which allows to access approximate height on each polygon. // rcPolyMeshDetail* m_dmesh = new rcPolyMeshDetail; if (!m_dmesh) { printf("buildNavigation: Out of memory 'pmdtl'.\n"); return false; } if (!rcBuildPolyMeshDetail(*m_pmesh, *m_chf, m_cfg.detailSampleDist, m_cfg.detailSampleMaxError, *m_dmesh)) { printf("buildNavigation: Could not build detail mesh.\n"); } if (!m_keepInterResults) { delete m_chf; m_chf = 0; delete m_cset; m_cset = 0; } // At this point the navigation mesh data is ready, you can access it from m_pmesh. // See duDebugDrawPolyMesh or dtCreateNavMeshData as examples how to access the data. // // (Optional) Step 8. Create Detour data from Recast poly mesh. // // The GUI may allow more max points per polygon than Detour can handle. // Only build the detour navmesh if we do not exceed the limit. if (m_cfg.maxVertsPerPoly <= DT_VERTS_PER_POLYGON) { unsigned char* navData = 0; int navDataSize = 0; // Update poly flags from areas. for (int i = 0; i < m_pmesh->npolys; ++i) { // for now all generated navmesh is walkable by everyone. // else there will be no pathfinding at all! m_pmesh->flags[i] = RC_WALKABLE_AREA; } dtNavMeshCreateParams params; memset(¶ms, 0, sizeof(params)); params.verts = m_pmesh->verts; params.vertCount = m_pmesh->nverts; params.polys = m_pmesh->polys; params.polyAreas = m_pmesh->areas; params.polyFlags = m_pmesh->flags; params.polyCount = m_pmesh->npolys; params.nvp = m_pmesh->nvp; params.detailMeshes = m_dmesh->meshes; params.detailVerts = m_dmesh->verts; params.detailVertsCount = m_dmesh->nverts; params.detailTris = m_dmesh->tris; params.detailTriCount = m_dmesh->ntris; params.offMeshConVerts = 0; params.offMeshConRad = 0; params.offMeshConDir = 0; params.offMeshConAreas = 0; params.offMeshConFlags = 0; params.offMeshConCount = 0; params.walkableHeight = 2.0f; params.walkableRadius = 0.6f; params.walkableClimb = 0.9f; vcopy(params.bmin, m_pmesh->bmin); vcopy(params.bmax, m_pmesh->bmax); params.cs = m_cfg.cs; params.ch = m_cfg.ch; printf("vertcount : %05u\n",params.vertCount); printf("polycount : %05u\n",params.polyCount); printf("detailVertsCount: %05u\n",params.detailVertsCount); printf("detailTriCount : %05u\n",params.detailTriCount); printf("walkableClimb : %.2f\n",params.walkableClimb); printf("walkableRadius : %.2f\n",params.walkableRadius); printf("walkableHeight : %.2f\n",params.walkableHeight); if (!dtCreateNavMeshData(¶ms, &navData, &navDataSize)) { printf("Could not build Detour navmesh.\n"); return false; } // navData now contains the MoveMap printf("Generated Navigation Mesh! Size: %i bytes/ %i kB / %i MB\n",navDataSize,navDataSize/1024,navDataSize/(1024*1024)); char tmp[14]; sprintf(tmp, "%03u%02u%02u.mmap",iMap,ix,iy); std::string savefilepath = gMMapDataDir + "/" + tmp; ofstream inf( savefilepath.c_str(),ofstream::binary ); if( inf ) { inf.write( (char*)( &navData[0] ), navDataSize ) ; } printf("MoveMap saved under %s\n", savefilepath.c_str()); delete [] navData; } // debugLoadNavMesh(); return (result); }
AABox GlyphFace::getBoundingBox() const { double e = 100*EPSILON; return bboxToWorld(AABox(Vector(glyph->xMin, glyph->yMin, -e), Vector(glyph->xMax, glyph->yMax, e))); }
AABox ExtrudedLine::getBoundingBox() const { double e = 100*EPSILON; return bboxToWorld(AABox(Vector(c1[0]-e, c1[1]-e, -EPSILON), Vector(c2[0]+e, c2[1]+e, 1+EPSILON))); }