void DecalSet::OnWorldBoundingBoxUpdate() { if (!skinned_) { if (boundingBoxDirty_) CalculateBoundingBox(); worldBoundingBox_ = boundingBox_.Transformed(node_->GetWorldTransform()); } else { // When using skinning, update world bounding box based on the bones BoundingBox worldBox; for (Vector<Bone>::ConstIterator i = bones_.Begin(); i != bones_.End(); ++i) { Node* boneNode = i->node_; if (!boneNode) continue; // Use hitbox if available. If not, use only half of the sphere radius /// \todo The sphere radius should be multiplied with bone scale if (i->collisionMask_ & BONECOLLISION_BOX) worldBox.Merge(i->boundingBox_.Transformed(boneNode->GetWorldTransform())); else if (i->collisionMask_ & BONECOLLISION_SPHERE) worldBox.Merge(Sphere(boneNode->GetWorldPosition(), i->radius_ * 0.5f)); } worldBoundingBox_ = worldBox; } }
void BillboardSet::OnWorldBoundingBoxUpdate() { unsigned enabledBillboards = 0; const Matrix3x4& worldTransform = node_->GetWorldTransform(); Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY; Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE; BoundingBox worldBox; for (unsigned i = 0; i < billboards_.Size(); ++i) { if (!billboards_[i]->enabled_) continue; float size = INV_SQRT_TWO * (billboards_[i]->size_.x_ * billboardScale.x_ + billboards_[i]->size_.y_ * billboardScale.y_); Vector3 center = billboardTransform * billboards_[i]->position_; Vector3 edge = Vector3::ONE * size; worldBox.Merge(BoundingBox(center - edge, center + edge)); ++enabledBillboards; } // Always merge the node's own position to ensure particle emitter updates continue when the relative mode is switched worldBox.Merge(node_->GetWorldPosition()); worldBoundingBox_ = worldBox; }
void BillboardSet::OnWorldBoundingBoxUpdate() { unsigned enabledBillboards = 0; const Matrix3x4& worldTransform = node_->GetWorldTransform(); Matrix3x4 billboardTransform = relative_ ? worldTransform : Matrix3x4::IDENTITY; Vector3 billboardScale = scaled_ ? worldTransform.Scale() : Vector3::ONE; BoundingBox worldBox; for (unsigned i = 0; i < billboards_.Size(); ++i) { if (!billboards_[i].enabled_) continue; float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_); Vector3 center = billboardTransform * billboards_[i].position_; Vector3 edge = Vector3::ONE * size; worldBox.Merge(BoundingBox(center - edge, center + edge)); ++enabledBillboards; } // If no billboards enabled, the bounding box is just the node's world position if (!enabledBillboards) worldBox.Merge(node_->GetWorldPosition()); worldBoundingBox_ = worldBox; }
//显示选中物体的包围盒 void ObjectPositionEditor::HandlePostRenderUpdate(StringHash eventType, VariantMap& eventData) { EditorRoot* pEditorRoot = EditorRoot::Instance(); DebugRenderer* debug = pEditorRoot->scene_->GetComponent<DebugRenderer>(); if(debug == NULL) return; vector<Node*> nodes = pEditorRoot->GetUnionSelections(); for(int i = 0;i < nodes.size();i ++) { Node* node = nodes[i]; if(node->GetComponent<Skybox>() != NULL) continue; debug->AddNode(node,1.0f,false); const Vector<SharedPtr<Component> >& components = node->GetComponents(); for(int j = 0;j < node->GetNumComponents();j ++) { Drawable* drawable = dynamic_cast<Drawable*>(components[j].Get()); if(drawable != NULL) { debug->AddBoundingBox(drawable->GetWorldBoundingBox(),Color::WHITE,true); } } } //计算总的 if(nodes.size() > 1) { BoundingBox allBox; for(int i = 0;i < nodes.size();i ++) { Node* node = nodes[i]; if(node->GetComponent<Skybox>() != NULL) continue; const Vector<SharedPtr<Component> >& components = node->GetComponents(); for(int j = 0;j < node->GetNumComponents();j ++) { Drawable* drawable = dynamic_cast<Drawable*>(components[j].Get()); if(drawable != NULL) { allBox.Merge(drawable->GetWorldBoundingBox()); } } } debug->AddBoundingBox(allBox,Color::BLUE,true); } if(CurrentHoverObject != NULL) { CurrentHoverObject->DrawDebugGeometry(debug,false); } }
BoundingBox Camera::GetViewBox(const Frustum* frustum, const Scene* scene, bool receivers, bool casters) { BoundingBox result; std::vector<SceneNode*> visibles; scene->GetVisibleNodes(frustum, visibles); for (auto& visible : visibles) { auto material = visible->GetMaterial().get(); if (!material) continue; if ((receivers && material->ReceiveShadows()) || (casters && material->CastShadow())) { BoundingBox bb(visible->GetWorldBoundingBox()); result.Merge(bb); } } return result; }
void StaticModelGroup::OnWorldBoundingBoxUpdate() { // Update transforms and bounding box at the same time to have to go through the objects only once unsigned index = 0; BoundingBox worldBox; for (unsigned i = 0; i < instanceNodes_.Size(); ++i) { Node* node = instanceNodes_[i]; if (!node || !node->IsEnabled()) continue; const Matrix3x4& worldTransform = node->GetWorldTransform(); worldTransforms_[index++] = worldTransform; worldBox.Merge(boundingBox_.Transformed(worldTransform)); } worldBoundingBox_ = worldBox; // Store the amount of valid instances we found instead of resizing worldTransforms_. This is because this function may be // called from multiple worker threads simultaneously numWorldTransforms_ = index; }
void Terrain::CreatePatchGeometry(TerrainPatch* patch) { URHO3D_PROFILE(CreatePatchGeometry); unsigned row = (unsigned)(patchSize_ + 1); VertexBuffer* vertexBuffer = patch->GetVertexBuffer(); Geometry* geometry = patch->GetGeometry(); Geometry* maxLodGeometry = patch->GetMaxLodGeometry(); Geometry* occlusionGeometry = patch->GetOcclusionGeometry(); if (vertexBuffer->GetVertexCount() != row * row) vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT); SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]); SharedArrayPtr<unsigned char> occlusionCpuVertexData(new unsigned char[row * row * sizeof(Vector3)]); float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount()); float* positionData = (float*)cpuVertexData.Get(); float* occlusionData = (float*)occlusionCpuVertexData.Get(); BoundingBox box; unsigned occlusionLevel = occlusionLodLevel_; if (occlusionLevel > numLodLevels_ - 1) occlusionLevel = numLodLevels_ - 1; if (vertexData) { const IntVector2& coords = patch->GetCoordinates(); int lodExpand = (1 << (occlusionLevel)) - 1; int halfLodExpand = (1 << (occlusionLevel)) / 2; for (int z = 0; z <= patchSize_; ++z) { for (int x = 0; x <= patchSize_; ++x) { int xPos = coords.x_ * patchSize_ + x; int zPos = coords.y_ * patchSize_ + z; // Position Vector3 position((float)x * spacing_.x_, GetRawHeight(xPos, zPos), (float)z * spacing_.z_); *vertexData++ = position.x_; *vertexData++ = position.y_; *vertexData++ = position.z_; *positionData++ = position.x_; *positionData++ = position.y_; *positionData++ = position.z_; box.Merge(position); // For vertices that are part of the occlusion LOD, calculate the minimum height in the neighborhood // to prevent false positive occlusion due to inaccuracy between occlusion LOD & visible LOD float minHeight = position.y_; if (halfLodExpand > 0 && (x & lodExpand) == 0 && (z & lodExpand) == 0) { int minX = Max(xPos - halfLodExpand, 0); int maxX = Min(xPos + halfLodExpand, numVertices_.x_ - 1); int minZ = Max(zPos - halfLodExpand, 0); int maxZ = Min(zPos + halfLodExpand, numVertices_.y_ - 1); for (int nZ = minZ; nZ <= maxZ; ++nZ) { for (int nX = minX; nX <= maxX; ++nX) minHeight = Min(minHeight, GetRawHeight(nX, nZ)); } } *occlusionData++ = position.x_; *occlusionData++ = minHeight; *occlusionData++ = position.z_; // Normal Vector3 normal = GetRawNormal(xPos, zPos); *vertexData++ = normal.x_; *vertexData++ = normal.y_; *vertexData++ = normal.z_; // Texture coordinate Vector2 texCoord((float)xPos / (float)numVertices_.x_, 1.0f - (float)zPos / (float)numVertices_.y_); *vertexData++ = texCoord.x_; *vertexData++ = texCoord.y_; // Tangent Vector3 xyz = (Vector3::RIGHT - normal * normal.DotProduct(Vector3::RIGHT)).Normalized(); *vertexData++ = xyz.x_; *vertexData++ = xyz.y_; *vertexData++ = xyz.z_; *vertexData++ = 1.0f; } } vertexBuffer->Unlock(); vertexBuffer->ClearDataLost(); } patch->SetBoundingBox(box); if (drawRanges_.Size()) { unsigned occlusionDrawRange = occlusionLevel << 4; geometry->SetIndexBuffer(indexBuffer_); geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false); geometry->SetRawVertexData(cpuVertexData, MASK_POSITION); maxLodGeometry->SetIndexBuffer(indexBuffer_); maxLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false); maxLodGeometry->SetRawVertexData(cpuVertexData, MASK_POSITION); occlusionGeometry->SetIndexBuffer(indexBuffer_); occlusionGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[occlusionDrawRange].first_, drawRanges_[occlusionDrawRange].second_, false); occlusionGeometry->SetRawVertexData(occlusionCpuVertexData, MASK_POSITION); } patch->ResetLod(); }
void LoadMesh(const String& inputFileName, bool generateTangents, bool splitSubMeshes, bool exportMorphs) { File meshFileSource(context_); meshFileSource.Open(inputFileName); if (!meshFile_->Load(meshFileSource)) ErrorExit("Could not load input file " + inputFileName); XMLElement root = meshFile_->GetRoot("mesh"); XMLElement subMeshes = root.GetChild("submeshes"); XMLElement skeletonLink = root.GetChild("skeletonlink"); if (root.IsNull()) ErrorExit("Could not load input file " + inputFileName); String skeletonName = skeletonLink.GetAttribute("name"); if (!skeletonName.Empty()) LoadSkeleton(GetPath(inputFileName) + GetFileName(skeletonName) + ".skeleton.xml"); // Check whether there's benefit of avoiding 32bit indices by splitting each submesh into own buffer XMLElement subMesh = subMeshes.GetChild("submesh"); unsigned totalVertices = 0; unsigned maxSubMeshVertices = 0; while (subMesh) { materialNames_.Push(subMesh.GetAttribute("material")); XMLElement geometry = subMesh.GetChild("geometry"); if (geometry) { unsigned vertices = geometry.GetInt("vertexcount"); totalVertices += vertices; if (maxSubMeshVertices < vertices) maxSubMeshVertices = vertices; } ++numSubMeshes_; subMesh = subMesh.GetNext("submesh"); } XMLElement sharedGeometry = root.GetChild("sharedgeometry"); if (sharedGeometry) { unsigned vertices = sharedGeometry.GetInt("vertexcount"); totalVertices += vertices; if (maxSubMeshVertices < vertices) maxSubMeshVertices = vertices; } if (!sharedGeometry && (splitSubMeshes || (totalVertices > 65535 && maxSubMeshVertices <= 65535))) { useOneBuffer_ = false; vertexBuffers_.Resize(numSubMeshes_); indexBuffers_.Resize(numSubMeshes_); } else { vertexBuffers_.Resize(1); indexBuffers_.Resize(1); } subMesh = subMeshes.GetChild("submesh"); unsigned indexStart = 0; unsigned vertexStart = 0; unsigned subMeshIndex = 0; PODVector<unsigned> vertexStarts; vertexStarts.Resize(numSubMeshes_); while (subMesh) { XMLElement geometry = subMesh.GetChild("geometry"); XMLElement faces = subMesh.GetChild("faces"); // If no submesh vertexbuffer, process the shared geometry, but do it only once unsigned vertices = 0; if (!geometry) { vertexStart = 0; if (!subMeshIndex) geometry = root.GetChild("sharedgeometry"); } if (geometry) vertices = geometry.GetInt("vertexcount"); ModelSubGeometryLodLevel subGeometryLodLevel; ModelVertexBuffer* vBuf; ModelIndexBuffer* iBuf; if (useOneBuffer_) { vBuf = &vertexBuffers_[0]; if (vertices) vBuf->vertices_.Resize(vertexStart + vertices); iBuf = &indexBuffers_[0]; subGeometryLodLevel.vertexBuffer_ = 0; subGeometryLodLevel.indexBuffer_ = 0; } else { vertexStart = 0; indexStart = 0; vBuf = &vertexBuffers_[subMeshIndex]; vBuf->vertices_.Resize(vertices); iBuf = &indexBuffers_[subMeshIndex]; subGeometryLodLevel.vertexBuffer_ = subMeshIndex; subGeometryLodLevel.indexBuffer_ = subMeshIndex; } // Store the start vertex for later use vertexStarts[subMeshIndex] = vertexStart; // Ogre may have multiple buffers in one submesh. These will be merged into one XMLElement bufferDef; if (geometry) bufferDef = geometry.GetChild("vertexbuffer"); while (bufferDef) { if (bufferDef.HasAttribute("positions")) vBuf->elementMask_ |= MASK_POSITION; if (bufferDef.HasAttribute("normals")) vBuf->elementMask_ |= MASK_NORMAL; if (bufferDef.HasAttribute("texture_coords")) { vBuf->elementMask_ |= MASK_TEXCOORD1; if (bufferDef.GetInt("texture_coords") > 1) vBuf->elementMask_ |= MASK_TEXCOORD2; } unsigned vertexNum = vertexStart; if (vertices) { XMLElement vertex = bufferDef.GetChild("vertex"); while (vertex) { XMLElement position = vertex.GetChild("position"); if (position) { // Convert from right- to left-handed float x = position.GetFloat("x"); float y = position.GetFloat("y"); float z = position.GetFloat("z"); Vector3 vec(x, y, -z); vBuf->vertices_[vertexNum].position_ = vec; boundingBox_.Merge(vec); } XMLElement normal = vertex.GetChild("normal"); if (normal) { // Convert from right- to left-handed float x = normal.GetFloat("x"); float y = normal.GetFloat("y"); float z = normal.GetFloat("z"); Vector3 vec(x, y, -z); vBuf->vertices_[vertexNum].normal_ = vec; } XMLElement uv = vertex.GetChild("texcoord"); if (uv) { float x = uv.GetFloat("u"); float y = uv.GetFloat("v"); Vector2 vec(x, y); vBuf->vertices_[vertexNum].texCoord1_ = vec; if (vBuf->elementMask_ & MASK_TEXCOORD2) { uv = uv.GetNext("texcoord"); if (uv) { float x = uv.GetFloat("u"); float y = uv.GetFloat("v"); Vector2 vec(x, y); vBuf->vertices_[vertexNum].texCoord2_ = vec; } } } vertexNum++; vertex = vertex.GetNext("vertex"); } } bufferDef = bufferDef.GetNext("vertexbuffer"); } unsigned triangles = faces.GetInt("count"); unsigned indices = triangles * 3; XMLElement triangle = faces.GetChild("face"); while (triangle) { unsigned v1 = triangle.GetInt("v1"); unsigned v2 = triangle.GetInt("v2"); unsigned v3 = triangle.GetInt("v3"); iBuf->indices_.Push(v3 + vertexStart); iBuf->indices_.Push(v2 + vertexStart); iBuf->indices_.Push(v1 + vertexStart); triangle = triangle.GetNext("face"); } subGeometryLodLevel.indexStart_ = indexStart; subGeometryLodLevel.indexCount_ = indices; if (vertexStart + vertices > 65535) iBuf->indexSize_ = sizeof(unsigned); XMLElement boneAssignments = subMesh.GetChild("boneassignments"); if (bones_.Size()) { if (boneAssignments) { XMLElement boneAssignment = boneAssignments.GetChild("vertexboneassignment"); while (boneAssignment) { unsigned vertex = boneAssignment.GetInt("vertexindex") + vertexStart; unsigned bone = boneAssignment.GetInt("boneindex"); float weight = boneAssignment.GetFloat("weight"); BoneWeightAssignment assign; assign.boneIndex_ = bone; assign.weight_ = weight; // Source data might have 0 weights. Disregard these if (assign.weight_ > 0.0f) { subGeometryLodLevel.boneWeights_[vertex].Push(assign); // Require skinning weight to be sufficiently large before vertex contributes to bone hitbox if (assign.weight_ > 0.33f) { // Check distance of vertex from bone to get bone max. radius information Vector3 bonePos = bones_[bone].derivedPosition_; Vector3 vertexPos = vBuf->vertices_[vertex].position_; float distance = (bonePos - vertexPos).Length(); if (distance > bones_[bone].radius_) { bones_[bone].collisionMask_ |= 1; bones_[bone].radius_ = distance; } // Build the hitbox for the bone bones_[bone].boundingBox_.Merge(bones_[bone].inverseWorldTransform_ * (vertexPos)); bones_[bone].collisionMask_ |= 2; } } boneAssignment = boneAssignment.GetNext("vertexboneassignment"); } } if ((subGeometryLodLevel.boneWeights_.Size()) && bones_.Size()) { vBuf->elementMask_ |= MASK_BLENDWEIGHTS | MASK_BLENDINDICES; bool sorted = false; // If amount of bones is larger than supported by HW skinning, must remap per submesh if (bones_.Size() > maxBones_) { HashMap<unsigned, unsigned> usedBoneMap; unsigned remapIndex = 0; for (HashMap<unsigned, PODVector<BoneWeightAssignment> >::Iterator i = subGeometryLodLevel.boneWeights_.Begin(); i != subGeometryLodLevel.boneWeights_.End(); ++i) { // Sort the bone assigns by weight Sort(i->second_.Begin(), i->second_.End(), CompareWeights); // Use only the first 4 weights for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j) { unsigned originalIndex = i->second_[j].boneIndex_; if (!usedBoneMap.Contains(originalIndex)) { usedBoneMap[originalIndex] = remapIndex; remapIndex++; } i->second_[j].boneIndex_ = usedBoneMap[originalIndex]; } } // If still too many bones in one subgeometry, error if (usedBoneMap.Size() > maxBones_) ErrorExit("Too many bones (limit " + String(maxBones_) + ") in submesh " + String(subMeshIndex + 1)); // Write mapping of vertex buffer bone indices to original bone indices subGeometryLodLevel.boneMapping_.Resize(usedBoneMap.Size()); for (HashMap<unsigned, unsigned>::Iterator j = usedBoneMap.Begin(); j != usedBoneMap.End(); ++j) subGeometryLodLevel.boneMapping_[j->second_] = j->first_; sorted = true; } for (HashMap<unsigned, PODVector<BoneWeightAssignment> >::Iterator i = subGeometryLodLevel.boneWeights_.Begin(); i != subGeometryLodLevel.boneWeights_.End(); ++i) { // Sort the bone assigns by weight, if not sorted yet in bone remapping pass if (!sorted) Sort(i->second_.Begin(), i->second_.End(), CompareWeights); float totalWeight = 0.0f; float normalizationFactor = 0.0f; // Calculate normalization factor in case there are more than 4 blend weights, or they do not add up to 1 for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j) totalWeight += i->second_[j].weight_; if (totalWeight > 0.0f) normalizationFactor = 1.0f / totalWeight; for (unsigned j = 0; j < i->second_.Size() && j < 4; ++j) { vBuf->vertices_[i->first_].blendIndices_[j] = i->second_[j].boneIndex_; vBuf->vertices_[i->first_].blendWeights_[j] = i->second_[j].weight_ * normalizationFactor; } // If there are less than 4 blend weights, fill rest with zero for (unsigned j = i->second_.Size(); j < 4; ++j) { vBuf->vertices_[i->first_].blendIndices_[j] = 0; vBuf->vertices_[i->first_].blendWeights_[j] = 0.0f; } vBuf->vertices_[i->first_].hasBlendWeights_ = true; } } } else if (boneAssignments) PrintLine("No skeleton loaded, skipping skinning information"); // Calculate center for the subgeometry Vector3 center = Vector3::ZERO; for (unsigned i = 0; i < iBuf->indices_.Size(); i += 3) { center += vBuf->vertices_[iBuf->indices_[i]].position_; center += vBuf->vertices_[iBuf->indices_[i + 1]].position_; center += vBuf->vertices_[iBuf->indices_[i + 2]].position_; } if (iBuf->indices_.Size()) center /= (float)iBuf->indices_.Size(); subGeometryCenters_.Push(center); indexStart += indices; vertexStart += vertices; OptimizeIndices(&subGeometryLodLevel, vBuf, iBuf); PrintLine("Processed submesh " + String(subMeshIndex + 1) + ": " + String(vertices) + " vertices " + String(triangles) + " triangles"); Vector<ModelSubGeometryLodLevel> thisSubGeometry; thisSubGeometry.Push(subGeometryLodLevel); subGeometries_.Push(thisSubGeometry); subMesh = subMesh.GetNext("submesh"); subMeshIndex++; } // Process LOD levels, if any XMLElement lods = root.GetChild("levelofdetail"); if (lods) { try { // For now, support only generated LODs, where the vertices are the same XMLElement lod = lods.GetChild("lodgenerated"); while (lod) { float distance = M_EPSILON; if (lod.HasAttribute("fromdepthsquared")) distance = sqrtf(lod.GetFloat("fromdepthsquared")); if (lod.HasAttribute("value")) distance = lod.GetFloat("value"); XMLElement lodSubMesh = lod.GetChild("lodfacelist"); while (lodSubMesh) { unsigned subMeshIndex = lodSubMesh.GetInt("submeshindex"); unsigned triangles = lodSubMesh.GetInt("numfaces"); ModelSubGeometryLodLevel newLodLevel; ModelSubGeometryLodLevel& originalLodLevel = subGeometries_[subMeshIndex][0]; // Copy all initial values newLodLevel = originalLodLevel; ModelVertexBuffer* vBuf; ModelIndexBuffer* iBuf; if (useOneBuffer_) { vBuf = &vertexBuffers_[0]; iBuf = &indexBuffers_[0]; } else { vBuf = &vertexBuffers_[subMeshIndex]; iBuf = &indexBuffers_[subMeshIndex]; } unsigned indexStart = iBuf->indices_.Size(); unsigned indexCount = triangles * 3; unsigned vertexStart = vertexStarts[subMeshIndex]; newLodLevel.distance_ = distance; newLodLevel.indexStart_ = indexStart; newLodLevel.indexCount_ = indexCount; // Append indices to the original index buffer XMLElement triangle = lodSubMesh.GetChild("face"); while (triangle) { unsigned v1 = triangle.GetInt("v1"); unsigned v2 = triangle.GetInt("v2"); unsigned v3 = triangle.GetInt("v3"); iBuf->indices_.Push(v3 + vertexStart); iBuf->indices_.Push(v2 + vertexStart); iBuf->indices_.Push(v1 + vertexStart); triangle = triangle.GetNext("face"); } OptimizeIndices(&newLodLevel, vBuf, iBuf); subGeometries_[subMeshIndex].Push(newLodLevel); PrintLine("Processed LOD level for submesh " + String(subMeshIndex + 1) + ": distance " + String(distance)); lodSubMesh = lodSubMesh.GetNext("lodfacelist"); } lod = lod.GetNext("lodgenerated"); } } catch (...) {} } // Process poses/morphs // First find out all pose definitions if (exportMorphs) { try { Vector<XMLElement> poses; XMLElement posesRoot = root.GetChild("poses"); if (posesRoot) { XMLElement pose = posesRoot.GetChild("pose"); while (pose) { poses.Push(pose); pose = pose.GetNext("pose"); } } // Then process animations using the poses XMLElement animsRoot = root.GetChild("animations"); if (animsRoot) { XMLElement anim = animsRoot.GetChild("animation"); while (anim) { String name = anim.GetAttribute("name"); float length = anim.GetFloat("length"); HashSet<unsigned> usedPoses; XMLElement tracks = anim.GetChild("tracks"); if (tracks) { XMLElement track = tracks.GetChild("track"); while (track) { XMLElement keyframes = track.GetChild("keyframes"); if (keyframes) { XMLElement keyframe = keyframes.GetChild("keyframe"); while (keyframe) { float time = keyframe.GetFloat("time"); XMLElement poseref = keyframe.GetChild("poseref"); // Get only the end pose if (poseref && time == length) usedPoses.Insert(poseref.GetInt("poseindex")); keyframe = keyframe.GetNext("keyframe"); } } track = track.GetNext("track"); } } if (usedPoses.Size()) { ModelMorph newMorph; newMorph.name_ = name; if (useOneBuffer_) newMorph.buffers_.Resize(1); else newMorph.buffers_.Resize(usedPoses.Size()); unsigned bufIndex = 0; for (HashSet<unsigned>::Iterator i = usedPoses.Begin(); i != usedPoses.End(); ++i) { XMLElement pose = poses[*i]; unsigned targetSubMesh = pose.GetInt("index"); XMLElement poseOffset = pose.GetChild("poseoffset"); if (useOneBuffer_) newMorph.buffers_[bufIndex].vertexBuffer_ = 0; else newMorph.buffers_[bufIndex].vertexBuffer_ = targetSubMesh; newMorph.buffers_[bufIndex].elementMask_ = MASK_POSITION; ModelVertexBuffer* vBuf = &vertexBuffers_[newMorph.buffers_[bufIndex].vertexBuffer_]; while (poseOffset) { // Convert from right- to left-handed unsigned vertexIndex = poseOffset.GetInt("index") + vertexStarts[targetSubMesh]; float x = poseOffset.GetFloat("x"); float y = poseOffset.GetFloat("y"); float z = poseOffset.GetFloat("z"); Vector3 vec(x, y, -z); if (vBuf->morphCount_ == 0) { vBuf->morphStart_ = vertexIndex; vBuf->morphCount_ = 1; } else { unsigned first = vBuf->morphStart_; unsigned last = first + vBuf->morphCount_ - 1; if (vertexIndex < first) first = vertexIndex; if (vertexIndex > last) last = vertexIndex; vBuf->morphStart_ = first; vBuf->morphCount_ = last - first + 1; } ModelVertex newVertex; newVertex.position_ = vec; newMorph.buffers_[bufIndex].vertices_.Push(MakePair(vertexIndex, newVertex)); poseOffset = poseOffset.GetNext("poseoffset"); } if (!useOneBuffer_) ++bufIndex; } morphs_.Push(newMorph); PrintLine("Processed morph " + name + " with " + String(usedPoses.Size()) + " sub-poses"); } anim = anim.GetNext("animation"); } } } catch (...) {} } // Check any of the buffers for vertices with missing blend weight assignments for (unsigned i = 0; i < vertexBuffers_.Size(); ++i) { if (vertexBuffers_[i].elementMask_ & MASK_BLENDWEIGHTS) { for (unsigned j = 0; j < vertexBuffers_[i].vertices_.Size(); ++j) if (!vertexBuffers_[i].vertices_[j].hasBlendWeights_) ErrorExit("Found a vertex with missing skinning information"); } } // Tangent generation if (generateTangents) { for (unsigned i = 0; i < subGeometries_.Size(); ++i) { for (unsigned j = 0; j < subGeometries_[i].Size(); ++j) { ModelVertexBuffer& vBuf = vertexBuffers_[subGeometries_[i][j].vertexBuffer_]; ModelIndexBuffer& iBuf = indexBuffers_[subGeometries_[i][j].indexBuffer_]; unsigned indexStart = subGeometries_[i][j].indexStart_; unsigned indexCount = subGeometries_[i][j].indexCount_; // If already has tangents, do not regenerate if (vBuf.elementMask_ & MASK_TANGENT || vBuf.vertices_.Empty() || iBuf.indices_.Empty()) continue; vBuf.elementMask_ |= MASK_TANGENT; if ((vBuf.elementMask_ & (MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1)) != (MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1)) ErrorExit("To generate tangents, positions normals and texcoords are required"); GenerateTangents(&vBuf.vertices_[0], sizeof(ModelVertex), &iBuf.indices_[0], sizeof(unsigned), indexStart, indexCount, offsetof(ModelVertex, normal_), offsetof(ModelVertex, texCoord1_), offsetof(ModelVertex, tangent_)); PrintLine("Generated tangents"); } } } }
void Terrain::CreatePatchGeometry(TerrainPatch* patch) { PROFILE(CreatePatchGeometry); unsigned row = patchSize_ + 1; VertexBuffer* vertexBuffer = patch->GetVertexBuffer(); Geometry* geometry = patch->GetGeometry(); Geometry* maxLodGeometry = patch->GetMaxLodGeometry(); Geometry* minLodGeometry = patch->GetMinLodGeometry(); if (vertexBuffer->GetVertexCount() != row * row) vertexBuffer->SetSize(row * row, MASK_POSITION | MASK_NORMAL | MASK_TEXCOORD1 | MASK_TANGENT); SharedArrayPtr<unsigned char> cpuVertexData(new unsigned char[row * row * sizeof(Vector3)]); float* vertexData = (float*)vertexBuffer->Lock(0, vertexBuffer->GetVertexCount()); float* positionData = (float*)cpuVertexData.Get(); BoundingBox box; if (vertexData) { const IntVector2& coords = patch->GetCoordinates(); for (int z1 = 0; z1 <= patchSize_; ++z1) { for (int x1 = 0; x1 <= patchSize_; ++x1) { int xPos = coords.x_ * patchSize_ + x1; int zPos = coords.y_ * patchSize_ + z1; // Position Vector3 position((float)x1 * spacing_.x_, GetRawHeight(xPos, zPos), (float)z1 * spacing_.z_); *vertexData++ = position.x_; *vertexData++ = position.y_; *vertexData++ = position.z_; *positionData++ = position.x_; *positionData++ = position.y_; *positionData++ = position.z_; box.Merge(position); // Normal Vector3 normal = GetRawNormal(xPos, zPos); *vertexData++ = normal.x_; *vertexData++ = normal.y_; *vertexData++ = normal.z_; // Texture coordinate Vector2 texCoord((float)xPos / (float)numVertices_.x_, 1.0f - (float)zPos / (float)numVertices_.y_); *vertexData++ = texCoord.x_; *vertexData++ = texCoord.y_; // Tangent Vector3 xyz = (Vector3::RIGHT - normal * normal.DotProduct(Vector3::RIGHT)).Normalized(); *vertexData++ = xyz.x_; *vertexData++ = xyz.y_; *vertexData++ = xyz.z_; *vertexData++ = 1.0f; } } vertexBuffer->Unlock(); vertexBuffer->ClearDataLost(); } patch->SetBoundingBox(box); if (drawRanges_.Size()) { unsigned lastDrawRange = drawRanges_.Size() - 1; geometry->SetIndexBuffer(indexBuffer_); geometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false); geometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION); maxLodGeometry->SetIndexBuffer(indexBuffer_); maxLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[0].first_, drawRanges_[0].second_, false); maxLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION); minLodGeometry->SetIndexBuffer(indexBuffer_); minLodGeometry->SetDrawRange(TRIANGLE_LIST, drawRanges_[lastDrawRange].first_, drawRanges_[lastDrawRange].second_, false); minLodGeometry->SetRawVertexData(cpuVertexData, sizeof(Vector3), MASK_POSITION); } // Offset the occlusion geometry by vertex spacing to reduce possibility of over-aggressive occlusion patch->SetOcclusionOffset(-0.5f * (spacing_.x_ + spacing_.z_)); patch->ResetLod(); }
void WriteVertex(float*& dest, aiMesh* mesh, unsigned index, unsigned elementMask, BoundingBox& box, const Matrix3x4& vertexTransform, const Matrix3& normalTransform, Vector<PODVector<unsigned char> >& blendIndices, Vector<PODVector<float> >& blendWeights) { Vector3 vertex = vertexTransform * ToVector3(mesh->mVertices[index]); box.Merge(vertex); *dest++ = vertex.x_; *dest++ = vertex.y_; *dest++ = vertex.z_; if (elementMask & MASK_NORMAL) { Vector3 normal = normalTransform * ToVector3(mesh->mNormals[index]); *dest++ = normal.x_; *dest++ = normal.y_; *dest++ = normal.z_; } if (elementMask & MASK_COLOR) { *((unsigned*)dest) = Color(mesh->mColors[0][index].r, mesh->mColors[0][index].g, mesh->mColors[0][index].b, mesh->mColors[0][index].a).ToUInt(); ++dest; } if (elementMask & MASK_TEXCOORD1) { Vector3 texCoord = ToVector3(mesh->mTextureCoords[0][index]); *dest++ = texCoord.x_; *dest++ = texCoord.y_; } if (elementMask & MASK_TEXCOORD2) { Vector3 texCoord = ToVector3(mesh->mTextureCoords[1][index]); *dest++ = texCoord.x_; *dest++ = texCoord.y_; } if (elementMask & MASK_TANGENT) { Vector3 tangent = normalTransform * ToVector3(mesh->mTangents[index]); Vector3 normal = normalTransform * ToVector3(mesh->mNormals[index]); Vector3 bitangent = normalTransform * ToVector3(mesh->mBitangents[index]); // Check handedness float w = 1.0f; if ((tangent.CrossProduct(normal)).DotProduct(bitangent) < 0.5f) w = -1.0f; *dest++ = tangent.x_; *dest++ = tangent.y_; *dest++ = tangent.z_; *dest++ = w; } if (elementMask & MASK_BLENDWEIGHTS) { for (unsigned i = 0; i < 4; ++i) { if (i < blendWeights[index].Size()) *dest++ = blendWeights[index][i]; else *dest++ = 0.0f; } } if (elementMask & MASK_BLENDINDICES) { unsigned char* destBytes = (unsigned char*)dest; ++dest; for (unsigned i = 0; i < 4; ++i) { if (i < blendIndices[index].Size()) *destBytes++ = blendIndices[index][i]; else *destBytes++ = 0; } } }