VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
shadowCatcher.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4//
5#include "shadowCatcher.h"
6
7#include "spdlog/spdlog.h"
8
9#include "framework/entity.h"
13#include "scene/meshInstance.h"
16
17namespace visutwin::canvas
18{
20 {
21 _planeScale = value;
22 if (_planeEntity) {
23 _planeEntity->setLocalScale(_planeScale, 1.0f, _planeScale);
24 }
25 }
26
27 void ShadowCatcher::setYOffset(float value)
28 {
29 _yOffset = value;
30 if (_planeEntity) {
31 _planeEntity->setLocalPosition(0.0f, _yOffset, 0.0f);
32 }
33 }
34
36 {
37 auto* owner = entity();
38 if (!owner || !owner->engine()) {
39 spdlog::error("ShadowCatcher::initialize — no owner entity or engine");
40 return;
41 }
42
43 // Create the shadow-catching plane entity as a child of the owner
44 _planeEntity = new Entity();
45 _planeEntity->setEngine(owner->engine());
46
47 // Create the shadow catcher material
48 // black diffuse, black specular, no emissive, no skybox,
49 // multiplicative blend, shadow catcher flag, no depth write
50 _material = new StandardMaterial();
51 _material->setName("shadow-catcher");
52 _material->setDiffuse(Color(0.0f, 0.0f, 0.0f, 1.0f));
53 _material->setSpecular(Color(0.0f, 0.0f, 0.0f, 1.0f));
54 _material->setEmissive(Color(0.0f, 0.0f, 0.0f, 1.0f));
55 _material->setUseSkybox(false);
56 _material->setUseFog(false);
57 _material->setShadowCatcher(true);
58
59 // Set multiplicative blend state: output * framebuffer
60 auto blendState = std::make_shared<BlendState>(BlendState::multiplicativeBlend());
61 _material->setBlendState(blendState);
62 _material->setTransparent(true);
63
64 // disable depth write so the shadow plane doesn't
65 // occlude objects behind/below it in the depth buffer.
66 auto depthState = std::make_shared<DepthState>(DepthState::noWrite());
67 _material->setDepthState(depthState);
68
69 // Add RenderComponent with plane type and the shadow catcher material
70 auto* renderComp = static_cast<RenderComponent*>(_planeEntity->addComponent<RenderComponent>());
71 if (renderComp) {
72 renderComp->setMaterial(_material);
73 renderComp->setType("plane");
74
75 // Disable shadow casting on the plane's mesh instances
76 for (auto* mi : renderComp->meshInstances()) {
77 mi->setCastShadow(false);
78 }
79 spdlog::info("ShadowCatcher: RenderComponent created, {} mesh instances",
80 renderComp->meshInstances().size());
81 } else {
82 spdlog::error("ShadowCatcher: failed to add RenderComponent to plane entity");
83 }
84
85 // Position the plane: centered below entity, scaled to desired size
86 _planeEntity->setLocalPosition(0.0f, _yOffset, 0.0f);
87 _planeEntity->setLocalScale(Vector3(_planeScale, 1.0f, _planeScale));
88
89 // Add as child of the owner entity
90 owner->addChild(_planeEntity);
91
92 spdlog::info("ShadowCatcher: created {}x{} shadow plane at y={:.1f}, transparent={}, shadowCatcher={}",
93 _planeScale, _planeScale, _yOffset, _material->transparent(), _material->shadowCatcher());
94 }
95}
static BlendState multiplicativeBlend()
static DepthState noWrite()
Definition depthState.h:46
ECS entity — a GraphNode that hosts components defining its behavior.
Definition entity.h:32
Entity * entity() const
Definition script.h:71
Full PBR material with metalness/roughness workflow and advanced surface features.
RGBA color with floating-point components in [0, 1].
Definition color.h:18
3D vector for positions, directions, and normals with multi-backend SIMD acceleration.
Definition vector3.h:29