VisuTwin Canvas
C++ 3D Engine — Metal Backend
Loading...
Searching...
No Matches
standardMaterial.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025-2026 Arnis Lektauers
3//
4// Created by Arnis Lektauers on 11.10.2025.
5//
6
7#include "standardMaterial.h"
8
9#include <algorithm>
10#include <cmath>
11
12namespace visutwin::canvas
13{
14 namespace
15 {
16 constexpr float DEG_TO_RAD_SM = 3.14159265358979323846f / 180.0f;
17
18 void packTransformSM(const TextureTransform& t, float row0[4], float row1[4])
19 {
20 const float cr = std::cos(t.rotation * DEG_TO_RAD_SM);
21 const float sr = std::sin(t.rotation * DEG_TO_RAD_SM);
22 row0[0] = cr * t.tiling.x;
23 row0[1] = -sr * t.tiling.y;
24 row0[2] = t.offset.x;
25 row0[3] = 0.0f;
26 row1[0] = sr * t.tiling.x;
27 row1[1] = cr * t.tiling.y;
28 row1[2] = 1.0f - t.tiling.y - t.offset.y;
29 row1[3] = 0.0f;
30 }
31 }
36
38 {
39 // StandardMaterial defaults.
40 setTransparent(false);
42
43 _diffuse = Color(1.0f, 1.0f, 1.0f, 1.0f);
44 _diffuseMap = nullptr;
45 _specular = Color(0.0f, 0.0f, 0.0f, 1.0f);
46 _metalness = 0.0f;
47 _useMetalness = true;
48 _metalnessMap = nullptr;
49 _gloss = 0.25f;
50 _glossInvert = false;
51 _glossMap = nullptr;
52 _emissive = Color(0.0f, 0.0f, 0.0f, 1.0f);
53 _emissiveIntensity = 1.0f;
54 _emissiveMap = nullptr;
55 _normalMap = nullptr;
56 _bumpiness = 1.0f;
57 _heightMap = nullptr;
58 _heightMapFactor = 0.05f;
59 _anisotropy = 0.0f;
60 _transmissionFactor = 0.0f;
61 _refractionIndex = 1.5f;
62 _thickness = 0.0f;
63 _opacity = 1.0f;
64 _opacityMap = nullptr;
65 _aoMap = nullptr;
66
67 _diffuseMapTiling = Vector2(1.0f, 1.0f);
68 _diffuseMapOffset = Vector2(0.0f, 0.0f);
69 _diffuseMapRotation = 0.0f;
70 _normalMapTiling = Vector2(1.0f, 1.0f);
71 _normalMapOffset = Vector2(0.0f, 0.0f);
72 _normalMapRotation = 0.0f;
73 _metalnessMapTiling = Vector2(1.0f, 1.0f);
74 _metalnessMapOffset = Vector2(0.0f, 0.0f);
75 _metalnessMapRotation = 0.0f;
76 _aoMapTiling = Vector2(1.0f, 1.0f);
77 _aoMapOffset = Vector2(0.0f, 0.0f);
78 _aoMapRotation = 0.0f;
79 _emissiveMapTiling = Vector2(1.0f, 1.0f);
80 _emissiveMapOffset = Vector2(0.0f, 0.0f);
81 _emissiveMapRotation = 0.0f;
82
83 _reflectionMap = nullptr;
84 _clearCoat = 0.0f;
85 _clearCoatGloss = 1.0f;
86 _clearCoatGlossInvert = false;
87 _clearCoatBumpiness = 1.0f;
88 _clearCoatMap = nullptr;
89 _clearCoatGlossMap = nullptr;
90 _clearCoatNormalMap = nullptr;
91
92 _sheenColor = Color(0.0f, 0.0f, 0.0f, 1.0f);
93 _sheenRoughness = 0.0f;
94 _sheenMap = nullptr;
95 _iridescenceIntensity = 0.0f;
96 _iridescenceIOR = 1.3f;
97 _iridescenceThicknessMin = 100.0f;
98 _iridescenceThicknessMax = 400.0f;
99 _iridescenceMap = nullptr;
100 _iridescenceThicknessMap = nullptr;
101 _specularColor = Color(1.0f, 1.0f, 1.0f, 1.0f);
102 _glossiness = 1.0f;
103 _specGlossMap = nullptr;
104 _detailNormalScale = 1.0f;
105 _detailNormalMap = nullptr;
106 _detailNormalTransform = TextureTransform();
107 _displacementScale = 0.0f;
108 _displacementBias = 0.5f;
109 _displacementMap = nullptr;
110 _useOrenNayar = false;
111
112 _useFog = true;
113 _useLighting = true;
114 _useSkybox = true;
115 _twoSidedLighting = false;
116
118 setAlphaCutoff(0.0f);
119 setOccludeDirect(false);
122
123 _dirtyShader = true;
124 }
125
127 {
128 // Push StandardMaterial per-map transforms into base Material transform fields
129 // so that Material::updateUniforms() packs them into the GPU struct.
130 auto* self = const_cast<StandardMaterial*>(this);
131 self->setBaseColorTransform({_diffuseMapTiling, _diffuseMapOffset, _diffuseMapRotation});
132 self->setNormalTransform({_normalMapTiling, _normalMapOffset, _normalMapRotation});
133 self->setMetalRoughTransform({_metalnessMapTiling, _metalnessMapOffset, _metalnessMapRotation});
134 self->setOcclusionTransform({_aoMapTiling, _aoMapOffset, _aoMapRotation});
135 self->setEmissiveTransform({_emissiveMapTiling, _emissiveMapOffset, _emissiveMapRotation});
136
137 // Start with base Material implementation which reads typed properties + parameter overrides.
138 // This handles the case where the GLB parser (or other code) sets properties on the base
139 // Material rather than on StandardMaterial-specific members.
140 Material::updateUniforms(uniforms);
141
142 // Apply StandardMaterial-specific overrides on top when StandardMaterial properties
143 // have been explicitly set (i.e. when using the StandardMaterial API directly).
144 if (_diffuseMap || !baseColorTexture()) {
145 // StandardMaterial owns the diffuse color; override base color.
146 uniforms.baseColor[0] = _diffuse.r;
147 uniforms.baseColor[1] = _diffuse.g;
148 uniforms.baseColor[2] = _diffuse.b;
149 uniforms.baseColor[3] = _opacity;
150
151 uniforms.metallicFactor = _metalness;
152
153 // Gloss convention: gloss=1 is smooth, roughness=0 is smooth.
154 // If glossInvert, the value is already roughness.
155 uniforms.roughnessFactor = _glossInvert ? _gloss : (1.0f - _gloss);
156
157 uniforms.normalScale = _bumpiness;
158
159 // Emissive: emissive color * intensity, sRGB space (shader converts to linear).
160 uniforms.emissiveColor[0] = _emissive.r * _emissiveIntensity;
161 uniforms.emissiveColor[1] = _emissive.g * _emissiveIntensity;
162 uniforms.emissiveColor[2] = _emissive.b * _emissiveIntensity;
163 uniforms.emissiveColor[3] = 1.0f;
164 }
165
166 // StandardMaterial adds twoSidedLighting support to the doubleSided flag.
167 if (_twoSidedLighting) {
168 uniforms.flags |= (1u << 3); // bit 3: doubleSided
169 }
170
171 // Override texture flags with StandardMaterial-specific texture maps (if set).
172 if (_diffuseMap) uniforms.flags |= 1u; // bit 0: hasBaseColorMap
173 if (_normalMap) uniforms.flags |= (1u << 2); // bit 2: hasNormalMap
174 if (_metalnessMap) uniforms.flags |= (1u << 6); // bit 6: hasMetallicRoughnessMap
175 if (_aoMap) uniforms.flags |= (1u << 9); // bit 9: hasOcclusionMap
176 if (_emissiveMap) uniforms.flags |= (1u << 11); // bit 11: hasEmissiveMap
177
178 // anisotropic specular.
179 uniforms.anisotropy = _anisotropy;
180
181 // transmission / refraction.
182 uniforms.transmissionFactor = _transmissionFactor;
183 uniforms.refractionIndex = _refractionIndex;
184 uniforms.thickness = _thickness;
185
186 // parallax / height map uniform packing.
187 if (_heightMap) {
188 uniforms.heightMapFactor = _heightMapFactor;
189 uniforms.flags |= (1u << 17); // bit 17: hasHeightMap
190 }
191
192 // clearcoat uniform packing.
193 if (_clearCoat > 0.0f) {
194 uniforms.clearCoatFactor = _clearCoat;
195 const float ccGloss = _clearCoatGlossInvert ? (1.0f - _clearCoatGloss) : _clearCoatGloss;
196 uniforms.clearCoatRoughness = 1.0f - ccGloss;
197 uniforms.clearCoatBumpiness = _clearCoatBumpiness;
198 if (_clearCoatMap) uniforms.flags |= (1u << 14); // bit 14: hasClearCoatMap
199 if (_clearCoatGlossMap) uniforms.flags |= (1u << 15); // bit 15: hasClearCoatGlossMap
200 if (_clearCoatNormalMap) uniforms.flags |= (1u << 16); // bit 16: hasClearCoatNormalMap
201 }
202
203 // sheen uniform packing (KHR_materials_sheen).
204 uniforms.sheenColor[0] = _sheenColor.r;
205 uniforms.sheenColor[1] = _sheenColor.g;
206 uniforms.sheenColor[2] = _sheenColor.b;
207 uniforms.sheenColor[3] = _sheenRoughness;
208 if (_sheenMap) uniforms.flags |= (1u << 18); // bit 18: hasSheenMap
209
210 // iridescence uniform packing (KHR_materials_iridescence).
211 uniforms.iridescenceParams[0] = _iridescenceIntensity;
212 uniforms.iridescenceParams[1] = _iridescenceIOR;
213 uniforms.iridescenceParams[2] = _iridescenceThicknessMin;
214 uniforms.iridescenceParams[3] = _iridescenceThicknessMax;
215 if (_iridescenceMap) uniforms.flags |= (1u << 19); // bit 19: hasIridescenceMap
216 if (_iridescenceThicknessMap) uniforms.flags |= (1u << 20); // bit 20: hasIridescenceThicknessMap
217
218 // spec-gloss uniform packing (KHR_materials_pbrSpecularGlossiness).
219 uniforms.specGlossParams[0] = _specularColor.r;
220 uniforms.specGlossParams[1] = _specularColor.g;
221 uniforms.specGlossParams[2] = _specularColor.b;
222 uniforms.specGlossParams[3] = _glossiness;
223 if (_specGlossMap) uniforms.flags |= (1u << 21); // bit 21: hasSpecGlossMap
224
225 // detail normals + displacement uniform packing.
226 uniforms.detailDisplacementParams[0] = _detailNormalScale;
227 uniforms.detailDisplacementParams[1] = _displacementScale;
228 uniforms.detailDisplacementParams[2] = _displacementBias;
229 uniforms.detailDisplacementParams[3] = 0.0f;
230 if (_detailNormalMap) {
231 uniforms.flags |= (1u << 22); // bit 22: hasDetailNormalMap
232 packTransformSM(_detailNormalTransform, uniforms.detailNormalTransform0, uniforms.detailNormalTransform1);
233 }
234 if (_displacementMap) uniforms.flags |= (1u << 24); // bit 24: hasDisplacementMap
235 }
236
237 void StandardMaterial::getTextureSlots(std::vector<TextureSlot>& slots) const
238 {
239 // Start with base Material implementation (picks up textures set via base API).
241
242 // Override with StandardMaterial-specific textures where set.
243 auto overrideSlot = [&](int slotIndex, Texture* texture) {
244 if (!texture) {
245 return;
246 }
247 for (auto& [slot, tex] : slots) {
248 if (slot == slotIndex) {
249 tex = texture;
250 return;
251 }
252 }
253 slots.push_back({slotIndex, texture});
254 };
255 overrideSlot(0, _diffuseMap);
256 overrideSlot(1, _normalMap);
257 overrideSlot(3, _metalnessMap);
258 overrideSlot(4, _aoMap);
259 overrideSlot(5, _emissiveMap);
260 overrideSlot(7, _clearCoatMap);
261 overrideSlot(9, _reflectionMap);
262 overrideSlot(13, _clearCoatGlossMap);
263 overrideSlot(14, _clearCoatNormalMap);
264 overrideSlot(17, _heightMap);
265 overrideSlot(18, _sheenMap);
266 overrideSlot(19, _iridescenceMap);
267 overrideSlot(20, _iridescenceThicknessMap);
268 overrideSlot(21, _specGlossMap);
269 overrideSlot(22, _detailNormalMap);
270 overrideSlot(23, _displacementMap);
271 }
272}
virtual void getTextureSlots(std::vector< TextureSlot > &slots) const
Definition material.cpp:330
Texture * baseColorTexture() const
Definition material.h:175
void setOccludeDirect(const bool value)
Definition material.h:215
void setAlphaCutoff(const float value)
Definition material.h:246
virtual void updateUniforms(MaterialUniforms &uniforms) const
Definition material.cpp:182
void setOccludeSpecular(const uint32_t value)
Definition material.h:217
void setCullMode(const CullMode mode)
Definition material.h:170
void setOccludeSpecularIntensity(const float value)
Definition material.h:219
void setTransparent(const bool value)
Definition material.h:155
void setAlphaMode(const AlphaMode mode)
Definition material.h:243
void getTextureSlots(std::vector< TextureSlot > &slots) const override
void updateUniforms(MaterialUniforms &uniforms) const override
GPU texture resource supporting 2D, cubemap, volume, and array formats with mipmap management.
Definition texture.h:57
constexpr uint32_t SPECOCC_AO
Definition constants.h:37
RGBA color with floating-point components in [0, 1].
Definition color.h:18
2D vector for UV coordinates, screen positions, and 2D math.
Definition vector2.h:18