1 /* $NoKeywords: $ */
2 /*
3 //
4 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
5 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
6 // McNeel & Associates.
7 //
8 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
9 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
10 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
11 //
12 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
13 //
14 ////////////////////////////////////////////////////////////////
15 */
16 
17 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
18 
19 
20 ////////////////////////////////////////////////////////////////
21 //   Class ON_Material
22 ////////////////////////////////////////////////////////////////
23 
24 ON_OBJECT_IMPLEMENT(ON_Material,ON_Object,"60B5DBBC-E660-11d3-BFE4-0010830122F0");
25 
26 double ON_Material::m_max_shine = 255.0f;
27 
MaxShine()28 double ON_Material::MaxShine()
29 {
30   return m_max_shine;
31 }
32 
Default()33 void ON_Material::Default()
34 {
35   PurgeUserData();
36 
37   m_material_index = 0;
38   m_material_id = ON_nil_uuid;
39   m_material_name.Destroy();
40   m_flamingo_library.Destroy();
41 
42   m_ambient.SetRGB( 0, 0, 0 );
43   m_diffuse.SetRGB( 128, 128, 128 );
44   m_emission.SetRGB( 0, 0, 0 );
45   m_specular.SetRGB( 255, 255, 255 );
46   //m_reflection = m_specular;
47   //m_transparent = m_diffuse;
48   m_reflection.SetRGB( 255, 255, 255 );
49   m_transparent.SetRGB( 255, 255, 255 );
50 
51   m_index_of_refraction = 1.0;
52   m_reflectivity = 0.0;
53 
54   m_shine = 0.0;
55   m_transparency = 0.0;
56 
57   m_bShared = false;
58   m_bDisableLighting = false;
59   m_reserved1[0] = 0;
60   m_reserved1[1] = 0;
61 #if defined(ON_64BIT_POINTER)
62   m_reserved2[0] = 0;
63   m_reserved2[1] = 0;
64   m_reserved2[2] = 0;
65   m_reserved2[3] = 0;
66 #endif
67 
68   m_textures.Destroy();
69 
70   m_plugin_id = ON_nil_uuid;
71 
72   m_material_channel.Destroy();
73 }
74 
75 // Default constructor
ON_Material()76 ON_Material::ON_Material()
77 {
78   Default();
79 }
80 
~ON_Material()81 ON_Material::~ON_Material()
82 {}
83 
84 ON_BOOL32
IsValid(ON_TextLog *) const85 ON_Material::IsValid( ON_TextLog* ) const
86 {
87   return true;
88 }
89 
90 
91 void
Dump(ON_TextLog & dump) const92 ON_Material::Dump( ON_TextLog& dump ) const
93 {
94   const wchar_t* s;
95   dump.Print("index = %d\n",MaterialIndex());
96   dump.Print("id = "); dump.Print(m_material_id); dump.Print("\n");
97 
98   s = m_material_name;
99   if ( !s )
100     s = L"";
101   dump.Print("name = \"%ls\"\n",s);
102 
103   dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n");
104   dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n");
105   dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n");
106   dump.Print("specular rgb = "); dump.PrintRGB( m_specular ); dump.Print("\n");
107   dump.Print("reflection rgb = "); dump.PrintRGB( m_reflection ); dump.Print("\n");
108   dump.Print("transparent rgb = "); dump.PrintRGB( m_transparent ); dump.Print("\n");
109   dump.Print("shine = %g%%\n",100.0*m_shine/ON_Material::MaxShine() );
110   dump.Print("transparency = %g%%\n",100.0*m_transparency);
111   dump.Print("reflectivity = %g%%\n",100.0*m_reflectivity);
112   dump.Print("index of refraction = %g\n",m_index_of_refraction);
113 
114   dump.Print("plug-in id = "); dump.Print(m_plugin_id); dump.Print("\n");
115   int i;
116   for( i = 0; i < m_textures.Count(); i++ )
117   {
118     dump.Print("texture[%d]:\n",i);
119     dump.PushIndent();
120     m_textures[i].Dump(dump);
121     dump.PopIndent();
122   }
123 }
124 
125 
MaterialPlugInUuid() const126 ON_UUID ON_Material::MaterialPlugInUuid() const
127 {
128   return m_plugin_id;
129 }
130 
SetMaterialPlugInUuid(ON_UUID u)131 void ON_Material::SetMaterialPlugInUuid( ON_UUID u )
132 {
133   m_plugin_id = u;
134 }
135 
Write(ON_BinaryArchive & file) const136 ON_BOOL32 ON_Material::Write( ON_BinaryArchive& file ) const
137 {
138   bool rc = false;
139   if ( file.Archive3dmVersion() <= 3 )
140   {
141     // V2 or V3 file format
142     rc = WriteV3Helper(file);
143   }
144   else
145   {
146     // V4 file format
147 
148     // The chunk version 2.0 prevents old V3 IO code from attempting
149     // to read this material
150     rc = file.Write3dmChunkVersion(2,0); // never change the 2,0
151 
152 
153     // version 1.2 field (20061113*)
154     // version 1.3 fields (20100917*)
155     if (rc) rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3);
156     if (rc)
157     {
158       for(;;)
159       {
160         if ( rc ) rc = file.WriteUuid(m_material_id);
161         if ( rc ) rc = file.WriteInt(m_material_index);
162         if ( rc ) rc = file.WriteString(m_material_name);
163 
164         if ( rc ) rc = file.WriteUuid(m_plugin_id);
165 
166         if ( rc ) rc = file.WriteColor( m_ambient );
167         if ( rc ) rc = file.WriteColor( m_diffuse );
168         if ( rc ) rc = file.WriteColor( m_emission );
169         if ( rc ) rc = file.WriteColor( m_specular );
170         if ( rc ) rc = file.WriteColor( m_reflection );
171         if ( rc ) rc = file.WriteColor( m_transparent );
172 
173         if ( rc ) rc = file.WriteDouble( m_index_of_refraction );
174         if ( rc ) rc = file.WriteDouble( m_reflectivity );
175         if ( rc ) rc = file.WriteDouble( m_shine );
176         if ( rc ) rc = file.WriteDouble( m_transparency );
177 
178         if ( !rc )
179           break;
180 
181         // array of textures written in a way that user data persists
182         rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
183         if (rc)
184         {
185           int i, count = m_textures.Count();
186           rc = file.WriteInt(count);
187           for ( i = 0; i < count && rc; i++ )
188           {
189             rc = file.WriteObject(&m_textures[i]);
190           }
191           if ( !file.EndWrite3dmChunk() )
192             rc = false;
193         }
194 
195         //version 1.1 field
196         if (rc) rc = file.WriteString(m_flamingo_library);
197 
198         // version 1.2 field (20061113)
199         if (rc) rc = file.WriteArray(m_material_channel);
200 
201         // version 1.3 fields (20100917*)
202         rc = file.WriteBool(m_bShared);
203         if (!rc) break;
204         rc = file.WriteBool(m_bDisableLighting);
205         if (!rc) break;
206 
207         break;
208       }
209       if (!file.EndWrite3dmChunk() )
210         rc = false;
211     }
212   }
213   return rc;
214 }
215 
WriteV3Helper(ON_BinaryArchive & file) const216 bool ON_Material::WriteV3Helper( ON_BinaryArchive& file ) const
217 {
218   int i;
219   // V2 and V3 file format
220 
221   bool rc = file.Write3dmChunkVersion(1,1);
222   if ( rc ) rc = file.WriteColor( m_ambient );
223   if ( rc ) rc = file.WriteColor( m_diffuse );
224   if ( rc ) rc = file.WriteColor( m_emission );
225   if ( rc ) rc = file.WriteColor( m_specular );
226   if ( rc ) rc = file.WriteDouble( Shine() );
227   if ( rc ) rc = file.WriteDouble( m_transparency );
228 
229   if ( rc ) rc = file.WriteChar( (unsigned char)1 ); // OBSOLETE // m_casts_shadows
230   if ( rc ) rc = file.WriteChar( (unsigned char)1 ); // OBSOLETE // m_shows_shadows
231 
232   if ( rc ) rc = file.WriteChar( (unsigned char)0 ); // OBSOLETE // m_wire_mode
233   if ( rc ) rc = file.WriteChar( (unsigned char)2 ); // OBSOLETE // m_wire_density
234 
235   if ( rc ) rc = file.WriteColor( ON_Color(0,0,0) ); // OBSOLETE // m_wire_color
236 
237   if (rc)
238   {
239     // OBSOLETE - line style info never used
240     short s = 0;
241     if (rc) rc = file.WriteShort(s);
242     if (rc) rc = file.WriteShort(s);
243     if (rc) rc = file.WriteDouble(0.0);
244     if (rc) rc = file.WriteDouble(1.0);
245   }
246 
247   ON_wString filename;
248   int j = 0;
249   i = FindTexture( NULL, ON_Texture::bitmap_texture );
250   if ( i >= 0 )
251   {
252     const ON_Texture& tmap = m_textures[i];
253     if ( tmap.m_filename.Length() > 0  )
254     {
255       filename = tmap.m_filename;
256       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
257     }
258   }
259   // OBSOLETE // if ( rc ) rc = file.WriteString( TextureBitmapFileName() );
260   // OBSOLETE // i = TextureMode();
261   // OBSOLETE // if ( rc ) rc = file.WriteInt( i );
262   // OBSOLETE // if ( rc ) rc = file.WriteInt( m_texture_bitmap_index );
263   if ( rc ) rc = file.WriteString(filename);
264   if ( rc ) rc = file.WriteInt( j );
265   if ( rc ) rc = file.WriteInt( 0 );
266 
267   filename.Destroy();
268   j = 0;
269   double bump_scale = 1.0;
270   i = FindTexture( NULL, ON_Texture::bump_texture );
271   if ( i >= 0 )
272   {
273     const ON_Texture& tmap = m_textures[i];
274     if ( tmap.m_filename.Length() > 0  )
275     {
276       filename = tmap.m_filename;
277       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
278       bump_scale = tmap.m_bump_scale[1];
279     }
280   }
281   // OBSOLETE //if ( rc ) rc = file.WriteString( BumpBitmapFileName() );
282   // OBSOLETE //i = BumpMode();
283   // OBSOLETE //if ( rc ) rc = file.WriteInt( i );
284   // OBSOLETE //if ( rc ) rc = file.WriteInt( m_bump_bitmap_index );
285   // OBSOLETE //if ( rc ) rc = file.WriteDouble( m_bump_scale );
286   if ( rc ) rc = file.WriteString( filename );
287   if ( rc ) rc = file.WriteInt( j );
288   if ( rc ) rc = file.WriteInt( 0 );
289   if ( rc ) rc = file.WriteDouble( bump_scale );
290 
291   filename.Destroy();
292   j = 0;
293   i = FindTexture( NULL, ON_Texture::emap_texture );
294   if ( i >= 0 )
295   {
296     const ON_Texture& tmap = m_textures[i];
297     if ( tmap.m_filename.Length() > 0  )
298     {
299       filename = tmap.m_filename;
300       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
301     }
302   }
303   // OBSOLETE //if ( rc ) rc = file.WriteString( EmapBitmapFileName() );
304   // OBSOLETE //i = EmapMode();
305   // OBSOLETE //if ( rc ) rc = file.WriteInt( i );
306   // OBSOLETE //if ( rc ) rc = file.WriteInt( m_emap_bitmap_index );
307   if ( rc ) rc = file.WriteString( filename );
308   if ( rc ) rc = file.WriteInt( j );
309   if ( rc ) rc = file.WriteInt( 0 );
310 
311   if ( rc ) rc = file.WriteInt( m_material_index );
312 
313   if ( rc ) rc = file.WriteUuid( m_plugin_id );
314   if ( rc ) rc = file.WriteString( m_flamingo_library );
315   if ( rc ) rc = file.WriteString( m_material_name );
316 
317 
318   // 1.1 fields
319   if (rc) rc = file.WriteUuid( m_material_id );
320   if (rc) rc = file.WriteColor( m_reflection);
321   if (rc) rc = file.WriteColor( m_transparent);
322   if (rc) rc = file.WriteDouble( m_index_of_refraction );
323 
324   return rc;
325 }
326 
Read(ON_BinaryArchive & file)327 ON_BOOL32 ON_Material::Read( ON_BinaryArchive& file )
328 {
329   Default();
330   int major_version = 0;
331   int minor_version = 0;
332   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
333   if (rc)
334   {
335     if ( 1 == major_version )
336     {
337       rc = ReadV3Helper(file,minor_version);
338     }
339     else if ( 2 == major_version )
340     {
341       // fancy V4 material
342       // V4 file format
343 
344       // The chunk version 2.0 prevents old V3 IO code from attempting
345       // to read this material
346       rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
347       if (rc)
348       {
349         for(;;)
350         {
351           if ( rc ) rc = file.ReadUuid(m_material_id);
352           if ( rc ) rc = file.ReadInt(&m_material_index);
353           if ( rc ) rc = file.ReadString(m_material_name);
354 
355           if ( rc ) rc = file.ReadUuid(m_plugin_id);
356 
357           if ( rc ) rc = file.ReadColor( m_ambient );
358           if ( rc ) rc = file.ReadColor( m_diffuse );
359           if ( rc ) rc = file.ReadColor( m_emission );
360           if ( rc ) rc = file.ReadColor( m_specular );
361           if ( rc ) rc = file.ReadColor( m_reflection );
362           if ( rc ) rc = file.ReadColor( m_transparent );
363 
364           if ( rc
365                && file.ArchiveOpenNURBSVersion() < 200912010
366                && 128 == m_transparent.Red()
367                && 128 == m_transparent.Green()
368                && 128 == m_transparent.Blue()
369                )
370           {
371             // Prior to 1 Dec 2009 the ON_Material::Defaults() set
372             // m_transparent to 128,128,128.  This was the wrong
373             // value for the default.  This "hack" is here to
374             // make it appear that the default was always white.
375             m_transparent = m_diffuse;
376           }
377 
378           if ( rc ) rc = file.ReadDouble( &m_index_of_refraction );
379           if ( rc ) rc = file.ReadDouble( &m_reflectivity );
380           if ( rc ) rc = file.ReadDouble( &m_shine );
381           if ( rc ) rc = file.ReadDouble( &m_transparency );
382 
383           if ( !rc )
384             break;
385 
386           // array of textures read in a way that user data persists
387           int texmajver = 0;
388           int texminver = 0;
389           rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&texmajver,&texminver);
390           if (rc)
391           {
392             if ( 1 == texmajver )
393             {
394               int i, count = 0;
395               rc = file.ReadInt(&count);
396               if (rc) m_textures.Reserve(count);
397               for ( i = 0; i < count && rc; i++ )
398               {
399                 int trc = file.ReadObject(m_textures.AppendNew());
400                 if ( trc <= 0 )
401                   rc = false;
402                 else if ( trc > 1 )
403                   m_textures.Remove();
404               }
405             }
406             if ( !file.EndRead3dmChunk() )
407               rc = false;
408           }
409 
410           if ( rc && minor_version >= 1 )
411           {
412             rc = file.ReadString(m_flamingo_library);
413             if ( !rc ) break;
414 
415             if ( minor_version >= 2 )
416             {
417               // version 1.2 field (20061113)
418               rc = file.ReadArray(m_material_channel);
419               if (!rc) break;
420 
421               if ( minor_version >= 3 )
422               {
423                 // version 1.3 fields (20100917*)
424                 rc = file.ReadBool(&m_bShared);
425                 if (!rc) break;
426                 rc = file.ReadBool(&m_bDisableLighting);
427                 if (!rc) break;
428               }
429             }
430 
431           }
432 
433           break;
434         }
435         if (!file.EndRead3dmChunk() )
436           rc = false;
437       }
438     }
439   }
440   return rc;
441 }
442 
ReadV3Helper(ON_BinaryArchive & file,int minor_version)443 bool ON_Material::ReadV3Helper( ON_BinaryArchive& file, int minor_version )
444 {
445   double shine = 0.0, transparency = 0.0;
446   int i, j;
447   bool rc = true;
448   {
449     // common to all version 1.x formats
450     if ( rc ) rc = file.ReadColor( m_ambient );
451     if ( rc ) rc = file.ReadColor( m_diffuse );
452     if ( rc ) rc = file.ReadColor( m_emission );
453     if ( rc ) rc = file.ReadColor( m_specular );
454     if ( rc ) rc = file.ReadDouble( &shine );
455     if ( rc ) SetShine(shine);
456     if ( rc ) rc = file.ReadDouble( &transparency );
457     if ( rc ) SetTransparency(transparency);
458 
459     unsigned char obsolete_uc;
460     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_casts_shadows
461     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_shows_shadows
462 
463     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_mode
464     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_density
465 
466     ON_Color obsolete_color;
467     if ( rc ) rc = file.ReadColor( obsolete_color ); // m_wire_color
468 
469     if (rc)
470     {
471       // OBSOLETE if ( rc ) rc = file.ReadLineStyle( m_wire_style );
472       short s;
473       double x;
474       if (rc) rc = file.ReadShort(&s);
475       if (rc) rc = file.ReadShort(&s);
476       if (rc) rc = file.ReadDouble(&x);
477       if (rc) rc = file.ReadDouble(&x);
478     }
479 
480     ON_wString str;
481 
482     if ( rc ) rc = file.ReadString( str ); //sTextureBitmapFileName
483     i = 0;
484     j = 0;
485     if ( rc ) rc = file.ReadInt( &i );
486     // OBSOLETE // if ( rc ) SetTextureMode( ON::TextureMode(i) );
487     if ( rc ) rc = file.ReadInt( &j );//&m_texture_bitmap_index
488 
489     if ( rc && !str.IsEmpty() )
490     {
491       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::bitmap_texture)];
492       if ( 2 == i )
493       {
494         texture.m_mode = ON_Texture::decal_texture;
495       }
496       else
497       {
498         texture.m_mode = ON_Texture::modulate_texture;
499       }
500     }
501 
502     if ( rc ) rc = file.ReadString( str ); // sBumpBitmapFileName
503     if ( rc ) rc = file.ReadInt( &i );
504    // OBSOLETE // if ( rc ) SetBumpMode( ON::TextureMode(i) );
505     if ( rc ) rc = file.ReadInt( &j );//&m_bump_bitmap_index );
506     double bump_scale = 0.0;
507     if ( rc ) rc = file.ReadDouble( &bump_scale );
508 
509     if ( rc && !str.IsEmpty() )
510     {
511       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::bump_texture)];
512       if ( 2 == i )
513       {
514         texture.m_mode = ON_Texture::decal_texture;
515       }
516       else
517       {
518         texture.m_mode = ON_Texture::modulate_texture;
519       }
520       texture.m_bump_scale.Set(0.0,bump_scale);
521     }
522 
523     if ( rc ) rc = file.ReadString( str ); // sEmapBitmapFileName
524     if ( rc ) rc = file.ReadInt( &i );
525     // OBSOLETE // if ( rc ) SetEmapMode( ON::TextureMode(i) );
526     if ( rc ) rc = file.ReadInt( &j ); //&m_emap_bitmap_index;
527 
528     if ( rc && !str.IsEmpty() )
529     {
530       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::emap_texture)];
531       if ( 2 == i )
532       {
533         texture.m_mode = ON_Texture::decal_texture;
534       }
535       else
536       {
537         texture.m_mode = ON_Texture::modulate_texture;
538       }
539     }
540 
541     if ( rc ) rc = file.ReadInt( &m_material_index );
542 
543     if ( rc ) rc = file.ReadUuid( m_plugin_id );
544     if ( rc ) rc = file.ReadString( m_flamingo_library );
545     if ( rc ) rc = file.ReadString( m_material_name );
546 
547     if ( minor_version >= 1 )
548     {
549       // 1.1 fields
550       if (rc) rc = file.ReadUuid( m_material_id );
551       if (rc) rc = file.ReadColor( m_reflection);
552       if (rc) rc = file.ReadColor( m_transparent);
553       if (rc) rc = file.ReadDouble( &m_index_of_refraction );
554     }
555     else
556     {
557       // old material needs a valid id.
558       ON_CreateUuid(m_material_id);
559     }
560 
561   }
562 
563   return rc;
564 }
565 
ObjectType() const566 ON::object_type ON_Material::ObjectType() const
567 {
568    return ON::material_object;
569 }
570 
FindTexture(const wchar_t * filename,ON_Texture::TYPE type,int i0) const571 int ON_Material::FindTexture( const wchar_t* filename,
572                               ON_Texture::TYPE type,
573                               int i0
574                               ) const
575 {
576   int i, count = m_textures.Count();
577   for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ )
578   {
579     if (    type != m_textures[i].m_type
580          && type != ON_Texture::no_texture_type )
581     {
582       continue;
583     }
584     if ( filename && m_textures[i].m_filename.CompareNoCase(filename) )
585     {
586       continue;
587     }
588     return i;
589   }
590   return -1;
591 }
592 
FindTexture(ON_UUID texture_id) const593 int ON_Material::FindTexture( ON_UUID texture_id ) const
594 {
595   int i, count = m_textures.Count();
596   for (i = 0; i < count; i++ )
597   {
598     if ( !ON_UuidCompare(&texture_id,&m_textures[i].m_texture_id) )
599       return i;
600   }
601   return -1;
602 }
603 
DeleteTexture(const wchar_t * filename,ON_Texture::TYPE type)604 int ON_Material::DeleteTexture(const wchar_t* filename,ON_Texture::TYPE type )
605 {
606   int deleted_count = 0;
607   int i;
608 
609   if ( !filename && type == ON_Texture::no_texture_type )
610   {
611     deleted_count = m_textures.Count();
612     m_textures.Destroy();
613   }
614   else
615   {
616     for ( i = m_textures.Count()-1; i >= 0; i--)
617     {
618       if ( type != ON_Texture::no_texture_type && type != m_textures[i].m_type )
619         continue;
620       if ( filename && m_textures[i].m_filename.CompareNoCase(filename) )
621         continue;
622       m_textures.Remove(i);
623       deleted_count++;
624     }
625   }
626   return deleted_count;
627 }
628 
AddTexture(const ON_Texture & tx)629 int ON_Material::AddTexture( const ON_Texture& tx )
630 {
631   // has to copy user data
632   int i = FindTexture( tx.m_filename, tx.m_type );
633   if ( i < 0 )
634   {
635     i = m_textures.Count();
636     m_textures.Append(tx);
637   }
638   else
639   {
640     m_textures[i] = tx;
641   }
642   if ( ON_UuidIsNil(m_textures[i].m_texture_id) )
643   {
644     ON_CreateUuid(m_textures[i].m_texture_id);
645   }
646 
647   return i;
648 }
649 
650 
AddTexture(const wchar_t * filename,ON_Texture::TYPE type)651 int ON_Material::AddTexture(const wchar_t* filename,ON_Texture::TYPE type)
652 {
653   int ti = FindTexture(NULL,type);
654   if ( ti < 0 )
655   {
656     ti = m_textures.Count();
657     m_textures.AppendNew();
658   }
659   if (ti >= 0 )
660   {
661     m_textures[ti].m_filename = filename;
662     m_textures[ti].m_type = type;
663     m_textures[ti].m_mode = ON_Texture::modulate_texture;
664     m_textures[ti].m_magfilter = ON_Texture::linear_filter;
665     ON_CreateUuid(m_textures[ti].m_texture_id);
666   }
667   return ti;
668 }
669 
670 // Shine values are in range 0.0 to ON_Material::GetMaxShine()
Shine() const671 double ON_Material::Shine() const
672 {
673   return m_shine;
674 }
675 
SetShine(double shine)676 void ON_Material::SetShine( double shine )
677 {
678   if ( shine < 0.0 )
679     m_shine = 0.0;
680   else if ( shine > m_max_shine)
681     m_shine = m_max_shine;
682   else
683     m_shine = (float)shine;
684 }
685 
686   // Transparency values are in range 0.0 = opaque to 1.0 = transparent
Transparency() const687 double ON_Material::Transparency( ) const
688 {
689   return  m_transparency;
690 }
691 
SetTransparency(double transparency)692 void ON_Material::SetTransparency( double transparency )
693 {
694   if ( transparency < 0.0 )
695     m_transparency = 0.0f;
696   else if ( transparency > 1.0)
697     m_transparency = 1.0f;
698   else
699     m_transparency = (float)transparency;
700 }
701 
operator ==(const ON_Material & src) const702 bool ON_Material::operator==( const ON_Material& src ) const
703 {
704   return Compare(src) ? false : true;
705 }
706 
operator !=(const ON_Material & src) const707 bool ON_Material::operator!=( const ON_Material& src ) const
708 {
709   return Compare(src) ? true : false;
710 }
711 
CompareDouble(double a,double b)712 static int CompareDouble( double a, double b )
713 {
714   return ( ( a < b ) ? -1 : ((a > b) ? 1 : 0) );
715 }
716 
CompareXform(const ON_Xform & a,const ON_Xform & b)717 static int CompareXform( const ON_Xform& a, const ON_Xform& b )
718 {
719   int i,j;
720   const double* da = &a.m_xform[0][0];
721   const double* db = &b.m_xform[0][0];
722   i = 16;
723   j = 0;
724   while ( i-- && !j)
725   {
726     j = CompareDouble(*da++,*db++);
727   }
728 
729   return j;
730 }
731 
CompareTextureDoubles(std::size_t count,const double * a,const double * b)732 static int CompareTextureDoubles( std::size_t count, const double* a, const double* b )
733 {
734   while ( count-- )
735   {
736     if ( *a < *b )
737       return -1;
738     if ( *a > *b )
739       return  1;
740     a++;
741     b++;
742   }
743   return 0;
744 }
745 
Compare(const ON_Texture & other) const746 int ON_Texture::Compare( const ON_Texture& other ) const
747 {
748   int rc = ON_UuidCompare( &m_texture_id, &other.m_texture_id );
749   while(!rc)
750   {
751     rc = m_mapping_channel_id - other.m_mapping_channel_id;
752     if (rc) break;
753 
754     rc = m_filename.CompareNoCase(other.m_filename);
755     if (rc) break;
756 
757     rc = ((int)m_bOn) - ((int)other.m_bOn);
758     if (rc) break;
759 
760     rc = ((int)m_type) - ((int)other.m_type);
761     if (rc) break;
762 
763     rc = ((int)m_mode) - ((int)other.m_mode);
764     if (rc) break;
765 
766     rc = ((int)m_minfilter) - ((int)other.m_minfilter);
767     if (rc) break;
768 
769     rc = ((int)m_magfilter) - ((int)other.m_magfilter);
770     if (rc) break;
771 
772     rc = ((int)m_wrapu) - ((int)other.m_wrapu);
773     if (rc) break;
774 
775     rc = ((int)m_wrapv) - ((int)other.m_wrapv);
776     if (rc) break;
777 
778     rc = ((int)m_wrapw) - ((int)other.m_wrapw);
779     if (rc) break;
780 
781     rc = CompareXform(m_uvw, other.m_uvw);
782     if (rc) break;
783 
784     rc = m_border_color.Compare(other.m_border_color);
785     if (rc) break;
786 
787     rc = m_transparent_color.Compare(other.m_transparent_color);
788     if (rc) break;
789 
790     rc = m_bump_scale.Compare(other.m_bump_scale);
791     if (rc) break;
792 
793     rc = CompareTextureDoubles( 1, &m_blend_constant_A, &other.m_blend_constant_A );
794     if (rc) break;
795 
796     rc = CompareTextureDoubles( sizeof(m_blend_A)/sizeof(m_blend_A[0]), m_blend_A, other.m_blend_A);
797     if (rc) break;
798 
799     rc = CompareTextureDoubles( sizeof(m_blend_RGB)/sizeof(m_blend_RGB[0]), m_blend_RGB, other.m_blend_RGB);
800     if (rc) break;
801 
802     break;
803   }
804 
805   return rc;
806 }
807 
Compare(const ON_Material & other) const808 int ON_Material::Compare( const ON_Material& other ) const
809 {
810   // do NOT test m_material_index
811 
812   int rc = ON_UuidCompare( &m_material_id, &other.m_material_id );
813   while(!rc)
814   {
815     rc = m_material_name.CompareNoCase( other.m_material_name );
816     if (rc) break;
817 
818     rc = m_ambient.Compare(other.m_ambient);
819     if (rc) break;
820 
821     rc = m_diffuse.Compare( other.m_diffuse );
822     if (rc) break;
823 
824     rc = m_diffuse.Compare( other.m_diffuse );
825     if (rc) break;
826 
827     rc = m_emission.Compare( other.m_emission );
828     if (rc) break;
829 
830     rc = m_specular.Compare( other.m_specular );
831     if (rc) break;
832 
833     rc = m_reflection.Compare( other.m_reflection );
834     if (rc) break;
835 
836     rc = m_transparent.Compare( other.m_transparent );
837     if (rc) break;
838 
839     rc = CompareDouble(m_index_of_refraction,other.m_index_of_refraction);
840     if (rc) break;
841 
842     rc = CompareDouble(m_reflectivity,other.m_reflectivity);
843     if (rc) break;
844 
845     rc = CompareDouble(m_shine,other.m_shine);
846     if (rc) break;
847 
848     rc = CompareDouble(m_transparency,other.m_transparency);
849     if (rc) break;
850 
851     rc = ON_UuidCompare( &m_plugin_id, &other.m_plugin_id );
852     if (rc) break;
853 
854     const int tcount = m_textures.Count();
855     rc = tcount - other.m_textures.Count();
856     int i;
857     for ( i = 0; i < tcount && !rc; i++ )
858     {
859       rc = m_textures[i].Compare( other.m_textures[i] );
860     }
861     if (rc) break;
862 
863 
864 
865     break;
866   }
867 
868   return rc;
869 }
870 
Ambient() const871 ON_Color ON_Material::Ambient() const
872 {
873   return m_ambient & 0x00FFFFFF;
874 }
875 
Diffuse() const876 ON_Color ON_Material::Diffuse( ) const
877 {
878   return m_diffuse & 0x00FFFFFF;
879 }
880 
Emission() const881 ON_Color ON_Material::Emission( ) const
882 {
883   return m_emission & 0x00FFFFFF;
884 }
885 
Specular() const886 ON_Color ON_Material::Specular() const
887 {
888   return m_specular & 0x00FFFFFF;
889 }
890 
SetAmbient(ON_Color c)891 void ON_Material::SetAmbient( ON_Color  c )
892 {
893   m_ambient = c;
894 }
895 
SetDiffuse(ON_Color c)896 void ON_Material::SetDiffuse(  ON_Color c )
897 {
898   m_diffuse = c ;
899 }
900 
SetEmission(ON_Color c)901 void ON_Material::SetEmission( ON_Color c )
902 {
903   m_emission = c ;
904 }
905 
SetSpecular(ON_Color c)906 void ON_Material::SetSpecular( ON_Color c )
907 {
908   m_specular = c;
909 }
910 
MaterialIndex() const911 int ON_Material::MaterialIndex() const
912 {
913   return m_material_index;
914 }
915 
SetMaterialIndex(int i)916 void ON_Material::SetMaterialIndex( int i )
917 {
918   m_material_index = i;
919 }
920 
MaterialName() const921 const wchar_t* ON_Material::MaterialName( ) const
922 {
923 	return m_material_name;
924 }
925 
SetMaterialName(const wchar_t * sMaterialName)926 void ON_Material::SetMaterialName( const wchar_t* sMaterialName )
927 {
928   m_material_name = sMaterialName;
929 }
930 
931 ////////////////////////////////////////////////////////////////
932 //   Class ON_Texture
933 ////////////////////////////////////////////////////////////////
934 
935 ON_OBJECT_IMPLEMENT(ON_Texture,ON_Object,"D6FF106D-329B-4f29-97E2-FD282A618020");
936 
ON_Texture()937 ON_Texture::ON_Texture()
938 {
939   Default(); // used to set defaults
940 }
941 
~ON_Texture()942 ON_Texture::~ON_Texture()
943 {
944 }
945 
IsValid(ON_TextLog * text_log) const946 ON_BOOL32 ON_Texture::IsValid( ON_TextLog* text_log ) const
947 {
948   if ( no_texture_type == m_type || force_32bit_texture_type == m_type )
949   {
950     if ( text_log )
951     {
952       text_log->Print("ON_Texture m_type has invalid value.\n");
953     }
954     return false;
955   }
956 
957   // TODO ...
958 
959   return true;
960 }
961 
962 // overrides virtual ON_Object::Dump
Dump(ON_TextLog &) const963 void ON_Texture::Dump( ON_TextLog& ) const
964 {
965 
966 }
967 
968 // overrides virtual ON_Object::SizeOf
SizeOf() const969 unsigned int ON_Texture::SizeOf() const
970 {
971   unsigned int sz = ON_Object::SizeOf();
972   sz += sizeof(*this) - sizeof(ON_Object);
973   sz += m_filename.Length()*sizeof(wchar_t);
974   return sz;
975 }
976 
977 // overrides virtual ON_Object::Write
Write(ON_BinaryArchive & binary_archive) const978 ON_BOOL32 ON_Texture::Write(
979         ON_BinaryArchive& binary_archive
980       ) const
981 {
982   bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
983   if (rc)
984   {
985 
986     for(;;)
987     {
988       // 1.0 values
989       rc = binary_archive.WriteUuid(m_texture_id);
990       if (!rc) break;
991       rc = binary_archive.WriteInt(m_mapping_channel_id);
992       if (!rc) break;
993       rc = binary_archive.WriteString(m_filename);
994       if (!rc) break;
995       rc = binary_archive.WriteBool(m_bOn);
996       if (!rc) break;
997       rc = binary_archive.WriteInt(m_type);
998       if (!rc) break;
999       rc = binary_archive.WriteInt(m_mode);
1000       if (!rc) break;
1001       rc = binary_archive.WriteInt(m_minfilter);
1002       if (!rc) break;
1003       rc = binary_archive.WriteInt(m_magfilter);
1004       if (!rc) break;
1005       rc = binary_archive.WriteInt(m_wrapu);
1006       if (!rc) break;
1007       rc = binary_archive.WriteInt(m_wrapv);
1008       if (!rc) break;
1009       rc = binary_archive.WriteInt(m_wrapw);
1010       if (!rc) break;
1011       rc = binary_archive.WriteXform(m_uvw);
1012       if (!rc) break;
1013       rc = binary_archive.WriteColor(m_border_color);
1014       if (!rc) break;
1015       rc = binary_archive.WriteColor(m_transparent_color);
1016       if (!rc) break;
1017       rc = binary_archive.WriteUuid(m_transparency_texture_id);
1018       if (!rc) break;
1019       rc = binary_archive.WriteInterval(m_bump_scale);
1020       if (!rc) break;
1021       rc = binary_archive.WriteDouble(m_blend_constant_A);
1022       if (!rc) break;
1023       rc = binary_archive.WriteDouble(4,m_blend_A);
1024       if (!rc) break;
1025       rc = binary_archive.WriteColor(m_blend_constant_RGB);
1026       if (!rc) break;
1027       rc = binary_archive.WriteDouble(4,m_blend_RGB);
1028       if (!rc) break;
1029       rc = binary_archive.WriteInt(m_blend_order);
1030       if (!rc) break;
1031 
1032       break;
1033     }
1034 
1035 
1036     if ( !binary_archive.EndWrite3dmChunk() )
1037       rc = false;
1038   }
1039   return rc;
1040 }
1041 
TypeFromInt(int i)1042 ON_Texture::TYPE ON_Texture::TypeFromInt( int i )
1043 {
1044   ON_Texture::TYPE type = no_texture_type;
1045 
1046   switch((unsigned int)i)
1047   {
1048   case no_texture_type:
1049     type = no_texture_type;
1050     break;
1051   case bitmap_texture:
1052     type = bitmap_texture;
1053     break;
1054   case bump_texture:
1055     type = bump_texture;
1056     break;
1057   case emap_texture:
1058     type = emap_texture;
1059     break;
1060   case transparency_texture:
1061     type = transparency_texture;
1062     break;
1063   case force_32bit_texture_type:
1064     type = force_32bit_texture_type;
1065     break;
1066   }
1067 
1068   return type;
1069 }
1070 
ModeFromInt(int i)1071 ON_Texture::MODE ON_Texture::ModeFromInt( int i )
1072 {
1073   ON_Texture::MODE mode = no_texture_mode;
1074   switch((unsigned int)i)
1075   {
1076   case no_texture_mode:
1077     mode = no_texture_mode;
1078     break;
1079   case modulate_texture:
1080     mode = modulate_texture;
1081     break;
1082   case decal_texture:
1083     mode = decal_texture;
1084     break;
1085   case blend_texture:
1086     mode = blend_texture;
1087     break;
1088   case force_32bit_texture_mode:
1089     mode = force_32bit_texture_mode;
1090     break;
1091   }
1092   return mode;
1093 }
1094 
FilterFromInt(int i)1095 ON_Texture::FILTER ON_Texture::FilterFromInt( int i )
1096 {
1097   ON_Texture::FILTER filter = linear_filter;
1098   switch((unsigned int)i)
1099   {
1100   case nearest_filter:
1101     filter = nearest_filter;
1102     break;
1103   case linear_filter:
1104     filter = linear_filter;
1105     break;
1106   case force_32bit_texture_filter:
1107     filter = force_32bit_texture_filter;
1108     break;
1109   }
1110   return filter;
1111 }
1112 
WrapFromInt(int i)1113 ON_Texture::WRAP ON_Texture::WrapFromInt( int i )
1114 {
1115   ON_Texture::WRAP wrap = repeat_wrap;
1116 
1117   switch((unsigned int)i)
1118   {
1119   case repeat_wrap:
1120     wrap = repeat_wrap;
1121     break;
1122   case clamp_wrap:
1123     wrap = clamp_wrap;
1124     break;
1125   case force_32bit_texture_wrap:
1126     wrap = force_32bit_texture_wrap;
1127     break;
1128   }
1129 
1130   return wrap;
1131 }
1132 
1133 
1134 
1135 
1136 
1137 // overrides virtual ON_Object::Read
Read(ON_BinaryArchive & binary_archive)1138 ON_BOOL32 ON_Texture::Read(
1139         ON_BinaryArchive& binary_archive
1140       )
1141 {
1142   Default();
1143 
1144   int major_version = 0;
1145   int minor_version = 0;
1146   bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
1147   if (rc)
1148   {
1149 
1150     if ( 1 != major_version )
1151     {
1152       rc = false;
1153     }
1154     else
1155     {
1156       int i;
1157       for(;;)
1158       {
1159         // 1.0 values
1160         rc = binary_archive.ReadUuid( m_texture_id );
1161         if (!rc) break;
1162 
1163         rc = binary_archive.ReadInt( &m_mapping_channel_id );
1164         if (!rc) break;
1165 
1166         rc = binary_archive.ReadString(m_filename);
1167         if (!rc) break;
1168 
1169         rc = binary_archive.ReadBool(&m_bOn);
1170         if (!rc) break;
1171 
1172         rc = binary_archive.ReadInt(&i);
1173         if (!rc) break;
1174         m_type = ON_Texture::TypeFromInt(i);
1175 
1176         rc = binary_archive.ReadInt(&i);
1177         if (!rc) break;
1178         m_mode = ON_Texture::ModeFromInt(i);
1179 
1180         rc = binary_archive.ReadInt(&i);
1181         if (!rc) break;
1182         m_minfilter = ON_Texture::FilterFromInt(i);
1183 
1184         rc = binary_archive.ReadInt(&i);
1185         if (!rc) break;
1186         m_magfilter = ON_Texture::FilterFromInt(i);
1187 
1188         rc = binary_archive.ReadInt(&i);
1189         if (!rc) break;
1190         m_wrapu = ON_Texture::WrapFromInt(i);
1191 
1192         rc = binary_archive.ReadInt(&i);
1193         if (!rc) break;
1194         m_wrapv = ON_Texture::WrapFromInt(i);
1195 
1196         rc = binary_archive.ReadInt(&i);
1197         if (!rc) break;
1198         m_wrapw = ON_Texture::WrapFromInt(i);
1199 
1200         rc = binary_archive.ReadXform(m_uvw);
1201         if (!rc) break;
1202 
1203         rc = binary_archive.ReadColor(m_border_color);
1204         if (!rc) break;
1205 
1206         rc = binary_archive.ReadColor(m_transparent_color);
1207         if (!rc) break;
1208 
1209         rc = binary_archive.ReadUuid(m_transparency_texture_id);
1210         if (!rc) break;
1211 
1212         rc = binary_archive.ReadInterval(m_bump_scale);
1213         if (!rc) break;
1214 
1215         rc = binary_archive.ReadDouble(&m_blend_constant_A);
1216         if (!rc) break;
1217         rc = binary_archive.ReadDouble(4,m_blend_A);
1218         if (!rc) break;
1219         rc = binary_archive.ReadColor(m_blend_constant_RGB);
1220         if (!rc) break;
1221         rc = binary_archive.ReadDouble(4,m_blend_RGB);
1222         if (!rc) break;
1223 
1224         rc = binary_archive.ReadInt(&m_blend_order);
1225         if (!rc) break;
1226 
1227 
1228 
1229         break;
1230       }
1231     }
1232 
1233     if ( !binary_archive.EndRead3dmChunk() )
1234       rc = false;
1235   }
1236   return rc;
1237 }
1238 
1239 
Default()1240 void ON_Texture::Default()
1241 {
1242   PurgeUserData();
1243   m_texture_id = ON_nil_uuid;
1244   m_mapping_channel_id = 0;
1245   m_filename.Destroy();
1246   m_filename_bRelativePath = false;
1247   m_bOn = true;
1248   m_type = bitmap_texture;
1249   m_mode = modulate_texture;
1250   m_minfilter = linear_filter;
1251   m_magfilter = linear_filter;
1252   m_wrapu = repeat_wrap;
1253   m_wrapv = repeat_wrap;
1254   m_wrapw = repeat_wrap;
1255   m_uvw.Identity();
1256   m_border_color = ON_UNSET_COLOR;
1257   m_transparent_color = ON_UNSET_COLOR;
1258   m_transparency_texture_id = ON_nil_uuid;
1259   m_bump_scale.Set(0.0,1.0);
1260   m_blend_constant_A = 1.0;
1261   m_blend_A[0] = m_blend_A[1] = 1.0; m_blend_A[2] =  m_blend_A[3] = 0.0;
1262   m_blend_constant_RGB.SetRGB(0,0,0);
1263   m_blend_RGB[0] = m_blend_RGB[1] = 1.0; m_blend_RGB[2] = m_blend_RGB[3] = 0.0;
1264   m_blend_order = 0;
1265   m_runtime_ptr_id = ON_nil_uuid;
1266   m_runtime_ptr = 0;
1267 }
1268 
1269 
1270 ON_OBJECT_IMPLEMENT(ON_TextureMapping,ON_Object,"32EC997A-C3BF-4ae5-AB19-FD572B8AD554");
1271 
1272 
TypeFromInt(int i)1273 ON_TextureMapping::TYPE ON_TextureMapping::TypeFromInt(int i)
1274 {
1275   ON_TextureMapping::TYPE t;
1276   switch(i)
1277   {
1278   case srfp_mapping:
1279     t = srfp_mapping;
1280     break;
1281   case plane_mapping:
1282     t = plane_mapping;
1283     break;
1284   case cylinder_mapping:
1285     t = cylinder_mapping;
1286     break;
1287   case sphere_mapping:
1288     t = sphere_mapping;
1289     break;
1290   case box_mapping:
1291     t = box_mapping;
1292     break;
1293   case mesh_mapping_primitive:
1294     t = mesh_mapping_primitive;
1295     break;
1296   case srf_mapping_primitive:
1297     t = srf_mapping_primitive;
1298     break;
1299   case brep_mapping_primitive:
1300     t = brep_mapping_primitive;
1301     break;
1302   default:
1303     t = no_mapping;
1304     break;
1305   }
1306   return t;
1307 }
1308 
ProjectionFromInt(int i)1309 ON_TextureMapping::PROJECTION ON_TextureMapping::ProjectionFromInt(int i)
1310 {
1311   ON_TextureMapping::PROJECTION p;
1312   switch(i)
1313   {
1314   case clspt_projection:
1315     p = clspt_projection;
1316     break;
1317   case ray_projection:
1318     p = ray_projection;
1319     break;
1320   default:
1321     p = no_projection;
1322     break;
1323   }
1324   return p;
1325 }
1326 
TextureSpaceFromInt(int i)1327 ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromInt(int i)
1328 {
1329 	ON_TextureMapping::TEXTURE_SPACE ts = single;
1330 
1331 	switch(i)
1332 	{
1333 	case single:
1334 		ts = single;
1335 		break;
1336 	case divided:
1337 		ts = divided;
1338 		break;
1339 	}
1340 	return ts;
1341 }
1342 
ON_TextureMapping()1343 ON_TextureMapping::ON_TextureMapping()
1344 {
1345   m_mapping_primitive = 0;
1346   Default();
1347 }
1348 
~ON_TextureMapping()1349 ON_TextureMapping::~ON_TextureMapping()
1350 {
1351   if ( m_mapping_primitive )
1352   {
1353     delete m_mapping_primitive;
1354     m_mapping_primitive = 0;
1355   }
1356 }
1357 
1358 // The copy constructor and operator= overrides are needed
1359 // to ensure m_mapping_primitive is properly copied.
ON_TextureMapping(const ON_TextureMapping & src)1360 ON_TextureMapping::ON_TextureMapping(const ON_TextureMapping& src)
1361                   : ON_Object(src)
1362 {
1363   m_mapping_id    = src.m_mapping_id;
1364   m_mapping_index = src.m_mapping_index;
1365   m_mapping_name  = src.m_mapping_name;
1366   m_type          = src.m_type;
1367   m_projection    = src.m_projection;
1368   m_bCapped		    = src.m_bCapped;
1369 	m_texture_space = src.m_texture_space;
1370   m_Pxyz          = src.m_Pxyz;
1371   m_Nxyz          = src.m_Nxyz;
1372   m_uvw           = src.m_uvw;
1373   m_mapping_primitive = ( src.m_mapping_primitive )
1374                       ? src.m_mapping_primitive->Duplicate()
1375                       : 0;
1376 }
1377 
operator =(const ON_TextureMapping & src)1378 ON_TextureMapping& ON_TextureMapping::operator=(const ON_TextureMapping& src)
1379 {
1380   if ( this != &src )
1381   {
1382     if ( m_mapping_primitive )
1383     {
1384       delete m_mapping_primitive;
1385       m_mapping_primitive = 0;
1386     }
1387     ON_Object::operator=(src);
1388     m_mapping_id    = src.m_mapping_id;
1389     m_mapping_index = src.m_mapping_index;
1390     m_mapping_name  = src.m_mapping_name;
1391     m_type          = src.m_type;
1392     m_projection    = src.m_projection;
1393     m_bCapped			  = src.m_bCapped;
1394 		m_texture_space = src.m_texture_space;
1395     m_Pxyz          = src.m_Pxyz;
1396     m_Nxyz          = src.m_Nxyz;
1397     m_uvw           = src.m_uvw;
1398     if ( src.m_mapping_primitive )
1399       m_mapping_primitive = src.m_mapping_primitive->Duplicate();
1400   }
1401   return *this;
1402 }
1403 
Default()1404 void ON_TextureMapping::Default()
1405 {
1406   PurgeUserData();
1407   if ( m_mapping_primitive )
1408   {
1409     delete m_mapping_primitive;
1410     m_mapping_primitive = 0;
1411   }
1412 
1413   m_mapping_id = ON_nil_uuid;
1414   m_mapping_index = 0;
1415   m_mapping_name.Destroy();
1416   m_type = no_mapping;
1417   m_projection = no_projection;
1418   m_texture_space = single;
1419   m_Pxyz.Identity();
1420   m_Nxyz.Identity();
1421   m_uvw.Identity();
1422   m_bCapped = false;
1423 }
1424 
IsValid(ON_TextLog * text_log) const1425 ON_BOOL32 ON_TextureMapping::IsValid( ON_TextLog* text_log ) const
1426 {
1427   if ( m_type != ON_TextureMapping::TypeFromInt(m_type) )
1428   {
1429     if ( text_log )
1430     {
1431       text_log->Print("ON_TextureMapping m_type = %d is not a valid value.\n",m_type);
1432     }
1433     return false;
1434   }
1435 
1436   if ( m_projection != ON_TextureMapping::ProjectionFromInt(m_projection) )
1437   {
1438     if ( text_log )
1439     {
1440       text_log->Print("ON_TextureMapping m_projection = %d is not a valid value.\n",m_projection);
1441     }
1442     return false;
1443   }
1444 
1445   if (m_texture_space != ON_TextureMapping::TextureSpaceFromInt(m_texture_space))
1446   {
1447 	  if (text_log)
1448 	  {
1449 		  text_log->Print("ON_TextureMapping m_texture_space = %d is not a valid value.\n",m_texture_space);
1450 	  }
1451 	  return false;
1452   }
1453 
1454   return true;
1455 }
1456 
Dump(ON_TextLog & text_log) const1457 void ON_TextureMapping::Dump( ON_TextLog& text_log ) const
1458 {
1459   text_log.Print("Texture mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n");
1460   text_log.PushIndent();
1461 
1462   text_log.Print("type: ");
1463   switch(m_type)
1464   {
1465   case no_mapping:
1466     text_log.Print("no mapping\n");
1467     break;
1468   case plane_mapping:
1469     text_log.Print("plane mapping\n");
1470     break;
1471   case cylinder_mapping:
1472     text_log.Print("cylinder mapping\n");
1473     break;
1474   case sphere_mapping:
1475     text_log.Print("sphere mapping\n");
1476     break;
1477 	case box_mapping:
1478     text_log.Print("box mapping\n");
1479     break;
1480   default:
1481     text_log.Print("%d\n",m_type);
1482     break;
1483   }
1484 
1485   text_log.Print("projection: ");
1486   switch(m_projection)
1487   {
1488   case no_projection:
1489     text_log.Print("no projection\n");
1490     break;
1491   case clspt_projection:
1492     text_log.Print("closest point to mesh vertex\n");
1493     break;
1494   case ray_projection:
1495     text_log.Print("mesh normal ray intersection\n");
1496     break;
1497   default:
1498     text_log.Print("%d\n",m_projection);
1499     break;
1500   }
1501 
1502 	text_log.Print("texture_space: ");
1503   switch(m_texture_space)
1504   {
1505   case single:
1506     text_log.Print("single texture space\n");
1507     break;
1508   case clspt_projection:
1509     text_log.Print("divided texture space\n");
1510     break;
1511   default:
1512     text_log.Print("%d\n",m_texture_space);
1513     break;
1514   }
1515 
1516   text_log.Print("XYZ point transformation:\n");
1517   text_log.PushIndent();
1518   text_log.Print(m_Pxyz);
1519   text_log.PopIndent();
1520 
1521   text_log.Print("XYZ normal transformation:\n");
1522   text_log.PushIndent();
1523   text_log.Print(m_Nxyz);
1524   text_log.PopIndent();
1525 
1526   text_log.Print("UVW transformation:\n");
1527   text_log.PushIndent();
1528   text_log.Print(m_uvw);
1529   text_log.PopIndent();
1530 
1531   text_log.PopIndent();
1532 }
1533 
SizeOf() const1534 unsigned int ON_TextureMapping::SizeOf() const
1535 {
1536   unsigned int sz = ON_Object::SizeOf();
1537   sz = sizeof(*this) - sizeof(ON_Object);
1538   return sz;
1539 }
1540 
ReverseTextureCoordinate(int dir)1541 bool ON_TextureMapping::ReverseTextureCoordinate( int dir )
1542 {
1543   bool rc = false;
1544   if ( 0 <= dir && dir <= 3 )
1545   {
1546     ON_Xform x(1.0);
1547     x.m_xform[dir][dir] = -1.0;
1548     x.m_xform[dir][3] = 1.0;
1549     m_uvw = x*m_uvw;
1550     rc = true;
1551   }
1552   return rc;
1553 }
1554 
SwapTextureCoordinate(int i,int j)1555 bool ON_TextureMapping::SwapTextureCoordinate( int i, int j )
1556 {
1557   bool rc = false;
1558   if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
1559   {
1560     ON_Xform x(1.0);
1561     x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
1562     x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
1563     m_uvw = x*m_uvw;
1564     rc = true;
1565   }
1566   return rc;
1567 }
1568 
TileTextureCoordinate(int dir,double count,double offset)1569 bool ON_TextureMapping::TileTextureCoordinate( int dir, double count, double offset )
1570 {
1571   bool rc = false;
1572   if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
1573   {
1574     ON_Xform x(1.0);
1575     x.m_xform[dir][dir] = count;
1576     x.m_xform[dir][3] = offset;
1577     m_uvw = x*m_uvw;
1578     rc = true;
1579   }
1580   return rc;
1581 }
1582 
1583 
ReverseTextureCoordinate(int dir)1584 bool ON_Texture::ReverseTextureCoordinate( int dir )
1585 {
1586   bool rc = false;
1587   if ( 0 <= dir && dir <= 3 )
1588   {
1589     ON_Xform x(1.0);
1590     x.m_xform[dir][dir] = -1.0;
1591     x.m_xform[dir][3] = 1.0;
1592     m_uvw = x*m_uvw;
1593     rc = true;
1594   }
1595   return rc;
1596 }
1597 
SwapTextureCoordinate(int i,int j)1598 bool ON_Texture::SwapTextureCoordinate( int i, int j )
1599 {
1600   bool rc = false;
1601   if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
1602   {
1603     ON_Xform x(1.0);
1604     x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
1605     x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
1606     m_uvw = x*m_uvw;
1607     rc = true;
1608   }
1609   return rc;
1610 }
1611 
TileTextureCoordinate(int dir,double count,double offset)1612 bool ON_Texture::TileTextureCoordinate( int dir, double count, double offset )
1613 {
1614   bool rc = false;
1615   if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
1616   {
1617     ON_Xform x(1.0);
1618     x.m_xform[dir][dir] = count;
1619     x.m_xform[dir][3] = offset;
1620     m_uvw = x*m_uvw;
1621     rc = true;
1622   }
1623   return rc;
1624 }
1625 
IsTiled(int dir,double * count,double * offset) const1626 bool ON_Texture::IsTiled( int dir, double* count, double* offset ) const
1627 {
1628   if ( count )
1629     *count = 1.0;
1630   if ( offset )
1631     *offset = 0.0;
1632 
1633   if ( 0 <= dir && dir <= 3 )
1634   {
1635     int row0=-1, row, col;
1636     for ( row = 0; row < 3; row++ )
1637     {
1638       for ( col = 0; col < 3; col++ )
1639       {
1640         if ( col != dir && 0.0 != m_uvw.m_xform[row][col] )
1641           break;
1642       }
1643       if ( 3 == col )
1644       {
1645         if ( -1 == row0 )
1646         {
1647           row0 = row;
1648         }
1649         else
1650           return false;
1651       }
1652     }
1653     if ( row0 >= 0 )
1654     {
1655       if (count)
1656         *count = m_uvw.m_xform[row0][dir];
1657       if ( offset )
1658         *offset = m_uvw.m_xform[row0][3];
1659       return true;
1660     }
1661   }
1662 
1663   return false;
1664 }
1665 
1666 static const double on__overflow_tol = 1.0e100;
1667 
1668 static
BestHitHelper(double t0,double t1)1669 int BestHitHelper(double t0, double t1)
1670 {
1671   return ((t0 < 0.0 && t1 > t0) || (0.0 <= t1 && t1 < t0)) ? 1 : 0;
1672 }
1673 
1674 static
IntersectBoxRayHelper(const ON_3dPoint & rst,const ON_3dVector & n,int dir,double * s)1675 int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, double* s)
1676 {
1677   /*
1678   returns:
1679     0 = ray parallel to sides
1680     1 = ray hit left side (x=-1)
1681     2 = ray hit right side (x=+1)
1682     3 = ray hit back side (y=-1)
1683     4 = ray hit front side (y=+1)
1684     5 = ray hit bottom side (z=-1)
1685     6 = ray hit top side (z=+1)
1686   */
1687   double nx = (&n.x)[dir];
1688   ON_3dPoint Q;
1689   double t,t0,t1;
1690 
1691   // protect against overflow
1692   t = fabs(nx)*on__overflow_tol;
1693   t0 = (-1.0 - (&rst.x)[dir]);
1694   t1 = ( 1.0 - (&rst.x)[dir]);
1695   if ( fabs(t0) >= t || fabs(t1) >= t )
1696   {
1697     *s = ON_UNSET_VALUE;
1698     return 0;
1699   }
1700 
1701   t0 /= nx;
1702   Q = rst + t0*n;
1703   if ( dir )
1704   {
1705     t = Q.x;
1706     Q.x = Q[dir];
1707     Q[dir] = t;
1708   }
1709   if ( fabs(Q.x+1.0) > ON_SQRT_EPSILON
1710         || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
1711         || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
1712         )
1713   {
1714     // The ray's intersection with the plane missed the
1715     // (-1,+1)x(-1,+1) square that is the side of the box.
1716     t0 = ON_UNSET_VALUE;
1717   }
1718 
1719   t1 /= nx;
1720   Q = rst + t1*n;
1721   if ( dir )
1722   {
1723     t = Q.x;
1724     Q.x = Q[dir];
1725     Q[dir] = t;
1726   }
1727   if ( fabs(Q.x-1.0) > ON_SQRT_EPSILON
1728         || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
1729         || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
1730         )
1731   {
1732     // The ray's intersection with the plane missed the
1733     // (-1,+1)x(-1,+1) square that is the side of the box.
1734     t1 = ON_UNSET_VALUE;
1735     if ( ON_UNSET_VALUE == t0 )
1736     {
1737       *s = ON_UNSET_VALUE;
1738       return 0;
1739     }
1740   }
1741 
1742   int rc;
1743   if ( ON_UNSET_VALUE == t0 || 1 == BestHitHelper(t0,t1) )
1744   {
1745     rc = 2 + 2*dir;
1746     *s = t1;
1747   }
1748   else
1749   {
1750     rc = 1 + 2*dir;
1751     *s = t0;
1752   }
1753   return rc;
1754 }
1755 
1756 
EvaluatePlaneMapping(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T) const1757 int ON_TextureMapping::EvaluatePlaneMapping(
1758   const ON_3dPoint& P,
1759   const ON_3dVector& N,
1760   ON_3dPoint* T
1761   ) const
1762 {
1763   // The matrix m_Pxyz transforms the world  coordinate
1764   // "mapping rectangle" into the rectangle
1765   //   -1 <= r <= 1, -1 <= s <= 1, and  (-1 <= t <= 1)
1766 
1767   ON_3dPoint rst(m_Pxyz*P);
1768 
1769   if ( ray_projection == m_projection )
1770   {
1771     ON_3dVector n(m_Nxyz*N);
1772     if ( fabs(rst.z) < fabs(n.z)*on__overflow_tol )
1773     {
1774       double t = -rst.z/n.z;
1775       rst.x = rst.x + t*n.x;
1776       rst.y = rst.y + t*n.y;
1777     }
1778   }
1779 
1780   // convert -1 <= r <= 1, -1 <= s <= 1
1781   // to normalized texture coordinate
1782 	rst.x = 0.5*rst.x + 0.5;
1783 	rst.y = 0.5*rst.y + 0.5;
1784 
1785   // Apply texture coordinate transformation
1786   *T = m_uvw*rst;
1787 
1788   //See docs - if m_bCapped is false, then planar is truely flat.
1789   if (!m_bCapped)
1790 	  T->z = 0.0;
1791 
1792   return 1;
1793 }
1794 
EvaluateSphereMapping(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T) const1795 int ON_TextureMapping::EvaluateSphereMapping(
1796 											  const ON_3dPoint& P,
1797 											  const ON_3dVector& N,
1798 											  ON_3dPoint* T
1799 											  ) const
1800 {
1801   // The matrix m_Pxyz transforms the world coordinate
1802   // "mapping sphere" into the sphere centered at
1803   // rst = (0,0,0) with radius 1.0.
1804 
1805   ON_3dPoint rst(m_Pxyz*P);
1806 	const double r = ((const ON_3dVector*)(&rst.x))->Length();
1807 	double t0, t1;
1808 
1809 	if ( ray_projection == m_projection )
1810 	{
1811 		ON_3dVector n(m_Nxyz*N);
1812 		// Shoot a ray from P in the direction N and see if it
1813 		// hits the sphere.
1814 		int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z),
1815 			2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z),
1816 			(rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0,
1817 			&t0, &t1 );
1818 		if (rc >= 0 )
1819 		{
1820 			if ( 2 != rc && 1 == BestHitHelper(t0,t1) )
1821 			{
1822 				t0 = t1;
1823 			}
1824 			rst = rst + t0*n;
1825 		}
1826 	}
1827 
1828 	// convert sphere 3d location to longitude, latitude, radius
1829 	double longitude = (0.0 != rst.y || 0.0 != rst.x)
1830 		? atan2(rst.y,rst.x)
1831 		: 0.0;
1832 	double latitude = (0.0 != rst.z)
1833 		? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length())
1834 		: 0.0;
1835 	if ( latitude > ON_PI )
1836 		latitude -= 2.0*ON_PI;
1837 
1838   // convert longitude to normalized texture coordinate
1839 	rst.x = 0.5*longitude/ON_PI;
1840 	if ( rst.x < -ON_EPSILON )
1841 		rst.x += 1.0;
1842 	else if (rst.x < 0.0)
1843 		rst.x = 0.0;
1844 	else if (rst.x > 1.0)
1845 		rst.x = 1.0;
1846 
1847   // convert longitude to normalized texture coordinate
1848 	rst.y = latitude/ON_PI + 0.5;
1849   if ( rst.y <= 0.0 )
1850     rst.y = 0.0;
1851 	else if ( rst.y > 1.0 )
1852 		  rst.y = 1.0;
1853 
1854   // radius is already normalized
1855 	rst.z = r;
1856 
1857   // apply texture coordinate transformation
1858 	*T = m_uvw*rst;
1859 
1860   return 1;
1861 }
1862 
EvaluateCylinderMapping(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T) const1863 int ON_TextureMapping::EvaluateCylinderMapping(
1864 												const ON_3dPoint& P,
1865 												const ON_3dVector& N,
1866 												ON_3dPoint* T
1867 												) const
1868 {
1869   // The matrix m_Pxyz transforms the world coordinate
1870   // "mapping cylinder" into the cylinder centered at
1871   // rst = (0,0,0) with radius 1.0.  The axis runs
1872   // from rst = (0,0,-1) to rst = (0,0,+1).
1873 
1874 	ON_3dPoint rst(m_Pxyz*P);
1875 
1876 	ON_3dPoint Q;
1877 	const double r = ((const ON_2dVector*)(&rst.x))->Length();
1878 	double t, t0, t1;
1879 	int side0, side1;
1880 	PROJECTION mapping_proj = m_projection;
1881 
1882 	side0 = 0;
1883 	if ( ON_TextureMapping::ray_projection == mapping_proj )
1884 	{
1885 		ON_3dVector n(m_Nxyz*N);
1886 		t = 0.0;
1887 
1888 		if ( m_bCapped )
1889 		{
1890 			// shoot at caps
1891 			//  The < t check prevents overflow when the
1892 			//  ray is nearly parallel to the cap.
1893 			t = fabs(n.z)*on__overflow_tol;
1894 			if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t )
1895 			{
1896 				side0 = 2;
1897 				side1 = 3;
1898 
1899 				t0 = (-1.0 - rst.z)/n.z;
1900 				Q = rst + t0*n;
1901 				if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON
1902 					|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
1903 				{
1904           // The ray's intersection with the bottom plane missed the
1905           // radius 1 disk that is the bottom of the cylinder.
1906 					side0 = 0;
1907 				}
1908 
1909 				t1 = ( 1.0 - rst.z)/n.z;
1910 				Q = rst + t1*n;
1911 				if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON
1912 					|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
1913 				{
1914           // The ray's intersection with the top plane missed the
1915           // radius 1 disk that is the top of the cylinder.
1916 					side1 = 0;
1917 				}
1918 				if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
1919 				{
1920 					side0 = side1;
1921 					t = t1;
1922 				}
1923 				else
1924 				{
1925 					t = t0;
1926 				}
1927 			}
1928 		}
1929 
1930 		// shoot ray at the cylinder wall
1931 		int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y),
1932 			2.0*(rst.x*n.x+rst.y*n.y),
1933 			(rst.x*rst.x+rst.y*rst.y) - 1.0,
1934 			&t0, &t1 );
1935 		if (rc >= 0 )
1936 		{
1937 			if ( 2 != rc  && 1 == BestHitHelper(t0,t1) )
1938 			{
1939 				t0 = t1;
1940 			}
1941 			if ( 0 == side0 )
1942 			{
1943 				// Either the caps are missing or the ray missed the caps.
1944         // The best hit is the cylinder wall.
1945 				side0 = 1;
1946 				rst = rst + t0*n;
1947 			}
1948 			else if ( 1 != BestHitHelper(t0,t) )
1949 			{
1950 				// The cylinder is capped and the ray hit the cap,
1951         // hit the infinite cylinder wall, and the wall
1952         // hit is "first".  If the ray hits the finite
1953         // cylinder wall, the I will use the wall hit.
1954 				t1 = rst.z + t0*n.z;
1955 				if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON )
1956 				{
1957 					// use the hit on the cylinder wall
1958 					side0 = 1;
1959 					rst.x = rst.x + t0*n.x;
1960 					rst.y = rst.y + t0*n.y;
1961           rst.x = t1;
1962 				}
1963 			}
1964 		}
1965 
1966 		if ( side0 > 1 )
1967 		{
1968 			// best hit is on a cap
1969 			rst = rst + t*n;
1970 		}
1971 	}
1972 
1973 	if ( m_bCapped && 0 == side0 )
1974 	{
1975     if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON )
1976     {
1977       if ( fabs(rst.z) > r )
1978       {
1979         side0 = (rst.z < 0.0) ? 2 : 3;
1980       }
1981     }
1982     else if ( r <= 1.001 )
1983     {
1984       // The point is inside the capped cylinder.
1985       // Use normal to dermine which surface to use
1986       // for closest point test.
1987 		  ON_3dVector n(m_Nxyz*N);
1988       if (  ( fabs(n.z) > fabs(n.x) && fabs(n.z) > fabs(n.y) ) )
1989       {
1990         side0 = (n.z < 0.0) ? 2 : 3;
1991       }
1992     }
1993 	}
1994 
1995 	if ( 2 == side0 || 3 == side0 )
1996 	{
1997     // The cylinder is capped and P maps to
1998     // the top (1 == side0) or bottom (2 == side0)
1999 
2000     if ( 2 == side0 )
2001     {
2002       // This is the same convention as box mapping.
2003       // Put another way, if you change the mapping
2004       // between box and cylinder, you get the same
2005       // picture on the top and bottom.
2006       rst.x = -rst.x;
2007     }
2008 
2009 		if ( ON_TextureMapping::divided == m_texture_space )
2010 		{
2011 		  if ( r >= 1.0-ON_SQRT_EPSILON )
2012 		  {
2013 			  rst.x /= (r+ON_SQRT_EPSILON);
2014 			  rst.y /= (r+ON_SQRT_EPSILON);
2015 		  }
2016     }
2017     else if ( r > 1.0 )
2018 	  {
2019 		  rst.x /= r;
2020 		  rst.y /= r;
2021 	  }
2022 
2023 
2024     // convert to normalized texture coordinates
2025 		rst.x = 0.5*rst.x + 0.5;
2026     if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0;
2027 		rst.y = 0.5*rst.y + 0.5;
2028     if ( rst.y < 0.0) rst.y = 0.0; else if (rst.y > 1.0) rst.y = 1.0;
2029 
2030 		if ( ON_TextureMapping::divided == m_texture_space )
2031 		{
2032       // bottom uses 4/6 <= x <= 5/6 region of the texture map.
2033       // top uses 5/6 <= x <= 1 region of the texture map.
2034 			rst.x = (2.0 + side0 + rst.x)/6.0;
2035 		}
2036 	}
2037 	else
2038 	{
2039     // P maps to side of the cylinder.
2040     //
2041     // convert longitude to normalized texture coordinate
2042 		t = (0.0 != rst.y || 0.0 != rst.x) ? atan2(rst.y,rst.x) : 0.0;
2043 		rst.x = 0.5*t/ON_PI;
2044 		if ( rst.x < -ON_EPSILON )
2045 			rst.x += 1.0;
2046 		else if (rst.x < 0.0 )
2047 			rst.x = 0.0;
2048 		else if (rst.x > 1.0 )
2049 			rst.x = 1.0;
2050 
2051     if ( ON_TextureMapping::divided == m_texture_space )
2052     {
2053       // side uses 0 <= x <= 2/3 region of the texture map
2054       rst.x *= 2.0;
2055 			rst.x /= 3.0;
2056     }
2057 
2058     // convert height to normalized texture coordinate
2059   	rst.y = 0.5*rst.z + 0.5;
2060     if ( m_bCapped )
2061     {
2062       // clamp height
2063       if ( rst.y < 0.0 ) rst.y = 0.0; else if ( rst.y > 1.0 ) rst.y = 1.0;
2064     }
2065     side0 = 1;
2066 	}
2067 	rst.z = r;
2068 
2069 	*T = m_uvw*rst;
2070 
2071   return side0;
2072 }
2073 
EvaluateBoxMapping(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T) const2074 int ON_TextureMapping::EvaluateBoxMapping(
2075 										   const ON_3dPoint& P,
2076 										   const ON_3dVector& N,
2077 										   ON_3dPoint* T
2078 										   ) const
2079 {
2080   // The matrix m_Pxyz transforms the world coordinate
2081   // "mapping cylinder" into the cylinder centered at
2082   // rst = (0,0,0) with radius 1.0.  The axis runs
2083   // from rst = (0,0,-1) to rst = (0,0,+1).
2084 
2085   ON_3dPoint rst(m_Pxyz*P);
2086 
2087 	ON_3dVector n(m_Nxyz*N);
2088   n.Unitize();
2089 
2090 	int side0, side1;
2091 	double t0, t1;
2092 
2093 	side0 = 0;
2094 	t0 = 0.0;
2095 
2096   // side flag
2097   //  1 =  left side (x=-1)
2098   //  2 =  right side (x=+1)
2099   //  3 =  back side (y=-1)
2100   //  4 =  front side (y=+1)
2101   //  5 =  bottom side (z=-1)
2102   //  6 =  top side (z=+1)
2103 
2104   if ( ON_TextureMapping::ray_projection == m_projection )
2105 	{
2106 
2107 		if ( m_bCapped )
2108 		{
2109 			// intersect ray with top and bottom
2110 			side0 = IntersectBoxRayHelper(rst,n,2,&t0);
2111 		}
2112 		// intersect ray with front and back
2113 		side1 = IntersectBoxRayHelper(rst,n,0,&t1);
2114 		if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
2115 		{
2116 			side0 = side1;
2117 			t0 = t1;
2118 		}
2119 		// intersect ray with left and right
2120 		side1 = IntersectBoxRayHelper(rst,n,1,&t1);
2121 		if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
2122 		{
2123 			side0 = side1;
2124 			t0 = t1;
2125 		}
2126 		if ( 0 != side0 )
2127 		{
2128 			// ray hit the box
2129 			rst = rst + t0*n;
2130 		}
2131 	}
2132 
2133   if ( 0 == side0 )
2134   {
2135     // set side0 = side closest to the point
2136     side1 = (fabs(rst.x) >= fabs(rst.y)) ? 0 : 1;
2137     if ( m_bCapped && fabs(rst.z) > fabs(((double*)&rst.x)[side1]) )
2138       side1 = 2;
2139     t1 = (&rst.x)[side1];
2140     if ( t1 < 0.0 )
2141     {
2142       side0 = 2*side1 + 1;
2143     }
2144     else
2145     {
2146       side0 = 2*side1 + 2;
2147     }
2148 
2149     //if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )...
2150     //// The point is inside the box.  If the normal
2151     //// is not zero, then use it to choose the side
2152     //// used for the closest point projection.
2153 
2154     side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1;
2155     if ( m_bCapped && fabs(n.z) > fabs((&n.x)[side1]))
2156     {
2157       side1 = 2;
2158     }
2159     t1 = n[side1];
2160     if ( 0.0 != t1 )
2161     {
2162       if ( t1 < 0.0 )
2163         side0 = 2*side1 + 1;
2164       else if ( t1 > 0.0 )
2165         side0 = 2*side1 + 2;
2166     }
2167   }
2168 
2169 	double shift = 0.0;
2170 
2171   // side flag
2172   //  1 =  left side (x=-1)
2173   //  2 =  right side (x=+1)
2174   //  3 =  back side (y=-1)
2175   //  4 =  front side (y=+1)
2176   //  5 =  bottom side (z=-1)
2177   //  6 =  top side (z=+1)
2178 
2179 	switch(side0)
2180 	{
2181 	case 1: // x = -1
2182 		rst.x = -rst.y;
2183 		rst.y =  rst.z;
2184 		shift =  3.0;
2185 		break;
2186 	case 2: // x = +1
2187 		rst.x =  rst.y;
2188 		rst.y =  rst.z;
2189 		shift =  1.0;
2190 		break;
2191 	case 3: // y = -1
2192 		rst.y =  rst.z;
2193 		shift =  0.0;
2194 		break;
2195 	case 4: // y = +1
2196 		rst.x = -rst.x;
2197 		rst.y =  rst.z;
2198 		shift =  2.0;
2199 		break;
2200 	case 5: // z = -1
2201 		rst.x = -rst.x;
2202 		shift =  4.0;
2203 		break;
2204 	case 6: // z = +1
2205 		shift =  5.0;
2206 		break;
2207 	}
2208 
2209   // normalize texture coordinates
2210   rst.x = 0.5*rst.x + 0.5;
2211   rst.y = 0.5*rst.y + 0.5;
2212 	rst.z = 0.0;
2213 
2214 	if( divided == m_texture_space)
2215 	{
2216     rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0);
2217 	}
2218 
2219 	*T = m_uvw*rst;
2220 
2221   return side0;
2222 }
2223 
Evaluate(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T,const ON_Xform & P_xform,const ON_Xform & N_xform) const2224 int ON_TextureMapping::Evaluate(
2225         const ON_3dPoint& P,
2226         const ON_3dVector& N,
2227         ON_3dPoint* T,
2228         const ON_Xform& P_xform,
2229         const ON_Xform& N_xform
2230         ) const
2231 {
2232   int rc;
2233   ON_3dPoint Q = P*P_xform;
2234   if ( ON_TextureMapping::ray_projection == m_projection )
2235   {
2236     // need a transformed normal
2237     ON_3dVector V = N_xform*N;
2238     V.Unitize();
2239     rc = Evaluate(Q,V,T);
2240   }
2241   else
2242   {
2243     // normal is ignored
2244     rc = Evaluate(Q,N,T);
2245   }
2246   return rc;
2247 }
2248 
Evaluate(const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T) const2249 int ON_TextureMapping::Evaluate(
2250         const ON_3dPoint& P,
2251         const ON_3dVector& N,
2252         ON_3dPoint* T
2253         ) const
2254 {
2255   int rc;
2256 
2257 	switch(m_type)
2258 	{
2259 	case srfp_mapping:
2260 		*T = m_uvw * P; // Do NOT apply m_Pxyz here.
2261     rc = 1;
2262 		break;
2263 	case sphere_mapping:
2264 		rc = EvaluateSphereMapping(P,N,T);
2265 		break;
2266 	case cylinder_mapping:
2267 		rc = EvaluateCylinderMapping(P,N,T);
2268 		break;
2269 	case box_mapping:
2270 		rc = EvaluateBoxMapping(P,N,T);
2271 		break;
2272 
2273 	case mesh_mapping_primitive:
2274     rc = 0;
2275 		break;
2276 
2277 	case srf_mapping_primitive:
2278     rc = 0;
2279 		break;
2280 
2281 	case brep_mapping_primitive:
2282     rc = 0;
2283 		break;
2284 
2285 	default:
2286 		rc = EvaluatePlaneMapping(P,N,T);
2287 		break;
2288 	}
2289   return rc;
2290 }
2291 
MappingCRC() const2292 ON__UINT32 ON_TextureMapping::MappingCRC() const
2293 {
2294   // include any member that can change values returned by Evaluate
2295   ON__UINT32 crc32 = 0x12345678;
2296   crc32 = ON_CRC32(crc32,sizeof(m_type),&m_type);
2297   if ( ON_TextureMapping::srfp_mapping != m_type )
2298   {
2299     // As of 21 June 2006 m_Pxyz cannot effect srfp_mapping,
2300     // so it shouldn't be included in the CRC for srfp_mappings.
2301     crc32 = ON_CRC32(crc32,sizeof(m_projection),    &m_projection);
2302     crc32 = ON_CRC32(crc32,sizeof(m_texture_space), &m_texture_space);
2303     crc32 = ON_CRC32(crc32,sizeof(m_bCapped),		    &m_bCapped);
2304     crc32 = ON_CRC32(crc32,sizeof(m_Pxyz),          &m_Pxyz);
2305     // do not include m_Nxyz here - it won't help and may hurt
2306 
2307 	  if ( 0 != m_mapping_primitive )
2308 	  {
2309       switch( m_type )
2310       {
2311       case ON_TextureMapping::mesh_mapping_primitive:
2312         {
2313           const ON_Mesh* mesh = ON_Mesh::Cast(m_mapping_primitive);
2314           if ( 0 == mesh )
2315             break;
2316           crc32 = mesh->DataCRC(crc32);
2317           if ( mesh->HasTextureCoordinates() )
2318           {
2319             // 25 August 2010 Dale Lear
2320             //   Including m_T[] in crc32 per Jussi and Andy email discussion.
2321             //   This is probably correct because users will expect the
2322             //   "picture" on the mesh to be applied to the target in
2323             //   a visual way.
2324             const ON_2fPoint* tex = mesh->m_T.Array();
2325             crc32 = ON_CRC32(crc32,mesh->m_T.Count()*sizeof(tex[0]),tex);
2326           }
2327         }
2328         break;
2329 
2330       case ON_TextureMapping::brep_mapping_primitive:
2331         {
2332           const ON_Brep* brep = ON_Brep::Cast(m_mapping_primitive);
2333           if ( 0 == brep )
2334             break;
2335           crc32 = brep->DataCRC(crc32);
2336           // 25 August 2010 Dale Lear
2337           //   Should brep's render meshes be included in the crc?
2338           //   The texture that is being mapped is actually
2339           //   being applied to the brep by the render mesh's
2340           //   m_T[] values and some users will want to see
2341           //   the "picture" on the brep mapped to the
2342           //   "picture" on the
2343           //   target.
2344         }
2345         break;
2346 
2347       case ON_TextureMapping::srf_mapping_primitive:
2348         {
2349           const ON_Surface* surface = ON_Surface::Cast(m_mapping_primitive);
2350           if ( 0 == surface )
2351             break;
2352           crc32 = surface->DataCRC(crc32);
2353         }
2354         break;
2355 
2356       case ON_TextureMapping::no_mapping:
2357       case ON_TextureMapping::srfp_mapping:
2358       case ON_TextureMapping::plane_mapping:
2359       case ON_TextureMapping::cylinder_mapping:
2360       case ON_TextureMapping::sphere_mapping:
2361       case ON_TextureMapping::box_mapping:
2362       case ON_TextureMapping::force_32bit_mapping_projection:
2363       default:
2364         break;
2365       }
2366     }
2367 
2368   }
2369 
2370   crc32 = ON_CRC32(crc32,sizeof(m_uvw), &m_uvw);
2371   return crc32;
2372 }
2373 
RequiresVertexNormals() const2374 bool ON_TextureMapping::RequiresVertexNormals() const
2375 {
2376   if ( ON_TextureMapping::srfp_mapping == m_type )
2377     return false;
2378 
2379 	if(m_projection == ray_projection)
2380     return true;
2381 
2382   if(m_type == box_mapping)
2383     return true;
2384 	if(m_type == cylinder_mapping && m_bCapped)
2385     return true;
2386 
2387 	return false;
2388 }
2389 
IsPeriodic(void) const2390 bool ON_TextureMapping::IsPeriodic(void) const
2391 {
2392 	return (m_type == sphere_mapping || m_type == cylinder_mapping);
2393 }
2394 
HasMatchingTextureCoordinates(const ON_Mesh & mesh,const ON_Xform * mesh_xform) const2395 bool ON_TextureMapping::HasMatchingTextureCoordinates(
2396        const ON_Mesh& mesh,
2397        const ON_Xform* mesh_xform
2398        ) const
2399 {
2400   bool rc = (mesh.HasTextureCoordinates())
2401           ? HasMatchingTextureCoordinates(mesh.m_Ttag,mesh_xform)
2402           : false;
2403 
2404   return rc;
2405 }
2406 
HasMatchingTextureCoordinates(const ON_MappingTag & tag,const ON_Xform * mesh_xform) const2407 bool ON_TextureMapping::HasMatchingTextureCoordinates(
2408        const ON_MappingTag& tag,
2409        const ON_Xform* mesh_xform
2410        ) const
2411 {
2412   bool rc = false;
2413 
2414   // DO NOT COMPARE m_mapping_id's in this function.
2415   // This function returns true if the tc values
2416   // calculated by the mapping will be the same
2417   // as the mapping that was used to set the tag.
2418   if ( tag.m_mapping_crc == MappingCRC() )
2419   {
2420     rc = true;
2421 
2422     // zero transformations indicate the mapping
2423     // values are independent of the 3d location
2424     // of the mesh.  The srfp_mapping != m_type
2425     // check is used because these mappings are
2426     // alwasy independent of 3d location but
2427     // the transformations are often set.
2428     if ( ON_TextureMapping::srfp_mapping != m_type
2429          && mesh_xform
2430          && mesh_xform->IsValid()
2431          && !mesh_xform->IsZero()
2432          && !tag.m_mesh_xform.IsZero()
2433        )
2434     {
2435       // compare xforms - these can have a bit of slop
2436       const double* a = &mesh_xform->m_xform[0][0];
2437       const double* b = &tag.m_mesh_xform.m_xform[0][0];
2438       for ( int i = 16; i--; /*empty*/ )
2439       {
2440         if ( fabs(*a++ - *b++) > ON_SQRT_EPSILON )
2441         {
2442           rc = false;
2443           break;
2444         }
2445       }
2446     }
2447   }
2448 
2449   return rc;
2450 }
2451 
2452 static
GetSPTCHelper(const ON_Mesh & mesh,const ON_TextureMapping & mapping,float * tc,int tc_stride)2453 bool GetSPTCHelper(
2454   const ON_Mesh& mesh,
2455   const ON_TextureMapping& mapping,
2456   float* tc,
2457   int tc_stride
2458   )
2459 {
2460   const int vcnt = mesh.m_V.Count();
2461   if ( vcnt <= 0 )
2462     return false;
2463   if ( !mesh.HasSurfaceParameters() )
2464     return false;
2465   const ON_2dPoint* S = mesh.m_S.Array();
2466   if ( !S )
2467     return false;
2468 
2469   int i;
2470   double u, v, a, b;
2471 
2472   // srf_udom and srf_vdom record the range
2473   // of parameters saved in the m_S[] array.
2474   ON_Interval srf_udom = mesh.m_srf_domain[0];
2475   ON_Interval srf_vdom = mesh.m_srf_domain[1];
2476   if ( !srf_udom.IsIncreasing() || !srf_vdom.IsIncreasing() )
2477   {
2478     // Attempt to calculate it from m_S[].
2479     srf_udom.m_t[0] = srf_udom.m_t[1] = S[0].x;
2480     srf_vdom.m_t[0] = srf_vdom.m_t[1] = S[0].y;
2481     for ( i = 1; i < vcnt; i++ )
2482     {
2483       u = S[i].x;
2484       if      (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u;
2485       else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u;
2486       v = S[i].y;
2487       if      (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v;
2488       else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v;
2489     }
2490     if (    !srf_udom.IsIncreasing()
2491          || !srf_vdom.IsIncreasing() )
2492     {
2493       return false;
2494     }
2495   }
2496 
2497   bool bHaveUVWXform =   mapping.m_uvw.IsValid()
2498                      && !mapping.m_uvw.IsIdentity()
2499                      && !mapping.m_uvw.IsZero();
2500 
2501   if ( mesh.HasPackedTextureRegion() )
2502   {
2503     // Packed textures are not compatible with the use
2504     // of m_uvw.  m_uvw is ignored in this block
2505     // of code on purpose.  //SEE BELOW
2506     const ON_Interval tex_udom = mesh.m_packed_tex_domain[0];
2507     const ON_Interval tex_vdom = mesh.m_packed_tex_domain[1];
2508     for ( i = 0; i < vcnt; i++)
2509     {
2510 		//ALB 2011.01.14
2511 		//Added support for m_uvw in packed textures.  Even though this conceptually makes
2512 		//very little sense, it's one of the most requested features for the texture mapping
2513 		//system, so I grudgingly add it.
2514 		if (bHaveUVWXform)
2515 		{
2516 			const ON_2dPoint si = mapping.m_uvw*S[i];
2517 			u = si.x;
2518 			v = si.y;
2519 		}
2520 		else
2521 		{
2522 			u = S[i].x;
2523 			v = S[i].y;
2524 		}
2525 
2526 	    // (u, v) = known surface parameter
2527 	    if ( mesh.m_packed_tex_rotate )
2528 	    {
2529         // verify this by checking with mesher
2530 	       a = 1.0 - srf_vdom.NormalizedParameterAt( v );
2531 	       b = srf_udom.NormalizedParameterAt( u );
2532 	    }
2533 	    else
2534 	    {
2535 	      a = srf_udom.NormalizedParameterAt( u );
2536 	      b = srf_vdom.NormalizedParameterAt( v );
2537 	    }
2538 
2539       // When textures are packed, tex_udom and tex_vdom
2540       // are subintervals of (0,1).
2541 	    u = tex_udom.ParameterAt(a);
2542 	    v = tex_vdom.ParameterAt(b);
2543 
2544 	    tc[0] = (float)u;
2545 	    tc[1] = (float)v;
2546       tc += tc_stride;
2547     }
2548   }
2549   else if ( bHaveUVWXform )
2550   {
2551     const ON_Xform xform(mapping.m_uvw);
2552     ON_3dPoint P;
2553     for ( i = 0; i < vcnt; i++)
2554     {
2555 	    // normalize surface parameter
2556       P.x = srf_udom.NormalizedParameterAt( S[i].x );
2557       P.y = srf_vdom.NormalizedParameterAt( S[i].y );
2558       P.z = 0.0;
2559 
2560       // apply m_uvw transformation
2561       P = xform*P;
2562 
2563       tc[0] = (float)P.x;
2564       tc[1] = (float)P.y;
2565       tc += tc_stride;
2566     }
2567   }
2568   else
2569   {
2570     // m_srf_tex_rotate is ignored on purpose.
2571     // It only applies if the texture is packed.
2572     for ( i = 0; i < vcnt; i++)
2573     {
2574 	    // tc = normalized surface parameter
2575       a = srf_udom.NormalizedParameterAt( S[i].x );
2576       b = srf_vdom.NormalizedParameterAt( S[i].y );
2577 
2578 	    tc[0] = (float)a;
2579 	    tc[1] = (float)b;
2580       tc += tc_stride;
2581     }
2582   }
2583 
2584   return true;
2585 }
2586 
2587 
GetTextureCoordinates(const ON_Mesh & mesh,ON_SimpleArray<ON_3fPoint> & T,const ON_Xform * mesh_xform,bool bLazy,ON_SimpleArray<int> * Tside) const2588 bool ON_TextureMapping::GetTextureCoordinates(
2589           const ON_Mesh& mesh,
2590           ON_SimpleArray<ON_3fPoint>& T,
2591           const ON_Xform* mesh_xform,
2592           bool bLazy,
2593           ON_SimpleArray<int>* Tside
2594           ) const
2595 {
2596   if ( Tside )
2597     Tside->SetCount(0);
2598 
2599   int i;
2600   const int vcnt = mesh.m_V.Count();
2601   if ( vcnt <= 0 )
2602     return false;
2603 
2604   if ( bLazy )
2605   {
2606     int tci, tccount = mesh.m_TC.Count();
2607     for ( tci = 0; tci < tccount; tci++ )
2608     {
2609       if ( vcnt == mesh.m_TC[tci].m_T.Count() )
2610       {
2611         if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
2612         {
2613           T = mesh.m_TC[tci].m_T;
2614           return true;
2615         }
2616       }
2617     }
2618 
2619     if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
2620     {
2621       T.Reserve(vcnt);
2622       T.SetCount(vcnt);
2623       const ON_2fPoint* f = mesh.m_T.Array();
2624       ON_3fPoint* d = T.Array();
2625       for ( i = vcnt; i--; f++, d++ )
2626       {
2627         d->x = f->x;
2628         d->y = f->y;
2629         d->z = 0.0f;
2630       }
2631       return true;
2632     }
2633   }
2634 
2635 	bool rc = false;
2636 
2637   if ( ON_TextureMapping::srfp_mapping == m_type )
2638   {
2639     // uv textures from surface parameterization
2640     T.Reserve(vcnt);
2641     T.SetCount(vcnt);
2642     T.Zero();
2643     rc = GetSPTCHelper(mesh,*this,&T[0].x,3);
2644 	}
2645   else
2646   {
2647     ON_3dPoint  P, tc;
2648 		ON_3dVector N(0.0,0.0,0.0);
2649 
2650 		const ON_3fPoint*  mesh_V = mesh.m_V.Array();
2651 		const ON_3fVector* mesh_N = mesh.HasVertexNormals()
2652                               ? mesh.m_N.Array()
2653                               : 0;
2654 
2655     T.Reserve(vcnt);
2656     T.SetCount(vcnt);
2657 
2658     int* Tsd = 0;
2659     if ( Tside )
2660     {
2661       Tside->Reserve(vcnt);
2662       Tside->SetCount(vcnt);
2663       Tsd = Tside->Array();
2664       memset(Tsd,0,vcnt*sizeof(Tsd[0]));
2665     }
2666 
2667     ON_Xform P_xform(1.0), N_xform(1.0);
2668     const double* PT = 0;
2669     const double* NT = 0;
2670     if ( mesh_xform )
2671     {
2672       if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
2673       {
2674         // ignore transformation
2675         mesh_xform = 0;
2676       }
2677       else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
2678       {
2679         PT = &P_xform[0][0];
2680         NT = &N_xform[0][0];
2681       }
2682       else
2683       {
2684         mesh_xform = 0;
2685       }
2686     }
2687 
2688     const float* f;
2689     double w;
2690     int sd;
2691 
2692 		if (clspt_projection == m_projection && ON_TextureMapping::mesh_mapping_primitive == m_type && NULL != m_mapping_primitive)
2693 		{
2694       rc = false;
2695 		}
2696 		else if ( mesh_N &&
2697           (   ray_projection == m_projection
2698            || ON_TextureMapping::box_mapping == m_type
2699            || ON_TextureMapping::cylinder_mapping == m_type
2700            || ON_TextureMapping::mesh_mapping_primitive == m_type
2701 		   )
2702         )
2703   	{
2704 			// calculation uses mesh vertex normal
2705       if ( PT && NT )
2706       {
2707         // need to transform vertex and normal
2708         // before calculating texture coordinates
2709 			  for (i = 0; i < vcnt; i++)
2710 			  {
2711           f = &mesh_V[i].x;
2712 				  w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
2713           w = (0.0 != w) ? 1.0/w : 1.0;
2714 				  P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
2715 				  P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
2716 				  P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
2717 
2718           f = &mesh_N[i].x;
2719           N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
2720 				  N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
2721 				  N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
2722           N.Unitize();
2723 				  sd = Evaluate(P,N,&tc);
2724 				  T[i] = tc;
2725           if ( Tsd ) Tsd[i] = sd;
2726 			  }
2727       }
2728 			else
2729       {
2730         // mesh vertex and normal are ok
2731 			  for (i = 0; i < vcnt; i++)
2732 			  {
2733 				  P = mesh_V[i];
2734 				  N = mesh_N[i];
2735 				  sd = Evaluate(P,N,&tc);
2736 				  T[i] = tc;
2737           if ( Tsd ) Tsd[i] = sd;
2738 			  }
2739       }
2740 		}
2741 		else if ( PT )
2742     {
2743       // normal is not used
2744       // mesh vertex needs to be transformed
2745       for ( i = 0; i < vcnt; i++ )
2746       {
2747         f = &mesh_V[i].x;
2748 			  w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
2749         w = (0.0 != w) ? 1.0/w : 1.0;
2750 			  P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
2751 			  P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
2752 			  P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
2753         sd = Evaluate(P,N,&tc);
2754 			  T[i] = tc;
2755         if ( Tsd )
2756           Tsd[i] = sd;
2757 		  }
2758     }
2759     else
2760 		{
2761 			// normal is not used and mesh vertex is ok
2762 			for ( i = 0; i < vcnt; i++ )
2763 			{
2764 				P = mesh_V[i];
2765 				sd = Evaluate(P,N,&tc);
2766 				T[i] = tc;
2767 				if ( Tsd )
2768 					Tsd[i] = sd;
2769 			}
2770     }
2771     rc = true;
2772 	}
2773 
2774 	return rc;
2775 }
2776 
2777 static
ThreeToTwoHelper(const ON_SimpleArray<ON_3fPoint> & T3,ON_SimpleArray<ON_2fPoint> & T2)2778 void ThreeToTwoHelper(
2779       const ON_SimpleArray<ON_3fPoint>& T3,
2780       ON_SimpleArray<ON_2fPoint>& T2
2781       )
2782 {
2783   int i = T3.Count();
2784   const ON_3fPoint* t3 = T3.Array();
2785 
2786   T2.Reserve(i);
2787   T2.SetCount(i);
2788   ON_2fPoint* t2 = T2.Array();
2789   while(i--)
2790   {
2791     t2->x = t3->x;
2792     t2->y = t3->y;
2793     t2++;
2794     t3++;
2795   }
2796 }
2797 
GetTextureCoordinates(const ON_Mesh & mesh,ON_SimpleArray<ON_2fPoint> & T,const ON_Xform * mesh_xform,bool bLazy,ON_SimpleArray<int> * Tside) const2798 bool ON_TextureMapping::GetTextureCoordinates(
2799             const ON_Mesh& mesh,
2800             ON_SimpleArray<ON_2fPoint>& T,
2801             const ON_Xform* mesh_xform,
2802             bool bLazy,
2803             ON_SimpleArray<int>* Tside
2804             ) const
2805 {
2806   bool rc = false;
2807   if ( Tside )
2808     Tside->SetCount(0);
2809   if ( bLazy )
2810   {
2811     if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
2812     {
2813       if ( T.Array() != mesh.m_T.Array() )
2814       {
2815         // different arrays - copy
2816         T = mesh.m_T;
2817       }
2818       return true;
2819     }
2820     else
2821     {
2822       int vcnt = mesh.m_V.Count();
2823       int tci, tccount = mesh.m_TC.Count();
2824       for ( tci = 0; tci < tccount; tci++ )
2825       {
2826         if ( vcnt == mesh.m_TC[tci].m_T.Count() )
2827         {
2828           if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
2829           {
2830             // copy T3d[] results to T[]
2831             ThreeToTwoHelper(mesh.m_TC[tci].m_T,T);
2832             return true;
2833           }
2834         }
2835       }
2836     }
2837   }
2838 
2839   if ( ON_TextureMapping::srfp_mapping == m_type )
2840   {
2841     // uv textures from surface parameterization
2842     T.Reserve(mesh.m_V.Count());
2843     T.SetCount(mesh.m_V.Count());
2844     T.Zero();
2845     rc = GetSPTCHelper(mesh,*this,&T[0].x,2);
2846   }
2847   else
2848   {
2849     T.SetCount(0);
2850 	  ON_SimpleArray<ON_3fPoint> T3;
2851     if ( GetTextureCoordinates(mesh, T3, mesh_xform, false, Tside ) )
2852     {
2853       // copy T3d[] results to T[]
2854       ThreeToTwoHelper(T3,T);
2855       rc = true;
2856 	  }
2857   }
2858 	return rc;
2859 }
2860 
2861 
2862 //bool ON_Mesh::GetSurfaceParameterTextureXform(
2863 //          class ON_Xform& StoT
2864 //          ) const
2865 //
2866 //{
2867 //  bool rc = false;
2868 //  StoT.Identity();
2869 //
2870 //  // Gets default mesh mapping
2871 //  const ON_Interval surface_u_domain(m_srf_domain[0]);
2872 //  const ON_Interval surface_v_domain(m_srf_domain[1]);
2873 //  const ON_Interval texture_u_domain(m_tex_domain[0]);
2874 //  const ON_Interval texture_v_domain(m_tex_domain[1]);
2875 //  bool bRotateTexture = m_srf_tex_rotate;
2876 //  if (   surface_u_domain.IsInterval()
2877 //      && surface_v_domain.IsInterval()
2878 //      && texture_u_domain.IsInterval()
2879 //      && texture_v_domain.IsInterval()
2880 //      )
2881 //  {
2882 //    double du = 1.0/surface_u_domain.Length();
2883 //    double dv = 1.0/surface_v_domain.Length();
2884 //    ON_Xform x1(1.0), x2(1.0), x3(1.0);
2885 //    x1.m_xform[0][0] = du; x1.m_xform[0][3] = -surface_u_domain[0]*du;
2886 //    x1.m_xform[1][1] = dv; x1.m_xform[1][3] = -surface_v_domain[0]*dv;
2887 //    if ( bRotateTexture )
2888 //    {
2889 //      x2.m_xform[0][0] =  0.0; x2.m_xform[0][1] = -1.0; x2.m_xform[0][3] = 1.0;
2890 //      x2.m_xform[1][0] =  1.0; x2.m_xform[1][1] =  0.0;
2891 //    }
2892 //    x3.m_xform[0][0] = texture_u_domain.Length(); x3.m_xform[0][3] = texture_u_domain[0];
2893 //    x3.m_xform[1][1] = texture_v_domain.Length(); x3.m_xform[1][3] = texture_v_domain[0];
2894 //
2895 //    // transforms surface(u,v) to texture(u,v)
2896 //    StoT = x3*x2*x1;
2897 //
2898 //    rc = true;
2899 //  }
2900 //  return rc;
2901 //}
2902 
2903 class ON__CMeshFaceTC
2904 {
2905   // DO NOT PUT THIS CLASS IN A HEADER FILE
2906   // IT IS A PRIVATE HELPER CLASS.
2907 public:
2908   int   fi;
2909   int   quad[4];
2910   float Tx[4];
2911   bool  bSetT[4];
2912 };
2913 
2914 class ON__CChangeTextureCoordinateHelper
2915 {
2916   // DO NOT PUT THIS CLASS IN A HEADER FILE
2917   // IT IS A PRIVATE HELPER CLASS.
2918 public:
2919   ON__CChangeTextureCoordinateHelper( ON_Mesh& mesh, int newvcnt, float*& mesh_T );
2920   ~ON__CChangeTextureCoordinateHelper();
2921 
2922   int DupVertex(int vi);
2923   void ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y, float* mesh_T, int mesh_T_stride );
2924 
2925   int m_tci;
2926 
2927   ON_Mesh& m_mesh;
2928   ON_3dPointArray* m_mesh_dV;
2929   bool m_bHasVertexNormals;
2930   bool m_bHasVertexTextures;
2931   bool m_bHasVertexColors;
2932   bool m_bHasSurfaceParameters;
2933   bool m_bHasPrincipalCurvatures;
2934   bool m_bHasHiddenVertices;
2935 
2936   bool m_bHasCachedTextures;
2937   ON_SimpleArray< ON_TextureCoordinates* > m_TC;
2938 
2939   // m_vuse[] is an array of length = original number of
2940   // vertices in m_mesh and m_vuse[vi] = number of faces
2941   // that reference vertex vi. If this vertex needs to be
2942   // split, vuse[vi] is decremented.  The ultimate goal
2943   // is to split a few times as needed so we don't
2944   // bloat the mesh with repeated calls to changing
2945   // texture maps. m_vuse[] is set the first time
2946   // DupVertex() is called.
2947   int m_vuse_count;
2948   ON_SimpleArray< unsigned int > m_vuse;
2949 private:
2950   // no implementation
2951   ON__CChangeTextureCoordinateHelper(const ON__CChangeTextureCoordinateHelper&);
2952   ON__CChangeTextureCoordinateHelper& operator=(const ON__CChangeTextureCoordinateHelper&);
2953 };
2954 
ChangeTextureCoordinate(int * Fvi,int fvi,float x,float y,float * mesh_T,int mesh_T_stride)2955 void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y,
2956                                                              float* mesh_T, int mesh_T_stride )
2957 {
2958   int oldvi = Fvi[fvi];
2959   float* T = mesh_T+(oldvi*mesh_T_stride);
2960   if ( x != T[0] || (y != ON_UNSET_FLOAT && y != T[1]) )
2961   {
2962     int newvi = DupVertex(oldvi);
2963     T = mesh_T + (newvi*mesh_T_stride);
2964     T[0] = x;
2965     if ( y != ON_UNSET_FLOAT )
2966      T[1] = y;
2967 
2968     if ( 2 == fvi && oldvi == Fvi[3] )
2969     {
2970       Fvi[2] = newvi;
2971       Fvi[3] = newvi;
2972     }
2973     else
2974     {
2975       Fvi[fvi] = newvi;
2976     }
2977   }
2978 }
2979 
2980 
ON__CChangeTextureCoordinateHelper(ON_Mesh & mesh,int newvcnt,float * & mesh_T)2981 ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper(
2982     ON_Mesh& mesh,
2983     int newvcnt,
2984     float*& mesh_T )
2985 : m_mesh(mesh)
2986 , m_mesh_dV(0)
2987 , m_vuse_count(0)
2988 {
2989   // adding vertices invalidates this cached information.
2990   m_mesh.DestroyTopology();
2991   m_mesh.DestroyPartition();
2992   m_mesh.DestroyTree();
2993 
2994   m_tci = -1;
2995 
2996   const int vcnt = m_mesh.m_V.Count();
2997 
2998   // It is critical to reserve enough room in the arrays
2999   // before duplication starts.  Otherwise, during duplication,
3000   // a dyanamic array can be reallocated, which will make
3001   // saved array base pointers will be invalid, and you crash
3002   // the next time they are used.
3003 
3004   m_mesh.m_V.Reserve(vcnt+newvcnt);
3005 
3006   if (    m_mesh.HasDoublePrecisionVertices()
3007        && m_mesh.DoublePrecisionVerticesAreValid()
3008      )
3009   {
3010     m_mesh_dV = &m_mesh.DoublePrecisionVertices();
3011     m_mesh_dV->Reserve(vcnt+newvcnt);
3012   }
3013   else
3014   {
3015     m_mesh.DestroyDoublePrecisionVertices();
3016   }
3017 
3018   m_bHasVertexNormals = m_mesh.HasVertexNormals();
3019   if ( m_bHasVertexNormals )
3020     m_mesh.m_N.Reserve(vcnt+newvcnt);
3021 
3022   m_bHasVertexTextures = m_mesh.HasTextureCoordinates();
3023   if ( m_bHasVertexTextures )
3024   {
3025     float* p = (float*)m_mesh.m_T.Array();
3026     m_mesh.m_T.Reserve(vcnt+newvcnt);
3027     if ( p == mesh_T )
3028       mesh_T = (float*)m_mesh.m_T.Array();
3029   }
3030 
3031   m_bHasVertexColors = m_mesh.HasVertexColors();
3032   if ( m_bHasVertexColors )
3033     m_mesh.m_C.Reserve(vcnt+newvcnt);
3034 
3035   m_bHasSurfaceParameters = m_mesh.HasSurfaceParameters();
3036   if ( m_bHasSurfaceParameters )
3037     m_mesh.m_S.Reserve(vcnt+newvcnt);
3038 
3039   m_bHasPrincipalCurvatures = m_mesh.HasPrincipalCurvatures();
3040   if ( m_bHasPrincipalCurvatures )
3041     m_mesh.m_K.Reserve(vcnt+newvcnt);
3042 
3043   m_bHasHiddenVertices = (0 != m_mesh.HiddenVertexArray());
3044   if ( m_bHasHiddenVertices )
3045     m_mesh.m_H.Reserve(vcnt+newvcnt);
3046 
3047   // Set m_TC[] to be the subset of m_mesh.m_TC[] that is
3048   // valid for duplication.
3049   m_bHasCachedTextures = false;
3050   int tci, tccount = m_mesh.m_TC.Count();
3051   m_TC.Reserve(tccount);
3052   for ( tci = 0; tci < tccount; tci++ )
3053   {
3054     ON_TextureCoordinates& tc = m_mesh.m_TC[tci];
3055     if ( vcnt == tc.m_T.Count() )
3056     {
3057       m_bHasCachedTextures = true;
3058       float* p = (float*)tc.m_T.Array();
3059       tc.m_T.Reserve(vcnt+newvcnt);
3060       if ( p == mesh_T )
3061         mesh_T = (float*)tc.m_T.Array();
3062       m_TC.Append( &tc );
3063     }
3064   }
3065 }
3066 
3067 
~ON__CChangeTextureCoordinateHelper()3068 ON__CChangeTextureCoordinateHelper::~ON__CChangeTextureCoordinateHelper()
3069 {
3070   if ( 0 != m_mesh_dV )
3071   {
3072     m_mesh.SetDoublePrecisionVerticesAsValid();
3073     m_mesh.SetSinglePrecisionVerticesAsValid();
3074     m_mesh_dV = 0;
3075   }
3076 }
3077 
DupVertex(int vi)3078 int ON__CChangeTextureCoordinateHelper::DupVertex(int vi)
3079 {
3080   if ( 0 == m_vuse_count )
3081   {
3082     // m_vuse[] is an array of length = original number of
3083     // vertices in m_mesh and m_vuse[vi] = number of faces
3084     // that reference vertex vi. If this vertex needs to be
3085     // split, vuse[vi] is decremented.  The ultimate goal
3086     // is to split a few times as needed so we don't
3087     // bloat the mesh with repeated calls to changing
3088     // texture maps. m_vuse[] is set the first time
3089     // DupVertex() is called.
3090     m_vuse_count = m_mesh.m_V.Count();
3091     m_vuse.Reserve(m_vuse_count);
3092     m_vuse.SetCount(m_vuse_count);
3093     m_vuse.Zero();
3094     for ( int fi = 0; fi < m_mesh.m_F.Count(); fi++ )
3095     {
3096       const int* Fvi = m_mesh.m_F[fi].vi;
3097       int i = Fvi[0];
3098       if ( i >= 0 && i < m_vuse_count )
3099         m_vuse[i]++;
3100       i = Fvi[1];
3101       if ( i >= 0 && i < m_vuse_count )
3102         m_vuse[i]++;
3103       i = Fvi[2];
3104       if ( i >= 0 && i < m_vuse_count )
3105         m_vuse[i]++;
3106       i = Fvi[3];
3107       if ( Fvi[2] != i && i >= 0 && i < m_vuse_count )
3108         m_vuse[i]++;
3109     }
3110   }
3111 
3112   if ( vi >= 0 && vi < m_vuse_count )
3113   {
3114     if ( m_vuse[vi] <= 1 )
3115       return vi; // only one face uses this vertex - no need to dup the vertex
3116 
3117     // otherwise we will duplicate this vertex, reducing its use count by 1.
3118     m_vuse[vi]--;
3119   }
3120 
3121 
3122   m_mesh.m_V.AppendNew();
3123   *m_mesh.m_V.Last() = m_mesh.m_V[vi];
3124   if ( 0 != m_mesh_dV )
3125   {
3126     m_mesh_dV->AppendNew();
3127     *(m_mesh_dV->Last()) = m_mesh_dV->operator[](vi);
3128   }
3129   if ( m_bHasVertexTextures )
3130   {
3131     m_mesh.m_T.AppendNew();
3132     *m_mesh.m_T.Last() = m_mesh.m_T[vi];
3133   }
3134   if ( m_bHasVertexNormals )
3135   {
3136     m_mesh.m_N.AppendNew();
3137     *m_mesh.m_N.Last() = m_mesh.m_N[vi];
3138   }
3139   if ( m_bHasVertexColors )
3140   {
3141     m_mesh.m_C.AppendNew();
3142     *m_mesh.m_C.Last() = m_mesh.m_C[vi];
3143   }
3144   if ( m_bHasSurfaceParameters )
3145   {
3146     m_mesh.m_S.AppendNew();
3147     *m_mesh.m_S.Last() = m_mesh.m_S[vi];
3148   }
3149   if ( m_bHasPrincipalCurvatures )
3150   {
3151     m_mesh.m_K.AppendNew();
3152     *m_mesh.m_K.Last() = m_mesh.m_K[vi];
3153   }
3154   if ( m_bHasHiddenVertices )
3155   {
3156     m_mesh.m_H.AppendNew();
3157     if ( 0 != (*m_mesh.m_H.Last() = m_mesh.m_H[vi]) )
3158       m_mesh.m_hidden_count++;
3159   }
3160 
3161   if ( m_bHasCachedTextures )
3162   {
3163     // Note:  This m_TC[] is the subset of m_mesh.m_TC[]
3164     //        that need to be duped.  The constructor
3165     //        insures that m_TC[i] is not NULL and
3166     //        has the right count and capacity.
3167     //
3168     //        DO NOT REFERENCE m_mesh.m_TC[] in this block.
3169     int tccount = m_TC.Count();
3170     for ( int i = 0; i < tccount; i++ )
3171     {
3172       ON_SimpleArray<ON_3fPoint>& T = m_TC[i]->m_T;
3173       T.AppendNew();
3174       *T.Last() = T[vi];
3175     }
3176   }
3177 
3178   return m_mesh.m_V.Count()-1;
3179 }
3180 
3181 
3182 static
PoleFix(float t0,float t1)3183 float PoleFix( float t0,  float t1 )
3184 {
3185   float t = ( ON_UNSET_FLOAT == t0 )
3186           ? t1
3187           : ((ON_UNSET_FLOAT == t1 ) ? t0 : (0.5f*(t0+t1)));
3188   return t;
3189 }
3190 
3191 static
IntersectBoxSideRayHelper(int side,const ON_3dPoint & rst,const ON_3dVector & n,double * s)3192 int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector& n, double* s)
3193 {
3194   /*
3195   returns:
3196     0 = ray parallel to sides
3197     1 = ray hit left side (x=-1)
3198     2 = ray hit right side (x=+1)
3199     3 = ray hit back side (y=-1)
3200     4 = ray hit front side (y=+1)
3201     5 = ray hit bottom side (z=-1)
3202     6 = ray hit top side (z=+1)
3203   */
3204   double nx;
3205   ON_3dPoint Q;
3206   double t,t0,t1;
3207   int dir;
3208 
3209 
3210   switch(side)
3211   {
3212   case 1: // =  left side (x=-1)
3213     t1 = -1.0;
3214     dir = 0;
3215     break;
3216   case 2: //   right side (x=+1)
3217     t1 = 1.0;
3218     dir = 0;
3219     break;
3220   case 3: //   back side (y=-1)
3221     t1 = -1.0;
3222     dir = 1;
3223     break;
3224   case 4: //   front side (y=+1)
3225     t1 = 1.0;
3226     dir = 1;
3227     break;
3228   case 5: //   bottom side (z=-1)
3229     t1 = -1.0;
3230     dir = 2;
3231     break;
3232   case 6: //   top side (z=+1)
3233     t1 = 1.0;
3234     dir = 2;
3235     break;
3236   default:
3237     *s = ON_UNSET_VALUE;
3238     return 0;
3239     break;
3240   }
3241 
3242   // protect against overflow
3243   nx = (&n.x)[dir];
3244   t0 = (t1 - (&rst.x)[dir]);
3245   if ( fabs(t0) >= fabs(nx)*on__overflow_tol )
3246   {
3247     *s = ON_UNSET_VALUE;
3248     return 0;
3249   }
3250 
3251   t0 /= nx;
3252   Q = rst + t0*n;
3253   if ( dir )
3254   {
3255     t = Q.x;
3256     Q.x = Q[dir];
3257     Q[dir] = t;
3258   }
3259   if ( fabs(Q.x-t1) > ON_SQRT_EPSILON || fabs(Q.y) > 1.0e8 || fabs(Q.z) > 1.0e8 )
3260   {
3261     *s = ON_UNSET_VALUE;
3262     return 0;
3263   }
3264 
3265 
3266   *s = t0;
3267   return side;
3268 }
3269 
3270 static
EvBoxSideTextureCoordinateHelper2(int side,const ON_TextureMapping & box_mapping,const ON_3dPoint & P,const ON_3dVector & N,ON_3dPoint * T)3271 bool EvBoxSideTextureCoordinateHelper2(
3272                        int side,
3273                        const ON_TextureMapping& box_mapping,
3274 										   const ON_3dPoint& P,
3275 										   const ON_3dVector& N,
3276 										   ON_3dPoint* T
3277 										   )
3278 {
3279   // side flag
3280   //  1 =  left side (x=-1)
3281   //  2 =  right side (x=+1)
3282   //  3 =  back side (y=-1)
3283   //  4 =  front side (y=+1)
3284   //  5 =  bottom side (z=-1)
3285   //  6 =  top side (z=+1)
3286   // The matrix m_Pxyz transforms the world coordinate
3287   // "mapping cylinder" into the cylinder centered at
3288   // rst = (0,0,0) with radius 1.0.  The axis runs
3289   // from rst = (0,0,-1) to rst = (0,0,+1).
3290 
3291   ON_3dPoint rst(box_mapping.m_Pxyz*P);
3292 
3293 	ON_3dVector n(box_mapping.m_Nxyz*N);
3294   n.Unitize();
3295 
3296   // side flag
3297   //  1 =  left side (x=-1)
3298   //  2 =  right side (x=+1)
3299   //  3 =  back side (y=-1)
3300   //  4 =  front side (y=+1)
3301   //  5 =  bottom side (z=-1)
3302   //  6 =  top side (z=+1)
3303 
3304   if ( ON_TextureMapping::ray_projection == box_mapping.m_projection )
3305 	{
3306     double s;
3307     if ( side == IntersectBoxSideRayHelper(side, rst, n, &s) )
3308     {
3309 		  // ray hit the box side
3310 		  rst = rst + s*n;
3311     }
3312 	}
3313 
3314 	double shift = 0.0;
3315 
3316   // side flag
3317   //  1 =  left side (x=-1)
3318   //  2 =  right side (x=+1)
3319   //  3 =  back side (y=-1)
3320   //  4 =  front side (y=+1)
3321   //  5 =  bottom side (z=-1)
3322   //  6 =  top side (z=+1)
3323 
3324 	switch(side)
3325 	{
3326 	case 1: // x = -1
3327 		rst.x = -rst.y;
3328 		rst.y =  rst.z;
3329 		shift =  3.0;
3330 		break;
3331 	case 2: // x = +1
3332 		rst.x =  rst.y;
3333 		rst.y =  rst.z;
3334 		shift =  1.0;
3335 		break;
3336 	case 3: // y = -1
3337 		rst.y =  rst.z;
3338 		shift =  0.0;
3339 		break;
3340 	case 4: // y = +1
3341 		rst.x = -rst.x;
3342 		rst.y =  rst.z;
3343 		shift =  2.0;
3344 		break;
3345 	case 5: // z = -1
3346 		rst.x = -rst.x;
3347 		shift =  4.0;
3348 		break;
3349 	case 6: // z = +1
3350 		shift =  5.0;
3351 		break;
3352   default:
3353     return 0;
3354     break;
3355 	}
3356 
3357   // normalize texture coordinates
3358   rst.x = 0.5*rst.x + 0.5;
3359   rst.y = 0.5*rst.y + 0.5;
3360 	rst.z = 0.0;
3361 
3362 	if( ON_TextureMapping::divided == box_mapping.m_texture_space)
3363 	{
3364     rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0);
3365 	}
3366 
3367 	*T = box_mapping.m_uvw*rst;
3368 
3369   return true;
3370 }
3371 
3372 static
EvBoxSideTextureCoordinateHelper1(const ON_Mesh & mesh,const ON_Xform * mesh_xform,int vi,int side,const ON_TextureMapping & box_mapping,float * Tx,float * Ty)3373 bool EvBoxSideTextureCoordinateHelper1(
3374           const ON_Mesh& mesh,
3375           const ON_Xform* mesh_xform,
3376           int vi,
3377           int side,
3378           const ON_TextureMapping& box_mapping,
3379           float* Tx,
3380           float* Ty
3381           )
3382 {
3383 	bool rc = false;
3384   ON_3dPoint  P, tc;
3385 	ON_3dVector N(0.0,0.0,0.0);
3386 
3387 	const ON_3fPoint*  mesh_V = mesh.m_V.Array();
3388 	const ON_3fVector* mesh_N = mesh.HasVertexNormals()
3389                             ? mesh.m_N.Array()
3390                             : 0;
3391 
3392   ON_Xform P_xform(1.0), N_xform(1.0);
3393   const double* PT = 0;
3394   const double* NT = 0;
3395   if ( mesh_xform )
3396   {
3397     if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
3398     {
3399       // ignore transformation
3400       mesh_xform = 0;
3401     }
3402     else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
3403     {
3404       PT = &P_xform[0][0];
3405       NT = &N_xform[0][0];
3406     }
3407     else
3408     {
3409       mesh_xform = 0;
3410     }
3411   }
3412 
3413   const float* f;
3414   double w;
3415 
3416   if ( mesh_N && ON_TextureMapping::ray_projection == box_mapping.m_projection )
3417 	{
3418 		// calculation uses mesh vertex normal
3419     if ( PT && NT )
3420     {
3421       // need to transform vertex and normal
3422       // before calculating texture coordinates
3423       f = &mesh_V[vi].x;
3424 		  w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
3425       w = (0.0 != w) ? 1.0/w : 1.0;
3426 		  P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
3427 		  P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
3428 		  P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
3429 
3430       f = &mesh_N[vi].x;
3431       N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
3432 		  N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
3433 		  N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
3434       N.Unitize();
3435     }
3436     else
3437     {
3438       // mesh vertex and normal are ok
3439 		  P = mesh_V[vi];
3440 		  N = mesh_N[vi];
3441     }
3442 	}
3443 	else if ( PT )
3444   {
3445     // normal is not used
3446     // mesh vertex needs to be transformed
3447     f = &mesh_V[vi].x;
3448 	  w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
3449     w = (0.0 != w) ? 1.0/w : 1.0;
3450 	  P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
3451 	  P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
3452 	  P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
3453   }
3454   else
3455   {
3456     // normal is not used and mesh vertex is ok
3457     P = mesh_V[vi];
3458   }
3459 
3460 
3461   rc = EvBoxSideTextureCoordinateHelper2(side,box_mapping,P,N,&tc);
3462   if (rc)
3463   {
3464     rc = tc.IsValid();
3465     if (rc)
3466     {
3467       *Tx = (float)tc.x;
3468       *Ty = (float)tc.y;
3469     }
3470   }
3471 	return rc;
3472 }
3473 
3474 
3475 class ON__CNewMeshFace
3476 {
3477 public:
3478   int fi;
3479   int newvcnt;
3480   bool bNewV[4];
3481   ON_2fPoint tc[4];
3482 };
3483 
3484 static
TcDistanceHelper(const ON_2fPoint & tc)3485 float TcDistanceHelper(const ON_2fPoint& tc)
3486 {
3487   float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x;
3488   if ( dx < 0.0f)
3489     return 0.0f;
3490   float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y;
3491   if ( dy < 0.0f)
3492     return 0.0f;
3493   return (dx < dy) ? dx : dy;
3494 }
3495 
3496 static
AdjustSingleBoxTextureCoordinatesHelper(ON_Mesh & mesh,const ON_Xform * mesh_xform,float * mesh_T,int mesh_T_stride,const int * Tsd,const ON_TextureMapping & box_mapping)3497 void AdjustSingleBoxTextureCoordinatesHelper(
3498           ON_Mesh& mesh,
3499           const ON_Xform* mesh_xform,
3500           float* mesh_T,
3501           int    mesh_T_stride,
3502           const int* Tsd,
3503           const ON_TextureMapping& box_mapping
3504           )
3505 {
3506   const int vcnt = mesh.m_V.Count();
3507   const int fcnt = mesh.m_F.Count();
3508   if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() || !Tsd )
3509     return;
3510   const ON_MeshFace* mesh_F = mesh.m_F.Array();
3511   const int* Fvi;
3512   int j, k, fi, sd[4], fvicnt, side, newvcnt=0;
3513   ON__CNewMeshFace mf;
3514   ON_2fPoint tc;
3515   ON_SimpleArray<ON__CNewMeshFace> mflist(512);
3516   float d;
3517   for ( fi = 0; fi < fcnt; fi++ )
3518   {
3519     Fvi = mesh_F[fi].vi;
3520     sd[0] = Tsd[Fvi[0]];
3521     sd[1] = Tsd[Fvi[1]];
3522     sd[2] = Tsd[Fvi[2]];
3523     sd[3] = Tsd[Fvi[3]];
3524     if ( sd[0] == sd[1] && sd[0] == sd[2] && sd[0] == sd[3] )
3525     {
3526       // all texture coords are on same side of box
3527       continue;
3528     }
3529     fvicnt = (Fvi[2] != Fvi[3]) ? 4 : 3;
3530 
3531     memset(&mf,0,sizeof(mf));
3532     mf.tc[0] = mesh_T + (Fvi[0]*mesh_T_stride);
3533     mf.tc[1] = mesh_T + (Fvi[1]*mesh_T_stride);
3534     mf.tc[2] = mesh_T + (Fvi[2]*mesh_T_stride);
3535     mf.tc[3] = mesh_T + (Fvi[3]*mesh_T_stride);
3536 
3537     // find the side we will use for this face
3538     side = sd[0];
3539     d = TcDistanceHelper(mf.tc[0]);
3540     for ( j = 1; j < fvicnt; j++ )
3541     {
3542       float d1 = TcDistanceHelper(mf.tc[j]);
3543       if (d1 > d)
3544       {
3545         side = sd[j];
3546         d = d1;
3547       }
3548     }
3549 
3550     // Jussi, 5th September 2011:
3551     // This 'continue' only works for faces having one or more of its tc's in (0,1)x(0,1).
3552     // I have commented it out as a fix to RR 90329.
3553     //if ( d <= 0.0f )
3554     //  continue;
3555 
3556     for ( j = 0; j < fvicnt; j++ )
3557     {
3558       if ( sd[j] != side )
3559       {
3560         // calculate new tc for this side
3561         if ( EvBoxSideTextureCoordinateHelper1(
3562           mesh,
3563           mesh_xform,
3564           Fvi[j],
3565           side,
3566           box_mapping,
3567           &tc.x,&tc.y) )
3568         {
3569           if ( tc.x != mf.tc[j].x || tc.y != mf.tc[j].y )
3570           {
3571             mf.tc[j] = tc;
3572             mf.bNewV[j] = true;
3573             mf.newvcnt++;
3574           }
3575         }
3576         else
3577           break;
3578       }
3579     }
3580     if ( j >= fvicnt && mf.newvcnt > 0 )
3581     {
3582       mf.fi = fi;
3583       newvcnt += mf.newvcnt;
3584       mflist.Append(mf);
3585     }
3586   }
3587 
3588   if ( newvcnt <= 0 )
3589     return;
3590 
3591   ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T);
3592 
3593   const int mflist_count = mflist.Count();
3594 
3595   for ( k = 0; k < mflist_count; k++ )
3596   {
3597     mf = mflist[k];
3598     int* fvi = mesh.m_F[mf.fi].vi;
3599     fvicnt = (fvi[2]!=fvi[3]) ? 4 : 3;
3600     for ( j = 0; j < fvicnt; j++ )
3601     {
3602       if ( mf.bNewV[j] )
3603       {
3604         helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride);
3605       }
3606     }
3607   }
3608 }
3609 
3610 static
AdjustMeshPeriodicTextureCoordinatesHelper(ON_Mesh & mesh,const ON_Xform * mesh_xform,float * mesh_T,int mesh_T_stride,const int * Tsd,double two_pi_tc,const ON_TextureMapping & mapping)3611 void AdjustMeshPeriodicTextureCoordinatesHelper(
3612           ON_Mesh& mesh,
3613           const ON_Xform* mesh_xform,
3614           float* mesh_T,
3615           int    mesh_T_stride,
3616           const int* Tsd,
3617           double two_pi_tc,
3618           const ON_TextureMapping& mapping
3619           )
3620 {
3621   // This helper adjusts texture coordinates on faces that
3622   // span the seam on mapping spheres and cylinders and
3623   // resolves the mulitiple valued problem that
3624   // exists at the poles of sphere mappings.
3625 
3626   const int vcnt = mesh.m_V.Count();
3627   const int fcnt = mesh.m_F.Count();
3628   if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() )
3629     return;
3630 
3631   // see if any texture coordinate adjustment is necessary
3632   const ON_TextureMapping::TYPE mapping_type = mapping.m_type;
3633   const bool bSphereCheck = ( ON_TextureMapping::sphere_mapping == mapping_type );
3634   const bool bCylinderCheck = (Tsd && ON_TextureMapping::cylinder_mapping == mapping_type);
3635   const bool bBoxCheck = (Tsd && ON_TextureMapping::box_mapping == mapping_type);
3636 
3637   if ( bBoxCheck && ON_TextureMapping::single == mapping.m_texture_space )
3638   {
3639     AdjustSingleBoxTextureCoordinatesHelper( mesh, mesh_xform, mesh_T, mesh_T_stride, Tsd, mapping );
3640     return;
3641   }
3642 
3643   ON_Workspace ws;
3644   int* quad = ws.GetIntMemory(vcnt); // ~ws will free quad memory
3645   float* Tx = (float*)ws.GetMemory(vcnt*sizeof(Tx[0]));
3646   float t;
3647   int vi, ti, q=0;
3648   int ftc_count = 0;
3649 
3650   const float ang0 = (float)(0.25*two_pi_tc);
3651   const float ang1 = (float)(0.75*two_pi_tc);
3652 
3653 
3654   for ( vi = ti = 0; vi < vcnt; vi++, ti += mesh_T_stride )
3655   {
3656     quad[vi] = 0;
3657     Tx[vi] = mesh_T[ti];
3658     if ( bCylinderCheck )
3659     {
3660       if ( 1 != Tsd[vi] )
3661         continue;
3662     }
3663     else if ( bBoxCheck )
3664     {
3665       if ( 1 != Tsd[vi] && 3 != Tsd[vi] )
3666         continue;
3667     }
3668     else if ( bSphereCheck )
3669     {
3670       t = mesh_T[ti+1]; // t = "v" texture coordinate
3671       if ( t < 0.001f )
3672       {
3673         quad[vi] = 8; q |= 8; // south pole point
3674         ftc_count++;
3675         continue;
3676       }
3677       if ( t > 0.999f )
3678       {
3679         quad[vi] = 8; q |= 8; // north pole point
3680         ftc_count++;
3681         continue;
3682       }
3683     }
3684 
3685     t = Tx[vi]; // t = "u" texture coordinate
3686     if ( t < ang0 )
3687     {
3688       quad[vi] = 1; q |= 1; // longitude < pi/2
3689       ftc_count++;
3690     }
3691     else if ( t > ang1 )
3692     {
3693       quad[vi] = 4; q |= 4; // longitude > 3pi/2
3694       ftc_count++;
3695     }
3696   }
3697 
3698   if ( 0 == q || 1 == q || 4 == q )
3699   {
3700     // nothing needs to be adjusted
3701     return;
3702   }
3703 
3704   // 4*ftc_count = (over) estimate of the number of faces that
3705   // will be changed.
3706   ON_SimpleArray<ON__CMeshFaceTC> ftc_list(ftc_count*4 + 128);
3707   ftc_count = 0;
3708   const ON_MeshFace* F = mesh.m_F.Array();
3709   const int* Fvi;
3710   int fi;
3711   ON__CMeshFaceTC ftc;
3712   memset(&ftc,0,sizeof(ftc));
3713   float t0, t1;
3714 
3715   for ( fi = 0; fi < fcnt; fi++ )
3716   {
3717     Fvi = F[fi].vi;
3718 
3719     ftc.quad[0] = quad[Fvi[0]];
3720     ftc.quad[1] = quad[Fvi[1]];
3721     ftc.quad[2] = quad[Fvi[2]];
3722     ftc.quad[3] = quad[Fvi[3]];
3723 
3724     q = (ftc.quad[0] | ftc.quad[1] | ftc.quad[2] | ftc.quad[3]);
3725     if ( 0 == q || 1 == q || 4 == q )
3726     {
3727       // no adjustments need to be made
3728       continue;
3729     }
3730 
3731     // ftc.fi will be set to fi if a texture coordinate needs to be adjusted
3732     ftc.fi = -1;
3733 
3734     ftc.Tx[0] = Tx[Fvi[0]];
3735     ftc.Tx[1] = Tx[Fvi[1]];
3736     ftc.Tx[2] = Tx[Fvi[2]];
3737     ftc.Tx[3] = Tx[Fvi[3]];
3738 
3739     if ( 0 != (8&q) )
3740     {
3741       // see if check for north/south sphere mapping poles and fix them
3742       if ( 8 == ftc.quad[0] )
3743       {
3744         t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3];
3745         t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
3746         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
3747         {
3748           ftc.Tx[0] = PoleFix(t0,t1);
3749           ftc.quad[0] = ((ftc.Tx[0] < ang0) ? 1 : ((ftc.Tx[0] > ang1) ? 4 : 0));
3750           q |= ftc.quad[0];
3751           ftc.fi = fi;
3752         }
3753       }
3754       if ( 8 == ftc.quad[1] )
3755       {
3756         t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
3757         t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
3758         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
3759         {
3760           ftc.Tx[1] = PoleFix(t0,t1);
3761           ftc.quad[1] = ((ftc.Tx[1] < ang0) ? 1 : ((ftc.Tx[1] > ang1) ? 4 : 0));
3762           q |= ftc.quad[1];
3763           ftc.fi = fi;
3764         }
3765       }
3766       if ( 8 == ftc.quad[2] )
3767       {
3768         int k = (Fvi[2] == Fvi[3]) ? 0 : 3;
3769         t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
3770         t1 = (8 == ftc.quad[k]) ? ON_UNSET_FLOAT : ftc.Tx[k];
3771         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
3772         {
3773           ftc.Tx[2] = PoleFix(t0,t1);
3774           ftc.quad[2] = ((ftc.Tx[2] < ang0) ? 1 : ((ftc.Tx[2] > ang1) ? 4 : 0));
3775           if ( !k )
3776           {
3777             ftc.Tx[3] = ftc.Tx[2];
3778             ftc.quad[3] = ftc.quad[2];
3779           }
3780           q |= ftc.quad[2];
3781           ftc.fi = fi;
3782         }
3783       }
3784       if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] )
3785       {
3786         t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
3787         t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
3788         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
3789         {
3790           ftc.Tx[3] = PoleFix(t0,t1);
3791           ftc.quad[3] = ((ftc.Tx[3] < ang0) ? 1 : ((ftc.Tx[3] > ang1) ? 4 : 0));
3792           q |= ftc.quad[3];
3793           ftc.fi = fi;
3794         }
3795       }
3796     }
3797 
3798     if ( 5 == (5&q) )
3799     {
3800       // The face has corners on both sides of the seam
3801       if ( two_pi_tc == 1.0 )
3802       {
3803         if ( 1 == ftc.quad[0] ) {ftc.Tx[0] += 1.0f; ftc.fi = fi;}
3804         if ( 1 == ftc.quad[1] ) {ftc.Tx[1] += 1.0f; ftc.fi = fi;}
3805         if ( 1 == ftc.quad[2] ) {ftc.Tx[2] += 1.0f; ftc.fi = fi;}
3806         if ( 1 == ftc.quad[3] ) {ftc.Tx[3] += 1.0f; ftc.fi = fi;}
3807       }
3808       else
3809       {
3810         // With divided textures, wrapping the texture coordinate
3811         // does not work because it wraps into a region of the
3812         // texture not use by this "side".  In this case, the
3813         // only thing to do is to pick the best end of the texture
3814         // map and clamp the tcs that hang over.  If the mesh
3815         // has edges near the texture seam, the picture will
3816         // still look ok.
3817         float f0=0.0f, f1=0.0f, twopitc = (float)two_pi_tc;;
3818         //int f0cnt=0, f1cnt=0;
3819         if ( 1 == ftc.quad[0] ) f0 += ftc.Tx[0]; else if ( 4 == ftc.quad[0] ) f1 += twopitc-ftc.Tx[0];
3820         if ( 1 == ftc.quad[1] ) f0 += ftc.Tx[1]; else if ( 4 == ftc.quad[1] ) f1 += twopitc-ftc.Tx[1];
3821         if ( 1 == ftc.quad[2] ) f0 += ftc.Tx[2]; else if ( 4 == ftc.quad[2] ) f1 += twopitc-ftc.Tx[2];
3822         if (Fvi[2] != Fvi[3])
3823         {
3824           if ( 1 == ftc.quad[3] ) f0 += ftc.Tx[3]; else if ( 4 == ftc.quad[3] ) f1 += twopitc-ftc.Tx[3];
3825         }
3826         if (f0 >= f1 )
3827         {
3828           // "most" of the face is on the left side of the texture
3829           // If a vertex is on the right side, clamp its tc to 0.
3830           if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;}
3831           if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;}
3832           if ( 4 == ftc.quad[2] ) {ftc.Tx[2] = 0.0f; ftc.fi = fi;}
3833           if ( 4 == ftc.quad[3] ) {ftc.Tx[3] = 0.0f; ftc.fi = fi;}
3834         }
3835         else
3836         {
3837           // "most" of the face is on the right side of the texture
3838           // If a vertex is on the left side, clamp its tc to two_pi_tc.
3839           if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;}
3840           if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;}
3841           if ( 1 == ftc.quad[2] ) {ftc.Tx[2] = twopitc; ftc.fi = fi;}
3842           if ( 1 == ftc.quad[3] ) {ftc.Tx[3] = twopitc; ftc.fi = fi;}
3843         }
3844       }
3845     }
3846 
3847     if ( ftc.fi >= 0 )
3848     {
3849       // face will require special handling
3850       ftc_list.Append(ftc);
3851     }
3852   }
3853 
3854   ftc_count = ftc_list.Count();
3855   if ( ftc_count <= 0 )
3856     return;
3857 
3858   // Count the number of new vertices that will be added.
3859   int ftci;
3860   int newvcnt = 0;
3861   for ( ftci = 0; ftci < ftc_count; ftci++ )
3862   {
3863     ON__CMeshFaceTC& ftc = ftc_list[ftci];
3864     Fvi = F[ftc.fi].vi;
3865     if ( ftc.Tx[0] != Tx[Fvi[0]] )
3866     {
3867       ftc.bSetT[0] = true;
3868       newvcnt++;
3869     }
3870     if ( ftc.Tx[1] != Tx[Fvi[1]] )
3871     {
3872       ftc.bSetT[1] = true;
3873       newvcnt++;
3874     }
3875     if ( ftc.Tx[2] != Tx[Fvi[2]] )
3876     {
3877       ftc.bSetT[2] = true;
3878       newvcnt++;
3879     }
3880     if ( Fvi[2] != Fvi[3] )
3881     {
3882       if ( ftc.Tx[3] != Tx[Fvi[3]] )
3883       {
3884         ftc.bSetT[3] = true;
3885         newvcnt++;
3886       }
3887     }
3888   }
3889 
3890   if ( newvcnt <= 0 )
3891     return;
3892 
3893 
3894   F = 0; // Setting them to NULL makes sure anybody who
3895          // tries to use them below will crash.
3896 
3897   // reserve room for new vertex information
3898   ON__CChangeTextureCoordinateHelper helper(mesh,newvcnt,mesh_T);
3899 
3900   // add vertices and update mesh faces
3901   for ( ftci = 0; ftci < ftc_count; ftci++ )
3902   {
3903     const ON__CMeshFaceTC& ftc = ftc_list[ftci];
3904     int* meshFvi = mesh.m_F[ftc.fi].vi;
3905 
3906     if ( ftc.bSetT[0] )
3907     {
3908       helper.ChangeTextureCoordinate(meshFvi,0,ftc.Tx[0],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
3909     }
3910     if ( ftc.bSetT[1] )
3911     {
3912       helper.ChangeTextureCoordinate(meshFvi,1,ftc.Tx[1],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
3913     }
3914     if ( ftc.bSetT[2] )
3915     {
3916       helper.ChangeTextureCoordinate(meshFvi,2,ftc.Tx[2],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
3917     }
3918     if ( ftc.bSetT[3] )
3919     {
3920       helper.ChangeTextureCoordinate(meshFvi,3,ftc.Tx[3],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
3921     }
3922   }
3923 }
3924 
3925 static
SeamCheckHelper(const ON_TextureMapping & mp,double & two_pi_tc,ON_SimpleArray<int> & Tside,ON_SimpleArray<int> * & Tsd)3926 bool SeamCheckHelper( const ON_TextureMapping& mp,
3927                       double& two_pi_tc,
3928                       ON_SimpleArray<int>& Tside,
3929                       ON_SimpleArray<int>*& Tsd )
3930 {
3931   bool bSeamCheck = false;
3932   switch(mp.m_type)
3933   {
3934     case ON_TextureMapping::box_mapping:
3935       if ( ON_TextureMapping::divided == mp.m_texture_space )
3936       {
3937         if ( mp.m_bCapped )
3938           two_pi_tc = 2.0/3.0;
3939         Tsd = &Tside;
3940         bSeamCheck = true;
3941       }
3942       else if ( ON_TextureMapping::single == mp.m_texture_space )
3943       {
3944         Tsd = &Tside;
3945         bSeamCheck = true;
3946       }
3947       break;
3948 
3949     case ON_TextureMapping::cylinder_mapping:
3950       if ( ON_TextureMapping::divided == mp.m_texture_space )
3951       {
3952         two_pi_tc = 2.0/3.0;
3953         Tsd = &Tside;
3954       }
3955       bSeamCheck = true;
3956       break;
3957 
3958     case ON_TextureMapping::sphere_mapping:
3959       bSeamCheck = true;
3960       break;
3961 
3962     default:
3963       // intentionally skip other enum values
3964       break;
3965   }
3966 
3967   return bSeamCheck;
3968 }
3969 
3970 //If there are unused vertices, this may not work correctly - but it is very fast.
HasSharedVertices(const ON_Mesh & mesh)3971 static inline bool HasSharedVertices(const ON_Mesh& mesh)
3972 {
3973   return mesh.m_V.Count() < ((mesh.TriangleCount() * 3) + (mesh.QuadCount() * 4));
3974 }
3975 
3976 
SetCachedTextureCoordinates(const class ON_TextureMapping & mapping,const class ON_Xform * mesh_xform,bool bLazy)3977 const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
3978         const class ON_TextureMapping& mapping,
3979 				const class ON_Xform* mesh_xform,
3980         bool bLazy
3981         )
3982 {
3983   if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
3984     ComputeVertexNormals();
3985 
3986   ON_TextureMapping mp = mapping;
3987   double two_pi_tc = 1.0;
3988   ON_SimpleArray<int> Tside;
3989   ON_SimpleArray<int>* Tsd = 0;
3990   bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
3991   if ( bSeamCheck )
3992     mp.m_uvw.Identity();
3993 
3994   ON_TextureCoordinates* TC = 0;
3995   {
3996     for ( int i = 0; i < m_TC.Count(); i++ )
3997     {
3998       if ( m_TC[i].m_tag.m_mapping_id == mapping.m_mapping_id )
3999       {
4000         TC = &m_TC[i];
4001         break;
4002       }
4003     }
4004   }
4005   if ( bLazy && TC && mapping.HasMatchingTextureCoordinates( TC->m_tag, mesh_xform ) )
4006     return TC;
4007 
4008   if ( !TC )
4009   {
4010     m_TC.AppendNew();
4011     TC = m_TC.Last();
4012   }
4013 
4014   // Use mp instead of mapping to call GetTextureCoordinates()
4015   // because m_uvw must be the identity if we have seams.
4016   if ( !mp.GetTextureCoordinates( *this,TC->m_T,mesh_xform,false,Tsd) )
4017   {
4018     int tci = (int)(TC - m_TC.Array());
4019     m_TC.Remove(tci);
4020     return 0;
4021   }
4022 
4023   TC->m_tag.Set(mapping);
4024   if (    mesh_xform && mesh_xform->IsValid()
4025        && !mesh_xform->IsIdentity()
4026        && !mesh_xform->IsZero()
4027      )
4028   {
4029     TC->m_tag.m_mesh_xform = *mesh_xform;
4030   }
4031 
4032   TC->m_dim = 2;
4033 
4034   if ( bSeamCheck &&  m_F.Count() > 0 && TC->m_T.Count() == m_V.Count() )
4035   {
4036     float* mesh_T = (float*)TC->m_T.Array();
4037     int mesh_T_stride = sizeof(TC->m_T[0])/sizeof(mesh_T[0]);
4038     if ( Tsd && Tside.Count() != m_V.Count() )
4039       Tsd = 0;
4040     AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
4041     mesh_T = 0; // when the array is grown, the pointer may become invalid
4042     if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
4043     {
4044       // Apply the uvw transformation that is on mapping
4045       // to the texture coordinates.
4046       ON_3dPoint T;
4047       int vi, vcnt = TC->m_T.Count();
4048       ON_3fPoint* meshT = TC->m_T.Array();
4049       for ( vi = 0; vi < vcnt; vi++ )
4050       {
4051         T = meshT[vi];
4052         T = mapping.m_uvw*T;
4053         meshT[vi] = T;
4054       }
4055     }
4056   }
4057 
4058   return TC;
4059 }
4060 
SetTextureCoordinates(const class ON_TextureMapping & mapping,const class ON_Xform * mesh_xform,bool bLazy)4061 bool ON_Mesh::SetTextureCoordinates(
4062                   const class ON_TextureMapping& mapping,
4063                   const class ON_Xform* mesh_xform,
4064                   bool bLazy
4065                   )
4066 {
4067   if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
4068     ComputeVertexNormals();
4069 
4070   InvalidateTextureCoordinateBoundingBox();
4071 
4072   ON_SimpleArray<int> Tside;
4073   ON_SimpleArray<int>* Tsd = 0;
4074   ON_TextureMapping mp = mapping;
4075 
4076   double two_pi_tc = 1.0;
4077 
4078   bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
4079   if ( bSeamCheck )
4080     mp.m_uvw.Identity();
4081 
4082   // Use mp instead of mapping to call GetTextureCoordinates()
4083   // because m_uvw must be the identity if we have seams.
4084   bool rc = mp.GetTextureCoordinates(*this,m_T,mesh_xform,bLazy,Tsd);
4085 
4086   if (rc)
4087   {
4088     // update the texture coordinate tag
4089     m_Ttag.Set(mapping);
4090     if (    mesh_xform
4091          && mesh_xform->IsValid()
4092          && !mesh_xform->IsIdentity()
4093          && !mesh_xform->IsZero()
4094        )
4095     {
4096       m_Ttag.m_mesh_xform  = *mesh_xform;
4097     }
4098   }
4099 
4100   if ( rc && bSeamCheck && HasTextureCoordinates() && m_F.Count() > 0 )
4101   {
4102     float* mesh_T = (float*)m_T.Array();
4103     int mesh_T_stride = sizeof(m_T[0])/sizeof(mesh_T[0]);
4104     if ( Tsd && Tside.Count() != m_V.Count() )
4105       Tsd = 0;
4106     AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
4107     mesh_T = 0; // when the array is grown, the pointer may become invalid
4108     if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
4109     {
4110       // Apply the uvw transformation that is on mapping
4111       // to the texture coordinates.
4112       ON_2fPoint* meshT = m_T.Array();
4113       ON_3dPoint T;
4114       int vi, vcnt = m_T.Count();
4115       for ( vi = 0; vi < vcnt; vi++ )
4116       {
4117         T.x = meshT[vi].x;
4118         T.y = meshT[vi].y;
4119         T.z = 0.0;
4120         T = mapping.m_uvw*T;
4121         meshT[vi].x = (float)T.x;
4122         meshT[vi].y = (float)T.y;
4123       }
4124     }
4125   }
4126 
4127   return rc;
4128 }
4129 
ON_MappingChannel()4130 ON_MappingChannel::ON_MappingChannel()
4131 {
4132   Default();
4133 }
4134 
Default()4135 void ON_MappingChannel::Default()
4136 {
4137   memset(this,0,sizeof(*this));
4138   m_mapping_channel_id = 1;
4139   m_mapping_index = -1;
4140   m_object_xform.Identity();
4141 }
4142 
Compare(const ON_MappingChannel & other) const4143 int ON_MappingChannel::Compare( const ON_MappingChannel& other ) const
4144 {
4145   int rc = m_mapping_channel_id - other.m_mapping_channel_id;
4146   if (!rc)
4147     rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id);
4148   return rc;
4149 }
4150 
Write(ON_BinaryArchive & archive) const4151 bool ON_MappingChannel::Write( ON_BinaryArchive& archive ) const
4152 {
4153   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
4154   if (rc)
4155   {
4156     rc = archive.WriteInt(m_mapping_channel_id);
4157     if (rc) rc = archive.WriteUuid(m_mapping_id);
4158 
4159     // 1.1 field added 6 June 2006
4160     if (rc) rc = archive.WriteXform(m_object_xform);
4161 
4162     if ( !archive.EndWrite3dmChunk() )
4163       rc = false;
4164   }
4165   return rc;
4166 }
4167 
Read(ON_BinaryArchive & archive)4168 bool ON_MappingChannel::Read( ON_BinaryArchive& archive )
4169 {
4170   Default();
4171   int major_version = 0;
4172   int minor_version = 0;
4173   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
4174   if (rc)
4175   {
4176     rc = (1 == major_version);
4177     if (rc) rc = archive.ReadInt(&m_mapping_channel_id);
4178     if (rc) rc = archive.ReadUuid(m_mapping_id);
4179 
4180     if ( rc && minor_version >= 1 )
4181     {
4182       // 1.1 field added 6 June 2006
4183       if (rc) rc = archive.ReadXform(m_object_xform);
4184       if (rc
4185           && archive.ArchiveOpenNURBSVersion() < 200610030
4186           && m_object_xform.IsZero()
4187           )
4188       {
4189         // Between versions 200606060 and 200610030,
4190         // there was a bug that created some mapping
4191         // channels with zero transformations.  This
4192         // if clause finds those and sets them to the
4193         // identity.
4194         m_object_xform.Identity();
4195       }
4196     }
4197 
4198     if ( !archive.EndRead3dmChunk() )
4199       rc = false;
4200   }
4201   return rc;
4202 }
4203 
4204 
ON_MaterialRef()4205 ON_MaterialRef::ON_MaterialRef()
4206 {
4207   Default();
4208 }
4209 
ON_MappingRef()4210 ON_MappingRef::ON_MappingRef()
4211 {
4212   Default();
4213 }
4214 
Default()4215 void ON_MaterialRef::Default()
4216 {
4217   memset(this,0,sizeof(*this));
4218   // runtme index value of -1 means not set
4219   m_material_index          = -1;
4220   m_material_backface_index = -1;
4221   m_material_source = ON::material_from_layer;
4222 }
4223 
Default()4224 void ON_MappingRef::Default()
4225 {
4226   m_plugin_id = ON_nil_uuid;
4227   m_mapping_channels.Destroy();
4228 }
4229 
Compare(const ON_MaterialRef & other) const4230 int ON_MaterialRef::Compare( const ON_MaterialRef& other ) const
4231 {
4232   int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
4233   if (rc)
4234     rc = ((int)m_material_source) - ((int)other.m_material_source);
4235   if (!rc)
4236     rc = ON_UuidCompare(m_material_id,other.m_material_id);
4237   if (!rc)
4238     rc = ON_UuidCompare(m_material_backface_id,other.m_material_backface_id);
4239   return rc;
4240 }
4241 
Compare(const ON_MappingRef & other) const4242 int ON_MappingRef::Compare( const ON_MappingRef& other ) const
4243 {
4244   int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
4245   if ( !rc)
4246   {
4247     const int count = m_mapping_channels.Count();
4248     rc = count - other.m_mapping_channels.Count();
4249     if (!rc)
4250     {
4251       for ( int i = 0; i < count && !rc; i++ )
4252       {
4253         rc = m_mapping_channels[i].Compare(other.m_mapping_channels[i]);
4254       }
4255     }
4256   }
4257   return rc;
4258 }
4259 
4260 
Write(ON_BinaryArchive & archive) const4261 bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const
4262 {
4263   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 );
4264   if (rc)
4265   {
4266     if (rc) rc = archive.WriteUuid( m_plugin_id );
4267     if (rc) rc = archive.WriteUuid( m_material_id );
4268 
4269     // 23 May 2006 Dale lear
4270     //   m_mapping_channels[] was removed from ON_MaterialRef.
4271     //   To keep from breaking the file format, I need to
4272     //   write a zero as the array length.
4273     //
4274     //if (rc) rc = archive.WriteArray( m_mapping_channels );
4275     if (rc) rc = archive.WriteInt(0);
4276 
4277     // 23 May 2006 added
4278     if (rc) rc = archive.WriteUuid( m_material_backface_id );
4279     if (rc) rc = archive.WriteInt( m_material_source );
4280 
4281 
4282     if ( !archive.EndWrite3dmChunk() )
4283       rc = false;
4284   }
4285   return rc;
4286 }
4287 
Read(ON_BinaryArchive & archive)4288 bool ON_MaterialRef::Read( ON_BinaryArchive& archive )
4289 {
4290   Default();
4291   int major_version = 0;
4292   int minor_version = 0;
4293   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
4294   if (rc)
4295   {
4296     rc = (1 == major_version);
4297 
4298     if (rc) rc = archive.ReadUuid( m_plugin_id );
4299     if (rc) rc = archive.ReadUuid( m_material_id );
4300 
4301     // 23 May 2006 Dale lear
4302     //   m_mapping_channels[] was removed from ON_MaterialRef.
4303     //   To keep from breaking the file format, I need to
4304     //   write a zero as the array length.
4305     ON_SimpleArray<ON_MappingChannel> obsolete_mapping_channels;
4306     if (rc) rc = archive.ReadArray( obsolete_mapping_channels );
4307 
4308     if ( minor_version >= 1 )
4309     {
4310       if (rc) rc = archive.ReadUuid( m_material_backface_id );
4311       int i = m_material_source;
4312       if (rc) rc = archive.ReadInt( &i );
4313       if (rc) m_material_source = (unsigned char)ON::ObjectMaterialSource(i);
4314     }
4315 
4316     if ( !archive.EndRead3dmChunk() )
4317       rc = false;
4318   }
4319   return rc;
4320 }
4321 
MaterialSource() const4322 ON::object_material_source ON_MaterialRef::MaterialSource() const
4323 {
4324   return ON::ObjectMaterialSource(m_material_source);
4325 }
4326 
Write(ON_BinaryArchive & archive) const4327 bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const
4328 {
4329   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
4330   if (rc)
4331   {
4332     if (rc) rc = archive.WriteUuid( m_plugin_id );
4333     if (rc) rc = archive.WriteArray( m_mapping_channels );
4334 
4335     if ( !archive.EndWrite3dmChunk() )
4336       rc = false;
4337   }
4338   return rc;
4339 }
4340 
Read(ON_BinaryArchive & archive)4341 bool ON_MappingRef::Read( ON_BinaryArchive& archive )
4342 {
4343   Default();
4344   int major_version = 0;
4345   int minor_version = 0;
4346   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
4347   if (rc)
4348   {
4349     rc = (1 == major_version);
4350 
4351     if (rc) rc = archive.ReadUuid( m_plugin_id );
4352     if (rc) rc = archive.ReadArray( m_mapping_channels );
4353 
4354     if ( !archive.EndRead3dmChunk() )
4355       rc = false;
4356   }
4357   return rc;
4358 }
4359 
Transform(const ON_Xform & xform)4360 bool ON_MappingRef::Transform( const ON_Xform& xform )
4361 {
4362   int count = m_mapping_channels.Count();
4363   if ( count > 0 )
4364   {
4365     for ( ON_MappingChannel* mapping_channel = m_mapping_channels.Array();
4366           count--;
4367           mapping_channel++ )
4368     {
4369       mapping_channel->m_object_xform = xform*mapping_channel->m_object_xform;
4370     }
4371   }
4372   return true;
4373 }
4374 
ON_ObjectRenderingAttributes()4375 ON_ObjectRenderingAttributes::ON_ObjectRenderingAttributes()
4376 {
4377   Default();
4378 }
4379 
ON_RenderingAttributes()4380 ON_RenderingAttributes::ON_RenderingAttributes()
4381 {
4382   Default();
4383 }
4384 
Default()4385 void ON_ObjectRenderingAttributes::Default()
4386 {
4387   ON_RenderingAttributes::Default();
4388   m_mappings.Destroy();
4389   m_bCastsShadows = true;
4390   m_bReceivesShadows = true;
4391   m_bits = 0;
4392   m_reserved1 = 0;
4393 }
4394 
Default()4395 void ON_RenderingAttributes::Default()
4396 {
4397   m_materials.Destroy();
4398 }
4399 
EnableAdvancedTexturePreview(bool b)4400 void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b)
4401 {
4402   if ( b )
4403     m_bits |= 1;    // set bit 1
4404   else
4405     m_bits &= 0xFE; // clear bit 1
4406 }
4407 
AdvancedTexturePreview() const4408 bool ON_ObjectRenderingAttributes::AdvancedTexturePreview() const
4409 {
4410   return (0 != (1 & m_bits)) ? true : false;
4411 }
4412 
IsValid(ON_TextLog * text_log) const4413 bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const
4414 {
4415   // plug-in uuids must be unique
4416   int count;
4417   if( (count = m_materials.Count()) > 1 )
4418   {
4419     const ON_MaterialRef* mr = m_materials.Array();
4420     ON_UUID plugin_id;
4421     int i, j;
4422     for ( i = 0; i < count-1; i++ )
4423     {
4424       plugin_id = mr[i].m_plugin_id;
4425       for ( j = i+1; j < count; j++ )
4426       {
4427         if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
4428         {
4429           if( text_log )
4430           {
4431             text_log->Print("ON_RenderingAttributes error: m_materials[%d] and m_materials[%d] have the same plug-in id.\n",i,j);
4432           }
4433           return false;
4434         }
4435       }
4436     }
4437   }
4438   return true;
4439 }
4440 
IsValid(ON_TextLog * text_log) const4441 bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const
4442 {
4443   if ( !ON_RenderingAttributes::IsValid(text_log) )
4444     return false;
4445 
4446   // plug-in uuids must be unique
4447   int count;
4448   if( (count = m_mappings.Count()) > 1 )
4449   {
4450     const ON_MappingRef* mr = m_mappings.Array();
4451     ON_UUID plugin_id;
4452     int i, j;
4453     for ( i = 0; i < count-1; i++ )
4454     {
4455       plugin_id = mr[i].m_plugin_id;
4456       for ( j = i+1; j < count; j++ )
4457       {
4458         if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
4459         {
4460           if( text_log )
4461           {
4462             text_log->Print("ON_ObjectRenderingAttributes error: m_mappings[%d] and m_mappings[%d] have the same plug-in id.\n",i,j);
4463           }
4464           return false;
4465         }
4466       }
4467     }
4468   }
4469 
4470   return true;
4471 }
4472 
Compare(const ON_RenderingAttributes & other) const4473 int ON_RenderingAttributes::Compare( const ON_RenderingAttributes& other ) const
4474 {
4475   const int count = m_materials.Count();
4476   int rc = count - other.m_materials.Count();
4477   if (!rc)
4478   {
4479     int i;
4480     for ( i = 0; i < count && !rc; i++ )
4481     {
4482       rc = m_materials[i].Compare(other.m_materials[i]);
4483     }
4484   }
4485   return rc;
4486 }
4487 
MaterialRef(const ON_UUID & plugin_id) const4488 const ON_MaterialRef* ON_RenderingAttributes::MaterialRef( const ON_UUID& plugin_id ) const
4489 {
4490   int count;
4491   if ( (count = m_materials.Count()) > 0 )
4492   {
4493     for ( const ON_MaterialRef* mr = m_materials.Array(); count--; mr++ )
4494     {
4495       if ( plugin_id == mr->m_plugin_id )
4496         return mr;
4497     }
4498   }
4499   return 0;
4500 }
4501 
Compare(const ON_ObjectRenderingAttributes & other) const4502 int ON_ObjectRenderingAttributes::Compare( const ON_ObjectRenderingAttributes& other ) const
4503 {
4504   int rc = ON_RenderingAttributes::Compare(other);
4505   if (!rc)
4506   {
4507     int i;
4508     const int count = m_mappings.Count();
4509     rc = other.m_mappings.Count() - count;
4510     for ( i = 0; i < count && !rc; i++ )
4511     {
4512       rc = m_mappings[i].Compare(other.m_mappings[i]);
4513     }
4514     if ( !rc )
4515     {
4516       rc = ((int)(m_bCastsShadows?1:0)) - ((int)(other.m_bCastsShadows?1:0));
4517       if ( !rc )
4518       {
4519         rc = ((int)(m_bReceivesShadows?1:0)) - ((int)(other.m_bReceivesShadows?1:0));
4520       }
4521 	  if ( !rc )
4522 	  {
4523 	    rc = ((int)(AdvancedTexturePreview()?1:0)) - ((int)(other.AdvancedTexturePreview()?1:0));
4524 	  }
4525     }
4526   }
4527   return rc;
4528 }
4529 
Transform(const ON_Xform & xform)4530 bool ON_ObjectRenderingAttributes::Transform( const ON_Xform& xform )
4531 {
4532   int i;
4533   if ( (i = m_mappings.Count()) > 0 )
4534   {
4535     for( ON_MappingRef* mr = m_mappings.Array(); i--; mr++ )
4536       mr->Transform(xform);
4537   }
4538   return true;
4539 }
4540 
MappingRef(const ON_UUID & plugin_id) const4541 const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef(
4542   const ON_UUID& plugin_id ) const
4543 {
4544   int count;
4545   if ( (count = m_mappings.Count()) > 0 )
4546   {
4547     for ( const ON_MappingRef* mr = m_mappings.Array(); count--; mr++ )
4548     {
4549       if ( plugin_id == mr->m_plugin_id )
4550         return mr;
4551     }
4552   }
4553   return 0;
4554 }
4555 
AddMappingRef(const ON_UUID & plugin_id)4556 ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
4557   const ON_UUID& plugin_id
4558   )
4559 {
4560   ON_MappingRef* mr = 0;
4561   int count;
4562   if ( (count = m_mappings.Count()) > 0 )
4563   {
4564     for ( mr = const_cast<ON_MappingRef*>(m_mappings.Array()); count--; mr++ )
4565     {
4566       if ( plugin_id == mr->m_plugin_id )
4567         break;
4568     }
4569   }
4570 
4571   if ( !mr )
4572   {
4573     mr = &m_mappings.AppendNew();
4574     mr->m_plugin_id = plugin_id;
4575   }
4576 
4577   return mr;
4578 }
4579 
DeleteMappingRef(const ON_UUID & plugin_id)4580 bool ON_ObjectRenderingAttributes::DeleteMappingRef(
4581   const ON_UUID& plugin_id
4582   )
4583 {
4584   const ON_MappingRef* mr = MappingRef(plugin_id);
4585   if ( mr )
4586     m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion
4587   return (0 != mr);
4588 }
4589 
MappingChannel(const ON_UUID & plugin_id,const ON_UUID & mapping_id) const4590 const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
4591   const ON_UUID& plugin_id,
4592   const ON_UUID& mapping_id
4593   ) const
4594 {
4595   const ON_MappingRef* mr = MappingRef(plugin_id);
4596   if ( mr )
4597   {
4598     int count;
4599     if ( (count = mr->m_mapping_channels.Count()) > 0 )
4600     {
4601       for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
4602       {
4603         if ( mapping_id == mc->m_mapping_id )
4604           return mc;
4605       }
4606     }
4607   }
4608   return 0;
4609 }
4610 
MappingChannel(const ON_UUID & plugin_id,int mapping_channel_id) const4611 const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
4612   const ON_UUID& plugin_id,
4613   int mapping_channel_id
4614   ) const
4615 {
4616   const ON_MappingRef* mr = MappingRef(plugin_id);
4617   if ( mr )
4618   {
4619     int count;
4620     if ( (count = mr->m_mapping_channels.Count()) > 0 )
4621     {
4622       for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
4623       {
4624         if ( mapping_channel_id == mc->m_mapping_channel_id )
4625           return mc;
4626       }
4627     }
4628   }
4629   return 0;
4630 }
4631 
4632 
4633 
AddMappingChannel(const ON_UUID & plugin_id,int mapping_channel_id,const ON_UUID & mapping_id)4634 bool ON_ObjectRenderingAttributes::AddMappingChannel(
4635         const ON_UUID& plugin_id,
4636         int mapping_channel_id,
4637         const ON_UUID& mapping_id
4638         )
4639 {
4640   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
4641   if ( !mr )
4642   {
4643     mr = &m_mappings.AppendNew();
4644     mr->m_plugin_id = plugin_id;
4645     ON_MappingChannel& mc = mr->m_mapping_channels.AppendNew();
4646     mc.m_mapping_channel_id = mapping_channel_id;
4647     mc.m_mapping_id = mapping_id;
4648     mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
4649     mc.m_object_xform.Identity();
4650     return true;
4651   }
4652 
4653   return mr->AddMappingChannel(mapping_channel_id,mapping_id);
4654 }
4655 
DeleteMappingChannel(const ON_UUID & plugin_id,int mapping_channel_id)4656 bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
4657   const ON_UUID& plugin_id,
4658   int mapping_channel_id
4659   )
4660 {
4661   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
4662   return mr ? mr->DeleteMappingChannel(mapping_channel_id) : false;
4663 }
4664 
DeleteMappingChannel(const ON_UUID & plugin_id,const ON_UUID & mapping_id)4665 bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
4666   const ON_UUID& plugin_id,
4667   const ON_UUID& mapping_id
4668   )
4669 {
4670   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
4671   return mr ? mr->DeleteMappingChannel(mapping_id) : false;
4672 }
4673 
ChangeMappingChannel(const ON_UUID & plugin_id,int old_mapping_channel_id,int new_mapping_channel_id)4674 bool ON_ObjectRenderingAttributes::ChangeMappingChannel(
4675   const ON_UUID& plugin_id,
4676   int old_mapping_channel_id,
4677   int new_mapping_channel_id
4678   )
4679 {
4680   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
4681   return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false;
4682 }
4683 
MappingChannel(const ON_UUID & mapping_id) const4684 const ON_MappingChannel* ON_MappingRef::MappingChannel(
4685   const ON_UUID& mapping_id
4686   ) const
4687 {
4688   int count;
4689   if ( (count = m_mapping_channels.Count()) > 0 )
4690   {
4691     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
4692     {
4693       if ( mapping_id == mc->m_mapping_id )
4694         return mc;
4695     }
4696   }
4697   return 0;
4698 }
4699 
MappingChannel(int mapping_channel_id) const4700 const ON_MappingChannel* ON_MappingRef::MappingChannel(
4701   int mapping_channel_id
4702   ) const
4703 {
4704   int count;
4705   if ( (count = m_mapping_channels.Count()) > 0 )
4706   {
4707     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
4708     {
4709       if ( mapping_channel_id == mc->m_mapping_channel_id )
4710         return mc;
4711     }
4712   }
4713   return 0;
4714 }
4715 
4716 
4717 
AddMappingChannel(int mapping_channel_id,const ON_UUID & mapping_id)4718 bool ON_MappingRef::AddMappingChannel(
4719         int mapping_channel_id,
4720         const ON_UUID& mapping_id
4721         )
4722 {
4723   int i;
4724   if ( (i = m_mapping_channels.Count()) > 0 )
4725   {
4726     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); i--; mc++ )
4727     {
4728       if ( mapping_channel_id == mc->m_mapping_channel_id )
4729       {
4730         // a matching mapping channel id exists
4731         // return true if mapping_id matches
4732         return ( 0 == ON_UuidCompare(&mapping_id,&mc->m_mapping_id) );
4733       }
4734     }
4735   }
4736 
4737   ON_MappingChannel& mc   = m_mapping_channels.AppendNew();
4738   mc.m_mapping_channel_id = mapping_channel_id;
4739   mc.m_mapping_id         = mapping_id;
4740   mc.m_mapping_index      = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
4741   mc.m_object_xform.Identity();
4742 
4743   return true;
4744 }
4745 
DeleteMappingChannel(int mapping_channel_id)4746 bool ON_MappingRef::DeleteMappingChannel(int mapping_channel_id)
4747 {
4748   const ON_MappingChannel* mc = MappingChannel(mapping_channel_id);
4749   if ( mc )
4750   {
4751     m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
4752   }
4753   return ( 0 != mc);
4754 }
4755 
DeleteMappingChannel(const ON_UUID & mapping_id)4756 bool ON_MappingRef::DeleteMappingChannel(const ON_UUID& mapping_id)
4757 {
4758   const ON_MappingChannel* mc = MappingChannel(mapping_id);
4759   if ( mc )
4760   {
4761     m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
4762   }
4763   return ( 0 != mc);
4764 }
4765 
ChangeMappingChannel(int old_mapping_channel_id,int new_mapping_channel_id)4766 bool ON_MappingRef::ChangeMappingChannel(
4767   int old_mapping_channel_id,
4768   int new_mapping_channel_id
4769   )
4770 {
4771   ON_MappingChannel* mc = const_cast<ON_MappingChannel*>(MappingChannel(old_mapping_channel_id));
4772   if ( mc )
4773   {
4774     mc->m_mapping_channel_id = new_mapping_channel_id;
4775   }
4776   return ( 0 != mc );
4777 }
4778 
Write(ON_BinaryArchive & archive) const4779 bool ON_RenderingAttributes::Write( ON_BinaryArchive& archive ) const
4780 {
4781   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
4782   if ( !rc )
4783     return false;
4784   for(;;)
4785   {
4786     rc = archive.WriteArray(m_materials);
4787     if ( !rc ) break;
4788 
4789     break;
4790   }
4791   if ( !archive.EndWrite3dmChunk() )
4792     rc = false;
4793   return rc;
4794 }
4795 
Read(ON_BinaryArchive & archive)4796 bool ON_RenderingAttributes::Read( ON_BinaryArchive& archive )
4797 {
4798   Default();
4799   int major_version = 0;
4800   int minor_version = 0;
4801   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
4802   if (!rc)
4803     return false;
4804   for(;;)
4805   {
4806     rc = ( 1 == major_version );
4807     if (!rc) break;
4808     rc = archive.ReadArray(m_materials);
4809     if (!rc) break;
4810 
4811     break;
4812   }
4813   if ( !archive.EndRead3dmChunk() )
4814     rc = false;
4815   return rc;
4816 }
4817 
Write(ON_BinaryArchive & archive) const4818 bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const
4819 {
4820   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 );
4821   if ( !rc )
4822     return false;
4823   for(;;)
4824   {
4825     // DO NOT CALL ON_RenderingAttributes::Write
4826     rc = archive.WriteArray(m_materials);
4827     if ( !rc ) break;
4828     rc = archive.WriteArray(m_mappings);
4829     if ( !rc ) break;
4830 
4831     // version 1.2 fields added 20061129
4832     rc = archive.WriteBool(m_bCastsShadows);
4833     if ( !rc ) break;
4834     rc = archive.WriteBool(m_bReceivesShadows);
4835     if ( !rc ) break;
4836 
4837     // version 1.3 fields added 20101019
4838     bool b = AdvancedTexturePreview();
4839     rc = archive.WriteBool(b);
4840     if ( !rc ) break;
4841 
4842     break;
4843   }
4844   if ( !archive.EndWrite3dmChunk() )
4845     rc = false;
4846   return rc;
4847 }
4848 
Read(ON_BinaryArchive & archive)4849 bool ON_ObjectRenderingAttributes::Read( ON_BinaryArchive& archive )
4850 {
4851   Default();
4852   int major_version = 0;
4853   int minor_version = 0;
4854   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
4855   if (!rc)
4856     return false;
4857   for(;;)
4858   {
4859     rc = ( 1 == major_version && minor_version >= 1 );
4860     if (!rc) break;
4861 
4862     // DO NOT CALL ON_RenderingAttributes::Read
4863     if (rc) rc = archive.ReadArray(m_materials);
4864     if (!rc) break;
4865     if (rc) rc = archive.ReadArray(m_mappings);
4866     if (!rc) break;
4867 
4868     if ( minor_version <= 1 )
4869       break;
4870 
4871     // version 1.2 fields added 20061129
4872     rc = archive.ReadBool(&m_bCastsShadows);
4873     if ( !rc ) break;
4874     rc = archive.ReadBool(&m_bReceivesShadows);
4875     if ( !rc ) break;
4876 
4877     if ( minor_version <= 2 )
4878       break;
4879 
4880     // version 1.3 fields added 20101019
4881     bool b = AdvancedTexturePreview();
4882     rc = archive.ReadBool(&b);
4883     if ( !rc ) break;
4884     // Jussi 20120430: We don't want to enable advanced texture preview by default. It will be
4885     //                 turned on when needed (depending on active render plug-in etc).
4886     //EnableAdvancedTexturePreview(b);
4887 
4888     break;
4889   }
4890   if ( !archive.EndRead3dmChunk() )
4891     rc = false;
4892 
4893   return rc;
4894 }
4895 
4896 
SetSurfaceParameterMapping(void)4897 bool ON_TextureMapping::SetSurfaceParameterMapping(void)
4898 {
4899   Default();
4900 	m_type = srfp_mapping;
4901   ON_CreateUuid(m_mapping_id);
4902 	return true;
4903 }
4904 
4905 
SetPlaneMapping(const ON_Plane & plane,const ON_Interval & dx,const ON_Interval & dy,const ON_Interval & dz)4906 bool ON_TextureMapping::SetPlaneMapping(
4907           const ON_Plane& plane,
4908           const ON_Interval& dx,
4909           const ON_Interval& dy,
4910           const ON_Interval& dz
4911           )
4912 {
4913   Default();
4914 
4915   // Don't call plane.IsValid(), because the plane
4916   // equation does not matter and many developers
4917   // forget to set it correctly.
4918   if ( !plane.origin.IsValid() )
4919     return false;
4920   if ( !ON_IsRightHandFrame( plane.xaxis, plane.yaxis, plane.zaxis ) )
4921     return false;
4922   if ( !dx.IsValid() || !dy.IsValid() || !dz.IsValid() )
4923     return false;
4924 
4925   ON_3dPoint C = plane.PointAt(dx.Mid(),dy.Mid(),dz.Mid());
4926   C.x = (0.0 == C.x) ? 0.0 : -C.x;
4927   C.y = (0.0 == C.y) ? 0.0 : -C.y;
4928   C.z = (0.0 == C.z) ? 0.0 : -C.z;
4929   ON_3dVector xaxis = plane.xaxis;
4930   ON_3dVector yaxis = plane.yaxis;
4931   ON_3dVector zaxis = plane.zaxis;
4932 
4933   // Any "cleanup" needs to be done here
4934   // to xaxis, yaxis, zaxis.
4935 
4936   double sx,sy,sz;
4937   if ( 0.0 == (sx = dx.Length())) sx = 2.0;
4938   if ( 0.0 == (sy = dy.Length())) sy = 2.0;
4939   if ( 0.0 == (sz = dz.Length())) sz = 2.0;
4940 
4941   // The plane mapping matrix m_Pxyz transforms the
4942   // world coordinate rectangle to a (-1<=r<=1,
4943   // on plane to a
4944   // 1 X 1 square in the xy plane centered at the
4945   // origin.
4946 
4947   // m_Pxyz = surface point transformation
4948   ON_3dVector X = (2.0/sx)*xaxis;
4949   ON_3dVector Y = (2.0/sy)*yaxis;
4950   ON_3dVector Z = (2.0/sz)*zaxis;
4951 
4952   m_Pxyz.m_xform[0][0] = X.x;
4953   m_Pxyz.m_xform[0][1] = X.y;
4954   m_Pxyz.m_xform[0][2] = X.z;
4955   m_Pxyz.m_xform[0][3] = (X.x*C.x + X.y*C.y + X.z*C.z);
4956 
4957   m_Pxyz.m_xform[1][0] = Y.x;
4958   m_Pxyz.m_xform[1][1] = Y.y;
4959   m_Pxyz.m_xform[1][2] = Y.z;
4960   m_Pxyz.m_xform[1][3] = (Y.x*C.x + Y.y*C.y + Y.z*C.z);
4961 
4962   m_Pxyz.m_xform[2][0] = Z.x;
4963   m_Pxyz.m_xform[2][1] = Z.y;
4964   m_Pxyz.m_xform[2][2] = Z.z;
4965   m_Pxyz.m_xform[2][3] = (Z.x*C.x + Z.y*C.y + Z.z*C.z);
4966 
4967   m_Pxyz.m_xform[3][0] = 0.0;
4968   m_Pxyz.m_xform[3][1] = 0.0;
4969   m_Pxyz.m_xform[3][2] = 0.0;
4970   m_Pxyz.m_xform[3][3] = 1.0;
4971 
4972   // m_Nxyz = surface normal transformation
4973   //        = inverse transpose of upper 3x3 of m_Pxyz
4974   X = (0.5*sx)*xaxis;
4975   Y = (0.5*sy)*yaxis;
4976   Z = (0.5*sz)*zaxis;
4977   m_Nxyz.m_xform[0][0] = X.x;
4978   m_Nxyz.m_xform[0][1] = X.y;
4979   m_Nxyz.m_xform[0][2] = X.z;
4980   m_Nxyz.m_xform[0][3] = 0.0;
4981 
4982   m_Nxyz.m_xform[1][0] = Y.x;
4983   m_Nxyz.m_xform[1][1] = Y.y;
4984   m_Nxyz.m_xform[1][2] = Y.z;
4985   m_Nxyz.m_xform[1][3] = 0.0;
4986 
4987   m_Nxyz.m_xform[2][0] = Z.x;
4988   m_Nxyz.m_xform[2][1] = Z.y;
4989   m_Nxyz.m_xform[2][2] = Z.z;
4990   m_Nxyz.m_xform[2][3] = 0.0;
4991 
4992   m_Nxyz.m_xform[3][0] = 0.0;
4993   m_Nxyz.m_xform[3][1] = 0.0;
4994   m_Nxyz.m_xform[3][2] = 0.0;
4995   m_Nxyz.m_xform[3][3] = 1.0;
4996 
4997   m_type = plane_mapping;
4998   ON_CreateUuid(m_mapping_id);
4999 
5000 #if defined(ON_DEBUG)
5001   {
5002     ON_Plane p;
5003     p.xaxis = (2.0/sx)*plane.xaxis;
5004     p.yaxis = (2.0/sy)*plane.yaxis;
5005     p.zaxis = (2.0/sz)*plane.zaxis;
5006     p.origin.Set(-C.x,-C.y,-C.z);
5007     p.UpdateEquation();
5008     ON_Xform P_dbg, N_dbg;
5009     P_dbg.Rotation(p,ON_xy_plane);
5010     P_dbg.GetSurfaceNormalXform(N_dbg);
5011 
5012     for ( int i = 0; i < 4; i++ )
5013     {
5014       for ( int j = 0; j < 4; j++ )
5015       {
5016         if ( fabs(m_Pxyz[i][j] - P_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Pxyz[i][j])+128.0) )
5017         {
5018           ON_ERROR("m_Pxyz is nor right\n");
5019           break;
5020         }
5021         if ( fabs(m_Nxyz[i][j] - N_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Nxyz[i][j])+128.0) )
5022         {
5023           ON_ERROR("m_Nxyz is nor right\n");
5024           break;
5025         }
5026       }
5027     }
5028   }
5029 #endif
5030 	return true;
5031 }
5032 
SetBoxMapping(const ON_Plane & plane,ON_Interval dx,ON_Interval dy,ON_Interval dz,bool bCapped)5033 bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane,
5034                                       ON_Interval dx,
5035                                       ON_Interval dy,
5036                                       ON_Interval dz,
5037                                       bool bCapped
5038                                       )
5039 {
5040   bool rc = SetPlaneMapping(plane,dx,dy,dz);
5041   if (rc)
5042   {
5043     m_bCapped = bCapped;
5044     m_type = ON_TextureMapping::box_mapping;
5045   }
5046   return rc;
5047 }
5048 
SetCylinderMapping(const ON_Cylinder & cylinder,bool bIsCapped)5049 bool ON_TextureMapping::SetCylinderMapping(const ON_Cylinder& cylinder, bool bIsCapped)
5050 {
5051   ON_Interval dr, dh;
5052   if ( !ON_IsValid(cylinder.circle.radius ) )
5053     return false;
5054   double r = cylinder.circle.radius;
5055   if ( 0.0 == r )
5056     r = 1.0;
5057   dr.Set(-r,r);
5058   dh.Set(cylinder.height[0],cylinder.height[1]);
5059   if ( dh[0] == dh[1] )
5060   {
5061     if ( ON_UNSET_VALUE == dh[0] )
5062     {
5063       dh.Set(-1.0,1.0);
5064     }
5065     else
5066     {
5067       dh.m_t[0] -= 1.0;
5068       dh.m_t[0] += 1.0;
5069     }
5070   }
5071   if ( !dh.IsValid() )
5072     return false;
5073 
5074   bool rc = SetBoxMapping(cylinder.circle.plane,dr,dr,dh,bIsCapped);
5075   if (rc)
5076   {
5077 	  m_type = cylinder_mapping;
5078   }
5079 
5080 	return rc;
5081 }
5082 
SetSphereMapping(const ON_Sphere & sphere)5083 bool ON_TextureMapping::SetSphereMapping(const ON_Sphere& sphere)
5084 {
5085   ON_Interval dr(-sphere.radius,sphere.radius);
5086   bool rc = SetBoxMapping(sphere.plane,dr,dr,dr,false);
5087   if (rc)
5088   {
5089 	  m_type = sphere_mapping;
5090   }
5091 	return rc;
5092 }
5093 
5094 
5095 
GetMappingPlane(ON_Plane & plane,ON_Interval & dx,ON_Interval & dy,ON_Interval & dz) const5096 bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane,
5097                                         ON_Interval& dx,
5098                                         ON_Interval& dy,
5099                                         ON_Interval& dz
5100                                         ) const
5101 {
5102   ON_Xform xform(m_Pxyz);
5103 
5104   ON_3dVector S(((ON_3dVector*)&xform.m_xform[0])->Length(),
5105                 ((ON_3dVector*)&xform.m_xform[1])->Length(),
5106                 ((ON_3dVector*)&xform.m_xform[2])->Length());
5107 
5108   if ( 0.0 == S.x )
5109     return false;
5110   S.x = 1.0/S.x;
5111   if ( 0.0 == S.y )
5112     return false;
5113   S.y = 1.0/S.y;
5114   if ( 0.0 == S.z )
5115     return false;
5116   S.z = 1.0/S.z;
5117 
5118   xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x;
5119   xform.m_xform[0][3] *= S.x;
5120 
5121   xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y;
5122   xform.m_xform[1][3] *= S.y;
5123 
5124   xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z;
5125   xform.m_xform[2][3] *= S.z;
5126 
5127   xform.m_xform[3][0] = 0.0;
5128   xform.m_xform[3][1] = 0.0;
5129   xform.m_xform[3][2] = 0.0;
5130   xform.m_xform[3][3] = 1.0;
5131 
5132   ON_Xform inv(xform);
5133   if ( !inv.Invert() )
5134     return false;
5135 
5136   plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]);
5137   xform.m_xform[0][3] = 0.0;
5138   xform.m_xform[1][3] = 0.0;
5139   xform.m_xform[2][3] = 0.0;
5140   plane.xaxis = &xform.m_xform[0][0];
5141   plane.yaxis = &xform.m_xform[1][0];
5142   plane.zaxis = &xform.m_xform[2][0];
5143 
5144 	plane.UpdateEquation();
5145 
5146   dx.Set(-S.x,S.x);
5147   dy.Set(-S.y,S.y);
5148   dz.Set(-S.z,S.z);
5149 
5150   return plane.IsValid();
5151 }
5152 
GetMappingBox(ON_Plane & plane,ON_Interval & dx,ON_Interval & dy,ON_Interval & dz) const5153 bool ON_TextureMapping::GetMappingBox(ON_Plane& plane,
5154                                       ON_Interval& dx,
5155                                       ON_Interval& dy,
5156                                       ON_Interval& dz) const
5157 {
5158 	return GetMappingPlane(plane, dx, dy, dz);
5159 }
5160 
GetMappingCylinder(ON_Cylinder & cylinder) const5161 bool ON_TextureMapping::GetMappingCylinder(ON_Cylinder& cylinder) const
5162 {
5163   ON_Interval dx, dy, dz;
5164   ON_Plane plane;
5165   bool rc = GetMappingPlane(cylinder.circle.plane, dx, dy, dz);
5166   if (rc)
5167   {
5168     double r0 = 0.5*dx.Length();
5169     double r1 = 0.5*dy.Length();
5170     cylinder.circle.radius = (r0 == r1) ? r0 : 0.5*(r0+r1);
5171     cylinder.height[0] = dz[0];
5172     cylinder.height[1] = dz[1];
5173   }
5174 
5175   return rc && cylinder.IsValid();
5176 }
5177 
GetMappingSphere(ON_Sphere & sphere) const5178 bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const
5179 {
5180   ON_Interval dx, dy, dz;
5181   bool rc = GetMappingPlane(sphere.plane, dx, dy, dz);
5182   if (rc)
5183   {
5184     double r0 = 0.5*dx.Length();
5185     double r1 = 0.5*dy.Length();
5186     double r2 = 0.5*dz.Length();
5187     sphere.radius = (r0 == r1 && r0 == r2) ? r0 : (r0+r1+r2)/3.0;
5188   }
5189   return rc && sphere.IsValid();
5190 }
5191 
5192