void NavigationMesh::GetTileGeometry(NavBuildData* build, Vector<NavigationGeometryInfo>& geometryList, BoundingBox& box) { Matrix3x4 inverse = node_->GetWorldTransform().Inverse(); for (unsigned i = 0; i < geometryList.Size(); ++i) { if (box.IsInsideFast(geometryList[i].boundingBox_) != OUTSIDE) { const Matrix3x4& transform = geometryList[i].transform_; if (geometryList[i].component_->GetType() == OffMeshConnection::GetTypeStatic()) { OffMeshConnection* connection = static_cast<OffMeshConnection*>(geometryList[i].component_); Vector3 start = inverse * connection->GetNode()->GetWorldPosition(); Vector3 end = inverse * connection->GetEndPoint()->GetWorldPosition(); build->offMeshVertices_.Push(start); build->offMeshVertices_.Push(end); build->offMeshRadii_.Push(connection->GetRadius()); build->offMeshFlags_.Push((unsigned short)connection->GetMask()); build->offMeshAreas_.Push((unsigned char)connection->GetAreaID()); build->offMeshDir_.Push((unsigned char)(connection->IsBidirectional() ? DT_OFFMESH_CON_BIDIR : 0)); continue; } else if (geometryList[i].component_->GetType() == NavArea::GetTypeStatic()) { NavArea* area = static_cast<NavArea*>(geometryList[i].component_); NavAreaStub stub; stub.areaID_ = (unsigned char)area->GetAreaID(); stub.bounds_ = area->GetWorldBoundingBox(); build->navAreas_.Push(stub); continue; } #ifdef ATOMIC_PHYSICS CollisionShape* shape = dynamic_cast<CollisionShape*>(geometryList[i].component_); if (shape) { switch (shape->GetShapeType()) { case SHAPE_TRIANGLEMESH: { Model* model = shape->GetModel(); if (!model) continue; unsigned lodLevel = shape->GetLodLevel(); for (unsigned j = 0; j < model->GetNumGeometries(); ++j) AddTriMeshGeometry(build, model->GetGeometry(j, lodLevel), transform); } break; case SHAPE_CONVEXHULL: { ConvexData* data = static_cast<ConvexData*>(shape->GetGeometryData()); if (!data) continue; unsigned numVertices = data->vertexCount_; unsigned numIndices = data->indexCount_; unsigned destVertexStart = build->vertices_.Size(); for (unsigned j = 0; j < numVertices; ++j) build->vertices_.Push(transform * data->vertexData_[j]); for (unsigned j = 0; j < numIndices; ++j) build->indices_.Push(data->indexData_[j] + destVertexStart); } break; case SHAPE_BOX: { unsigned destVertexStart = build->vertices_.Size(); build->vertices_.Push(transform * Vector3(-0.5f, 0.5f, -0.5f)); build->vertices_.Push(transform * Vector3(0.5f, 0.5f, -0.5f)); build->vertices_.Push(transform * Vector3(0.5f, -0.5f, -0.5f)); build->vertices_.Push(transform * Vector3(-0.5f, -0.5f, -0.5f)); build->vertices_.Push(transform * Vector3(-0.5f, 0.5f, 0.5f)); build->vertices_.Push(transform * Vector3(0.5f, 0.5f, 0.5f)); build->vertices_.Push(transform * Vector3(0.5f, -0.5f, 0.5f)); build->vertices_.Push(transform * Vector3(-0.5f, -0.5f, 0.5f)); const unsigned indices[] = { 0, 1, 2, 0, 2, 3, 1, 5, 6, 1, 6, 2, 4, 5, 1, 4, 1, 0, 5, 4, 7, 5, 7, 6, 4, 0, 3, 4, 3, 7, 1, 0, 4, 1, 4, 5 }; for (unsigned j = 0; j < 36; ++j) build->indices_.Push(indices[j] + destVertexStart); } break; default: break; } continue; } #endif Drawable* drawable = dynamic_cast<Drawable*>(geometryList[i].component_); if (drawable) { const Vector<SourceBatch>& batches = drawable->GetBatches(); for (unsigned j = 0; j < batches.Size(); ++j) AddTriMeshGeometry(build, drawable->GetLodGeometry(j, geometryList[i].lodLevel_), transform); } } } }
void DynamicGeometry::CreateScene() { ResourceCache* cache = GetSubsystem<ResourceCache>(); scene_ = new Scene(context_); // Create the Octree component to the scene so that drawable objects can be rendered. Use default volume // (-1000, -1000, -1000) to (1000, 1000, 1000) scene_->CreateComponent<Octree>(); // Create a Zone for ambient light & fog control Node* zoneNode = scene_->CreateChild("Zone"); Zone* zone = zoneNode->CreateComponent<Zone>(); zone->SetBoundingBox(BoundingBox(-1000.0f, 1000.0f)); zone->SetFogColor(Color(0.2f, 0.2f, 0.2f)); zone->SetFogStart(200.0f); zone->SetFogEnd(300.0f); // Create a directional light Node* lightNode = scene_->CreateChild("DirectionalLight"); lightNode->SetDirection(Vector3(-0.6f, -1.0f, -0.8f)); // The direction vector does not need to be normalized Light* light = lightNode->CreateComponent<Light>(); light->SetLightType(LIGHT_DIRECTIONAL); light->SetColor(Color(0.4f, 1.0f, 0.4f)); light->SetSpecularIntensity(1.5f); // Get the original model and its unmodified vertices, which are used as source data for the animation Model* originalModel = cache->GetResource<Model>("Models/Box.mdl"); if (!originalModel) { ATOMIC_LOGERROR("Model not found, cannot initialize example scene"); return; } // Get the vertex buffer from the first geometry's first LOD level VertexBuffer* buffer = originalModel->GetGeometry(0, 0)->GetVertexBuffer(0); const unsigned char* vertexData = (const unsigned char*)buffer->Lock(0, buffer->GetVertexCount()); if (vertexData) { unsigned numVertices = buffer->GetVertexCount(); unsigned vertexSize = buffer->GetVertexSize(); // Copy the original vertex positions for (unsigned i = 0; i < numVertices; ++i) { const Vector3& src = *reinterpret_cast<const Vector3*>(vertexData + i * vertexSize); originalVertices_.Push(src); } buffer->Unlock(); // Detect duplicate vertices to allow seamless animation vertexDuplicates_.Resize(originalVertices_.Size()); for (unsigned i = 0; i < originalVertices_.Size(); ++i) { vertexDuplicates_[i] = i; // Assume not a duplicate for (unsigned j = 0; j < i; ++j) { if (originalVertices_[i].Equals(originalVertices_[j])) { vertexDuplicates_[i] = j; break; } } } } else { ATOMIC_LOGERROR("Failed to lock the model vertex buffer to get original vertices"); return; } // Create StaticModels in the scene. Clone the model for each so that we can modify the vertex data individually for (int y = -1; y <= 1; ++y) { for (int x = -1; x <= 1; ++x) { Node* node = scene_->CreateChild("Object"); node->SetPosition(Vector3(x * 2.0f, 0.0f, y * 2.0f)); StaticModel* object = node->CreateComponent<StaticModel>(); SharedPtr<Model> cloneModel = originalModel->Clone(); object->SetModel(cloneModel); // Store the cloned vertex buffer that we will modify when animating animatingBuffers_.Push(SharedPtr<VertexBuffer>(cloneModel->GetGeometry(0, 0)->GetVertexBuffer(0))); } } // Finally create one model (pyramid shape) and a StaticModel to display it from scratch // Note: there are duplicated vertices to enable face normals. We will calculate normals programmatically { const unsigned numVertices = 18; float vertexData[] = { // Position Normal 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, 0.0f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, 0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 0.0f, -0.5f, -0.5f, -0.5f, 0.0f, 0.0f, 0.0f }; const unsigned short indexData[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 }; // Calculate face normals now for (unsigned i = 0; i < numVertices; i += 3) { Vector3& v1 = *(reinterpret_cast<Vector3*>(&vertexData[6 * i])); Vector3& v2 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 1)])); Vector3& v3 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 2)])); Vector3& n1 = *(reinterpret_cast<Vector3*>(&vertexData[6 * i + 3])); Vector3& n2 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 1) + 3])); Vector3& n3 = *(reinterpret_cast<Vector3*>(&vertexData[6 * (i + 2) + 3])); Vector3 edge1 = v1 - v2; Vector3 edge2 = v1 - v3; n1 = n2 = n3 = edge1.CrossProduct(edge2).Normalized(); } SharedPtr<Model> fromScratchModel(new Model(context_)); SharedPtr<VertexBuffer> vb(new VertexBuffer(context_)); SharedPtr<IndexBuffer> ib(new IndexBuffer(context_)); SharedPtr<Geometry> geom(new Geometry(context_)); // Shadowed buffer needed for raycasts to work, and so that data can be automatically restored on device loss vb->SetShadowed(true); // We could use the "legacy" element bitmask to define elements for more compact code, but let's demonstrate // defining the vertex elements explicitly to allow any element types and order PODVector<VertexElement> elements; elements.Push(VertexElement(TYPE_VECTOR3, SEM_POSITION)); elements.Push(VertexElement(TYPE_VECTOR3, SEM_NORMAL)); vb->SetSize(numVertices, elements); vb->SetData(vertexData); ib->SetShadowed(true); ib->SetSize(numVertices, false); ib->SetData(indexData); geom->SetVertexBuffer(0, vb); geom->SetIndexBuffer(ib); geom->SetDrawRange(TRIANGLE_LIST, 0, numVertices); fromScratchModel->SetNumGeometries(1); fromScratchModel->SetGeometry(0, 0, geom); fromScratchModel->SetBoundingBox(BoundingBox(Vector3(-0.5f, -0.5f, -0.5f), Vector3(0.5f, 0.5f, 0.5f))); // Though not necessary to render, the vertex & index buffers must be listed in the model so that it can be saved properly Vector<SharedPtr<VertexBuffer> > vertexBuffers; Vector<SharedPtr<IndexBuffer> > indexBuffers; vertexBuffers.Push(vb); indexBuffers.Push(ib); // Morph ranges could also be not defined. Here we simply define a zero range (no morphing) for the vertex buffer PODVector<unsigned> morphRangeStarts; PODVector<unsigned> morphRangeCounts; morphRangeStarts.Push(0); morphRangeCounts.Push(0); fromScratchModel->SetVertexBuffers(vertexBuffers, morphRangeStarts, morphRangeCounts); fromScratchModel->SetIndexBuffers(indexBuffers); Node* node = scene_->CreateChild("FromScratchObject"); node->SetPosition(Vector3(0.0f, 3.0f, 0.0f)); StaticModel* object = node->CreateComponent<StaticModel>(); object->SetModel(fromScratchModel); } // Create the camera cameraNode_ = new Node(context_); cameraNode_->SetPosition(Vector3(0.0f, 2.0f, -20.0f)); Camera* camera = cameraNode_->CreateComponent<Camera>(); camera->SetFarClip(300.0f); }
//============================================================================= //============================================================================= void StaticModelPoolMgr::AddStaticModelData(Node *pNode, StaticModel *pStaticModel) { NvMeshData nvMeshData; StaticModelData staticModelData; Model *pModel = pStaticModel->GetModel(); // only one geom currently supprted assert( pModel && pModel->GetNumGeometries() == 1 && "multiple gemoetries currently NOT supported" ); Matrix3x4 objMatrix = pNode->GetTransform(); Quaternion objRotation = pNode->GetRotation(); Geometry *pGeometry = pModel->GetGeometry(0, 0); VertexBuffer *pVbuffer = pGeometry->GetVertexBuffer(0); unsigned uElementMask = pVbuffer->GetElementMask(); unsigned vertexSize = pVbuffer->GetVertexSize(); const unsigned char *pVertexData = (const unsigned char*)pVbuffer->Lock(0, pVbuffer->GetVertexCount()); // get verts, normals, uv, etc. if ( pVertexData ) { unsigned numVertices = pVbuffer->GetVertexCount(); for ( unsigned i = 0; i < numVertices; ++i ) { unsigned char *pDataAlign = (unsigned char *)(pVertexData + i * vertexSize); if ( uElementMask & MASK_POSITION ) { const Vector3 vPos = *reinterpret_cast<Vector3*>( pDataAlign ); pDataAlign += sizeof( Vector3 ); Vector3 vxformPos = objMatrix * vPos; // xform // verts list staticModelData.listVerts.Push( vxformPos ); nvMeshData.listVerts.push_back( Vec3f( vxformPos.x_, vxformPos.y_, vxformPos.z_ ) ); } if ( uElementMask & MASK_NORMAL ) { const Vector3 vNorm = *reinterpret_cast<Vector3*>( pDataAlign ); pDataAlign += sizeof( Vector3 ); // normal list Vector3 vxformNorm = objRotation * vNorm; // xform staticModelData.listNormals.Push( vxformNorm ); nvMeshData.listNormals.push_back( Vec3f( vxformNorm.x_, vxformNorm.y_, vxformNorm.z_ ) ); } if ( uElementMask & MASK_COLOR ) { const unsigned uColor = *reinterpret_cast<unsigned*>( pDataAlign ); pDataAlign += sizeof( unsigned ); } if ( uElementMask & MASK_TEXCOORD1 ) { const Vector2 vUV = *reinterpret_cast<Vector2*>( pDataAlign ); pDataAlign += sizeof( Vector2 ); // uv list staticModelData.listUVs.Push( vUV ); } // skip other mask elements - we got what we wanted } //unlock pVbuffer->Unlock(); } else { // error assert( false && "failed to unlock vertex buffer" ); } // get indeces IndexBuffer *pIbuffer = pGeometry->GetIndexBuffer(); const unsigned *pIndexData = (const unsigned *)pIbuffer->Lock( 0, pIbuffer->GetIndexCount() ); const unsigned short *pUShortData = (const unsigned short *)pIndexData; if ( pUShortData ) { unsigned numIndeces = pIbuffer->GetIndexCount(); unsigned indexSize = pIbuffer->GetIndexSize(); assert( indexSize == sizeof(unsigned short) ); for( unsigned i = 0; i < numIndeces; i += 3 ) { int idx0 = (int)pUShortData[i ]; int idx1 = (int)pUShortData[i+1]; int idx2 = (int)pUShortData[i+2]; staticModelData.listTris.Push( IntVector3( idx0, idx1, idx2 ) ); nvMeshData.listTris.push_back( Vec3i( idx0, idx1, idx2 ) ); } //unlock pIbuffer->Unlock(); } else { // error assert( false && "failed to unlock index buffer" ); } // rest of the static model data staticModelData.node = pNode; staticModelData.drawable = pStaticModel; staticModelData.begVertex = m_pNvScene->getNumVertices(); staticModelData.begTriList = m_pNvScene->getNumTriangles(); // resize coeff lists staticModelData.listVertexUnshadoweCoeff.Resize( staticModelData.listVerts.Size() ); staticModelData.listVertexShadowCoeff.Resize( staticModelData.listVerts.Size() ); staticModelData.listVertexDiffuseColor.Resize( staticModelData.listVerts.Size() ); staticModelData.listVertexSampleOcclude.resize( staticModelData.listVerts.Size() ); // find material matcolor VertexUtil *pVertexUtil = GetScene()->GetComponent<VertexUtil>(); staticModelData.materialColor = pVertexUtil->GetModelMaterialColor( pStaticModel ); // save model m_vStaticModelPool.Push( staticModelData ); // push it to nv Scene m_pNvScene->AddTriangleMesh( nvMeshData ); }