2 * Copyright (c) 2024 Samsung Electronics Co., Ltd.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
19 #include <dali-scene3d/internal/model-components/model-primitive-impl.h>
22 #include <dali/devel-api/adaptor-framework/image-loading.h>
23 #include <dali/public-api/animation/constraint.h>
24 #include <dali/public-api/object/type-registry-helper.h>
25 #include <dali/public-api/object/type-registry.h>
28 #include <dali-scene3d/internal/common/image-resource-loader.h>
29 #include <dali-scene3d/internal/light/light-impl.h>
30 #include <dali-scene3d/internal/model-components/material-impl.h>
31 #include <dali-scene3d/public-api/loader/environment-definition.h>
33 #include <dali/integration-api/debug.h>
34 #include <dali/public-api/object/property-array.h>
35 #include <dali/public-api/object/property-map.h>
37 #if defined(DEBUG_ENABLED)
38 #include <sys/types.h>
41 namespace fs = std::filesystem;
52 #if defined(DEBUG_ENABLED)
53 Debug::Filter* gLogFilter = Debug::Filter::New(Debug::NoLogging, false, "LOG_SCENE3D_MODEL_PRIMITIVE");
55 std::string tmpFilename(std::string prefix, std::string suffix)
59 std::ostringstream oss;
60 oss << prefix << getpid() << "_" << std::setfill('0') << std::setw(4) << id << suffix;
64 #define DALI_LOG_WRITE_FILE(filename, stream) \
66 fs::path tmp = fs::temp_directory_path(); \
68 std::ofstream ostrm(tmp, std::ios::out); \
73 inline Property::Map GetMap(Shader shader)
75 Property::Value program = shader[Shader::Property::PROGRAM];
76 Property::Map* map{nullptr};
77 if(program.GetType() == Property::ARRAY)
79 Property::Array* array = program.GetArray();
82 Property::Value& value = array->GetElementAt(0);
83 if(value.GetType() == Property::MAP)
89 else if(program.GetType() == Property::MAP)
91 map = program.GetMap();
97 return Property::Map();
102 * Creates control through type registry
106 return Scene3D::ModelPrimitive::New();
109 // Setup properties, signals and actions using the type-registry.
110 DALI_TYPE_REGISTRATION_BEGIN(Scene3D::ModelPrimitive, Dali::BaseHandle, Create);
111 DALI_TYPE_REGISTRATION_END()
113 static constexpr uint32_t INDEX_FOR_LIGHT_CONSTRAINT_TAG = 10;
115 constexpr uint32_t MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE = 300;
117 } // unnamed namespace
119 ModelPrimitivePtr ModelPrimitive::New()
121 ModelPrimitivePtr primitive = new ModelPrimitive();
123 primitive->Initialize();
128 ModelPrimitive::ModelPrimitive()
129 : mShaderManager(new Scene3D::Loader::ShaderManager())
133 ModelPrimitive::~ModelPrimitive()
137 GetImplementation(mMaterial).RemoveObserver(this);
142 void ModelPrimitive::Initialize()
146 void ModelPrimitive::SetRenderer(Dali::Renderer renderer)
148 mRenderer = renderer;
149 mGeometry = renderer.GetGeometry();
150 mTextureSet = renderer.GetTextures();
151 mShader = renderer.GetShader();
154 Dali::Renderer ModelPrimitive::GetRenderer() const
159 void ModelPrimitive::SetGeometry(Dali::Geometry geometry)
161 mGeometry = geometry;
165 Dali::Geometry ModelPrimitive::GetGeometry() const
170 void ModelPrimitive::SetMaterial(Dali::Scene3D::Material material, bool updateRenderer)
177 if(mMaterial != material)
179 // Stop observe from previous material.
182 GetImplementation(mMaterial).RemoveObserver(this);
185 mMaterial = material;
187 // Start observe from new material.
191 GetImplementation(mMaterial).AddObserver(this);
196 mIsMaterialChanged = true;
197 if(GetImplementation(mMaterial).IsResourceReady())
199 GetImplementation(mMaterial).UpdateMaterialData();
200 ApplyMaterialToRenderer();
203 UpdateShadowMapTexture();
204 UpdateImageBasedLightTexture();
208 Dali::Scene3D::Material ModelPrimitive::GetMaterial() const
213 void ModelPrimitive::AddPrimitiveObserver(ModelPrimitiveModifyObserver* observer)
215 mObservers.insert(observer);
218 void ModelPrimitive::RemovePrimitiveObserver(ModelPrimitiveModifyObserver* observer)
220 mObservers.erase(observer);
223 void ModelPrimitive::SetShadowMapTexture(Dali::Texture shadowMapTexture)
225 mShadowMapTexture = shadowMapTexture;
226 UpdateShadowMapTexture();
229 void ModelPrimitive::SetImageBasedLightTexture(Dali::Texture diffuseTexture, Dali::Texture specularTexture, float iblScaleFactor, uint32_t specularMipmapLevels)
231 mDiffuseTexture = diffuseTexture;
232 mSpecularTexture = specularTexture;
233 mIblScaleFactor = iblScaleFactor;
234 mSpecularMipmapLevels = specularMipmapLevels;
236 UpdateImageBasedLightTexture();
239 void ModelPrimitive::SetImageBasedLightScaleFactor(float iblScaleFactor)
241 mIblScaleFactor = iblScaleFactor;
242 if(mRenderer && mMaterial)
244 mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), iblScaleFactor);
248 void ModelPrimitive::UpdateShader(Scene3D::Loader::ShaderManagerPtr shaderManager, Loader::ShaderOption::HashType hash)
250 if(mShaderManager != shaderManager)
252 mShaderManager = (shaderManager) ? shaderManager : new Scene3D::Loader::ShaderManager();
253 if(mMaterial && GetImplementation(mMaterial).IsResourceReady())
255 ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag::SHADER, hash);
260 void ModelPrimitive::SetBlendShapeData(Scene3D::Loader::BlendShapes::BlendShapeData& data)
262 mBlendShapeData = std::move(data);
263 Scene3D::Loader::BlendShapes::ConfigureProperties(mBlendShapeData, mRenderer);
266 void ModelPrimitive::SetBlendShapeGeometry(Dali::Texture blendShapeGeometry)
268 mBlendShapeGeometry = blendShapeGeometry;
269 if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
271 if(mRenderer && mBlendShapeGeometry)
273 mRenderer.RegisterProperty("uBlendShapeGeometryWidth", static_cast<int>(mBlendShapeGeometry.GetWidth()));
274 mRenderer.RegisterProperty("uBlendShapeGeometryHeight", static_cast<int>(mBlendShapeGeometry.GetHeight()));
279 void ModelPrimitive::SetBlendShapeOptions(bool hasPositions, bool hasNormals, bool hasTangents, Scene3D::Loader::BlendShapes::Version version)
281 mHasPositions = hasPositions;
282 mHasNormals = hasNormals;
283 mHasTangents = hasTangents;
284 mBlendShapeVersion = version;
287 void ModelPrimitive::SetSkinned(bool isSkinned, uint32_t numberOfJointSets)
289 mHasSkinning = isSkinned;
290 mNumberOfJointSets = numberOfJointSets;
293 void ModelPrimitive::SetVertexColor(bool hasVertexColor)
295 mHasVertexColor = hasVertexColor;
298 // From MaterialModifyObserver
300 void ModelPrimitive::OnMaterialModified(Dali::Scene3D::Material material, MaterialModifyObserver::ModifyFlag flag)
302 ApplyMaterialToRenderer(flag);
305 void ModelPrimitive::ApplyMaterialToRenderer(MaterialModifyObserver::ModifyFlag flag, Loader::ShaderOption::HashType oldHash)
312 uint32_t shaderFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER));
313 if(mIsMaterialChanged || shaderFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::SHADER))
315 Scene3D::Loader::ShaderOption shaderOption = GetImplementation(mMaterial).GetShaderOption();
317 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::VEC4_TANGENT);
320 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SKINNING);
321 shaderOption.AddJointMacros(mNumberOfJointSets);
325 shaderOption.AddJointMacros(0);
329 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::COLOR_ATTRIBUTE);
331 if(mHasPositions || mHasNormals || mHasTangents)
335 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_POSITION);
339 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_NORMAL);
343 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_TANGENT);
345 if(mBlendShapeVersion == Scene3D::Loader::BlendShapes::Version::VERSION_2_0)
347 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::MORPH_VERSION_2_0);
350 if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
352 shaderOption.AddOption(Scene3D::Loader::ShaderOption::Type::SL_VERSION_LOW);
355 Shader newShader = mShaderManager->ProduceShader(shaderOption);
356 if(mShader != newShader)
358 DALI_LOG_INFO(gLogFilter, Debug::General, "Warning! Model primitive shader changed: OldHash:%x NewHash:%x\n", oldHash, shaderOption.GetOptionHash());
360 #if defined(DEBUG_ENABLED)
363 Property::Map oldMap = GetMap(mShader);
364 DALI_LOG_WRITE_FILE(tmpFilename("oldShader", ".txt"), "Vertex Shader:\n"
365 << oldMap["vertex"] << "\n\nFragmentShader: " << oldMap["fragment"] << "\n");
369 Property::Map newMap = GetMap(newShader);
370 DALI_LOG_WRITE_FILE(tmpFilename("newShader", ".txt"), "Vertex Shader:\n"
371 << newMap["vertex"] << "\n\nFragmentShader: " << newMap["fragment"] << "\n");
383 mRenderer.SetShader(mShader);
387 uint32_t textureFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE));
388 if(mIsMaterialChanged || textureFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::TEXTURE))
390 mTextureSet = GetImplementation(mMaterial).GetTextureSet();
392 if(mBlendShapeGeometry)
394 TextureSet newTextureSet = TextureSet::New();
395 newTextureSet.SetTexture(0u, mBlendShapeGeometry);
397 Sampler blendShapeSampler = Sampler::New();
398 blendShapeSampler.SetFilterMode(Dali::FilterMode::NEAREST, Dali::FilterMode::NEAREST);
399 newTextureSet.SetSampler(0u, blendShapeSampler);
401 const unsigned int numberOfTextures = mTextureSet.GetTextureCount();
402 for(unsigned int index = 0u; index < numberOfTextures; ++index)
404 const unsigned int newIndex = index + 1u;
405 newTextureSet.SetTexture(newIndex, mTextureSet.GetTexture(index));
406 newTextureSet.SetSampler(newIndex, mTextureSet.GetSampler(index));
409 mTextureSet = newTextureSet;
412 uint32_t textureCount = mTextureSet.GetTextureCount();
414 if(!mShadowMapTexture)
416 mShadowMapTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
418 mTextureSet.SetTexture(textureCount++, mShadowMapTexture);
420 Texture brdfTexture = Scene3D::Loader::EnvironmentDefinition::GetBrdfTexture();
421 if(!mSpecularTexture || !mDiffuseTexture)
423 Texture iblTexture = Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyCubeTextureWhiteRGB();
424 mDiffuseTexture = iblTexture;
425 mSpecularTexture = iblTexture;
428 mTextureSet.SetTexture(textureCount++, brdfTexture);
429 mTextureSet.SetTexture(textureCount++, mDiffuseTexture);
430 mTextureSet.SetTexture(textureCount, mSpecularTexture);
432 auto specularSampler = Sampler::New();
433 specularSampler.SetWrapMode(WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE, WrapMode::CLAMP_TO_EDGE);
434 specularSampler.SetFilterMode(FilterMode::LINEAR_MIPMAP_LINEAR, FilterMode::LINEAR);
435 mTextureSet.SetSampler(textureCount, specularSampler);
443 mRenderer.SetTextures(mTextureSet);
447 uint32_t uniformFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM));
448 if(mIsMaterialChanged || uniformFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::UNIFORM))
452 UpdateRendererUniform();
456 uint32_t propertyFlag = (flag & static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY));
457 if(mIsMaterialChanged || propertyFlag == static_cast<uint32_t>(MaterialModifyObserver::ModifyFlag::PROPERTY))
461 UpdateRendererProperty();
464 mIsMaterialChanged = false;
467 void ModelPrimitive::CreateRenderer()
469 if(!mShader || !mGeometry || !mTextureSet || mRenderer)
474 mRenderer = Renderer::New(mGeometry, mShader);
475 mRenderer.SetTextures(mTextureSet);
476 UpdateRendererUniform();
477 UpdateRendererProperty();
479 for(auto* observer : mObservers)
481 observer->OnRendererCreated(mRenderer);
485 void ModelPrimitive::UpdateShadowMapTexture()
487 if(mRenderer && mMaterial)
489 Dali::TextureSet textures = mRenderer.GetTextures();
495 uint32_t textureCount = textures.GetTextureCount();
496 if(mShadowMapTexture &&
497 textureCount >= GetImplementation(mMaterial).GetShadowMapTextureOffset() &&
498 textures.GetTexture(textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset()) != mShadowMapTexture)
500 Dali::TextureSet newTextures = Dali::TextureSet::New();
502 for(uint32_t index = 0u; index < textureCount; ++index)
504 Dali::Texture texture = textures.GetTexture(index);
505 if(index == textureCount - GetImplementation(mMaterial).GetShadowMapTextureOffset())
507 texture = (!!mShadowMapTexture) ? mShadowMapTexture : Dali::Scene3D::Internal::ImageResourceLoader::GetEmptyTextureWhiteRGB();
508 if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
510 mRenderer.RegisterProperty("uShadowMapWidth", static_cast<int>(texture.GetWidth()));
511 mRenderer.RegisterProperty("uShadowMapHeight", static_cast<int>(texture.GetHeight()));
515 newTextures.SetTexture(index, texture);
516 newTextures.SetSampler(index, textures.GetSampler(index));
519 mRenderer.SetTextures(newTextures);
524 void ModelPrimitive::UpdateImageBasedLightTexture()
526 if(mRenderer && mMaterial)
528 Dali::TextureSet textures = mRenderer.GetTextures();
534 uint32_t textureCount = textures.GetTextureCount();
535 if(textureCount > 2u &&
536 (textures.GetTexture(textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset()) != mDiffuseTexture ||
537 textures.GetTexture(textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset()) != mSpecularTexture))
539 Dali::TextureSet newTextures = Dali::TextureSet::New();
541 for(uint32_t index = 0u; index < textureCount; ++index)
543 Dali::Texture texture = textures.GetTexture(index);
544 if(index == textureCount - GetImplementation(mMaterial).GetDiffuseImageBasedLightTextureOffset())
546 texture = mDiffuseTexture;
548 else if(index == textureCount - GetImplementation(mMaterial).GetSpecularImageBasedLightTextureOffset())
550 texture = mSpecularTexture;
553 newTextures.SetTexture(index, texture);
554 newTextures.SetSampler(index, textures.GetSampler(index));
557 mRenderer.SetTextures(newTextures);
559 mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
560 mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
564 void ModelPrimitive::UpdateRendererUniform()
568 GetImplementation(mMaterial).SetRendererUniform(mRenderer);
569 mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightScaleFactorName().data(), mIblScaleFactor);
570 mRenderer.RegisterProperty(GetImplementation(mMaterial).GetImageBasedLightMaxLodUniformName().data(), static_cast<float>(mSpecularMipmapLevels));
572 if(DALI_UNLIKELY(Dali::Shader::GetShaderLanguageVersion() < MINIMUM_SHADER_VERSION_SUPPORT_TEXTURE_TEXEL_AND_SIZE))
574 if(mShadowMapTexture)
576 mRenderer.RegisterProperty("uShadowMapWidth", static_cast<int>(mShadowMapTexture.GetWidth()));
577 mRenderer.RegisterProperty("uShadowMapHeight", static_cast<int>(mShadowMapTexture.GetHeight()));
579 if(mBlendShapeGeometry)
581 mRenderer.RegisterProperty("uBlendShapeGeometryWidth", static_cast<int>(mBlendShapeGeometry.GetWidth()));
582 mRenderer.RegisterProperty("uBlendShapeGeometryHeight", static_cast<int>(mBlendShapeGeometry.GetHeight()));
588 void ModelPrimitive::UpdateRendererProperty()
592 GetImplementation(mMaterial).SetRendererProperty(mRenderer);
596 } // namespace Internal
598 } // namespace Scene3D