void ListView::ChangeSelection(int delta, bool additive) { unsigned numItems = GetNumItems(); if (selections_.Empty()) { // Select first item if there is no selection yet if (numItems > 0) SetSelection(0); if (abs(delta) == 1) return; } if (!multiselect_) additive = false; // If going downwards, use the last selection as a base. Otherwise use first unsigned selection = delta > 0 ? selections_.Back() : selections_.Front(); int direction = delta > 0 ? 1 : -1; unsigned newSelection = selection; unsigned okSelection = selection; PODVector<unsigned> indices = selections_; while (delta != 0) { newSelection += direction; if (newSelection >= numItems) break; UIElement* item = GetItem(newSelection); if (item->IsVisible()) { indices.Push(okSelection = newSelection); delta -= direction; } } if (!additive) SetSelection(okSelection); else SetSelections(indices); }
void PhysicsWorld::Raycast(PODVector<PhysicsRaycastResult>& result, const Ray& ray, float maxDistance, unsigned collisionMask) { PROFILE(PhysicsRaycast); btCollisionWorld::AllHitsRayResultCallback rayCallback(ToBtVector3(ray.origin_), ToBtVector3(ray.origin_ + maxDistance * ray.direction_)); rayCallback.m_collisionFilterGroup = (short)0xffff; rayCallback.m_collisionFilterMask = collisionMask; world_->rayTest(rayCallback.m_rayFromWorld, rayCallback.m_rayToWorld, rayCallback); for (int i = 0; i < rayCallback.m_collisionObjects.size(); ++i) { PhysicsRaycastResult newResult; newResult.body_ = static_cast<RigidBody*>(rayCallback.m_collisionObjects[i]->getUserPointer()); newResult.position_ = ToVector3(rayCallback.m_hitPointWorld[i]); newResult.normal_ = ToVector3(rayCallback.m_hitNormalWorld[i]); newResult.distance_ = (newResult.position_ - ray.origin_).Length(); result.Push(newResult); } Sort(result.Begin(), result.End(), CompareRaycastResults); }
VertexDeclaration::VertexDeclaration(Graphics* graphics, unsigned elementMask) : declaration_(0) { PODVector<VertexDeclarationElement> elements; unsigned offset = 0; for (unsigned i = 0; i < MAX_VERTEX_ELEMENTS; ++i) { VertexElement element = (VertexElement)i; if (elementMask & (1 << i)) { VertexDeclarationElement newElement; newElement.stream_ = 0; newElement.element_ = element; newElement.offset_ = offset; offset += VertexBuffer::elementSize[i]; elements.Push(newElement); } } Create(graphics, elements); }
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 AnimatedModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no bones or no bone-level testing, use the StaticModel test RayQueryLevel level = query.level_; if (level < RAY_TRIANGLE || !skeleton_.GetNumBones()) { StaticModel::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with bone-level tests if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; const Vector<Bone>& bones = skeleton_.GetBones(); Sphere boneSphere; for (unsigned i = 0; i < bones.Size(); ++i) { const Bone& bone = bones[i]; if (!bone.node_) continue; float distance; // Use hitbox if available if (bone.collisionMask_ & BONECOLLISION_BOX) { // Do an initial crude test using the bone's AABB const BoundingBox& box = bone.boundingBox_; const Matrix3x4& transform = bone.node_->GetWorldTransform(); distance = query.ray_.HitDistance(box.Transformed(transform)); if (distance >= query.maxDistance_) continue; if (level != RAY_AABB) { // Follow with an OBB test if required Matrix3x4 inverse = transform.Inverse(); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(box); if (distance >= query.maxDistance_) continue; } } else if (bone.collisionMask_ & BONECOLLISION_SPHERE) { boneSphere.center_ = bone.node_->GetWorldPosition(); boneSphere.radius_ = bone.radius_; distance = query.ray_.HitDistance(boneSphere); if (distance >= query.maxDistance_) continue; } else continue; // If the code reaches here then we have a hit RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = -query.ray_.direction_; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = i; results.Push(result); } }
bool ShaderVariation::Compile() { const String& sourceCode = owner_->GetSourceCode(type_); Vector<String> defines = defines_.Split(' '); // Set the entrypoint, profile and flags according to the shader being compiled const char* entryPoint = 0; const char* profile = 0; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (type_ == VS) { entryPoint = "VS"; defines.Push("COMPILEVS"); profile = "vs_3_0"; } else { entryPoint = "PS"; defines.Push("COMPILEPS"); profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } defines.Push("MAXBONES=" + String(Graphics::GetMaxBones())); // Collect defines into macros Vector<String> defineValues; PODVector<D3D_SHADER_MACRO> macros; for (unsigned i = 0; i < defines.Size(); ++i) { unsigned equalsPos = defines[i].Find('='); if (equalsPos != String::NPOS) { defineValues.Push(defines[i].Substring(equalsPos + 1)); defines[i].Resize(equalsPos); } else defineValues.Push("1"); } for (unsigned i = 0; i < defines.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines[i].CString(); macro.Definition = defineValues[i].CString(); macros.Push(macro); // In debug mode, check that all defines are referenced by the shader code #ifdef _DEBUG if (sourceCode.Find(defines[i]) == String::NPOS) ATOMIC_LOGWARNING("Shader " + GetFullName() + " does not use the define " + defines[i]); #endif } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); // Compile using D3DCompile ID3DBlob* shaderCode = 0; ID3DBlob* errorMsgs = 0; HRESULT hr = D3DCompile(sourceCode.CString(), sourceCode.Length(), owner_->GetName().CString(), ¯os.Front(), 0, entryPoint, profile, flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { // Do not include end zero unnecessarily compilerOutput_ = String((const char*)errorMsgs->GetBufferPointer(), (unsigned)errorMsgs->GetBufferSize() - 1); } else { if (type_ == VS) ATOMIC_LOGDEBUG("Compiled vertex shader " + GetFullName()); else ATOMIC_LOGDEBUG("Compiled pixel shader " + GetFullName()); // Inspect the produced bytecode using MojoShader, then strip and store it unsigned char* bufData = (unsigned char*)shaderCode->GetBufferPointer(); unsigned bufSize = (unsigned)shaderCode->GetBufferSize(); ParseParameters(bufData, bufSize); CopyStrippedCode(byteCode_, bufData, bufSize); } ATOMIC_SAFE_RELEASE(shaderCode); ATOMIC_SAFE_RELEASE(errorMsgs); return !byteCode_.Empty(); }
VertexDeclaration::VertexDeclaration(Graphics* graphics, ShaderVariation* vertexShader, VertexBuffer** vertexBuffers) : inputLayout_(0) { PODVector<D3D11_INPUT_ELEMENT_DESC> elementDescs; unsigned prevBufferDescs = 0; for (unsigned i = 0; i < MAX_VERTEX_STREAMS; ++i) { if (!vertexBuffers[i]) continue; const PODVector<VertexElement>& srcElements = vertexBuffers[i]->GetElements(); bool isExisting = false; for (unsigned j = 0; j < srcElements.Size(); ++j) { const VertexElement& srcElement = srcElements[j]; const char* semanticName = ShaderVariation::elementSemanticNames[srcElement.semantic_]; // Override existing element if necessary for (unsigned k = 0; k < prevBufferDescs; ++k) { if (elementDescs[k].SemanticName == semanticName && elementDescs[k].SemanticIndex == srcElement.index_) { isExisting = true; elementDescs[k].InputSlot = i; elementDescs[k].AlignedByteOffset = srcElement.offset_; elementDescs[k].InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; elementDescs[k].InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0; break; } } if (isExisting) continue; D3D11_INPUT_ELEMENT_DESC newDesc; newDesc.SemanticName = semanticName; newDesc.SemanticIndex = srcElement.index_; newDesc.Format = d3dElementFormats[srcElement.type_]; newDesc.InputSlot = (UINT)i; newDesc.AlignedByteOffset = srcElement.offset_; newDesc.InputSlotClass = srcElement.perInstance_ ? D3D11_INPUT_PER_INSTANCE_DATA : D3D11_INPUT_PER_VERTEX_DATA; newDesc.InstanceDataStepRate = srcElement.perInstance_ ? 1 : 0; elementDescs.Push(newDesc); } prevBufferDescs = elementDescs.Size(); } if (elementDescs.Empty()) return; const PODVector<unsigned char>& byteCode = vertexShader->GetByteCode(); HRESULT hr = graphics->GetImpl()->GetDevice()->CreateInputLayout(&elementDescs[0], (UINT)elementDescs.Size(), &byteCode[0], byteCode.Size(), (ID3D11InputLayout**)&inputLayout_); if (FAILED(hr)) { ATOMIC_SAFE_RELEASE(inputLayout_); ATOMIC_LOGERRORF("Failed to create input layout for shader %s due to missing vertex element(s) (HRESULT %x)", vertexShader->GetFullName().CString(), (unsigned)hr); } }
void OptimizeIndices(ModelSubGeometryLodLevel* subGeom, ModelVertexBuffer* vb, ModelIndexBuffer* ib) { PODVector<Triangle> oldTriangles; PODVector<Triangle> newTriangles; if (subGeom->indexCount_ % 3) { PrintLine("Index count is not divisible by 3, skipping index optimization"); return; } for (unsigned i = 0; i < vb->vertices_.Size(); ++i) { vb->vertices_[i].useCount_ = 0; vb->vertices_[i].cachePosition_ = -1; } for (unsigned i = subGeom->indexStart_; i < subGeom->indexStart_ + subGeom->indexCount_; i += 3) { Triangle triangle; triangle.v0_ = ib->indices_[i]; triangle.v1_ = ib->indices_[i + 1]; triangle.v2_ = ib->indices_[i + 2]; vb->vertices_[triangle.v0_].useCount_++; vb->vertices_[triangle.v1_].useCount_++; vb->vertices_[triangle.v2_].useCount_++; oldTriangles.Push(triangle); } for (unsigned i = 0; i < vb->vertices_.Size(); ++i) CalculateScore(vb->vertices_[i]); PODVector<unsigned> vertexCache; while (oldTriangles.Size()) { unsigned bestTriangle = M_MAX_UNSIGNED; float bestTriangleScore = -1.0f; // Find the best triangle at this point for (unsigned i = 0; i < oldTriangles.Size(); ++i) { Triangle& triangle = oldTriangles[i]; float triangleScore = vb->vertices_[triangle.v0_].score_ + vb->vertices_[triangle.v1_].score_ + vb->vertices_[triangle.v2_].score_; if (triangleScore > bestTriangleScore) { bestTriangle = i; bestTriangleScore = triangleScore; } } if (bestTriangle == M_MAX_UNSIGNED) { PrintLine("Could not find next triangle, aborting index optimization"); return; } // Add the best triangle Triangle triangleCopy = oldTriangles[bestTriangle]; newTriangles.Push(triangleCopy); oldTriangles.Erase(oldTriangles.Begin() + bestTriangle); // Reduce the use count vb->vertices_[triangleCopy.v0_].useCount_--; vb->vertices_[triangleCopy.v1_].useCount_--; vb->vertices_[triangleCopy.v2_].useCount_--; // Model the LRU cache behaviour // Erase the triangle vertices from the middle of the cache, if they were there for (unsigned i = 0; i < vertexCache.Size(); ++i) { if ((vertexCache[i] == triangleCopy.v0_) || (vertexCache[i] == triangleCopy.v1_) || (vertexCache[i] == triangleCopy.v2_)) { vertexCache.Erase(vertexCache.Begin() + i); --i; } } // Then push them to the front vertexCache.Insert(vertexCache.Begin(), triangleCopy.v0_); vertexCache.Insert(vertexCache.Begin(), triangleCopy.v1_); vertexCache.Insert(vertexCache.Begin(), triangleCopy.v2_); // Update positions & scores of all vertices in the cache // Give position -1 if vertex is going to be erased for (unsigned i = 0; i < vertexCache.Size(); ++i) { ModelVertex& vertex = vb->vertices_[vertexCache[i]]; if (i >= VERTEX_CACHE_SIZE) vertex.cachePosition_ = -1; else vertex.cachePosition_ = i; CalculateScore(vertex); } // Finally erase the extra vertices if (vertexCache.Size() > VERTEX_CACHE_SIZE) vertexCache.Resize(VERTEX_CACHE_SIZE); } // Rewrite the index data now unsigned i = subGeom->indexStart_; for (unsigned j = 0; j < newTriangles.Size(); ++j) { ib->indices_[i++] = newTriangles[j].v0_; ib->indices_[i++] = newTriangles[j].v1_; ib->indices_[i++] = newTriangles[j].v2_; } }
void Terrain::CreateIndexData() { URHO3D_PROFILE(CreateIndexData); PODVector<unsigned short> indices; drawRanges_.Clear(); unsigned row = (unsigned)(patchSize_ + 1); /* Build index data for each LOD level. Each LOD level except the lowest can stitch to the next lower LOD from the edges: north, south, west, east, or any combination of them, requiring 16 different versions of each LOD level's index data Normal edge: Stitched edge: +----+----+ +---------+ |\ |\ | |\ /| | \ | \ | | \ / | | \ | \ | | \ / | | \| \| | \ / | +----+----+ +----+----+ */ for (unsigned i = 0; i < numLodLevels_; ++i) { unsigned combinations = (i < numLodLevels_ - 1) ? 16 : 1; int skip = 1 << i; for (unsigned j = 0; j < combinations; ++j) { unsigned indexStart = indices.Size(); int zStart = 0; int xStart = 0; int zEnd = patchSize_; int xEnd = patchSize_; if (j & STITCH_NORTH) zEnd -= skip; if (j & STITCH_SOUTH) zStart += skip; if (j & STITCH_WEST) xStart += skip; if (j & STITCH_EAST) xEnd -= skip; // Build the main grid for (int z = zStart; z < zEnd; z += skip) { for (int x = xStart; x < xEnd; x += skip) { indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)(z * row + x + skip)); indices.Push((unsigned short)(z * row + x)); indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x + skip)); } } // Build the north edge if (j & STITCH_NORTH) { int z = patchSize_ - skip; for (int x = 0; x < patchSize_; x += skip * 2) { if (x > 0 || (j & STITCH_WEST) == 0) { indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)(z * row + x + skip)); indices.Push((unsigned short)(z * row + x)); } indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)((z + skip) * row + x + 2 * skip)); indices.Push((unsigned short)(z * row + x + skip)); if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0) { indices.Push((unsigned short)((z + skip) * row + x + 2 * skip)); indices.Push((unsigned short)(z * row + x + 2 * skip)); indices.Push((unsigned short)(z * row + x + skip)); } } } // Build the south edge if (j & STITCH_SOUTH) { int z = 0; for (int x = 0; x < patchSize_; x += skip * 2) { if (x > 0 || (j & STITCH_WEST) == 0) { indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x)); } indices.Push((unsigned short)(z * row + x)); indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x + 2 * skip)); if (x < patchSize_ - skip * 2 || (j & STITCH_EAST) == 0) { indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)((z + skip) * row + x + 2 * skip)); indices.Push((unsigned short)(z * row + x + 2 * skip)); } } } // Build the west edge if (j & STITCH_WEST) { int x = 0; for (int z = 0; z < patchSize_; z += skip * 2) { if (z > 0 || (j & STITCH_SOUTH) == 0) { indices.Push((unsigned short)(z * row + x)); indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x + skip)); } indices.Push((unsigned short)((z + 2 * skip) * row + x)); indices.Push((unsigned short)((z + skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x)); if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0) { indices.Push((unsigned short)((z + 2 * skip) * row + x)); indices.Push((unsigned short)((z + 2 * skip) * row + x + skip)); indices.Push((unsigned short)((z + skip) * row + x + skip)); } } } // Build the east edge if (j & STITCH_EAST) { int x = patchSize_ - skip; for (int z = 0; z < patchSize_; z += skip * 2) { if (z > 0 || (j & STITCH_SOUTH) == 0) { indices.Push((unsigned short)(z * row + x)); indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)(z * row + x + skip)); } indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)((z + 2 * skip) * row + x + skip)); indices.Push((unsigned short)(z * row + x + skip)); if (z < patchSize_ - skip * 2 || (j & STITCH_NORTH) == 0) { indices.Push((unsigned short)((z + skip) * row + x)); indices.Push((unsigned short)((z + 2 * skip) * row + x)); indices.Push((unsigned short)((z + 2 * skip) * row + x + skip)); } } } drawRanges_.Push(MakePair(indexStart, indices.Size() - indexStart)); } } indexBuffer_->SetSize(indices.Size(), false); indexBuffer_->SetData(&indices[0]); }
void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData) { // Disregard the click end if a drag is going on if (selectOnClickEnd_ && GetSubsystem<UI>()->IsDragging()) return; int button = eventData[UIMouseClick::P_BUTTON].GetInt(); int buttons = eventData[UIMouseClick::P_BUTTONS].GetInt(); int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt(); UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr()); // Check if the clicked element belongs to the list unsigned i = FindItem(element); if (i >= GetNumItems()) return; // If not editable, repeat the previous selection. This will send an event and allow eg. a dropdownlist to close if (!editable_) { SetSelections(selections_); return; } if (button == MOUSEB_LEFT) { // Single selection if (!multiselect_ || !qualifiers) SetSelection(i); // Check multiselect with shift & ctrl if (multiselect_) { if (qualifiers & QUAL_SHIFT) { if (selections_.Empty()) SetSelection(i); else { unsigned first = selections_.Front(); unsigned last = selections_.Back(); PODVector<unsigned> newSelections = selections_; if (i == first || i == last) { for (unsigned j = first; j <= last; ++j) newSelections.Push(j); } else if (i < first) { for (unsigned j = i; j <= first; ++j) newSelections.Push(j); } else if (i < last) { if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last))) { for (unsigned j = first; j <= i; ++j) newSelections.Push(j); } else { for (unsigned j = i; j <= last; ++j) newSelections.Push(j); } } else if (i > last) { for (unsigned j = last; j <= i; ++j) newSelections.Push(j); } SetSelections(newSelections); } } else if (qualifiers & QUAL_CTRL) ToggleSelection(i); } } // Propagate the click as an event. Also include right-clicks VariantMap& clickEventData = GetEventDataMap(); clickEventData[ItemClicked::P_ELEMENT] = this; clickEventData[ItemClicked::P_ITEM] = element; clickEventData[ItemClicked::P_SELECTION] = i; clickEventData[ItemClicked::P_BUTTON] = button; clickEventData[ItemClicked::P_BUTTONS] = buttons; clickEventData[ItemClicked::P_QUALIFIERS] = qualifiers; SendEvent(E_ITEMCLICKED, clickEventData); }
void CompileVariation(CompiledVariation* variation) { IncludeHandler includeHandler; PODVector<D3D_SHADER_MACRO> macros; // Insert variation-specific and global defines for (unsigned i = 0; i < variation->defines_.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = variation->defines_[i].CString(); macro.Definition = variation->defineValues_[i].CString(); macros.Push(macro); } for (unsigned i = 0; i < defines_.Size(); ++i) { D3D_SHADER_MACRO macro; macro.Name = defines_[i].CString(); macro.Definition = defineValues_[i].CString(); macros.Push(macro); } D3D_SHADER_MACRO endMacro; endMacro.Name = 0; endMacro.Definition = 0; macros.Push(endMacro); LPD3DBLOB shaderCode = NULL; LPD3DBLOB errorMsgs = NULL; // Set the profile, entrypoint and flags according to the shader being compiled String profile; String entryPoint; unsigned flags = D3DCOMPILE_OPTIMIZATION_LEVEL3; if (variation->type_ == VS) { entryPoint = "VS"; if (!useSM3_) profile = "vs_2_0"; else profile = "vs_3_0"; } else { entryPoint = "PS"; if (!useSM3_) profile = "ps_2_0"; else { profile = "ps_3_0"; flags |= D3DCOMPILE_PREFER_FLOW_CONTROL; } } // Compile using D3DCompiler HRESULT hr = D3DCompile(hlslCode_.CString(), hlslCode_.Length(), NULL, ¯os.Front(), &includeHandler, entryPoint.CString(), profile.CString(), flags, 0, &shaderCode, &errorMsgs); if (FAILED(hr)) { variation->errorMsg_ = String((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); compileFailed_ = true; } else { BYTE const *const bufData = static_cast<BYTE *>(shaderCode->GetBufferPointer()); SIZE_T const bufSize = shaderCode->GetBufferSize(); MOJOSHADER_parseData const *parseData = MOJOSHADER_parse("bytecode", bufData, bufSize, NULL, 0, NULL, 0, NULL, NULL, NULL); CopyStrippedCode(variation->byteCode_, shaderCode->GetBufferPointer(), shaderCode->GetBufferSize()); if (!variation->name_.Empty()) PrintLine("Compiled shader variation " + variation->name_ + ", code size " + String(variation->byteCode_.Size())); else PrintLine("Compiled base shader variation, code size " + String(variation->byteCode_.Size())); // Print warnings if any if (errorMsgs && errorMsgs->GetBufferSize()) { String warning((const char*)errorMsgs->GetBufferPointer(), errorMsgs->GetBufferSize()); PrintLine("WARNING: " + warning); } for(int i = 0; i < parseData->symbol_count; i++) { MOJOSHADER_symbol const& symbol = parseData->symbols[i]; String name(symbol.name); unsigned const reg = symbol.register_index; unsigned const regCount = symbol.register_count; // Check if the parameter is a constant or a texture sampler bool const isSampler = (name[0] == 's'); name = name.Substring(1); if (isSampler) { // Skip if it's a G-buffer sampler, which are aliases for the standard texture units if (name != "AlbedoBuffer" && name != "NormalBuffer" && name != "DepthBuffer" && name != "LightBuffer") { Parameter newTextureUnit(name, reg, 1); variation->textureUnits_.Push(newTextureUnit); } } else { Parameter newParam(name, reg, regCount); variation->constants_.Push(newParam); } } MOJOSHADER_freeParseData(parseData); // Create the last part of the output path (SM2/SM3) if it does not exist String outPath = GetPath(variation->outFileName_); if (!fileSystem_->DirExists(outPath)) fileSystem_->CreateDir(outPath); File outFile(context_); if (!outFile.Open(variation->outFileName_, FILE_WRITE)) { variation->errorMsg_ = "Could not open output file " + variation->outFileName_; compileFailed_ = true; } else { outFile.WriteFileID("USHD"); outFile.WriteShort((unsigned short)variation->type_); outFile.WriteShort(useSM3_ ? 3 : 2); outFile.WriteUInt(variation->constants_.Size()); for (unsigned i = 0; i < variation->constants_.Size(); ++i) { outFile.WriteString(variation->constants_[i].name_); outFile.WriteUByte(variation->constants_[i].register_); outFile.WriteUByte(variation->constants_[i].regCount_); } outFile.WriteUInt(variation->textureUnits_.Size()); for (unsigned i = 0; i < variation->textureUnits_.Size(); ++i) { outFile.WriteString(variation->textureUnits_[i].name_); outFile.WriteUByte(variation->textureUnits_[i].register_); } unsigned dataSize = variation->byteCode_.Size(); outFile.WriteUInt(dataSize); if (dataSize) outFile.Write(&variation->byteCode_[0], dataSize); } } if (shaderCode) shaderCode->Release(); if (errorMsgs) errorMsgs->Release(); }
void Text::UpdateText() { int width = 0; int height = 0; rowWidths_.Clear(); printText_.Clear(); PODVector<unsigned> printToText; if (font_) { const FontFace* face = font_->GetFace(fontSize_); if (!face) return; rowHeight_ = face->rowHeight_; int rowWidth = 0; int rowHeight = (int)(rowSpacing_ * rowHeight_); // First see if the text must be split up if (!wordWrap_) { printText_ = unicodeText_; printToText.Resize(printText_.Size()); for (unsigned i = 0; i < printText_.Size(); ++i) printToText[i] = i; } else { int maxWidth = GetWidth(); unsigned nextBreak = 0; unsigned lineStart = 0; for (unsigned i = 0; i < unicodeText_.Size(); ++i) { unsigned j; unsigned c = unicodeText_[i]; if (c != '\n') { bool ok = true; if (nextBreak <= i) { int futureRowWidth = rowWidth; for (j = i; j < unicodeText_.Size(); ++j) { unsigned d = unicodeText_[j]; if (d == ' ' || d == '\n') { nextBreak = j; break; } const FontGlyph* glyph = face->GetGlyph(d); if (glyph) { futureRowWidth += glyph->advanceX_; if (j < unicodeText_.Size() - 1) futureRowWidth += face->GetKerning(d, unicodeText_[j + 1]); } if (d == '-' && futureRowWidth <= maxWidth) { nextBreak = j + 1; break; } if (futureRowWidth > maxWidth) { ok = false; break; } } } if (!ok) { // If did not find any breaks on the line, copy until j, or at least 1 char, to prevent infinite loop if (nextBreak == lineStart) { while (i < j) { printText_.Push(unicodeText_[i]); printToText.Push(i); ++i; } } printText_.Push('\n'); printToText.Push(Min((int)i, (int)unicodeText_.Size() - 1)); rowWidth = 0; nextBreak = lineStart = i; } if (i < unicodeText_.Size()) { // When copying a space, position is allowed to be over row width c = unicodeText_[i]; const FontGlyph* glyph = face->GetGlyph(c); if (glyph) { rowWidth += glyph->advanceX_; if (i < text_.Length() - 1) rowWidth += face->GetKerning(c, unicodeText_[i + 1]); } if (rowWidth <= maxWidth) { printText_.Push(c); printToText.Push(i); } } } else { printText_.Push('\n'); printToText.Push(Min((int)i, (int)unicodeText_.Size() - 1)); rowWidth = 0; nextBreak = lineStart = i; } } } rowWidth = 0; for (unsigned i = 0; i < printText_.Size(); ++i) { unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* glyph = face->GetGlyph(c); if (glyph) { rowWidth += glyph->advanceX_; if (i < printText_.Size() - 1) rowWidth += face->GetKerning(c, printText_[i + 1]); } } else { width = Max(width, rowWidth); height += rowHeight; rowWidths_.Push(rowWidth); rowWidth = 0; } } if (rowWidth) { width = Max(width, rowWidth); height += rowHeight; rowWidths_.Push(rowWidth); } // Set row height even if text is empty if (!height) height = rowHeight; // Store position & size of each character charPositions_.Resize(unicodeText_.Size() + 1); charSizes_.Resize(unicodeText_.Size()); unsigned rowIndex = 0; int x = GetRowStartPosition(rowIndex); int y = 0; for (unsigned i = 0; i < printText_.Size(); ++i) { charPositions_[printToText[i]] = IntVector2(x, y); unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* glyph = face->GetGlyph(c); charSizes_[printToText[i]] = IntVector2(glyph ? glyph->advanceX_ : 0, rowHeight_); if (glyph) { x += glyph->advanceX_; if (i < printText_.Size() - 1) x += face->GetKerning(c, printText_[i + 1]); } } else { charSizes_[printToText[i]] = IntVector2::ZERO; x = GetRowStartPosition(++rowIndex); y += rowHeight; } } // Store the ending position charPositions_[unicodeText_.Size()] = IntVector2(x, y); } // Set minimum and current size according to the text size, but respect fixed width if set if (!IsFixedWidth()) { SetMinWidth(wordWrap_ ? 0 : width); SetWidth(width); } SetFixedHeight(height); }
void Text::GetBatches(PODVector<UIBatch>& batches, PODVector<float>& vertexData, const IntRect& currentScissor) { // Hovering and/or whole selection batch if ((hovering_ && hoverColor_.a_ > 0.0) || (selected_ && selectionColor_.a_ > 0.0f)) { bool both = hovering_ && selected_ && hoverColor_.a_ > 0.0 && selectionColor_.a_ > 0.0f; UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData); batch.AddQuad(0, 0, GetWidth(), GetHeight(), 0, 0, 0, 0, both ? selectionColor_.Lerp(hoverColor_, 0.5f) : (selected_ && selectionColor_.a_ > 0.0f ? selectionColor_ : hoverColor_)); UIBatch::AddOrMerge(batch, batches); } // Partial selection batch if (!selected_ && selectionLength_ && charSizes_.Size() >= selectionStart_ + selectionLength_ && selectionColor_.a_ > 0.0f) { UIBatch batch(this, BLEND_ALPHA, currentScissor, 0, &vertexData); IntVector2 currentStart = charPositions_[selectionStart_]; IntVector2 currentEnd = currentStart; for (unsigned i = selectionStart_; i < selectionStart_ + selectionLength_; ++i) { // Check if row changes, and start a new quad in that case if (charSizes_[i].x_ && charSizes_[i].y_) { if (charPositions_[i].y_ != currentStart.y_) { batch.AddQuad(currentStart.x_, currentStart.y_, currentEnd.x_ - currentStart.x_, currentEnd.y_ - currentStart.y_, 0, 0, 0, 0, selectionColor_); currentStart = charPositions_[i]; currentEnd = currentStart + charSizes_[i]; } else { currentEnd.x_ += charSizes_[i].x_; currentEnd.y_ = Max(currentStart.y_ + charSizes_[i].y_, currentEnd.y_); } } } if (currentEnd != currentStart) { batch.AddQuad(currentStart.x_, currentStart.y_, currentEnd.x_ - currentStart.x_, currentEnd.y_ - currentStart.y_, 0, 0, 0, 0, selectionColor_); } UIBatch::AddOrMerge(batch, batches); } // Text batch if (font_) { const FontFace* face = font_->GetFace(fontSize_); if (!face) return; if (face->textures_.Size() > 1) { // Only traversing thru the printText once regardless of number of textures/pages in the font Vector<PODVector<GlyphLocation> > pageGlyphLocations(face->textures_.Size()); unsigned rowIndex = 0; int x = GetRowStartPosition(rowIndex); int y = 0; for (unsigned i = 0; i < printText_.Size(); ++i) { unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* p = face->GetGlyph(c); if (!p) continue; pageGlyphLocations[p->page_].Push(GlyphLocation(x, y, p)); x += p->advanceX_; if (i < printText_.Size() - 1) x += face->GetKerning(c, printText_[i + 1]); } else { x = GetRowStartPosition(++rowIndex); y += rowHeight_; } } for (unsigned n = 0; n < face->textures_.Size(); ++n) { // One batch per texture/page UIBatch pageBatch(this, BLEND_ALPHA, currentScissor, face->textures_[n], &vertexData); const PODVector<GlyphLocation>& pageGlyphLocation = pageGlyphLocations[n]; for (unsigned i = 0; i < pageGlyphLocation.Size(); ++i) { const GlyphLocation& glyphLocation = pageGlyphLocation[i]; const FontGlyph& glyph = *glyphLocation.glyph_; pageBatch.AddQuad(glyphLocation.x_ + glyph.offsetX_, glyphLocation.y_ + glyph.offsetY_, glyph.width_, glyph.height_, glyph.x_, glyph.y_); } batches.Push(pageBatch); } } else { // If only one texture page, construct the UI batch directly unsigned rowIndex = 0; int x = GetRowStartPosition(rowIndex); int y = 0; UIBatch batch(this, BLEND_ALPHA, currentScissor, face->textures_[0], &vertexData); for (unsigned i = 0; i < printText_.Size(); ++i) { unsigned c = printText_[i]; if (c != '\n') { const FontGlyph* p = face->GetGlyph(c); if (!p) continue; batch.AddQuad(x + p->offsetX_, y + p->offsetY_, p->width_, p->height_, p->x_, p->y_); x += p->advanceX_; if (i < printText_.Size() - 1) x += face->GetKerning(c, printText_[i + 1]); } else { x = GetRowStartPosition(++rowIndex); y += rowHeight_; } } UIBatch::AddOrMerge(batch, batches); } } // Reset hovering for next frame hovering_ = false; }
void ListView::HandleUIMouseClick(StringHash eventType, VariantMap& eventData) { if (eventData[UIMouseClick::P_BUTTON].GetInt() != MOUSEB_LEFT) return; int qualifiers = eventData[UIMouseClick::P_QUALIFIERS].GetInt(); UIElement* element = static_cast<UIElement*>(eventData[UIMouseClick::P_ELEMENT].GetPtr()); // If not editable, repeat the previous selection. This will send an event and allow eg. a dropdownlist to close if (!editable_) { SetSelections(selections_); return; } unsigned numItems = GetNumItems(); for (unsigned i = 0; i < numItems; ++i) { if (element == GetItem(i)) { // Single selection if (!multiselect_ || !qualifiers) SetSelection(i); // Check multiselect with shift & ctrl if (multiselect_) { if (qualifiers & QUAL_SHIFT) { if (selections_.Empty()) SetSelection(i); else { unsigned first = selections_.Front(); unsigned last = selections_.Back(); PODVector<unsigned> newSelections = selections_; if (i == first || i == last) { for (unsigned j = first; j <= last; ++j) newSelections.Push(j); } else if (i < first) { for (unsigned j = i; j <= first; ++j) newSelections.Push(j); } else if (i < last) { if ((abs((int)i - (int)first)) <= (abs((int)i - (int)last))) { for (unsigned j = first; j <= i; ++j) newSelections.Push(j); } else { for (unsigned j = i; j <= last; ++j) newSelections.Push(j); } } else if (i > last) { for (unsigned j = last; j <= i; ++j) newSelections.Push(j); } SetSelections(newSelections); } } else if (qualifiers & QUAL_CTRL) ToggleSelection(i); } return; } } }
void Constraint::GetDependencyNodes(PODVector<Node*>& dest) { if (otherBody_ && otherBody_->GetNode()) dest.Push(otherBody_->GetNode()); }
bool CSComponentAssembly::ParseComponentClassJSON(const JSONValue& json) { if (!typeMap_.Size()) InitTypeMap(); String className = json.Get("name").GetString(); classNames_.Push(className); const JSONValue& jfields = json.Get("fields"); PODVector<StringHash> enumsAdded; if (jfields.IsArray()) { for (unsigned i = 0; i < jfields.GetArray().Size(); i++) { const JSONValue& jfield = jfields.GetArray().At(i); VariantType varType = VAR_NONE; bool isEnum = jfield.Get("isEnum").GetBool(); String typeName = jfield.Get("typeName").GetString(); String fieldName = jfield.Get("name").GetString(); String defaultValue = jfield.Get("defaultValue").GetString(); if (!defaultValue.Length()) { JSONArray caPos = jfield.Get("caPos").GetArray(); if (caPos.Size()) defaultValue = caPos[0].GetString(); } if (!defaultValue.Length()) { JSONObject caNamed = jfield.Get("caNamed").GetObject(); if (caNamed.Contains("DefaultValue")) defaultValue = caNamed["DefaultValue"].GetString(); } if (isEnum && assemblyEnums_.Contains(typeName) && !enumsAdded.Contains(fieldName)) { varType = VAR_INT; enumsAdded.Push(fieldName); const Vector<EnumInfo>& einfos = assemblyEnums_[typeName]; for (unsigned i = 0; i < einfos.Size(); i++) AddEnum(/*typeName*/fieldName, einfos[i], className); } if (varType == VAR_NONE && typeMap_.Contains(typeName)) varType = typeMap_[typeName]; if (varType == VAR_NONE) { // FIXME: We need to be able to test if a type is a ResourceRef, this isn't really the way to achieve that const HashMap<StringHash, SharedPtr<ObjectFactory>>& factories = context_->GetObjectFactories(); HashMap<StringHash, SharedPtr<ObjectFactory>>::ConstIterator itr = factories.Begin(); while (itr != factories.End()) { if (itr->second_->GetTypeName() == typeName) { varType = VAR_RESOURCEREF; break; } itr++; } if (varType == VAR_NONE) { ATOMIC_LOGERRORF("Component Class %s contains unmappable type %s in field %s", className.CString(), typeName.CString(), fieldName.CString()); continue; } } if (!defaultValue.Length() && varType == VAR_RESOURCEREF) { // We still need a default value for ResourceRef's so we know the classtype AddDefaultValue(fieldName, ResourceRef(typeName), className); } else { Variant value; if (varType == VAR_RESOURCEREF) { ResourceRef rref(typeName); rref.name_ = defaultValue; value = rref; } else { value.FromString(varType, defaultValue); } AddDefaultValue(fieldName, value, className); } AddField(fieldName, varType, className); } } return true; }
void StaticModelGroup::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results) { // If no bones or no bone-level testing, use the Drawable test RayQueryLevel level = query.level_; if (level < RAY_AABB) { Drawable::ProcessRayQuery(query, results); return; } // Check ray hit distance to AABB before proceeding with more accurate tests // GetWorldBoundingBox() updates the world transforms if (query.ray_.HitDistance(GetWorldBoundingBox()) >= query.maxDistance_) return; for (unsigned i = 0; i < numWorldTransforms_; ++i) { // Initial test using AABB float distance = query.ray_.HitDistance(boundingBox_.Transformed(worldTransforms_[i])); Vector3 normal = -query.ray_.direction_; // Then proceed to OBB and triangle-level tests if necessary if (level >= RAY_OBB && distance < query.maxDistance_) { Matrix3x4 inverse = worldTransforms_[i].Inverse(); Ray localRay = query.ray_.Transformed(inverse); distance = localRay.HitDistance(boundingBox_); if (level == RAY_TRIANGLE && distance < query.maxDistance_) { distance = M_INFINITY; for (unsigned j = 0; j < batches_.Size(); ++j) { Geometry* geometry = batches_[j].geometry_; if (geometry) { Vector3 geometryNormal; float geometryDistance = geometry->GetHitDistance(localRay, &geometryNormal); if (geometryDistance < query.maxDistance_ && geometryDistance < distance) { distance = geometryDistance; normal = (worldTransforms_[i] * Vector4(geometryNormal, 0.0f)).Normalized(); } } } } } if (distance < query.maxDistance_) { RayQueryResult result; result.position_ = query.ray_.origin_ + distance * query.ray_.direction_; result.normal_ = normal; result.distance_ = distance; result.drawable_ = this; result.node_ = node_; result.subObject_ = i; results.Push(result); } } }
void NavigationMesh::FindPath(PODVector<NavigationPathPoint>& dest, const Vector3& start, const Vector3& end, const Vector3& extents, const dtQueryFilter* filter) { ATOMIC_PROFILE(FindPath); dest.Clear(); if (!InitializeQuery()) return; // Navigation data is in local space. Transform path points from world to local const Matrix3x4& transform = node_->GetWorldTransform(); Matrix3x4 inverse = transform.Inverse(); Vector3 localStart = inverse * start; Vector3 localEnd = inverse * end; const dtQueryFilter* queryFilter = filter ? filter : queryFilter_.Get(); dtPolyRef startRef; dtPolyRef endRef; navMeshQuery_->findNearestPoly(&localStart.x_, &extents.x_, queryFilter, &startRef, 0); navMeshQuery_->findNearestPoly(&localEnd.x_, &extents.x_, queryFilter, &endRef, 0); if (!startRef || !endRef) return; int numPolys = 0; int numPathPoints = 0; navMeshQuery_->findPath(startRef, endRef, &localStart.x_, &localEnd.x_, queryFilter, pathData_->polys_, &numPolys, MAX_POLYS); if (!numPolys) return; Vector3 actualLocalEnd = localEnd; // If full path was not found, clamp end point to the end polygon if (pathData_->polys_[numPolys - 1] != endRef) navMeshQuery_->closestPointOnPoly(pathData_->polys_[numPolys - 1], &localEnd.x_, &actualLocalEnd.x_, 0); navMeshQuery_->findStraightPath(&localStart.x_, &actualLocalEnd.x_, pathData_->polys_, numPolys, &pathData_->pathPoints_[0].x_, pathData_->pathFlags_, pathData_->pathPolys_, &numPathPoints, MAX_POLYS); // Transform path result back to world space for (int i = 0; i < numPathPoints; ++i) { NavigationPathPoint pt; pt.position_ = transform * pathData_->pathPoints_[i]; pt.flag_ = (NavigationPathPointFlag)pathData_->pathFlags_[i]; // Walk through all NavAreas and find nearest unsigned nearestNavAreaID = 0; // 0 is the default nav area ID float nearestDistance = M_LARGE_VALUE; for (unsigned j = 0; j < areas_.Size(); j++) { NavArea* area = areas_[j].Get(); if (area && area->IsEnabledEffective()) { BoundingBox bb = area->GetWorldBoundingBox(); if (bb.IsInside(pt.position_) == INSIDE) { Vector3 areaWorldCenter = area->GetNode()->GetWorldPosition(); float distance = (areaWorldCenter - pt.position_).LengthSquared(); if (distance < nearestDistance) { nearestDistance = distance; nearestNavAreaID = area->GetAreaID(); } } } } pt.areaID_ = (unsigned char)nearestNavAreaID; dest.Push(pt); } }
void PrefabImporter::HandlePrefabSave(StringHash eventType, VariantMap& eventData) { using namespace PrefabSave; PrefabComponent* component = static_cast<PrefabComponent*>(eventData[P_PREFABCOMPONENT].GetPtr()); if (component->GetPrefabGUID() != asset_->GetGUID()) return; Node* node = component->GetNode(); if (!node) return; // flip temporary root children and components to not be temporary for save const Vector<SharedPtr<Component>>& rootComponents = node->GetComponents(); const Vector<SharedPtr<Node> >& children = node->GetChildren(); PODVector<Component*> tempComponents; PODVector<Node*> tempChildren; PODVector<Node*> filterNodes; for (unsigned i = 0; i < rootComponents.Size(); i++) { if (rootComponents[i]->IsTemporary()) { rootComponents[i]->SetTemporary(false); tempComponents.Push(rootComponents[i]); // Animated sprites contain a temporary node we don't want to save in the prefab // it would be nice if this was general purpose because have to test this when // breaking node as well if (rootComponents[i]->GetType() == AnimatedSprite2D::GetTypeStatic()) { AnimatedSprite2D* asprite = (AnimatedSprite2D*) rootComponents[i].Get(); if (asprite->GetRootNode()) filterNodes.Push(asprite->GetRootNode()); } } } for (unsigned i = 0; i < children.Size(); i++) { if (filterNodes.Contains(children[i].Get())) continue; if (children[i]->IsTemporary()) { children[i]->SetTemporary(false); tempChildren.Push(children[i]); } } // store original transform Vector3 pos = node->GetPosition(); Quaternion rot = node->GetRotation(); Vector3 scale = node->GetScale(); node->SetPosition(Vector3::ZERO); node->SetRotation(Quaternion::IDENTITY); node->SetScale(Vector3::ONE); component->SetTemporary(true); SharedPtr<File> file(new File(context_, asset_->GetPath(), FILE_WRITE)); node->SaveXML(*file); file->Close(); component->SetTemporary(false); // restore node->SetPosition(pos); node->SetRotation(rot); node->SetScale(scale); for (unsigned i = 0; i < tempComponents.Size(); i++) { tempComponents[i]->SetTemporary(true); } for (unsigned i = 0; i < tempChildren.Size(); i++) { tempChildren[i]->SetTemporary(true); } FileSystem* fs = GetSubsystem<FileSystem>(); fs->Copy(asset_->GetPath(), asset_->GetCachePath()); // reload it immediately so it is ready for use // TODO: The resource cache is reloading after this reload due to catching the file cache ResourceCache* cache = GetSubsystem<ResourceCache>(); XMLFile* xmlfile = cache->GetResource<XMLFile>(asset_->GetGUID()); cache->ReloadResource(xmlfile); VariantMap changedData; changedData[PrefabChanged::P_GUID] = asset_->GetGUID(); SendEvent(E_PREFABCHANGED, changedData); }