@@ -41,6 +41,7 @@ namespace cage
4141 {
4242 for (auto &it : textures.images .parts )
4343 {
44+ CAGE_ASSERT (it.image ->channels () == 3 );
4445 Holder<Image> img = newImage ();
4546 const Vec2i res = it.image ->resolution ();
4647 img->initialize (res, 1 , it.image ->format ());
@@ -59,13 +60,27 @@ namespace cage
5960 textures.name += Stringizer () + " _avg" ;
6061 }
6162
62- void expandChannels1To3 (MeshImportTexture &textures)
63+ void duplicateChannels1To3 (MeshImportTexture &textures)
6364 {
6465 for (auto &it : textures.images .parts )
6566 {
67+ CAGE_ASSERT (it.image ->channels () == 1 );
6668 const Image *arr[3 ] = { +it.image , +it.image , +it.image };
6769 it.image = imageChannelsJoin (arr);
6870 }
71+ if (find (textures.name , ' ?' ) != m)
72+ textures.name += Stringizer () + " _dup" ;
73+ }
74+
75+ void expandChannels2To3 (MeshImportTexture &textures)
76+ {
77+ for (auto &it : textures.images .parts )
78+ {
79+ CAGE_ASSERT (it.image ->channels () == 2 );
80+ const auto split = imageChannelsSplit (+it.image );
81+ const Image *arr[3 ] = { +split[0 ], +split[1 ], nullptr };
82+ it.image = imageChannelsJoin (arr);
83+ }
6984 if (find (textures.name , ' ?' ) != m)
7085 textures.name += Stringizer () + " _exp" ;
7186 }
@@ -74,16 +89,29 @@ namespace cage
7489 {
7590 if (it.images .parts [0 ].image ->channels () == 0 )
7691 CAGE_THROW_ERROR (Exception, " texture with zero channels" );
92+ if (it.images .parts [0 ].image ->channels () > 4 )
93+ CAGE_THROW_ERROR (Exception, " texture with too many channels" );
7794 switch (it.type )
7895 {
7996 case MeshImportTextureType::None:
8097 {
8198 CAGE_THROW_ERROR (Exception, " invalid texture type" );
99+ break ;
82100 }
83101 case MeshImportTextureType::Albedo:
84102 {
85- if (it.images .parts [0 ].image ->channels () > 4 )
86- CAGE_THROW_ERROR (Exception, " too many channels for albedo texture" );
103+ switch (it.images .parts [0 ].image ->channels ())
104+ {
105+ case 1 :
106+ break ;
107+ case 2 :
108+ CAGE_THROW_ERROR (Exception, " albedo cannot have 2 channels" );
109+ break ;
110+ case 3 :
111+ break ;
112+ case 4 :
113+ break ;
114+ }
87115 break ;
88116 }
89117 case MeshImportTextureType::Normal:
@@ -98,35 +126,47 @@ namespace cage
98126 case 3 :
99127 break ;
100128 case 4 :
101- limitChannels (it, 3 );
129+ limitChannels (it, 3 ); // strip alpha
102130 break ;
103- default :
104- CAGE_THROW_ERROR (Exception, " too many channels for normal texture" );
105131 }
106132 break ;
107133 }
108134 case MeshImportTextureType::Special:
109135 {
110- if (it.images .parts [0 ].image ->channels () > 4 )
111- CAGE_THROW_ERROR (Exception, " too many channels for special texture" );
136+ switch (it.images .parts [0 ].image ->channels ())
137+ {
138+ case 1 :
139+ break ;
140+ case 2 :
141+ expandChannels2To3 (it); // prevent confusing RG with intensity+alpha
142+ break ;
143+ case 3 :
144+ break ;
145+ case 4 :
146+ break ;
147+ }
112148 break ;
113149 }
114150 case MeshImportTextureType::Custom:
115- break ; // no limits
151+ {
152+ // no limits
153+ break ;
154+ }
116155 case MeshImportTextureType::AmbientOcclusion:
117156 {
118157 switch (it.images .parts [0 ].image ->channels ())
119158 {
120159 case 1 :
121160 break ;
161+ case 2 :
162+ CAGE_THROW_ERROR (Exception, " ambient occlusion texture cannot have 2 channels" );
163+ break ;
122164 case 3 :
123- pickChannel (it, 0 );
165+ pickChannel (it, 0 ); // extract from unreal texture
124166 break ;
125167 case 4 :
126- pickChannel (it, 0 );
168+ pickChannel (it, 0 ); // extract from unreal texture
127169 break ;
128- default :
129- CAGE_THROW_ERROR (Exception, " unexpected channels count for ambient occlusion texture" );
130170 }
131171 break ;
132172 }
@@ -141,14 +181,15 @@ namespace cage
141181 {
142182 case 1 :
143183 break ;
184+ case 2 :
185+ CAGE_THROW_ERROR (Exception, " bump texture cannot have 2 channels" );
186+ break ;
144187 case 3 :
145188 pickChannel (it, 1 );
146189 break ;
147190 case 4 :
148191 pickChannel (it, 1 );
149192 break ;
150- default :
151- CAGE_THROW_ERROR (Exception, " unexpected channels count for bump texture" );
152193 }
153194 break ;
154195 }
@@ -164,7 +205,7 @@ namespace cage
164205 case 1 :
165206 break ;
166207 case 3 :
167- averageChannels3To1 (it);
208+ averageChannels3To1 (it); // color to intensity
168209 break ;
169210 default :
170211 CAGE_THROW_ERROR (Exception, " unexpected channels count for emission texture" );
@@ -173,8 +214,7 @@ namespace cage
173214 }
174215 case MeshImportTextureType::GltfPbr:
175216 {
176- if (it.images .parts [0 ].image ->channels () > 4 )
177- CAGE_THROW_ERROR (Exception, " too many channels for gltf pbr texture" );
217+ // all ok
178218 break ;
179219 }
180220 case MeshImportTextureType::Mask:
@@ -194,14 +234,15 @@ namespace cage
194234 {
195235 case 1 :
196236 break ;
237+ case 2 :
238+ CAGE_THROW_ERROR (Exception, " metallic texture cannot have 2 channels" );
239+ break ;
197240 case 3 :
198- pickChannel (it, 2 );
241+ pickChannel (it, 2 ); // extract from unreal texture
199242 break ;
200243 case 4 :
201- pickChannel (it, 2 );
244+ pickChannel (it, 2 ); // extract from unreal texture
202245 break ;
203- default :
204- CAGE_THROW_ERROR (Exception, " unexpected channels count for metallic texture" );
205246 }
206247 break ;
207248 }
@@ -212,13 +253,14 @@ namespace cage
212253 case 1 :
213254 break ;
214255 case 2 :
215- pickChannel (it, 1 );
256+ pickChannel (it, 1 ); // extract from intensity+alpha texture
257+ break ;
258+ case 3 :
259+ CAGE_THROW_ERROR (Exception, " opacity texture cannot have 3 channels" );
216260 break ;
217261 case 4 :
218- pickChannel (it, 3 );
262+ pickChannel (it, 3 ); // extract from color+alpha texture
219263 break ;
220- default :
221- CAGE_THROW_ERROR (Exception, " unexpected channels count for opacity texture" );
222264 }
223265 break ;
224266 }
@@ -228,14 +270,15 @@ namespace cage
228270 {
229271 case 1 :
230272 break ;
273+ case 2 :
274+ CAGE_THROW_ERROR (Exception, " roughness texture cannot have 2 channels" );
275+ break ;
231276 case 3 :
232- pickChannel (it, 1 );
277+ pickChannel (it, 1 ); // extract from unreal texture
233278 break ;
234279 case 4 :
235- pickChannel (it, 1 );
280+ pickChannel (it, 1 ); // extract from unreal texture
236281 break ;
237- default :
238- CAGE_THROW_ERROR (Exception, " unexpected channels count for roughness texture" );
239282 }
240283 break ;
241284 }
@@ -260,15 +303,16 @@ namespace cage
260303 switch (it.images .parts [0 ].image ->channels ())
261304 {
262305 case 1 :
263- expandChannels1To3 (it);
306+ duplicateChannels1To3 (it); // intensity to color
307+ break ;
308+ case 2 :
309+ CAGE_THROW_ERROR (Exception, " specular texture cannot have 2 channels" );
264310 break ;
265311 case 3 :
266312 break ;
267313 case 4 :
268- limitChannels (it, 3 );
314+ limitChannels (it, 3 ); // strip alpha
269315 break ;
270- default :
271- CAGE_THROW_ERROR (Exception, " too many channels for specular texture" );
272316 }
273317 break ;
274318 }
@@ -288,6 +332,19 @@ namespace cage
288332 return res;
289333 }
290334
335+ uint32 composingChannelsTop (PointerRange<Holder<Image>> channels)
336+ {
337+ uint32 top = 0 ;
338+ for (uint32 i = 0 ; i < channels.size (); i++)
339+ {
340+ if (channels[i])
341+ top = i + 1 ;
342+ }
343+ if (top == 2 )
344+ top = 3 ; // prevent confusing RG with intensity+alpha
345+ return top;
346+ }
347+
291348 void composeAlbedoOpacity (PointerRangeHolder<MeshImportTexture> &textures)
292349 {
293350 const auto &find = [&](MeshImportTextureType type) -> const MeshImportTexture *
@@ -386,9 +443,12 @@ namespace cage
386443 channels[3 ] = std::move (em->images .parts [0 ].image );
387444 }
388445
446+ const uint32 top = composingChannelsTop (channels);
447+ CAGE_ASSERT (top > 0 );
448+
389449 {
390450 ImageImportPart p;
391- p.image = imageChannelsJoin (channels);
451+ p.image = imageChannelsJoin ({ channels, channels + top } );
392452 PointerRangeHolder<ImageImportPart> ps;
393453 ps.push_back (std::move (p));
394454 gltf->images .parts = std::move (ps);
@@ -414,6 +474,7 @@ namespace cage
414474 return ;
415475 const Image *src = +spec->images .parts [0 ].image ;
416476 const Vec2i res = src->resolution ();
477+ CAGE_ASSERT (src->channels () == 3 );
417478
418479 Holder<Image> ri = newImage ();
419480 ri->initialize (res, 1 , src->format ());
@@ -459,7 +520,6 @@ namespace cage
459520 return ;
460521
461522 Holder<Image> channels[4 ];
462- uint32 top = 0 ;
463523 String base;
464524 String names;
465525
@@ -476,7 +536,6 @@ namespace cage
476536 if (!names.empty ())
477537 names += " _" ;
478538 names += n;
479- top = cage::max (top, index + 1 );
480539 };
481540
482541 for (auto &it : textures)
@@ -500,18 +559,21 @@ namespace cage
500559 }
501560 }
502561
562+ const uint32 top = composingChannelsTop (channels);
503563 if (top == 0 )
504564 return ;
505565
506- ImageImportPart part;
507- part.image = imageChannelsJoin ({ channels, channels + top });
508- PointerRangeHolder<ImageImportPart> parts;
509- parts.push_back (std::move (part));
510- MeshImportTexture res;
511- res.images .parts = std::move (parts);
512- res.name = Stringizer () + base + " ?special_" + names;
513- res.type = MeshImportTextureType::Special;
514- textures.push_back (std::move (res));
566+ {
567+ ImageImportPart part;
568+ part.image = imageChannelsJoin ({ channels, channels + top });
569+ PointerRangeHolder<ImageImportPart> parts;
570+ parts.push_back (std::move (part));
571+ MeshImportTexture res;
572+ res.images .parts = std::move (parts);
573+ res.name = Stringizer () + base + " ?special_" + names;
574+ res.type = MeshImportTextureType::Special;
575+ textures.push_back (std::move (res));
576+ }
515577 }
516578
517579 void filterTextures (PointerRangeHolder<MeshImportTexture> &textures)
0 commit comments