// Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "Decal.h" #include "Engine/Content/Assets/MaterialInstance.h" #include "Engine/Serialization/Serialization.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Level/Scene/SceneRendering.h" #include "Engine/Graphics/RenderView.h" #include "Engine/Graphics/RenderTask.h" #include "Engine/Graphics/RenderTools.h" Decal::Decal(const SpawnParams& params) : Actor(params) , _size(100.0f) { _drawCategory = SceneRendering::PreRender; _bounds.Extents = _size * 0.5f; _bounds.Transformation = _transform; _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); } void Decal::SetSize(const Vector3& value) { const auto v = value.GetAbsolute(); if (v != _size) { _size = v; _bounds.Extents = v * 0.5f; _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); } } MaterialInstance* Decal::CreateAndSetVirtualMaterialInstance() { auto material = Material.Get(); CHECK_RETURN(material && !material->WaitForLoaded(), nullptr); const auto result = material->CreateVirtualInstance(); Material = result; return result; } #if USE_EDITOR #include "Engine/Debug/DebugDraw.h" void Decal::OnDebugDrawSelected() { DEBUG_DRAW_WIRE_BOX(_bounds, Color::BlueViolet, 0, true); // Base Actor::OnDebugDrawSelected(); } BoundingBox Decal::GetEditorBox() const { const Vector3 size(10.0f); return BoundingBox(_transform.Translation - size, _transform.Translation + size); } #endif void Decal::OnLayerChanged() { if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); } void Decal::Draw(RenderContext& renderContext) { MaterialBase* material = Material; if (EnumHasAnyFlags(renderContext.View.Flags, ViewFlags::Decals) && EnumHasAnyFlags(renderContext.View.Pass, DrawPass::GBuffer) && material && material->IsLoaded() && material->IsDecal()) { // Check if decal is being culled const auto lodView = (renderContext.LodProxyView ? renderContext.LodProxyView : &renderContext.View); const float screenRadiusSquared = RenderTools::ComputeBoundsScreenRadiusSquared(_sphere.Center - renderContext.View.Origin, (float)_sphere.Radius, *lodView) * renderContext.View.ModelLODDistanceFactorSqrt; if (Math::Square(DrawMinScreenSize * 0.5f) > screenRadiusSquared) return; RenderDecalData data; Transform transform = GetTransform(); transform.Scale *= _size; renderContext.View.GetWorldMatrix(transform, data.World); data.SortOrder = SortOrder; data.Material = material; renderContext.List->Decals.Add(data); } } void Decal::Serialize(SerializeStream& stream, const void* otherObj) { // Base Actor::Serialize(stream, otherObj); SERIALIZE_GET_OTHER_OBJ(Decal); SERIALIZE(Material); SERIALIZE_MEMBER(Size, _size); SERIALIZE(SortOrder); SERIALIZE(DrawMinScreenSize); } void Decal::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier) { // Base Actor::Deserialize(stream, modifier); DESERIALIZE(Material); DESERIALIZE_MEMBER(Size, _size); DESERIALIZE(SortOrder); DESERIALIZE(DrawMinScreenSize); _bounds.Extents = _size * 0.5f; } bool Decal::IntersectsItself(const Ray& ray, Real& distance, Vector3& normal) { return _bounds.Intersects(ray, distance, normal); } void Decal::OnEnable() { GetSceneRendering()->AddActor(this, _sceneRenderingKey); #if USE_EDITOR GetSceneRendering()->AddViewportIcon(this); #endif // Base Actor::OnEnable(); } void Decal::OnDisable() { #if USE_EDITOR GetSceneRendering()->RemoveViewportIcon(this); #endif GetSceneRendering()->RemoveActor(this, _sceneRenderingKey); // Base Actor::OnDisable(); } void Decal::OnTransformChanged() { // Base Actor::OnTransformChanged(); _bounds.Transformation = _transform; _bounds.GetBoundingBox(_box); BoundingSphere::FromBox(_box, _sphere); if (_sceneRenderingKey != -1) GetSceneRendering()->UpdateActor(this, _sceneRenderingKey); }