/src/assimp/code/AssetLib/AMF/AMFImporter_Material.cpp
Line | Count | Source |
1 | | /* |
2 | | --------------------------------------------------------------------------- |
3 | | Open Asset Import Library (assimp) |
4 | | --------------------------------------------------------------------------- |
5 | | |
6 | | Copyright (c) 2006-2026, assimp team |
7 | | |
8 | | All rights reserved. |
9 | | |
10 | | Redistribution and use of this software in source and binary forms, |
11 | | with or without modification, are permitted provided that the following |
12 | | conditions are met: |
13 | | |
14 | | * Redistributions of source code must retain the above |
15 | | copyright notice, this list of conditions and the |
16 | | following disclaimer. |
17 | | |
18 | | * Redistributions in binary form must reproduce the above |
19 | | copyright notice, this list of conditions and the |
20 | | following disclaimer in the documentation and/or other |
21 | | materials provided with the distribution. |
22 | | |
23 | | * Neither the name of the assimp team, nor the names of its |
24 | | contributors may be used to endorse or promote products |
25 | | derived from this software without specific prior |
26 | | written permission of the assimp team. |
27 | | |
28 | | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
29 | | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
30 | | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
31 | | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT |
32 | | OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
33 | | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
34 | | LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
35 | | DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
36 | | THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
37 | | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
38 | | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
39 | | --------------------------------------------------------------------------- |
40 | | */ |
41 | | |
42 | | /// \file AMFImporter_Material.cpp |
43 | | /// \brief Parsing data from material nodes. |
44 | | /// \date 2016 |
45 | | /// \author smal.root@gmail.com |
46 | | |
47 | | #ifndef ASSIMP_BUILD_NO_AMF_IMPORTER |
48 | | |
49 | | #include "AMFImporter.hpp" |
50 | | |
51 | | namespace Assimp { |
52 | | |
53 | | // <color |
54 | | // profile="" - The ICC color space used to interpret the three color channels <r>, <g> and <b>. |
55 | | // > |
56 | | // </color> |
57 | | // A color definition. |
58 | | // Multi elements - No. |
59 | | // Parent element - <material>, <object>, <volume>, <vertex>, <triangle>. |
60 | | // |
61 | | // "profile" can be one of "sRGB", "AdobeRGB", "Wide-Gamut-RGB", "CIERGB", "CIELAB", or "CIEXYZ". |
62 | | // Children elements: |
63 | | // <r>, <g>, <b>, <a> |
64 | | // Multi elements - No. |
65 | | // Red, Greed, Blue and Alpha (transparency) component of a color in sRGB space, values ranging from 0 to 1. The |
66 | | // values can be specified as constants, or as a formula depending on the coordinates. |
67 | 0 | void AMFImporter::ParseNode_Color(XmlNode &node) { |
68 | 0 | if (node.empty()) { |
69 | 0 | return; |
70 | 0 | } |
71 | | |
72 | 0 | const std::string &profile = node.attribute("profile").as_string(); |
73 | 0 | bool read_flag[4] = { false, false, false, false }; |
74 | 0 | AMFNodeElementBase *ne = new AMFColor(mNodeElement_Cur); |
75 | 0 | AMFColor &als = *((AMFColor *)ne); // alias for convenience |
76 | 0 | ParseHelper_Node_Enter(ne); |
77 | 0 | for (pugi::xml_node &child : node.children()) { |
78 | | // create new color object. |
79 | 0 | als.Profile = profile; |
80 | |
|
81 | 0 | const std::string &name = child.name(); |
82 | 0 | if ( name == "r") { |
83 | 0 | read_flag[0] = true; |
84 | 0 | XmlParser::getValueAsFloat(child, als.Color.r); |
85 | 0 | } else if (name == "g") { |
86 | 0 | read_flag[1] = true; |
87 | 0 | XmlParser::getValueAsFloat(child, als.Color.g); |
88 | 0 | } else if (name == "b") { |
89 | 0 | read_flag[2] = true; |
90 | 0 | XmlParser::getValueAsFloat(child, als.Color.b); |
91 | 0 | } else if (name == "a") { |
92 | 0 | read_flag[3] = true; |
93 | 0 | XmlParser::getValueAsFloat(child, als.Color.a); |
94 | 0 | } |
95 | | // check if <a> is absent. Then manually add "a == 1". |
96 | 0 | if (!read_flag[3]) { |
97 | 0 | als.Color.a = 1; |
98 | 0 | } |
99 | 0 | } |
100 | 0 | als.Composed = false; |
101 | 0 | mNodeElement_List.push_back(ne); // and to node element list because its a new object in graph. |
102 | 0 | ParseHelper_Node_Exit(); |
103 | | // check that all components was defined |
104 | 0 | if (!(read_flag[0] && read_flag[1] && read_flag[2])) { |
105 | 0 | throw DeadlyImportError("Not all color components are defined."); |
106 | 0 | } |
107 | 0 | } |
108 | | |
109 | | // <material |
110 | | // id="" - A unique material id. material ID "0" is reserved to denote no material (void) or sacrificial material. |
111 | | // > |
112 | | // </material> |
113 | | // An available material. |
114 | | // Multi elements - Yes. |
115 | | // Parent element - <amf>. |
116 | 0 | void AMFImporter::ParseNode_Material(XmlNode &node) { |
117 | | // create new object and assign read data |
118 | 0 | std::string id = node.attribute("id").as_string(); |
119 | 0 | AMFNodeElementBase *ne = new AMFMaterial(mNodeElement_Cur); |
120 | 0 | ((AMFMaterial*)ne)->ID = id; |
121 | | |
122 | | // Check for child nodes |
123 | 0 | if (!node.empty()) { |
124 | 0 | ParseHelper_Node_Enter(ne); |
125 | 0 | for (pugi::xml_node &child : node.children()) { |
126 | 0 | const std::string name = child.name(); |
127 | 0 | if (name == "color") { |
128 | 0 | ParseNode_Color(child); |
129 | 0 | } else if (name == "metadata") { |
130 | 0 | ParseNode_Metadata(child); |
131 | 0 | } |
132 | 0 | } |
133 | 0 | ParseHelper_Node_Exit(); |
134 | 0 | } else { |
135 | 0 | mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element |
136 | 0 | } |
137 | |
|
138 | 0 | mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph. |
139 | 0 | } |
140 | | |
141 | | // <texture |
142 | | // id="" - Assigns a unique texture id for the new texture. |
143 | | // width="" - Width (horizontal size, x) of the texture, in pixels. |
144 | | // height="" - Height (lateral size, y) of the texture, in pixels. |
145 | | // depth="" - Depth (vertical size, z) of the texture, in pixels. |
146 | | // type="" - Encoding of the data in the texture. Currently allowed values are "grayscale" only. In grayscale mode, each pixel is represented by one byte |
147 | | // in the range of 0-255. When the texture is referenced using the tex function, these values are converted into a single floating point number in the |
148 | | // range of 0-1 (see Annex 2). A full color graphics will typically require three textures, one for each of the color channels. A graphic involving |
149 | | // transparency may require a fourth channel. |
150 | | // tiled="" - If true then texture repeated when UV-coordinates is greater than 1. |
151 | | // > |
152 | | // </triangle> |
153 | | // Specifies an texture data to be used as a map. Lists a sequence of Base64 values specifying values for pixels from left to right then top to bottom, |
154 | | // then layer by layer. |
155 | | // Multi elements - Yes. |
156 | | // Parent element - <amf>. |
157 | 0 | void AMFImporter::ParseNode_Texture(XmlNode &node) { |
158 | 0 | const std::string id = node.attribute("id").as_string(); |
159 | 0 | const uint32_t width = node.attribute("width").as_uint(); |
160 | 0 | const uint32_t height = node.attribute("height").as_uint(); |
161 | 0 | uint32_t depth = node.attribute("depth").as_uint(); |
162 | 0 | const std::string type = node.attribute("type").as_string(); |
163 | 0 | bool tiled = node.attribute("tiled").as_bool(); |
164 | |
|
165 | 0 | if (node.empty()) { |
166 | 0 | return; |
167 | 0 | } |
168 | | |
169 | | // create new texture object. |
170 | 0 | AMFNodeElementBase *ne = new AMFTexture(mNodeElement_Cur); |
171 | |
|
172 | 0 | AMFTexture& als = *((AMFTexture*)ne);// alias for convenience |
173 | |
|
174 | 0 | std::string enc64_data; |
175 | 0 | XmlParser::getValueAsString(node, enc64_data); |
176 | | // Check for child nodes |
177 | | |
178 | | // check that all components was defined |
179 | 0 | if (id.empty()) { |
180 | 0 | throw DeadlyImportError("ID for texture must be defined."); |
181 | 0 | } |
182 | 0 | if (width < 1) { |
183 | 0 | throw DeadlyImportError("Invalid width for texture."); |
184 | 0 | } |
185 | 0 | if (height < 1) { |
186 | 0 | throw DeadlyImportError("Invalid height for texture."); |
187 | 0 | } |
188 | 0 | if (type != "grayscale") { |
189 | 0 | throw DeadlyImportError("Invalid type for texture."); |
190 | 0 | } |
191 | 0 | if (enc64_data.empty()) { |
192 | 0 | throw DeadlyImportError("Texture data not defined."); |
193 | 0 | } |
194 | | // copy data |
195 | 0 | als.ID = id; |
196 | 0 | als.Width = width; |
197 | 0 | als.Height = height; |
198 | 0 | als.Depth = depth; |
199 | 0 | als.Tiled = tiled; |
200 | 0 | ParseHelper_Decode_Base64(enc64_data, als.Data); |
201 | 0 | if (depth == 0) { |
202 | 0 | depth = (uint32_t)(als.Data.size() / (width * height)); |
203 | 0 | } |
204 | | // check data size |
205 | 0 | if ((width * height * depth) != als.Data.size()) { |
206 | 0 | throw DeadlyImportError("Texture has incorrect data size."); |
207 | 0 | } |
208 | | |
209 | 0 | mNodeElement_Cur->Child.push_back(ne);// Add element to child list of current element |
210 | 0 | mNodeElement_List.push_back(ne);// and to node element list because its a new object in graph. |
211 | 0 | } |
212 | | |
213 | | // <texmap |
214 | | // rtexid="" - Texture ID for red color component. |
215 | | // gtexid="" - Texture ID for green color component. |
216 | | // btexid="" - Texture ID for blue color component. |
217 | | // atexid="" - Texture ID for alpha color component. Optional. |
218 | | // > |
219 | | // </texmap>, old name: <map> |
220 | | // Specifies texture coordinates for triangle. |
221 | | // Multi elements - No. |
222 | | // Parent element - <triangle>. |
223 | | // Children elements: |
224 | | // <utex1>, <utex2>, <utex3>, <vtex1>, <vtex2>, <vtex3>. Old name: <u1>, <u2>, <u3>, <v1>, <v2>, <v3>. |
225 | | // Multi elements - No. |
226 | | // Texture coordinates for every vertex of triangle. |
227 | 0 | void AMFImporter::ParseNode_TexMap(XmlNode &node, const bool pUseOldName) { |
228 | | // Read attributes for node <color>. |
229 | 0 | AMFNodeElementBase *ne = new AMFTexMap(mNodeElement_Cur); |
230 | 0 | AMFTexMap &als = *((AMFTexMap *)ne); // |
231 | 0 | std::string rtexid, gtexid, btexid, atexid; |
232 | 0 | if (!node.empty()) { |
233 | 0 | for (pugi::xml_attribute &attr : node.attributes()) { |
234 | 0 | const std::string ¤tAttr = attr.name(); |
235 | 0 | if (currentAttr == "rtexid") { |
236 | 0 | rtexid = attr.as_string(); |
237 | 0 | } else if (currentAttr == "gtexid") { |
238 | 0 | gtexid = attr.as_string(); |
239 | 0 | } else if (currentAttr == "btexid") { |
240 | 0 | btexid = attr.as_string(); |
241 | 0 | } else if (currentAttr == "atexid") { |
242 | 0 | atexid = attr.as_string(); |
243 | 0 | } |
244 | 0 | } |
245 | 0 | } |
246 | | |
247 | | // create new texture coordinates object, alias for convenience |
248 | | // check data |
249 | 0 | if (rtexid.empty() && gtexid.empty() && btexid.empty()) { |
250 | 0 | throw DeadlyImportError("ParseNode_TexMap. At least one texture ID must be defined."); |
251 | 0 | } |
252 | | |
253 | | // Check for children nodes |
254 | 0 | if (node.children().begin() == node.children().end()) { |
255 | 0 | throw DeadlyImportError("Invalid children definition."); |
256 | 0 | } |
257 | | // read children nodes |
258 | 0 | bool read_flag[6] = { false, false, false, false, false, false }; |
259 | |
|
260 | 0 | if (!pUseOldName) { |
261 | 0 | ParseHelper_Node_Enter(ne); |
262 | 0 | for ( XmlNode ¤tNode : node.children()) { |
263 | 0 | const std::string &name = currentNode.name(); |
264 | 0 | if (name == "utex1") { |
265 | 0 | read_flag[0] = true; |
266 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[0].x); |
267 | 0 | } else if (name == "utex2") { |
268 | 0 | read_flag[1] = true; |
269 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[1].x); |
270 | 0 | } else if (name == "utex3") { |
271 | 0 | read_flag[2] = true; |
272 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[2].x); |
273 | 0 | } else if (name == "vtex1") { |
274 | 0 | read_flag[3] = true; |
275 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[0].y); |
276 | 0 | } else if (name == "vtex2") { |
277 | 0 | read_flag[4] = true; |
278 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[1].y); |
279 | 0 | } else if (name == "vtex3") { |
280 | 0 | read_flag[5] = true; |
281 | 0 | XmlParser::getValueAsReal(currentNode, als.TextureCoordinate[2].y); |
282 | 0 | } |
283 | 0 | } |
284 | 0 | ParseHelper_Node_Exit(); |
285 | 0 | } else { |
286 | 0 | for (pugi::xml_attribute &attr : node.attributes()) { |
287 | 0 | const std::string name = attr.name(); |
288 | 0 | if (name == "u") { |
289 | 0 | read_flag[0] = true; |
290 | 0 | als.TextureCoordinate[0].x = attr.as_float(); |
291 | 0 | } else if (name == "u2") { |
292 | 0 | read_flag[1] = true; |
293 | 0 | als.TextureCoordinate[1].x = attr.as_float(); |
294 | 0 | } else if (name == "u3") { |
295 | 0 | read_flag[2] = true; |
296 | 0 | als.TextureCoordinate[2].x = attr.as_float(); |
297 | 0 | } else if (name == "v1") { |
298 | 0 | read_flag[3] = true; |
299 | 0 | als.TextureCoordinate[0].y = attr.as_float(); |
300 | 0 | } else if (name == "v2") { |
301 | 0 | read_flag[4] = true; |
302 | 0 | als.TextureCoordinate[1].y = attr.as_float(); |
303 | 0 | } else if (name == "v3") { |
304 | 0 | read_flag[5] = true; |
305 | 0 | als.TextureCoordinate[0].y = attr.as_float(); |
306 | 0 | } |
307 | 0 | } |
308 | 0 | } |
309 | | |
310 | | // check that all components was defined |
311 | 0 | if (!(read_flag[0] && read_flag[1] && read_flag[2] && read_flag[3] && read_flag[4] && read_flag[5])) { |
312 | 0 | throw DeadlyImportError("Not all texture coordinates are defined."); |
313 | 0 | } |
314 | | |
315 | | // copy attributes data |
316 | 0 | als.TextureID_R = rtexid; |
317 | 0 | als.TextureID_G = gtexid; |
318 | 0 | als.TextureID_B = btexid; |
319 | 0 | als.TextureID_A = atexid; |
320 | |
|
321 | 0 | mNodeElement_List.push_back(ne); |
322 | 0 | } |
323 | | |
324 | | }// namespace Assimp |
325 | | |
326 | | #endif // !ASSIMP_BUILD_NO_AMF_IMPORTER |