Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const { if (projectionDirty_) UpdateProjection(); nearClip = Max(nearClip, projNearClip_); farClip = Min(farClip, projFarClip_); if (farClip < nearClip) farClip = nearClip; Frustum ret; if (customProjection_) { // DefineSplit() needs to project the near & far distances, so can not use a combined view-projection matrix. // Transform to world space afterward instead ret.DefineSplit(projection_, nearClip, farClip); ret.Transform(GetEffectiveWorldTransform()); } else { if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform()); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, GetEffectiveWorldTransform()); } return ret; }
Frustum Camera::GetViewSpaceFrustum() const { Frustum ret; if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), farClip_); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), farClip_); return ret; }
Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const { Frustum ret; nearClip = Max(nearClip, GetNearClip()); farClip = Min(farClip, farClip_); if (farClip < nearClip) farClip = nearClip; if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip); return ret; }
Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const { Frustum ret; Matrix3x4 worldTransform = GetEffectiveWorldTransform(); nearClip = Max(nearClip, GetNearClip()); farClip = Min(farClip, farClip_); if (farClip < nearClip) farClip = nearClip; if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, worldTransform); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, worldTransform); return ret; }
Frustum Camera::GetSplitFrustum(float nearClip, float farClip) const { Frustum ret; const Matrix3x4& worldTransform = node_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY; nearClip = Max(nearClip, GetNearClip()); farClip = Min(farClip, farClip_); if (farClip < nearClip) farClip = nearClip; if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip, worldTransform); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip, worldTransform); return ret; }
Frustum Camera::GetViewSpaceFrustum() const { if (projectionDirty_) UpdateProjection(); Frustum ret; if (customProjection_) ret.Define(projection_); else { if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, GetNearClip(), GetFarClip()); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, GetNearClip(), GetFarClip()); } return ret; }
Frustum Camera::GetViewSpaceSplitFrustum(float nearClip, float farClip) const { if (projectionDirty_) UpdateProjection(); nearClip = Max(nearClip, projNearClip_); farClip = Min(farClip, projFarClip_); if (farClip < nearClip) farClip = nearClip; Frustum ret; if (customProjection_) ret.DefineSplit(projection_, nearClip, farClip); else { if (!orthographic_) ret.Define(fov_, aspectRatio_, zoom_, nearClip, farClip); else ret.DefineOrtho(orthoSize_, aspectRatio_, zoom_, nearClip, farClip); } return ret; }
bool DecalSet::AddDecal(Drawable* target, const Vector3& worldPosition, const Quaternion& worldRotation, float size, float aspectRatio, float depth, const Vector2& topLeftUV, const Vector2& bottomRightUV, float timeToLive, float normalCutoff, unsigned subGeometry) { URHO3D_PROFILE(AddDecal); // Do not add decals in headless mode if (!node_ || !GetSubsystem<Graphics>()) return false; if (!target || !target->GetNode()) { URHO3D_LOGERROR("Null target drawable for decal"); return false; } // Check for animated target and switch into skinned/static mode if necessary AnimatedModel* animatedModel = dynamic_cast<AnimatedModel*>(target); if ((animatedModel && !skinned_) || (!animatedModel && skinned_)) { RemoveAllDecals(); skinned_ = animatedModel != 0; bufferDirty_ = true; } // Center the decal frustum on the world position Vector3 adjustedWorldPosition = worldPosition - 0.5f * depth * (worldRotation * Vector3::FORWARD); /// \todo target transform is not right if adding a decal to StaticModelGroup Matrix3x4 targetTransform = target->GetNode()->GetWorldTransform().Inverse(); // For an animated model, adjust the decal position back to the bind pose // To do this, need to find the bone the decal is colliding with if (animatedModel) { Skeleton& skeleton = animatedModel->GetSkeleton(); unsigned numBones = skeleton.GetNumBones(); Bone* bestBone = 0; float bestSize = 0.0f; for (unsigned i = 0; i < numBones; ++i) { Bone* bone = skeleton.GetBone(i); if (!bone->node_ || !bone->collisionMask_) continue; // Represent the decal as a sphere, try to find the biggest colliding bone Sphere decalSphere (bone->node_->GetWorldTransform().Inverse() * worldPosition, 0.5f * size / bone->node_->GetWorldScale().Length()); if (bone->collisionMask_ & BONECOLLISION_BOX) { float size = bone->boundingBox_.HalfSize().Length(); if (bone->boundingBox_.IsInside(decalSphere) && size > bestSize) { bestBone = bone; bestSize = size; } } else if (bone->collisionMask_ & BONECOLLISION_SPHERE) { Sphere boneSphere(Vector3::ZERO, bone->radius_); float size = bone->radius_; if (boneSphere.IsInside(decalSphere) && size > bestSize) { bestBone = bone; bestSize = size; } } } if (bestBone) targetTransform = (bestBone->node_->GetWorldTransform() * bestBone->offsetMatrix_).Inverse(); } // Build the decal frustum Frustum decalFrustum; Matrix3x4 frustumTransform = targetTransform * Matrix3x4(adjustedWorldPosition, worldRotation, 1.0f); decalFrustum.DefineOrtho(size, aspectRatio, 1.0, 0.0f, depth, frustumTransform); Vector3 decalNormal = (targetTransform * Vector4(worldRotation * Vector3::BACK, 0.0f)).Normalized(); decals_.Resize(decals_.Size() + 1); Decal& newDecal = decals_.Back(); newDecal.timeToLive_ = timeToLive; Vector<PODVector<DecalVertex> > faces; PODVector<DecalVertex> tempFace; unsigned numBatches = target->GetBatches().Size(); // Use either a specified subgeometry in the target, or all if (subGeometry < numBatches) GetFaces(faces, target, subGeometry, decalFrustum, decalNormal, normalCutoff); else { for (unsigned i = 0; i < numBatches; ++i) GetFaces(faces, target, i, decalFrustum, decalNormal, normalCutoff); } // Clip the acquired faces against all frustum planes for (unsigned i = 0; i < NUM_FRUSTUM_PLANES; ++i) { for (unsigned j = 0; j < faces.Size(); ++j) { PODVector<DecalVertex>& face = faces[j]; if (face.Empty()) continue; ClipPolygon(tempFace, face, decalFrustum.planes_[i], skinned_); face = tempFace; } } // Now triangulate the resulting faces into decal vertices for (unsigned i = 0; i < faces.Size(); ++i) { PODVector<DecalVertex>& face = faces[i]; if (face.Size() < 3) continue; for (unsigned j = 2; j < face.Size(); ++j) { newDecal.AddVertex(face[0]); newDecal.AddVertex(face[j - 1]); newDecal.AddVertex(face[j]); } } // Check if resulted in no triangles if (newDecal.vertices_.Empty()) { decals_.Pop(); return true; } if (newDecal.vertices_.Size() > maxVertices_) { URHO3D_LOGWARNING("Can not add decal, vertex count " + String(newDecal.vertices_.Size()) + " exceeds maximum " + String(maxVertices_)); decals_.Pop(); return false; } if (newDecal.indices_.Size() > maxIndices_) { URHO3D_LOGWARNING("Can not add decal, index count " + String(newDecal.indices_.Size()) + " exceeds maximum " + String(maxIndices_)); decals_.Pop(); return false; } // Calculate UVs Matrix4 projection(Matrix4::ZERO); projection.m11_ = (1.0f / (size * 0.5f)); projection.m00_ = projection.m11_ / aspectRatio; projection.m22_ = 1.0f / depth; projection.m33_ = 1.0f; CalculateUVs(newDecal, frustumTransform.Inverse(), projection, topLeftUV, bottomRightUV); // Transform vertices to this node's local space and generate tangents Matrix3x4 decalTransform = node_->GetWorldTransform().Inverse() * target->GetNode()->GetWorldTransform(); TransformVertices(newDecal, skinned_ ? Matrix3x4::IDENTITY : decalTransform); GenerateTangents(&newDecal.vertices_[0], sizeof(DecalVertex), &newDecal.indices_[0], sizeof(unsigned short), 0, newDecal.indices_.Size(), offsetof(DecalVertex, normal_), offsetof(DecalVertex, texCoord_), offsetof(DecalVertex, tangent_)); newDecal.CalculateBoundingBox(); numVertices_ += newDecal.vertices_.Size(); numIndices_ += newDecal.indices_.Size(); // Remove oldest decals if total vertices exceeded while (decals_.Size() && (numVertices_ > maxVertices_ || numIndices_ > maxIndices_)) RemoveDecals(1); URHO3D_LOGDEBUG("Added decal with " + String(newDecal.vertices_.Size()) + " vertices"); // If new decal is time limited, subscribe to scene post-update if (newDecal.timeToLive_ > 0.0f && !subscribed_) UpdateEventSubscription(false); MarkDecalsDirty(); return true; }