Updated model loader to handle multiple attr sets
[platform/core/uifw/dali-toolkit.git] / dali-scene3d / public-api / loader / shader-definition.cpp
1 /*
2  * Copyright (c) 2023 Samsung Electronics Co., Ltd.
3  *
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
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
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.
15  *
16  */
17
18 // CLASS HEADER
19 #include <dali-scene3d/public-api/loader/shader-definition.h>
20
21 // EXTERNAL INCLUDES
22 #include <dali/public-api/object/property-array.h>
23 #include <regex>
24
25 // INTERNAL INCLUDES
26 #include <dali-scene3d/internal/graphics/builtin-shader-extern-gen.h>
27 #include <dali-scene3d/public-api/loader/utils.h>
28
29 namespace Dali::Scene3D::Loader
30 {
31 namespace
32 {
33 const char* SHADER_HINT_OUTPUT_IS_TRANSPARENT("OUTPUT_IS_TRANSPARENT"); ///< Might generate transparent alpha from opaque inputs.
34 const char* SHADER_HINT_MODIFIES_GEOMETRY("MODIFIES_GEOMETRY");         ///< Might change position of vertices, this option disables any culling optimizations.
35 } // namespace
36
37 ShaderDefinition::ShaderDefinition(const ShaderDefinition& other)
38 : mRendererState(other.mRendererState),
39   mVertexShaderPath(other.mVertexShaderPath),
40   mFragmentShaderPath(other.mFragmentShaderPath),
41   mDefines(other.mDefines),
42   mHints(other.mHints),
43   mUniforms(other.mUniforms),
44   mUseBuiltInShader(other.mUseBuiltInShader)
45 {
46 }
47
48 void ApplyDefine(std::string& shaderCode, const std::string& definevar)
49 {
50   const std::string IF_1 = "#if 1";
51
52   std::size_t found = shaderCode.find(definevar);
53   while(found != std::string::npos)
54   {
55     // Greater then "@" character means is a letter,
56     // therefore is not has the definevar we looking for.
57     if((found + definevar.length()) < shaderCode.length() && shaderCode.at(found + definevar.length()) > '@')
58     {
59       found = shaderCode.find(definevar, found + definevar.length());
60       continue;
61     }
62     if(found > 0u && shaderCode.at(found - 1) > '@')
63     {
64       found = shaderCode.find(definevar, found + definevar.length());
65       continue;
66     }
67
68     std::size_t defidx     = shaderCode.rfind("#ifdef", found);
69     std::size_t newlineidx = shaderCode.rfind("\n", found);
70     if(newlineidx < defidx && defidx != std::string::npos)
71     {
72       shaderCode.replace(defidx, found - defidx + definevar.length(), IF_1);
73       found = defidx + IF_1.length();
74     }
75     else
76     {
77       found += definevar.length();
78     }
79     found = shaderCode.find(definevar, found);
80   }
81 }
82
83 void RedefineMacro(std::string& shaderCode, const std::string& macro, const std::string& value)
84 {
85   std::string definition = "#define " + macro;
86   std::size_t found      = shaderCode.find(definition);
87   if(found != std::string::npos)
88   {
89     std::size_t insertionPoint = found + definition.length();
90
91     // Automatically insert line-continuation character into value
92     std::regex                 re("\n");
93     std::sregex_token_iterator first{value.begin(), value.end(), re, -1}, last;
94     for(auto i = first; i != last; ++i)
95     {
96       std::string line = std::string("\\\n") + (*i).str();
97       shaderCode.insert(insertionPoint, line);
98       insertionPoint += line.length();
99     }
100   }
101 }
102
103 ShaderDefinition::RawData
104 ShaderDefinition::LoadRaw(const std::string& shadersPath) const
105 {
106   RawData raw;
107
108   bool fail = false;
109   if(!mUseBuiltInShader)
110   {
111     raw.mVertexShaderSource = LoadTextFile((shadersPath + mVertexShaderPath).c_str(), &fail);
112     if(!fail)
113     {
114       raw.mFragmentShaderSource = LoadTextFile((shadersPath + mFragmentShaderPath).c_str(), &fail);
115       if(fail)
116       {
117         ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mFragmentShaderPath << "'.";
118       }
119     }
120     else
121     {
122       ExceptionFlinger(ASSERT_LOCATION) << "Failed to load shader source from '" << shadersPath + mVertexShaderPath << "'.";
123     }
124   }
125   else
126   {
127     raw.mVertexShaderSource         = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_VERT.data();
128     raw.mFragmentShaderSource       = SHADER_DEFAULT_PHYSICALLY_BASED_SHADER_FRAG.data();
129     raw.mShadowVertexShaderSource   = SHADER_SHADOW_MAP_SHADER_VERT.data();
130     raw.mShadowFragmentShaderSource = SHADER_SHADOW_MAP_SHADER_FRAG.data();
131   }
132
133   if(!fail)
134   {
135     for(const auto& definevar : mDefines)
136     {
137       ApplyDefine(raw.mVertexShaderSource, definevar);
138       ApplyDefine(raw.mFragmentShaderSource, definevar);
139       ApplyDefine(raw.mShadowVertexShaderSource, definevar);
140     }
141     for(const auto& macroDef : mMacros)
142     {
143       RedefineMacro(raw.mVertexShaderSource, macroDef.macro, macroDef.definition);
144       RedefineMacro(raw.mFragmentShaderSource, macroDef.macro, macroDef.definition);
145       RedefineMacro(raw.mShadowVertexShaderSource, macroDef.macro, macroDef.definition);
146     }
147   }
148
149   return raw;
150 }
151
152 Shader ShaderDefinition::Load(RawData&& raw) const
153 {
154   uint32_t hints = Shader::Hint::NONE;
155   for(const auto& hint : mHints)
156   {
157     if(hint == SHADER_HINT_OUTPUT_IS_TRANSPARENT)
158     {
159       hints |= Shader::Hint::OUTPUT_IS_TRANSPARENT;
160     }
161     else if(hint == SHADER_HINT_MODIFIES_GEOMETRY)
162     {
163       hints |= Shader::Hint::MODIFIES_GEOMETRY;
164     }
165   }
166
167   Property::Map map[2];
168   map[0]["vertex"]        = raw.mVertexShaderSource;
169   map[0]["fragment"]      = raw.mFragmentShaderSource;
170   map[0]["renderPassTag"] = 0;
171   map[0]["hints"]         = static_cast<Shader::Hint::Value>(hints);
172
173   map[1]["vertex"]        = raw.mShadowVertexShaderSource;
174   map[1]["fragment"]      = raw.mShadowFragmentShaderSource;
175   map[1]["renderPassTag"] = 10;
176
177   Property::Array array;
178   array.PushBack(map[0]);
179   array.PushBack(map[1]);
180
181   Shader shader = Shader::New(array);
182   for(Property::Map::SizeType i0 = 0, i1 = mUniforms.Count(); i0 != i1; ++i0)
183   {
184     auto pair = mUniforms.GetKeyValue(i0);
185     DALI_ASSERT_ALWAYS(pair.first.type == Property::Key::STRING);
186     shader.RegisterProperty(pair.first.stringKey, pair.second);
187   }
188
189   return shader;
190 }
191
192 } // namespace Dali::Scene3D::Loader