opennurbs_material.cpp
Go to the documentation of this file.
00001 /* $NoKeywords: $ */
00002 /*
00003 //
00004 // Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
00005 // OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
00006 // McNeel & Associates.
00007 //
00008 // THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
00009 // ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
00010 // MERCHANTABILITY ARE HEREBY DISCLAIMED.
00011 //                              
00012 // For complete openNURBS copyright information see <http://www.opennurbs.org>.
00013 //
00015 */
00016 
00017 #include "pcl/surface/3rdparty/opennurbs/opennurbs.h"
00018 
00019 
00021 //   Class ON_Material
00023 
00024 ON_OBJECT_IMPLEMENT(ON_Material,ON_Object,"60B5DBBC-E660-11d3-BFE4-0010830122F0");
00025 
00026 double ON_Material::m_max_shine = 255.0f;
00027 
00028 double ON_Material::MaxShine()
00029 {
00030   return m_max_shine;
00031 }
00032 
00033 void ON_Material::Default()
00034 {
00035   PurgeUserData();
00036 
00037   m_material_index = 0;
00038   m_material_id = ON_nil_uuid;
00039   m_material_name.Destroy();
00040   m_flamingo_library.Destroy();
00041 
00042   m_ambient.SetRGB( 0, 0, 0 );
00043   m_diffuse.SetRGB( 128, 128, 128 );
00044   m_emission.SetRGB( 0, 0, 0 );
00045   m_specular.SetRGB( 255, 255, 255 );
00046   //m_reflection = m_specular;
00047   //m_transparent = m_diffuse;
00048   m_reflection.SetRGB( 255, 255, 255 );
00049   m_transparent.SetRGB( 255, 255, 255 );
00050 
00051   m_index_of_refraction = 1.0;
00052   m_reflectivity = 0.0;
00053 
00054   m_shine = 0.0;
00055   m_transparency = 0.0;
00056 
00057   m_bShared = false;
00058   m_bDisableLighting = false;
00059   m_reserved1[0] = 0;
00060   m_reserved1[1] = 0;
00061 #if defined(ON_64BIT_POINTER)
00062   m_reserved2[0] = 0;
00063   m_reserved2[1] = 0;
00064   m_reserved2[2] = 0;
00065   m_reserved2[3] = 0;
00066 #endif
00067 
00068   m_textures.Destroy();
00069 
00070   m_plugin_id = ON_nil_uuid;
00071 
00072   m_material_channel.Destroy();
00073 }
00074 
00075 // Default constructor
00076 ON_Material::ON_Material() 
00077 {
00078   Default();
00079 }
00080 
00081 ON_Material::~ON_Material()
00082 {}
00083 
00084 ON_BOOL32
00085 ON_Material::IsValid( ON_TextLog* text_log ) const
00086 {
00087   return true;
00088 }
00089 
00090 
00091 void
00092 ON_Material::Dump( ON_TextLog& dump ) const
00093 {
00094   const wchar_t* s;
00095   dump.Print("index = %d\n",MaterialIndex());
00096   dump.Print("id = "); dump.Print(m_material_id); dump.Print("\n");
00097   
00098   s = m_material_name;
00099   if ( !s ) 
00100     s = L"";
00101   dump.Print("name = \"%ls\"\n",s);
00102   
00103   dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n");
00104   dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n");
00105   dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n");
00106   dump.Print("specular rgb = "); dump.PrintRGB( m_specular ); dump.Print("\n");
00107   dump.Print("reflection rgb = "); dump.PrintRGB( m_reflection ); dump.Print("\n");
00108   dump.Print("transparent rgb = "); dump.PrintRGB( m_transparent ); dump.Print("\n");
00109   dump.Print("shine = %g%%\n",100.0*m_shine/ON_Material::MaxShine() );
00110   dump.Print("transparency = %g%%\n",100.0*m_transparency);
00111   dump.Print("reflectivity = %g%%\n",100.0*m_reflectivity);
00112   dump.Print("index of refraction = %g\n",m_index_of_refraction);
00113 
00114   dump.Print("plug-in id = "); dump.Print(m_plugin_id); dump.Print("\n");
00115   int i;
00116   for( i = 0; i < m_textures.Count(); i++ )
00117   {
00118     dump.Print("texture[%d]:\n",i);
00119     dump.PushIndent();
00120     m_textures[i].Dump(dump);
00121     dump.PopIndent();
00122   }
00123 }
00124 
00125 
00126 ON_UUID ON_Material::MaterialPlugInUuid() const
00127 {
00128   return m_plugin_id;
00129 }
00130 
00131 void ON_Material::SetMaterialPlugInUuid( ON_UUID u )
00132 {
00133   m_plugin_id = u;
00134 }
00135 
00136 ON_BOOL32 ON_Material::Write( ON_BinaryArchive& file ) const
00137 {
00138   bool rc = false;
00139   if ( file.Archive3dmVersion() <= 3 )
00140   {
00141     // V2 or V3 file format
00142     rc = WriteV3Helper(file);
00143   }
00144   else 
00145   {
00146     // V4 file format
00147 
00148     // The chunk version 2.0 prevents old V3 IO code from attempting
00149     // to read this material
00150     rc = file.Write3dmChunkVersion(2,0); // never change the 2,0
00151 
00152 
00153     // version 1.2 field (20061113*)
00154     // version 1.3 fields (20100917*)
00155     if (rc) rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,3);
00156     if (rc)
00157     {
00158       for(;;)
00159       {
00160         if ( rc ) rc = file.WriteUuid(m_material_id);
00161         if ( rc ) rc = file.WriteInt(m_material_index);
00162         if ( rc ) rc = file.WriteString(m_material_name);
00163 
00164         if ( rc ) rc = file.WriteUuid(m_plugin_id);
00165 
00166         if ( rc ) rc = file.WriteColor( m_ambient );
00167         if ( rc ) rc = file.WriteColor( m_diffuse );
00168         if ( rc ) rc = file.WriteColor( m_emission );
00169         if ( rc ) rc = file.WriteColor( m_specular );
00170         if ( rc ) rc = file.WriteColor( m_reflection );
00171         if ( rc ) rc = file.WriteColor( m_transparent );
00172 
00173         if ( rc ) rc = file.WriteDouble( m_index_of_refraction );
00174         if ( rc ) rc = file.WriteDouble( m_reflectivity );
00175         if ( rc ) rc = file.WriteDouble( m_shine );
00176         if ( rc ) rc = file.WriteDouble( m_transparency );
00177 
00178         if ( !rc )
00179           break;
00180 
00181         // array of textures written in a way that user data persists
00182         rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
00183         if (rc)
00184         {
00185           int i, count = m_textures.Count();
00186           rc = file.WriteInt(count);
00187           for ( i = 0; i < count && rc; i++ )
00188           {
00189             rc = file.WriteObject(&m_textures[i]);
00190           }
00191           if ( !file.EndWrite3dmChunk() )
00192             rc = false;
00193         }
00194 
00195         //version 1.1 field
00196         if (rc) rc = file.WriteString(m_flamingo_library);
00197 
00198         // version 1.2 field (20061113)
00199         if (rc) rc = file.WriteArray(m_material_channel);
00200 
00201         // version 1.3 fields (20100917*)
00202         rc = file.WriteBool(m_bShared);
00203         if (!rc) break;
00204         rc = file.WriteBool(m_bDisableLighting);
00205         if (!rc) break;
00206 
00207         break;
00208       }
00209       if (!file.EndWrite3dmChunk() )
00210         rc = false;
00211     }
00212   }
00213   return rc;
00214 }
00215 
00216 bool ON_Material::WriteV3Helper( ON_BinaryArchive& file ) const
00217 {
00218   int i;
00219   // V2 and V3 file format
00220 
00221   bool rc = file.Write3dmChunkVersion(1,1);
00222   if ( rc ) rc = file.WriteColor( m_ambient );
00223   if ( rc ) rc = file.WriteColor( m_diffuse );
00224   if ( rc ) rc = file.WriteColor( m_emission );
00225   if ( rc ) rc = file.WriteColor( m_specular );
00226   if ( rc ) rc = file.WriteDouble( Shine() );
00227   if ( rc ) rc = file.WriteDouble( m_transparency );
00228 
00229   if ( rc ) rc = file.WriteChar( (unsigned char)1 ); // OBSOLETE // m_casts_shadows
00230   if ( rc ) rc = file.WriteChar( (unsigned char)1 ); // OBSOLETE // m_shows_shadows
00231 
00232   if ( rc ) rc = file.WriteChar( (unsigned char)0 ); // OBSOLETE // m_wire_mode
00233   if ( rc ) rc = file.WriteChar( (unsigned char)2 ); // OBSOLETE // m_wire_density
00234 
00235   if ( rc ) rc = file.WriteColor( ON_Color(0,0,0) ); // OBSOLETE // m_wire_color
00236 
00237   if (rc)
00238   {
00239     // OBSOLETE - line style info never used
00240     short s = 0;
00241     if (rc) rc = file.WriteShort(s);
00242     if (rc) rc = file.WriteShort(s);
00243     if (rc) rc = file.WriteDouble(0.0);
00244     if (rc) rc = file.WriteDouble(1.0);
00245   }  
00246 
00247   ON_wString filename;
00248   int j = 0;
00249   i = FindTexture( NULL, ON_Texture::bitmap_texture );
00250   if ( i >= 0 )
00251   {
00252     const ON_Texture& tmap = m_textures[i];
00253     if ( tmap.m_filename.Length() > 0  )
00254     {
00255       filename = tmap.m_filename;
00256       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
00257     }
00258   }
00259   // OBSOLETE // if ( rc ) rc = file.WriteString( TextureBitmapFileName() );
00260   // OBSOLETE // i = TextureMode();
00261   // OBSOLETE // if ( rc ) rc = file.WriteInt( i );
00262   // OBSOLETE // if ( rc ) rc = file.WriteInt( m_texture_bitmap_index );
00263   if ( rc ) rc = file.WriteString(filename);
00264   if ( rc ) rc = file.WriteInt( j );
00265   if ( rc ) rc = file.WriteInt( 0 );
00266 
00267   filename.Destroy();
00268   j = 0;
00269   double bump_scale = 1.0;
00270   i = FindTexture( NULL, ON_Texture::bump_texture );
00271   if ( i >= 0 )
00272   {
00273     const ON_Texture& tmap = m_textures[i];
00274     if ( tmap.m_filename.Length() > 0  )
00275     {
00276       filename = tmap.m_filename;
00277       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
00278       bump_scale = tmap.m_bump_scale[1];
00279     }
00280   }
00281   // OBSOLETE //if ( rc ) rc = file.WriteString( BumpBitmapFileName() );
00282   // OBSOLETE //i = BumpMode();
00283   // OBSOLETE //if ( rc ) rc = file.WriteInt( i );
00284   // OBSOLETE //if ( rc ) rc = file.WriteInt( m_bump_bitmap_index );
00285   // OBSOLETE //if ( rc ) rc = file.WriteDouble( m_bump_scale );
00286   if ( rc ) rc = file.WriteString( filename );
00287   if ( rc ) rc = file.WriteInt( j );
00288   if ( rc ) rc = file.WriteInt( 0 );
00289   if ( rc ) rc = file.WriteDouble( bump_scale );
00290 
00291   filename.Destroy();
00292   j = 0;
00293   i = FindTexture( NULL, ON_Texture::emap_texture );
00294   if ( i >= 0 )
00295   {
00296     const ON_Texture& tmap = m_textures[i];
00297     if ( tmap.m_filename.Length() > 0  )
00298     {
00299       filename = tmap.m_filename;
00300       j = ( ON_Texture::decal_texture == tmap.m_mode ) ? 2 : 1;
00301     }
00302   }
00303   // OBSOLETE //if ( rc ) rc = file.WriteString( EmapBitmapFileName() );
00304   // OBSOLETE //i = EmapMode();
00305   // OBSOLETE //if ( rc ) rc = file.WriteInt( i );
00306   // OBSOLETE //if ( rc ) rc = file.WriteInt( m_emap_bitmap_index );
00307   if ( rc ) rc = file.WriteString( filename );
00308   if ( rc ) rc = file.WriteInt( j );
00309   if ( rc ) rc = file.WriteInt( 0 );
00310 
00311   if ( rc ) rc = file.WriteInt( m_material_index );
00312 
00313   if ( rc ) rc = file.WriteUuid( m_plugin_id );
00314   if ( rc ) rc = file.WriteString( m_flamingo_library );
00315   if ( rc ) rc = file.WriteString( m_material_name );
00316 
00317 
00318   // 1.1 fields
00319   if (rc) rc = file.WriteUuid( m_material_id );
00320   if (rc) rc = file.WriteColor( m_reflection);
00321   if (rc) rc = file.WriteColor( m_transparent);
00322   if (rc) rc = file.WriteDouble( m_index_of_refraction );
00323 
00324   return rc;
00325 }
00326 
00327 ON_BOOL32 ON_Material::Read( ON_BinaryArchive& file )
00328 {
00329   Default();
00330   int major_version = 0;
00331   int minor_version = 0;
00332   bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
00333   if (rc)
00334   {
00335     if ( 1 == major_version )
00336     {
00337       rc = ReadV3Helper(file,minor_version);
00338     }
00339     else if ( 2 == major_version )
00340     {
00341       // fancy V4 material
00342       // V4 file format
00343 
00344       // The chunk version 2.0 prevents old V3 IO code from attempting
00345       // to read this material
00346       rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
00347       if (rc)
00348       {
00349         for(;;)
00350         {
00351           if ( rc ) rc = file.ReadUuid(m_material_id);
00352           if ( rc ) rc = file.ReadInt(&m_material_index);
00353           if ( rc ) rc = file.ReadString(m_material_name);
00354 
00355           if ( rc ) rc = file.ReadUuid(m_plugin_id);
00356 
00357           if ( rc ) rc = file.ReadColor( m_ambient );
00358           if ( rc ) rc = file.ReadColor( m_diffuse );
00359           if ( rc ) rc = file.ReadColor( m_emission );
00360           if ( rc ) rc = file.ReadColor( m_specular );
00361           if ( rc ) rc = file.ReadColor( m_reflection );
00362           if ( rc ) rc = file.ReadColor( m_transparent );
00363 
00364           if ( rc 
00365                && file.ArchiveOpenNURBSVersion() < 200912010 
00366                && 128 == m_transparent.Red() 
00367                && 128 == m_transparent.Green()
00368                && 128 == m_transparent.Blue()
00369                )
00370           {
00371             // Prior to 1 Dec 2009 the ON_Material::Defaults() set
00372             // m_transparent to 128,128,128.  This was the wrong
00373             // value for the default.  This "hack" is here to 
00374             // make it appear that the default was always white.
00375             m_transparent = m_diffuse;
00376           }
00377 
00378           if ( rc ) rc = file.ReadDouble( &m_index_of_refraction );
00379           if ( rc ) rc = file.ReadDouble( &m_reflectivity );
00380           if ( rc ) rc = file.ReadDouble( &m_shine );
00381           if ( rc ) rc = file.ReadDouble( &m_transparency );
00382 
00383           if ( !rc )
00384             break;
00385 
00386           // array of textures read in a way that user data persists
00387           int texmajver = 0;
00388           int texminver = 0;
00389           rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&texmajver,&texminver);
00390           if (rc)
00391           {
00392             if ( 1 == texmajver )
00393             {
00394               int i, count = 0;
00395               rc = file.ReadInt(&count);
00396               if (rc) m_textures.Reserve(count);
00397               for ( i = 0; i < count && rc; i++ )
00398               {
00399                 int trc = file.ReadObject(m_textures.AppendNew());
00400                 if ( trc <= 0 )
00401                   rc = false;
00402                 else if ( trc > 1 )
00403                   m_textures.Remove();
00404               }
00405             }
00406             if ( !file.EndRead3dmChunk() )
00407               rc = false;
00408           }
00409 
00410           if ( rc && minor_version >= 1 )
00411           {
00412             rc = file.ReadString(m_flamingo_library);
00413             if ( !rc ) break;
00414 
00415             if ( minor_version >= 2 )
00416             {
00417               // version 1.2 field (20061113)
00418               rc = file.ReadArray(m_material_channel);
00419               if (!rc) break;
00420 
00421               if ( minor_version >= 3 )
00422               {
00423                 // version 1.3 fields (20100917*)
00424                 rc = file.ReadBool(&m_bShared);
00425                 if (!rc) break;
00426                 rc = file.ReadBool(&m_bDisableLighting);
00427                 if (!rc) break;
00428               }
00429             }
00430 
00431           }
00432 
00433           break;
00434         }
00435         if (!file.EndRead3dmChunk() )
00436           rc = false;
00437       }
00438     }
00439   }
00440   return rc;
00441 }
00442 
00443 bool ON_Material::ReadV3Helper( ON_BinaryArchive& file, int minor_version )
00444 {
00445   double shine = 0.0, transparency = 0.0;
00446   int i, j;
00447   bool rc = true;
00448   {
00449     // common to all version 1.x formats
00450     if ( rc ) rc = file.ReadColor( m_ambient );
00451     if ( rc ) rc = file.ReadColor( m_diffuse );
00452     if ( rc ) rc = file.ReadColor( m_emission );
00453     if ( rc ) rc = file.ReadColor( m_specular );
00454     if ( rc ) rc = file.ReadDouble( &shine );
00455     if ( rc ) SetShine(shine);
00456     if ( rc ) rc = file.ReadDouble( &transparency );
00457     if ( rc ) SetTransparency(transparency);
00458 
00459     unsigned char obsolete_uc;
00460     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_casts_shadows
00461     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_shows_shadows
00462 
00463     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_mode
00464     if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_density
00465 
00466     ON_Color obsolete_color;
00467     if ( rc ) rc = file.ReadColor( obsolete_color ); // m_wire_color
00468 
00469     if (rc)
00470     {
00471       // OBSOLETE if ( rc ) rc = file.ReadLineStyle( m_wire_style );
00472       short s;
00473       double x;
00474       if (rc) rc = file.ReadShort(&s);
00475       if (rc) rc = file.ReadShort(&s);
00476       if (rc) rc = file.ReadDouble(&x);
00477       if (rc) rc = file.ReadDouble(&x);
00478     }
00479 
00480     ON_wString str;
00481 
00482     if ( rc ) rc = file.ReadString( str ); //sTextureBitmapFileName
00483     i = 0;
00484     j = 0;
00485     if ( rc ) rc = file.ReadInt( &i );
00486     // OBSOLETE // if ( rc ) SetTextureMode( ON::TextureMode(i) );
00487     if ( rc ) rc = file.ReadInt( &j );//&m_texture_bitmap_index
00488 
00489     if ( rc && !str.IsEmpty() )
00490     {
00491       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::bitmap_texture)];
00492       if ( 2 == i )
00493       {
00494         texture.m_mode = ON_Texture::decal_texture;
00495       }
00496       else
00497       {
00498         texture.m_mode = ON_Texture::modulate_texture;
00499       }
00500     }
00501 
00502     if ( rc ) rc = file.ReadString( str ); // sBumpBitmapFileName
00503     if ( rc ) rc = file.ReadInt( &i );
00504    // OBSOLETE // if ( rc ) SetBumpMode( ON::TextureMode(i) );
00505     if ( rc ) rc = file.ReadInt( &j );//&m_bump_bitmap_index );
00506     double bump_scale = 0.0;
00507     if ( rc ) rc = file.ReadDouble( &bump_scale );
00508 
00509     if ( rc && !str.IsEmpty() )
00510     {
00511       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::bump_texture)];
00512       if ( 2 == i )
00513       {
00514         texture.m_mode = ON_Texture::decal_texture;
00515       }
00516       else
00517       {
00518         texture.m_mode = ON_Texture::modulate_texture;
00519       }
00520       texture.m_bump_scale.Set(0.0,bump_scale);
00521     }
00522 
00523     if ( rc ) rc = file.ReadString( str ); // sEmapBitmapFileName
00524     if ( rc ) rc = file.ReadInt( &i );
00525     // OBSOLETE // if ( rc ) SetEmapMode( ON::TextureMode(i) );
00526     if ( rc ) rc = file.ReadInt( &j ); //&m_emap_bitmap_index;
00527 
00528     if ( rc && !str.IsEmpty() )
00529     {
00530       ON_Texture& texture = m_textures[AddTexture(str,ON_Texture::emap_texture)];
00531       if ( 2 == i )
00532       {
00533         texture.m_mode = ON_Texture::decal_texture;
00534       }
00535       else
00536       {
00537         texture.m_mode = ON_Texture::modulate_texture;
00538       }
00539     }
00540 
00541     if ( rc ) rc = file.ReadInt( &m_material_index );
00542 
00543     if ( rc ) rc = file.ReadUuid( m_plugin_id );
00544     if ( rc ) rc = file.ReadString( m_flamingo_library );
00545     if ( rc ) rc = file.ReadString( m_material_name );
00546 
00547     if ( minor_version >= 1 )
00548     {
00549       // 1.1 fields
00550       if (rc) rc = file.ReadUuid( m_material_id );
00551       if (rc) rc = file.ReadColor( m_reflection);
00552       if (rc) rc = file.ReadColor( m_transparent);
00553       if (rc) rc = file.ReadDouble( &m_index_of_refraction );
00554     }
00555     else
00556     {
00557       // old material needs a valid id.
00558       ON_CreateUuid(m_material_id);
00559     }
00560 
00561   }
00562 
00563   return rc;
00564 }
00565 
00566 ON::object_type ON_Material::ObjectType() const
00567 {
00568    return ON::material_object;
00569 }
00570 
00571 int ON_Material::FindTexture( const wchar_t* filename, 
00572                               ON_Texture::TYPE type,
00573                               int i0
00574                               ) const
00575 {
00576   int i, count = m_textures.Count();
00577   for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ )
00578   {
00579     if (    type != m_textures[i].m_type 
00580          && type != ON_Texture::no_texture_type )
00581     {
00582       continue;
00583     }
00584     if ( filename && m_textures[i].m_filename.CompareNoCase(filename) )
00585     {
00586       continue;
00587     }
00588     return i;
00589   }
00590   return -1;
00591 }
00592 
00593 int ON_Material::FindTexture( ON_UUID texture_id ) const
00594 {
00595   int i, count = m_textures.Count();
00596   for (i = 0; i < count; i++ )
00597   {
00598     if ( !ON_UuidCompare(&texture_id,&m_textures[i].m_texture_id) )
00599       return i;
00600   }
00601   return -1;
00602 }
00603 
00604 int ON_Material::DeleteTexture(const wchar_t* filename,ON_Texture::TYPE type )
00605 {
00606   int deleted_count = 0;
00607   int i;
00608 
00609   if ( !filename && type == ON_Texture::no_texture_type )
00610   {
00611     deleted_count = m_textures.Count();
00612     m_textures.Destroy();
00613   }
00614   else
00615   {
00616     for ( i = m_textures.Count()-1; i >= 0; i--)
00617     {
00618       if ( type != ON_Texture::no_texture_type && type != m_textures[i].m_type )
00619         continue;
00620       if ( filename && m_textures[i].m_filename.CompareNoCase(filename) )
00621         continue;
00622       m_textures.Remove(i);
00623       deleted_count++;
00624     }
00625   }
00626   return deleted_count;
00627 }
00628 
00629 int ON_Material::AddTexture( const ON_Texture& tx )
00630 {
00631   // has to copy user data
00632   int i = FindTexture( tx.m_filename, tx.m_type );
00633   if ( i < 0 )
00634   {
00635     i = m_textures.Count();
00636     m_textures.Append(tx);
00637   }
00638   else
00639   {
00640     m_textures[i] = tx;
00641   }
00642   if ( ON_UuidIsNil(m_textures[i].m_texture_id) )
00643   {
00644     ON_CreateUuid(m_textures[i].m_texture_id);
00645   }
00646 
00647   return i;
00648 }
00649 
00650 
00651 int ON_Material::AddTexture(const wchar_t* filename,ON_Texture::TYPE type)
00652 {
00653   int ti = FindTexture(NULL,type);
00654   if ( ti < 0 )
00655   {
00656     ti = m_textures.Count();
00657     m_textures.AppendNew();
00658   }
00659   if (ti >= 0 )
00660   {
00661     m_textures[ti].m_filename = filename;
00662     m_textures[ti].m_type = type;
00663     m_textures[ti].m_mode = ON_Texture::modulate_texture;
00664     m_textures[ti].m_magfilter = ON_Texture::linear_filter;
00665     ON_CreateUuid(m_textures[ti].m_texture_id);
00666   }
00667   return ti;
00668 }
00669 
00670 // Shine values are in range 0.0 to ON_Material::GetMaxShine()
00671 double ON_Material::Shine() const
00672 {
00673   return m_shine;
00674 }
00675 
00676 void ON_Material::SetShine( double shine )
00677 {
00678   if ( shine < 0.0 )
00679     m_shine = 0.0;
00680   else if ( shine > m_max_shine)
00681     m_shine = m_max_shine;
00682   else
00683     m_shine = (float)shine;
00684 }
00685 
00686   // Transparency values are in range 0.0 = opaque to 1.0 = transparent
00687 double ON_Material::Transparency( ) const
00688 {
00689   return  m_transparency;
00690 }
00691 
00692 void ON_Material::SetTransparency( double transparency )
00693 {
00694   if ( transparency < 0.0 )
00695     m_transparency = 0.0f;
00696   else if ( transparency > 1.0)
00697     m_transparency = 1.0f;
00698   else
00699     m_transparency = (float)transparency;
00700 }
00701 
00702 bool ON_Material::operator==( const ON_Material& src ) const
00703 {
00704   return Compare(src) ? false : true;
00705 }
00706 
00707 bool ON_Material::operator!=( const ON_Material& src ) const
00708 {
00709   return Compare(src) ? true : false;
00710 }
00711 
00712 static int CompareDouble( double a, double b )
00713 {
00714   return ( ( a < b ) ? -1 : ((a > b) ? 1 : 0) );
00715 }
00716 
00717 static int CompareXform( const ON_Xform& a, const ON_Xform& b )
00718 {
00719   int i,j;
00720   const double* da = &a.m_xform[0][0];
00721   const double* db = &b.m_xform[0][0];
00722   i = 16;
00723   j = 0;
00724   while ( i-- && !j)
00725   {
00726     j = CompareDouble(*da++,*db++);
00727   }
00728 
00729   return j;
00730 }
00731 
00732 static int CompareTextureDoubles( size_t count, const double* a, const double* b )
00733 {
00734   while ( count-- )
00735   {
00736     if ( *a < *b )
00737       return -1;
00738     if ( *a > *b )
00739       return  1;
00740     a++;
00741     b++;
00742   }
00743   return 0;
00744 }
00745 
00746 int ON_Texture::Compare( const ON_Texture& other ) const
00747 {
00748   int rc = ON_UuidCompare( &m_texture_id, &other.m_texture_id );
00749   while(!rc)
00750   {
00751     rc = m_mapping_channel_id - other.m_mapping_channel_id;
00752     if (rc) break;
00753 
00754     rc = m_filename.CompareNoCase(other.m_filename);    
00755     if (rc) break;
00756 
00757     rc = ((int)m_bOn) - ((int)other.m_bOn);
00758     if (rc) break;
00759 
00760     rc = ((int)m_type) - ((int)other.m_type);
00761     if (rc) break;
00762 
00763     rc = ((int)m_mode) - ((int)other.m_mode);
00764     if (rc) break;
00765 
00766     rc = ((int)m_minfilter) - ((int)other.m_minfilter);
00767     if (rc) break;
00768 
00769     rc = ((int)m_magfilter) - ((int)other.m_magfilter);
00770     if (rc) break;
00771 
00772     rc = ((int)m_wrapu) - ((int)other.m_wrapu);
00773     if (rc) break;
00774 
00775     rc = ((int)m_wrapv) - ((int)other.m_wrapv);
00776     if (rc) break;
00777 
00778     rc = ((int)m_wrapw) - ((int)other.m_wrapw);
00779     if (rc) break;
00780 
00781     rc = CompareXform(m_uvw, other.m_uvw);
00782     if (rc) break;
00783 
00784     rc = m_border_color.Compare(other.m_border_color);
00785     if (rc) break;
00786 
00787     rc = m_transparent_color.Compare(other.m_transparent_color);
00788     if (rc) break;
00789 
00790     rc = m_bump_scale.Compare(other.m_bump_scale);
00791     if (rc) break;
00792 
00793     rc = CompareTextureDoubles( 1, &m_blend_constant_A, &other.m_blend_constant_A );
00794     if (rc) break;
00795 
00796     rc = CompareTextureDoubles( sizeof(m_blend_A)/sizeof(m_blend_A[0]), m_blend_A, other.m_blend_A);
00797     if (rc) break;
00798 
00799     rc = CompareTextureDoubles( sizeof(m_blend_RGB)/sizeof(m_blend_RGB[0]), m_blend_RGB, other.m_blend_RGB);
00800     if (rc) break;
00801 
00802     break;
00803   }
00804 
00805   return rc;
00806 }
00807 
00808 int ON_Material::Compare( const ON_Material& other ) const
00809 {
00810   // do NOT test m_material_index
00811 
00812   int rc = ON_UuidCompare( &m_material_id, &other.m_material_id );
00813   while(!rc)
00814   {
00815     rc = m_material_name.CompareNoCase( other.m_material_name );
00816     if (rc) break;
00817 
00818     rc = m_ambient.Compare(other.m_ambient);
00819     if (rc) break;
00820 
00821     rc = m_diffuse.Compare( other.m_diffuse );
00822     if (rc) break;
00823 
00824     rc = m_diffuse.Compare( other.m_diffuse );
00825     if (rc) break;
00826 
00827     rc = m_emission.Compare( other.m_emission );
00828     if (rc) break;
00829 
00830     rc = m_specular.Compare( other.m_specular );
00831     if (rc) break;
00832 
00833     rc = m_reflection.Compare( other.m_reflection );
00834     if (rc) break;
00835 
00836     rc = m_transparent.Compare( other.m_transparent );
00837     if (rc) break;
00838 
00839     rc = CompareDouble(m_index_of_refraction,other.m_index_of_refraction);
00840     if (rc) break;
00841 
00842     rc = CompareDouble(m_reflectivity,other.m_reflectivity);
00843     if (rc) break;
00844 
00845     rc = CompareDouble(m_shine,other.m_shine);
00846     if (rc) break;
00847 
00848     rc = CompareDouble(m_transparency,other.m_transparency);
00849     if (rc) break;
00850 
00851     rc = ON_UuidCompare( &m_plugin_id, &other.m_plugin_id );
00852     if (rc) break;
00853 
00854     const int tcount = m_textures.Count();
00855     rc = tcount - other.m_textures.Count();
00856     int i;
00857     for ( i = 0; i < tcount && !rc; i++ )
00858     {
00859       rc = m_textures[i].Compare( other.m_textures[i] );
00860     }
00861     if (rc) break;
00862 
00863 
00864 
00865     break;
00866   }
00867 
00868   return rc;  
00869 }
00870 
00871 ON_Color ON_Material::Ambient() const
00872 {
00873   return m_ambient & 0x00FFFFFF;
00874 }
00875 
00876 ON_Color ON_Material::Diffuse( ) const
00877 {
00878   return m_diffuse & 0x00FFFFFF;
00879 }
00880 
00881 ON_Color ON_Material::Emission( ) const
00882 {
00883   return m_emission & 0x00FFFFFF;
00884 }
00885 
00886 ON_Color ON_Material::Specular() const
00887 {
00888   return m_specular & 0x00FFFFFF;
00889 }
00890 
00891 void ON_Material::SetAmbient( ON_Color  c )
00892 {
00893   m_ambient = c;
00894 }
00895 
00896 void ON_Material::SetDiffuse(  ON_Color c )
00897 {
00898   m_diffuse = c ;
00899 }
00900 
00901 void ON_Material::SetEmission( ON_Color c )
00902 {
00903   m_emission = c ;
00904 }
00905 
00906 void ON_Material::SetSpecular( ON_Color c )
00907 {
00908   m_specular = c;
00909 }
00910 
00911 int ON_Material::MaterialIndex() const
00912 {
00913   return m_material_index;
00914 }
00915 
00916 void ON_Material::SetMaterialIndex( int i )
00917 {
00918   m_material_index = i;
00919 }
00920 
00921 const wchar_t* ON_Material::MaterialName( ) const
00922 {
00923         return m_material_name;
00924 }
00925 
00926 void ON_Material::SetMaterialName( const wchar_t* sMaterialName )
00927 {
00928   m_material_name = sMaterialName;
00929 }
00930 
00932 //   Class ON_Texture
00934 
00935 ON_OBJECT_IMPLEMENT(ON_Texture,ON_Object,"D6FF106D-329B-4f29-97E2-FD282A618020");
00936 
00937 ON_Texture::ON_Texture()
00938 {
00939   Default(); // used to set defaults
00940 }
00941 
00942 ON_Texture::~ON_Texture()
00943 {
00944 }
00945 
00946 ON_BOOL32 ON_Texture::IsValid( ON_TextLog* text_log ) const
00947 {
00948   if ( no_texture_type == m_type || force_32bit_texture_type == m_type )
00949   {
00950     if ( text_log )
00951     {
00952       text_log->Print("ON_Texture m_type has invalid value.\n");
00953     }
00954     return false;
00955   }
00956 
00957   // TODO ...
00958 
00959   return true;
00960 }
00961 
00962 // overrides virtual ON_Object::Dump
00963 void ON_Texture::Dump( ON_TextLog& ) const
00964 {
00965 
00966 }
00967 
00968 // overrides virtual ON_Object::SizeOf
00969 unsigned int ON_Texture::SizeOf() const
00970 {
00971   unsigned int sz = ON_Object::SizeOf();
00972   sz += sizeof(*this) - sizeof(ON_Object);
00973   sz += m_filename.Length()*sizeof(wchar_t);
00974   return sz;
00975 }
00976 
00977 // overrides virtual ON_Object::Write
00978 ON_BOOL32 ON_Texture::Write(
00979         ON_BinaryArchive& binary_archive
00980       ) const
00981 {
00982   bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
00983   if (rc)
00984   {
00985 
00986     for(;;)
00987     {
00988       // 1.0 values
00989       rc = binary_archive.WriteUuid(m_texture_id);
00990       if (!rc) break;
00991       rc = binary_archive.WriteInt(m_mapping_channel_id);
00992       if (!rc) break;
00993       rc = binary_archive.WriteString(m_filename);
00994       if (!rc) break;
00995       rc = binary_archive.WriteBool(m_bOn);
00996       if (!rc) break;
00997       rc = binary_archive.WriteInt(m_type);
00998       if (!rc) break;
00999       rc = binary_archive.WriteInt(m_mode);
01000       if (!rc) break;
01001       rc = binary_archive.WriteInt(m_minfilter);
01002       if (!rc) break;
01003       rc = binary_archive.WriteInt(m_magfilter);
01004       if (!rc) break;
01005       rc = binary_archive.WriteInt(m_wrapu);
01006       if (!rc) break;
01007       rc = binary_archive.WriteInt(m_wrapv);
01008       if (!rc) break;
01009       rc = binary_archive.WriteInt(m_wrapw);
01010       if (!rc) break;
01011       rc = binary_archive.WriteXform(m_uvw);
01012       if (!rc) break;
01013       rc = binary_archive.WriteColor(m_border_color);
01014       if (!rc) break;
01015       rc = binary_archive.WriteColor(m_transparent_color);
01016       if (!rc) break;
01017       rc = binary_archive.WriteUuid(m_transparency_texture_id);
01018       if (!rc) break;
01019       rc = binary_archive.WriteInterval(m_bump_scale);
01020       if (!rc) break;
01021       rc = binary_archive.WriteDouble(m_blend_constant_A);
01022       if (!rc) break;
01023       rc = binary_archive.WriteDouble(4,m_blend_A);
01024       if (!rc) break;
01025       rc = binary_archive.WriteColor(m_blend_constant_RGB);
01026       if (!rc) break;
01027       rc = binary_archive.WriteDouble(4,m_blend_RGB);
01028       if (!rc) break;
01029       rc = binary_archive.WriteInt(m_blend_order);
01030       if (!rc) break;
01031 
01032       break;
01033     }
01034 
01035 
01036     if ( !binary_archive.EndWrite3dmChunk() )
01037       rc = false;
01038   }
01039   return rc;
01040 }
01041 
01042 ON_Texture::TYPE ON_Texture::TypeFromInt( int i )
01043 {
01044   ON_Texture::TYPE type = no_texture_type;
01045 
01046   switch((unsigned int)i)
01047   {
01048   case no_texture_type:
01049     type = no_texture_type;
01050     break;
01051   case bitmap_texture:
01052     type = bitmap_texture;
01053     break;
01054   case bump_texture:
01055     type = bump_texture;
01056     break;
01057   case emap_texture:
01058     type = emap_texture;
01059     break;
01060   case transparency_texture:
01061     type = transparency_texture;
01062     break;
01063   case force_32bit_texture_type:
01064     type = force_32bit_texture_type;
01065     break;
01066   }
01067 
01068   return type;
01069 }
01070 
01071 ON_Texture::MODE ON_Texture::ModeFromInt( int i )
01072 {
01073   ON_Texture::MODE mode = no_texture_mode;
01074   switch((unsigned int)i)
01075   {
01076   case no_texture_mode:
01077     mode = no_texture_mode;
01078     break;
01079   case modulate_texture:
01080     mode = modulate_texture;
01081     break;
01082   case decal_texture:
01083     mode = decal_texture;
01084     break;
01085   case blend_texture:
01086     mode = blend_texture;
01087     break;
01088   case force_32bit_texture_mode:
01089     mode = force_32bit_texture_mode;
01090     break;
01091   }
01092   return mode;
01093 }
01094 
01095 ON_Texture::FILTER ON_Texture::FilterFromInt( int i )
01096 {
01097   ON_Texture::FILTER filter = linear_filter;
01098   switch((unsigned int)i)
01099   {
01100   case nearest_filter:
01101     filter = nearest_filter;
01102     break;
01103   case linear_filter:
01104     filter = linear_filter;
01105     break;
01106   case force_32bit_texture_filter:
01107     filter = force_32bit_texture_filter;
01108     break;
01109   }
01110   return filter;
01111 }
01112 
01113 ON_Texture::WRAP ON_Texture::WrapFromInt( int i )
01114 {
01115   ON_Texture::WRAP wrap = repeat_wrap;
01116 
01117   switch((unsigned int)i)
01118   {
01119   case repeat_wrap:
01120     wrap = repeat_wrap;
01121     break;
01122   case clamp_wrap:
01123     wrap = clamp_wrap;
01124     break;
01125   case force_32bit_texture_wrap:
01126     wrap = force_32bit_texture_wrap;
01127     break;
01128   }
01129 
01130   return wrap;
01131 }
01132 
01133 
01134 
01135 
01136 
01137 // overrides virtual ON_Object::Read
01138 ON_BOOL32 ON_Texture::Read(
01139         ON_BinaryArchive& binary_archive
01140       )
01141 {
01142   Default();
01143 
01144   int major_version = 0;
01145   int minor_version = 0;
01146   bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
01147   if (rc)
01148   {
01149 
01150     if ( 1 != major_version )
01151     {
01152       rc = false;
01153     }
01154     else
01155     {
01156       int i;
01157       for(;;)
01158       {
01159         // 1.0 values
01160         rc = binary_archive.ReadUuid( m_texture_id );
01161         if (!rc) break;
01162 
01163         rc = binary_archive.ReadInt( &m_mapping_channel_id );
01164         if (!rc) break;
01165 
01166         rc = binary_archive.ReadString(m_filename);
01167         if (!rc) break;
01168 
01169         rc = binary_archive.ReadBool(&m_bOn);
01170         if (!rc) break;
01171 
01172         rc = binary_archive.ReadInt(&i);
01173         if (!rc) break;
01174         m_type = ON_Texture::TypeFromInt(i);
01175 
01176         rc = binary_archive.ReadInt(&i);
01177         if (!rc) break;
01178         m_mode = ON_Texture::ModeFromInt(i);
01179 
01180         rc = binary_archive.ReadInt(&i);
01181         if (!rc) break;
01182         m_minfilter = ON_Texture::FilterFromInt(i);
01183 
01184         rc = binary_archive.ReadInt(&i);
01185         if (!rc) break;
01186         m_magfilter = ON_Texture::FilterFromInt(i);
01187 
01188         rc = binary_archive.ReadInt(&i);
01189         if (!rc) break;
01190         m_wrapu = ON_Texture::WrapFromInt(i);
01191 
01192         rc = binary_archive.ReadInt(&i);
01193         if (!rc) break;
01194         m_wrapv = ON_Texture::WrapFromInt(i);
01195 
01196         rc = binary_archive.ReadInt(&i);
01197         if (!rc) break;
01198         m_wrapw = ON_Texture::WrapFromInt(i);
01199 
01200         rc = binary_archive.ReadXform(m_uvw);
01201         if (!rc) break;
01202 
01203         rc = binary_archive.ReadColor(m_border_color);
01204         if (!rc) break;
01205 
01206         rc = binary_archive.ReadColor(m_transparent_color);
01207         if (!rc) break;
01208 
01209         rc = binary_archive.ReadUuid(m_transparency_texture_id);
01210         if (!rc) break;
01211 
01212         rc = binary_archive.ReadInterval(m_bump_scale);
01213         if (!rc) break;
01214 
01215         rc = binary_archive.ReadDouble(&m_blend_constant_A);
01216         if (!rc) break;
01217         rc = binary_archive.ReadDouble(4,m_blend_A);
01218         if (!rc) break;
01219         rc = binary_archive.ReadColor(m_blend_constant_RGB);
01220         if (!rc) break;
01221         rc = binary_archive.ReadDouble(4,m_blend_RGB);
01222         if (!rc) break;
01223 
01224         rc = binary_archive.ReadInt(&m_blend_order);
01225         if (!rc) break;
01226 
01227 
01228 
01229         break;
01230       }
01231     }
01232 
01233     if ( !binary_archive.EndRead3dmChunk() )
01234       rc = false;
01235   }
01236   return rc;
01237 }
01238 
01239 
01240 void ON_Texture::Default()
01241 {
01242   PurgeUserData();
01243   m_texture_id = ON_nil_uuid;
01244   m_mapping_channel_id = 0;
01245   m_filename.Destroy();
01246   m_filename_bRelativePath = false;
01247   m_bOn = true;
01248   m_type = bitmap_texture;
01249   m_mode = modulate_texture;
01250   m_minfilter = linear_filter;
01251   m_magfilter = linear_filter;
01252   m_wrapu = repeat_wrap;
01253   m_wrapv = repeat_wrap;
01254   m_wrapw = repeat_wrap;
01255   m_uvw.Identity();
01256   m_border_color = ON_UNSET_COLOR;
01257   m_transparent_color = ON_UNSET_COLOR;
01258   m_transparency_texture_id = ON_nil_uuid;
01259   m_bump_scale.Set(0.0,1.0);
01260   m_blend_constant_A = 1.0;
01261   m_blend_A[0] = m_blend_A[1] = 1.0; m_blend_A[2] =  m_blend_A[3] = 0.0;
01262   m_blend_constant_RGB.SetRGB(0,0,0);
01263   m_blend_RGB[0] = m_blend_RGB[1] = 1.0; m_blend_RGB[2] = m_blend_RGB[3] = 0.0;
01264   m_blend_order = 0;
01265   m_runtime_ptr_id = ON_nil_uuid;
01266   m_runtime_ptr = 0;
01267 }
01268 
01269 
01270 ON_OBJECT_IMPLEMENT(ON_TextureMapping,ON_Object,"32EC997A-C3BF-4ae5-AB19-FD572B8AD554");
01271 
01272 
01273 ON_TextureMapping::TYPE ON_TextureMapping::TypeFromInt(int i)
01274 {
01275   ON_TextureMapping::TYPE t;
01276   switch(i)
01277   {
01278   case srfp_mapping:
01279     t = srfp_mapping;
01280     break;
01281   case plane_mapping:
01282     t = plane_mapping;
01283     break;
01284   case cylinder_mapping:
01285     t = cylinder_mapping;
01286     break;
01287   case sphere_mapping:
01288     t = sphere_mapping;
01289     break;
01290   case box_mapping:
01291     t = box_mapping;
01292     break;
01293   case mesh_mapping_primitive:
01294     t = mesh_mapping_primitive;
01295     break;
01296   case srf_mapping_primitive:
01297     t = srf_mapping_primitive;
01298     break;
01299   case brep_mapping_primitive:
01300     t = brep_mapping_primitive;
01301     break;
01302   default:
01303     t = no_mapping;
01304     break;
01305   }
01306   return t;
01307 }
01308 
01309 ON_TextureMapping::PROJECTION ON_TextureMapping::ProjectionFromInt(int i)
01310 {
01311   ON_TextureMapping::PROJECTION p;
01312   switch(i)
01313   {
01314   case clspt_projection:
01315     p = clspt_projection;
01316     break;
01317   case ray_projection:
01318     p = ray_projection;
01319     break;
01320   default:
01321     p = no_projection;
01322     break;
01323   }
01324   return p;
01325 }
01326 
01327 ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromInt(int i)
01328 {
01329         ON_TextureMapping::TEXTURE_SPACE ts = single;
01330 
01331         switch(i)
01332         {
01333         case single:
01334                 ts = single;
01335                 break;
01336         case divided:
01337                 ts = divided;
01338                 break;
01339         }
01340         return ts;
01341 }
01342 
01343 ON_TextureMapping::ON_TextureMapping()
01344 {
01345   m_mapping_primitive = 0;
01346   Default();
01347 }
01348 
01349 ON_TextureMapping::~ON_TextureMapping()
01350 {
01351   if ( m_mapping_primitive )
01352   {
01353     delete m_mapping_primitive;
01354     m_mapping_primitive = 0;
01355   }
01356 }
01357 
01358 // The copy constructor and operator= overrides are needed
01359 // to ensure m_mapping_primitive is properly copied.
01360 ON_TextureMapping::ON_TextureMapping(const ON_TextureMapping& src)
01361                   : ON_Object(src)
01362 {
01363   m_mapping_id    = src.m_mapping_id;
01364   m_mapping_index = src.m_mapping_index;
01365   m_mapping_name  = src.m_mapping_name;
01366   m_type          = src.m_type;
01367   m_projection    = src.m_projection;
01368   m_bCapped                 = src.m_bCapped;
01369         m_texture_space = src.m_texture_space;
01370   m_Pxyz          = src.m_Pxyz;
01371   m_Nxyz          = src.m_Nxyz;
01372   m_uvw           = src.m_uvw;
01373   m_mapping_primitive = ( src.m_mapping_primitive )
01374                       ? src.m_mapping_primitive->Duplicate()
01375                       : 0;
01376 }
01377 
01378 ON_TextureMapping& ON_TextureMapping::operator=(const ON_TextureMapping& src)
01379 {
01380   if ( this != &src )
01381   {
01382     if ( m_mapping_primitive )
01383     {
01384       delete m_mapping_primitive;
01385       m_mapping_primitive = 0;
01386     }
01387     ON_Object::operator=(src);
01388     m_mapping_id    = src.m_mapping_id;
01389     m_mapping_index = src.m_mapping_index;
01390     m_mapping_name  = src.m_mapping_name;
01391     m_type          = src.m_type;
01392     m_projection    = src.m_projection;
01393     m_bCapped                     = src.m_bCapped;
01394                 m_texture_space = src.m_texture_space;
01395     m_Pxyz          = src.m_Pxyz;
01396     m_Nxyz          = src.m_Nxyz;
01397     m_uvw           = src.m_uvw;
01398     if ( src.m_mapping_primitive )
01399       m_mapping_primitive = src.m_mapping_primitive->Duplicate();
01400   }
01401   return *this;
01402 }
01403 
01404 void ON_TextureMapping::Default()
01405 {
01406   PurgeUserData();
01407   if ( m_mapping_primitive )
01408   {
01409     delete m_mapping_primitive;
01410     m_mapping_primitive = 0;
01411   }
01412 
01413   m_mapping_id = ON_nil_uuid;
01414   m_mapping_index = 0;
01415   m_mapping_name.Destroy();
01416   m_type = no_mapping;
01417   m_projection = no_projection;
01418   m_texture_space = single;
01419   m_Pxyz.Identity();
01420   m_Nxyz.Identity();
01421   m_uvw.Identity();
01422   m_bCapped = false;
01423 }
01424 
01425 ON_BOOL32 ON_TextureMapping::IsValid( ON_TextLog* text_log ) const
01426 {
01427   if ( m_type != ON_TextureMapping::TypeFromInt(m_type) )
01428   {
01429     if ( text_log )
01430     {
01431       text_log->Print("ON_TextureMapping m_type = %d is not a valid value.\n",m_type);
01432     }
01433     return false;
01434   }
01435 
01436   if ( m_projection != ON_TextureMapping::ProjectionFromInt(m_projection) )
01437   {
01438     if ( text_log )
01439     {
01440       text_log->Print("ON_TextureMapping m_projection = %d is not a valid value.\n",m_projection);
01441     }
01442     return false;
01443   }
01444 
01445   if (m_texture_space != ON_TextureMapping::TextureSpaceFromInt(m_texture_space))
01446   {
01447           if (text_log)
01448           {
01449                   text_log->Print("ON_TextureMapping m_texture_space = %d is not a valid value.\n",m_texture_space);
01450           }
01451           return false;
01452   }
01453 
01454   return true;
01455 }
01456 
01457 void ON_TextureMapping::Dump( ON_TextLog& text_log ) const
01458 {
01459   text_log.Print("Texture mapping id: "); text_log.Print(m_mapping_id); text_log.Print("\n");
01460   text_log.PushIndent();
01461 
01462   text_log.Print("type: ");
01463   switch(m_type)
01464   {
01465   case no_mapping:
01466     text_log.Print("no mapping\n");
01467     break;
01468   case plane_mapping:
01469     text_log.Print("plane mapping\n");
01470     break;
01471   case cylinder_mapping:
01472     text_log.Print("cylinder mapping\n");
01473     break;
01474   case sphere_mapping:
01475     text_log.Print("sphere mapping\n");
01476     break;
01477         case box_mapping:
01478     text_log.Print("box mapping\n");
01479     break;
01480   default:
01481     text_log.Print("%d\n",m_type);
01482     break;
01483   }
01484 
01485   text_log.Print("projection: ");
01486   switch(m_projection)
01487   {
01488   case no_projection:
01489     text_log.Print("no projection\n");
01490     break;
01491   case clspt_projection:
01492     text_log.Print("closest point to mesh vertex\n");
01493     break;
01494   case ray_projection:
01495     text_log.Print("mesh normal ray intersection\n");
01496     break;
01497   default:
01498     text_log.Print("%d\n",m_projection);
01499     break;
01500   }
01501 
01502         text_log.Print("texture_space: ");
01503   switch(m_texture_space)
01504   {
01505   case single:
01506     text_log.Print("single texture space\n");
01507     break;
01508   case clspt_projection:
01509     text_log.Print("divided texture space\n");
01510     break;
01511   default:
01512     text_log.Print("%d\n",m_texture_space);
01513     break;
01514   }
01515 
01516   text_log.Print("XYZ point transformation:\n");
01517   text_log.PushIndent();
01518   text_log.Print(m_Pxyz);
01519   text_log.PopIndent();
01520 
01521   text_log.Print("XYZ normal transformation:\n");
01522   text_log.PushIndent();
01523   text_log.Print(m_Nxyz);
01524   text_log.PopIndent();
01525 
01526   text_log.Print("UVW transformation:\n");
01527   text_log.PushIndent();
01528   text_log.Print(m_uvw);
01529   text_log.PopIndent();
01530 
01531   text_log.PopIndent();
01532 }
01533 
01534 unsigned int ON_TextureMapping::SizeOf() const
01535 {
01536   unsigned int sz = ON_Object::SizeOf();
01537   sz = sizeof(*this) - sizeof(ON_Object);
01538   return sz;
01539 }
01540 
01541 bool ON_TextureMapping::ReverseTextureCoordinate( int dir )
01542 {
01543   bool rc = false;
01544   if ( 0 <= dir && dir <= 3 )
01545   {
01546     ON_Xform x(1.0);
01547     x.m_xform[dir][dir] = -1.0;
01548     x.m_xform[dir][3] = 1.0;
01549     m_uvw = x*m_uvw;
01550     rc = true;
01551   }
01552   return rc;
01553 }
01554 
01555 bool ON_TextureMapping::SwapTextureCoordinate( int i, int j )
01556 {
01557   bool rc = false;
01558   if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
01559   {
01560     ON_Xform x(1.0);
01561     x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
01562     x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
01563     m_uvw = x*m_uvw;
01564     rc = true;
01565   }
01566   return rc;
01567 }
01568 
01569 bool ON_TextureMapping::TileTextureCoordinate( int dir, double count, double offset )
01570 {
01571   bool rc = false;
01572   if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
01573   {
01574     ON_Xform x(1.0);
01575     x.m_xform[dir][dir] = count;
01576     x.m_xform[dir][3] = offset;
01577     m_uvw = x*m_uvw;
01578     rc = true;
01579   }
01580   return rc;
01581 }
01582 
01583 
01584 bool ON_Texture::ReverseTextureCoordinate( int dir )
01585 {
01586   bool rc = false;
01587   if ( 0 <= dir && dir <= 3 )
01588   {
01589     ON_Xform x(1.0);
01590     x.m_xform[dir][dir] = -1.0;
01591     x.m_xform[dir][3] = 1.0;
01592     m_uvw = x*m_uvw;
01593     rc = true;
01594   }
01595   return rc;
01596 }
01597 
01598 bool ON_Texture::SwapTextureCoordinate( int i, int j )
01599 {
01600   bool rc = false;
01601   if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
01602   {
01603     ON_Xform x(1.0);
01604     x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
01605     x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
01606     m_uvw = x*m_uvw;
01607     rc = true;
01608   }
01609   return rc;
01610 }
01611 
01612 bool ON_Texture::TileTextureCoordinate( int dir, double count, double offset )
01613 {
01614   bool rc = false;
01615   if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
01616   {
01617     ON_Xform x(1.0);
01618     x.m_xform[dir][dir] = count;
01619     x.m_xform[dir][3] = offset;
01620     m_uvw = x*m_uvw;
01621     rc = true;
01622   }
01623   return rc;
01624 }
01625 
01626 bool ON_Texture::IsTiled( int dir, double* count, double* offset ) const
01627 {
01628   if ( count )
01629     *count = 1.0;
01630   if ( offset )
01631     *offset = 0.0;
01632 
01633   if ( 0 <= dir && dir <= 3 )
01634   {
01635     int row0=-1, row, col;
01636     for ( row = 0; row < 3; row++ )
01637     {
01638       for ( col = 0; col < 3; col++ )
01639       {
01640         if ( col != dir && 0.0 != m_uvw.m_xform[row][col] )
01641           break;
01642       }
01643       if ( 3 == col )
01644       {
01645         if ( -1 == row0 )
01646         {
01647           row0 = row;
01648         }
01649         else
01650           return false;
01651       }
01652     }
01653     if ( row0 >= 0 )
01654     {
01655       if (count)
01656         *count = m_uvw.m_xform[row0][dir];
01657       if ( offset )
01658         *offset = m_uvw.m_xform[row0][3];
01659       return true;
01660     }
01661   }
01662 
01663   return false;
01664 }
01665 
01666 static const double on__overflow_tol = 1.0e100;
01667 
01668 static
01669 int BestHitHelper(double t0, double t1)
01670 {
01671   return ((t0 < 0.0 && t1 > t0) || (0.0 <= t1 && t1 < t0)) ? 1 : 0;
01672 }
01673 
01674 static
01675 int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, double* s)
01676 {
01677   /*
01678   returns:
01679     0 = ray parallel to sides
01680     1 = ray hit left side (x=-1)
01681     2 = ray hit right side (x=+1)
01682     3 = ray hit back side (y=-1)
01683     4 = ray hit front side (y=+1)
01684     5 = ray hit bottom side (z=-1)
01685     6 = ray hit top side (z=+1)
01686   */
01687   double nx = (&n.x)[dir];
01688   ON_3dPoint Q;
01689   double t,t0,t1;
01690 
01691   // protect against overflow
01692   t = fabs(nx)*on__overflow_tol;
01693   t0 = (-1.0 - (&rst.x)[dir]);
01694   t1 = ( 1.0 - (&rst.x)[dir]);
01695   if ( fabs(t0) >= t || fabs(t1) >= t )
01696   {
01697     *s = ON_UNSET_VALUE;
01698     return 0;
01699   }
01700 
01701   t0 /= nx;
01702   Q = rst + t0*n;
01703   if ( dir )
01704   {
01705     t = Q.x;
01706     Q.x = Q[dir];
01707     Q[dir] = t;
01708   }
01709   if ( fabs(Q.x+1.0) > ON_SQRT_EPSILON
01710         || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
01711         || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
01712         )
01713   {
01714     // The ray's intersection with the plane missed the 
01715     // (-1,+1)x(-1,+1) square that is the side of the box.
01716     t0 = ON_UNSET_VALUE;
01717   }
01718 
01719   t1 /= nx;
01720   Q = rst + t1*n;
01721   if ( dir )
01722   {
01723     t = Q.x;
01724     Q.x = Q[dir];
01725     Q[dir] = t;
01726   }
01727   if ( fabs(Q.x-1.0) > ON_SQRT_EPSILON
01728         || Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
01729         || Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
01730         )
01731   {
01732     // The ray's intersection with the plane missed the 
01733     // (-1,+1)x(-1,+1) square that is the side of the box.
01734     t1 = ON_UNSET_VALUE;
01735     if ( ON_UNSET_VALUE == t0 )
01736     {
01737       *s = ON_UNSET_VALUE;
01738       return 0;
01739     }
01740   }
01741 
01742   int rc;
01743   if ( ON_UNSET_VALUE == t0 || 1 == BestHitHelper(t0,t1) )
01744   {
01745     rc = 2 + 2*dir;
01746     *s = t1;
01747   }
01748   else
01749   {
01750     rc = 1 + 2*dir;
01751     *s = t0;
01752   }
01753   return rc;
01754 }
01755 
01756 
01757 int ON_TextureMapping::EvaluatePlaneMapping( 
01758   const ON_3dPoint& P,
01759   const ON_3dVector& N,
01760   ON_3dPoint* T
01761   ) const
01762 {
01763   // The matrix m_Pxyz transforms the world  coordinate
01764   // "mapping rectangle" into the rectangle
01765   //   -1 <= r <= 1, -1 <= s <= 1, and  (-1 <= t <= 1)
01766 
01767   ON_3dPoint rst(m_Pxyz*P);
01768 
01769   if ( ray_projection == m_projection )
01770   {
01771     ON_3dVector n(m_Nxyz*N);
01772     if ( fabs(rst.z) < fabs(n.z)*on__overflow_tol )
01773     {
01774       double t = -rst.z/n.z;
01775       rst.x = rst.x + t*n.x;
01776       rst.y = rst.y + t*n.y;
01777     }
01778   }
01779 
01780   // convert -1 <= r <= 1, -1 <= s <= 1
01781   // to normalized texture coordinate
01782         rst.x = 0.5*rst.x + 0.5;
01783         rst.y = 0.5*rst.y + 0.5;
01784 
01785   // Apply texture coordinate transformation 
01786   *T = m_uvw*rst;
01787 
01788   //See docs - if m_bCapped is false, then planar is truely flat.
01789   if (!m_bCapped)
01790           T->z = 0.0;
01791 
01792   return 1;
01793 }
01794 
01795 int ON_TextureMapping::EvaluateSphereMapping( 
01796                                                                                           const ON_3dPoint& P,
01797                                                                                           const ON_3dVector& N,
01798                                                                                           ON_3dPoint* T
01799                                                                                           ) const
01800 {
01801   // The matrix m_Pxyz transforms the world coordinate
01802   // "mapping sphere" into the sphere centered at
01803   // rst = (0,0,0) with radius 1.0.
01804 
01805   ON_3dPoint rst(m_Pxyz*P);
01806         const double r = ((const ON_3dVector*)(&rst.x))->Length();
01807         double t0, t1;
01808         
01809         if ( ray_projection == m_projection )
01810         {
01811                 ON_3dVector n(m_Nxyz*N);
01812                 // Shoot a ray from P in the direction N and see if it 
01813                 // hits the sphere.
01814                 int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z), 
01815                         2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z), 
01816                         (rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0, 
01817                         &t0, &t1 );
01818                 if (rc >= 0 )
01819                 {
01820                         if ( 2 != rc && 1 == BestHitHelper(t0,t1) )
01821                         {
01822                                 t0 = t1;
01823                         }
01824                         rst = rst + t0*n;
01825                 }
01826         }
01827         
01828         // convert sphere 3d location to longitude, latitude, radius
01829         double longitude = (0.0 != rst.y || 0.0 != rst.x) 
01830                 ? atan2(rst.y,rst.x) 
01831                 : 0.0;
01832         double latitude = (0.0 != rst.z) 
01833                 ? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length()) 
01834                 : 0.0;
01835         if ( latitude > ON_PI )
01836                 latitude -= 2.0*ON_PI;
01837         
01838   // convert longitude to normalized texture coordinate
01839         rst.x = 0.5*longitude/ON_PI;
01840         if ( rst.x < -ON_EPSILON )
01841                 rst.x += 1.0;
01842         else if (rst.x < 0.0)
01843                 rst.x = 0.0;
01844         else if (rst.x > 1.0)
01845                 rst.x = 1.0;
01846 
01847   // convert longitude to normalized texture coordinate
01848         rst.y = latitude/ON_PI + 0.5;
01849   if ( rst.y <= 0.0 )
01850     rst.y = 0.0;
01851         else if ( rst.y > 1.0 )
01852                   rst.y = 1.0;
01853         
01854   // radius is already normalized
01855         rst.z = r;
01856         
01857   // apply texture coordinate transformation
01858         *T = m_uvw*rst;
01859 
01860   return 1;
01861 }
01862 
01863 int ON_TextureMapping::EvaluateCylinderMapping( 
01864                                                                                                 const ON_3dPoint& P,
01865                                                                                                 const ON_3dVector& N,
01866                                                                                                 ON_3dPoint* T
01867                                                                                                 ) const
01868 {
01869   // The matrix m_Pxyz transforms the world coordinate
01870   // "mapping cylinder" into the cylinder centered at
01871   // rst = (0,0,0) with radius 1.0.  The axis runs
01872   // from rst = (0,0,-1) to rst = (0,0,+1).
01873 
01874         ON_3dPoint rst(m_Pxyz*P);
01875 
01876         ON_3dPoint Q;
01877         const double r = ((const ON_2dVector*)(&rst.x))->Length();
01878         double t, t0, t1;
01879         int side0, side1;
01880         PROJECTION mapping_proj = m_projection;
01881         
01882         side0 = 0;
01883         if ( ON_TextureMapping::ray_projection == mapping_proj )
01884         {
01885                 ON_3dVector n(m_Nxyz*N);
01886                 t = 0.0;
01887                 
01888                 if ( m_bCapped )
01889                 {
01890                         // shoot at caps
01891                         //  The < t check prevents overflow when the 
01892                         //  ray is nearly parallel to the cap.
01893                         t = fabs(n.z)*on__overflow_tol;
01894                         if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t )
01895                         {
01896                                 side0 = 2;
01897                                 side1 = 3;
01898 
01899                                 t0 = (-1.0 - rst.z)/n.z;
01900                                 Q = rst + t0*n;
01901                                 if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON
01902                                         || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
01903                                 {
01904           // The ray's intersection with the bottom plane missed the 
01905           // radius 1 disk that is the bottom of the cylinder.
01906                                         side0 = 0;
01907                                 }
01908 
01909                                 t1 = ( 1.0 - rst.z)/n.z;
01910                                 Q = rst + t1*n;
01911                                 if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON
01912                                         || (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
01913                                 {
01914           // The ray's intersection with the top plane missed the 
01915           // radius 1 disk that is the top of the cylinder.
01916                                         side1 = 0;
01917                                 }
01918                                 if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
01919                                 {
01920                                         side0 = side1;
01921                                         t = t1;
01922                                 }
01923                                 else
01924                                 {
01925                                         t = t0;
01926                                 }
01927                         }
01928                 }
01929                 
01930                 // shoot ray at the cylinder wall
01931                 int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y), 
01932                         2.0*(rst.x*n.x+rst.y*n.y), 
01933                         (rst.x*rst.x+rst.y*rst.y) - 1.0, 
01934                         &t0, &t1 );
01935                 if (rc >= 0 )
01936                 {
01937                         if ( 2 != rc  && 1 == BestHitHelper(t0,t1) )
01938                         {
01939                                 t0 = t1;
01940                         }
01941                         if ( 0 == side0 )
01942                         {
01943                                 // Either the caps are missing or the ray missed the caps.
01944         // The best hit is the cylinder wall.
01945                                 side0 = 1;
01946                                 rst = rst + t0*n;
01947                         }
01948                         else if ( 1 != BestHitHelper(t0,t) )
01949                         {
01950                                 // The cylinder is capped and the ray hit the cap, 
01951         // hit the infinite cylinder wall, and the wall 
01952         // hit is "first".  If the ray hits the finite 
01953         // cylinder wall, the I will use the wall hit.
01954                                 t1 = rst.z + t0*n.z;
01955                                 if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON )
01956                                 {
01957                                         // use the hit on the cylinder wall
01958                                         side0 = 1;
01959                                         rst.x = rst.x + t0*n.x;
01960                                         rst.y = rst.y + t0*n.y;
01961           rst.x = t1;
01962                                 }
01963                         }
01964                 }
01965                 
01966                 if ( side0 > 1 )
01967                 {
01968                         // best hit is on a cap
01969                         rst = rst + t*n;
01970                 }
01971         }
01972         
01973         if ( m_bCapped && 0 == side0 )
01974         {
01975     if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON )
01976     {
01977       if ( fabs(rst.z) > r )
01978       {
01979         side0 = (rst.z < 0.0) ? 2 : 3;
01980       }
01981     }
01982     else if ( r <= 1.001 )
01983     {
01984       // The point is inside the capped cylinder.
01985       // Use normal to dermine which surface to use
01986       // for closest point test.
01987                   ON_3dVector n(m_Nxyz*N);
01988       if (  ( fabs(n.z) > fabs(n.x) && fabs(n.z) > fabs(n.y) ) )
01989       {
01990         side0 = (n.z < 0.0) ? 2 : 3;
01991       }
01992     }
01993         }
01994         
01995         if ( 2 == side0 || 3 == side0 )
01996         {
01997     // The cylinder is capped and P maps to 
01998     // the top (1 == side0) or bottom (2 == side0)
01999 
02000     if ( 2 == side0 )
02001     {
02002       // This is the same convention as box mapping.
02003       // Put another way, if you change the mapping 
02004       // between box and cylinder, you get the same
02005       // picture on the top and bottom.
02006       rst.x = -rst.x; 
02007     }
02008 
02009                 if ( ON_TextureMapping::divided == m_texture_space )
02010                 {
02011                   if ( r >= 1.0-ON_SQRT_EPSILON )
02012                   {
02013                           rst.x /= (r+ON_SQRT_EPSILON);
02014                           rst.y /= (r+ON_SQRT_EPSILON);
02015                   }
02016     }
02017     else if ( r > 1.0 )
02018           {
02019                   rst.x /= r;
02020                   rst.y /= r;
02021           }
02022 
02023     
02024     // convert to normalized texture coordinates
02025                 rst.x = 0.5*rst.x + 0.5;
02026     if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0;
02027                 rst.y = 0.5*rst.y + 0.5;
02028     if ( rst.y < 0.0) rst.y = 0.0; else if (rst.y > 1.0) rst.y = 1.0;
02029 
02030                 if ( ON_TextureMapping::divided == m_texture_space )
02031                 {
02032       // bottom uses 4/6 <= x <= 5/6 region of the texture map.
02033       // top uses 5/6 <= x <= 1 region of the texture map.
02034                         rst.x = (2.0 + side0 + rst.x)/6.0;
02035                 } 
02036         }
02037         else
02038         {
02039     // P maps to side of the cylinder.
02040     //
02041     // convert longitude to normalized texture coordinate
02042                 t = (0.0 != rst.y || 0.0 != rst.x) ? atan2(rst.y,rst.x) : 0.0;
02043                 rst.x = 0.5*t/ON_PI;
02044                 if ( rst.x < -ON_EPSILON )
02045                         rst.x += 1.0;
02046                 else if (rst.x < 0.0 )
02047                         rst.x = 0.0;
02048                 else if (rst.x > 1.0 )
02049                         rst.x = 1.0;
02050 
02051     if ( ON_TextureMapping::divided == m_texture_space )
02052     {
02053       // side uses 0 <= x <= 2/3 region of the texture map
02054       rst.x *= 2.0;
02055                         rst.x /= 3.0;
02056     }
02057 
02058     // convert height to normalized texture coordinate
02059         rst.y = 0.5*rst.z + 0.5;
02060     if ( m_bCapped )
02061     {
02062       // clamp height
02063       if ( rst.y < 0.0 ) rst.y = 0.0; else if ( rst.y > 1.0 ) rst.y = 1.0;
02064     }
02065     side0 = 1;
02066         }
02067         rst.z = r;      
02068         
02069         *T = m_uvw*rst;
02070 
02071   return side0;
02072 }
02073 
02074 int ON_TextureMapping::EvaluateBoxMapping( 
02075                                                                                    const ON_3dPoint& P,
02076                                                                                    const ON_3dVector& N,
02077                                                                                    ON_3dPoint* T
02078                                                                                    ) const
02079 {
02080   // The matrix m_Pxyz transforms the world coordinate
02081   // "mapping cylinder" into the cylinder centered at
02082   // rst = (0,0,0) with radius 1.0.  The axis runs
02083   // from rst = (0,0,-1) to rst = (0,0,+1).
02084 
02085   ON_3dPoint rst(m_Pxyz*P);
02086 
02087         ON_3dVector n(m_Nxyz*N);
02088   n.Unitize();
02089 
02090         int side0, side1;
02091         double t0, t1;
02092         
02093         side0 = 0;
02094         t0 = 0.0;
02095 
02096   // side flag
02097   //  1 =  left side (x=-1)
02098   //  2 =  right side (x=+1)
02099   //  3 =  back side (y=-1)
02100   //  4 =  front side (y=+1)
02101   //  5 =  bottom side (z=-1)
02102   //  6 =  top side (z=+1)
02103         
02104   if ( ON_TextureMapping::ray_projection == m_projection )
02105         {
02106                 
02107                 if ( m_bCapped )
02108                 {
02109                         // intersect ray with top and bottom
02110                         side0 = IntersectBoxRayHelper(rst,n,2,&t0);
02111                 }
02112                 // intersect ray with front and back
02113                 side1 = IntersectBoxRayHelper(rst,n,0,&t1);
02114                 if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
02115                 {
02116                         side0 = side1;
02117                         t0 = t1;
02118                 }
02119                 // intersect ray with left and right
02120                 side1 = IntersectBoxRayHelper(rst,n,1,&t1);
02121                 if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
02122                 {
02123                         side0 = side1;
02124                         t0 = t1;
02125                 }
02126                 if ( 0 != side0 )
02127                 {
02128                         // ray hit the box
02129                         rst = rst + t0*n;
02130                 }
02131         } 
02132 
02133   if ( 0 == side0 )
02134   {
02135     // set side0 = side closest to the point
02136     side1 = (fabs(rst.x) >= fabs(rst.y)) ? 0 : 1;
02137     if ( m_bCapped && fabs(rst.z) > fabs(((double*)&rst.x)[side1]) )
02138       side1 = 2;
02139     t1 = (&rst.x)[side1];
02140     if ( t1 < 0.0 )
02141     {
02142       side0 = 2*side1 + 1;
02143     }
02144     else 
02145     {
02146       side0 = 2*side1 + 2;
02147     }
02148     
02149     //if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )...
02153 
02154     side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1;
02155     if ( m_bCapped && fabs(n.z) > fabs((&n.x)[side1]))
02156     {
02157       side1 = 2;
02158     }
02159     t1 = n[side1];
02160     if ( 0.0 != t1 )
02161     {
02162       if ( t1 < 0.0 )
02163         side0 = 2*side1 + 1;
02164       else if ( t1 > 0.0 )
02165         side0 = 2*side1 + 2;
02166     }
02167   }
02168 
02169         double shift = 0.0;
02170         
02171   // side flag
02172   //  1 =  left side (x=-1)
02173   //  2 =  right side (x=+1)
02174   //  3 =  back side (y=-1)
02175   //  4 =  front side (y=+1)
02176   //  5 =  bottom side (z=-1)
02177   //  6 =  top side (z=+1)
02178 
02179         switch(side0)
02180         {
02181         case 1: // x = -1 
02182                 rst.x = -rst.y; 
02183                 rst.y =  rst.z; 
02184                 shift =  3.0;
02185                 break;
02186         case 2: // x = +1
02187                 rst.x =  rst.y;     
02188                 rst.y =  rst.z; 
02189                 shift =  1.0;
02190                 break;
02191         case 3: // y = -1
02192                 rst.y =  rst.z; 
02193                 shift =  0.0;
02194                 break;
02195         case 4: // y = +1
02196                 rst.x = -rst.x; 
02197                 rst.y =  rst.z; 
02198                 shift =  2.0;
02199                 break;
02200         case 5: // z = -1
02201                 rst.x = -rst.x; 
02202                 shift =  4.0;
02203                 break;
02204         case 6: // z = +1
02205                 shift =  5.0;
02206                 break;
02207         }
02208 
02209   // normalize texture coordinates
02210   rst.x = 0.5*rst.x + 0.5;
02211   rst.y = 0.5*rst.y + 0.5;
02212         rst.z = 0.0;
02213         
02214         if( divided == m_texture_space)
02215         {
02216     rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0);
02217         }
02218 
02219         *T = m_uvw*rst;
02220   
02221   return side0;
02222 }
02223 
02224 int ON_TextureMapping::Evaluate(
02225         const ON_3dPoint& P,
02226         const ON_3dVector& N,
02227         ON_3dPoint* T,
02228         const ON_Xform& P_xform,
02229         const ON_Xform& N_xform
02230         ) const
02231 {
02232   int rc;
02233   ON_3dPoint Q = P*P_xform;
02234   if ( ON_TextureMapping::ray_projection == m_projection )
02235   {
02236     // need a transformed normal
02237     ON_3dVector V = N_xform*N;
02238     V.Unitize();
02239     rc = Evaluate(Q,V,T);
02240   }
02241   else
02242   {
02243     // normal is ignored
02244     rc = Evaluate(Q,N,T);
02245   }
02246   return rc;
02247 }
02248 
02249 int ON_TextureMapping::Evaluate(
02250         const ON_3dPoint& P,
02251         const ON_3dVector& N,
02252         ON_3dPoint* T
02253         ) const
02254 {
02255   int rc;
02256 
02257         switch(m_type)
02258         {
02259         case srfp_mapping:
02260                 *T = m_uvw * P; // Do NOT apply m_Pxyz here.
02261     rc = 1;
02262                 break;
02263         case sphere_mapping:
02264                 rc = EvaluateSphereMapping(P,N,T);
02265                 break;
02266         case cylinder_mapping:
02267                 rc = EvaluateCylinderMapping(P,N,T);
02268                 break;
02269         case box_mapping:
02270                 rc = EvaluateBoxMapping(P,N,T);
02271                 break;
02272 
02273         case mesh_mapping_primitive:
02274     rc = 0;
02275                 break;
02276 
02277         case srf_mapping_primitive:
02278     rc = 0;
02279                 break;
02280 
02281         case brep_mapping_primitive:
02282     rc = 0;
02283                 break;
02284 
02285         default:
02286                 rc = EvaluatePlaneMapping(P,N,T);
02287                 break;
02288         }       
02289   return rc;
02290 }
02291 
02292 ON__UINT32 ON_TextureMapping::MappingCRC() const
02293 {
02294   // include any member that can change values returned by Evaluate
02295   ON__UINT32 crc32 = 0x12345678;
02296   crc32 = ON_CRC32(crc32,sizeof(m_type),&m_type);
02297   if ( ON_TextureMapping::srfp_mapping != m_type )
02298   {
02299     // As of 21 June 2006 m_Pxyz cannot effect srfp_mapping,
02300     // so it shouldn't be included in the CRC for srfp_mappings.
02301     crc32 = ON_CRC32(crc32,sizeof(m_projection),    &m_projection);
02302     crc32 = ON_CRC32(crc32,sizeof(m_texture_space), &m_texture_space);
02303     crc32 = ON_CRC32(crc32,sizeof(m_bCapped),               &m_bCapped);
02304     crc32 = ON_CRC32(crc32,sizeof(m_Pxyz),          &m_Pxyz);
02305     // do not include m_Nxyz here - it won't help and may hurt
02306 
02307           if ( 0 != m_mapping_primitive )
02308           {
02309       switch( m_type )
02310       {
02311       case ON_TextureMapping::mesh_mapping_primitive:
02312         {
02313           const ON_Mesh* mesh = ON_Mesh::Cast(m_mapping_primitive);
02314           if ( 0 == mesh )
02315             break;
02316           crc32 = mesh->DataCRC(crc32);
02317           if ( mesh->HasTextureCoordinates() )
02318           {
02319             // 25 August 2010 Dale Lear
02320             //   Including m_T[] in crc32 per Jussi and Andy email discussion.
02321             //   This is probably correct because users will expect the
02322             //   "picture" on the mesh to be applied to the target in
02323             //   a visual way.
02324             const ON_2fPoint* tex = mesh->m_T.Array();
02325             crc32 = ON_CRC32(crc32,mesh->m_T.Count()*sizeof(tex[0]),tex);
02326           }
02327         }
02328         break;
02329 
02330       case ON_TextureMapping::brep_mapping_primitive:
02331         {
02332           const ON_Brep* brep = ON_Brep::Cast(m_mapping_primitive);
02333           if ( 0 == brep )
02334             break;
02335           crc32 = brep->DataCRC(crc32);
02336           // 25 August 2010 Dale Lear
02337           //   Should brep's render meshes be included in the crc?
02338           //   The texture that is being mapped is actually
02339           //   being applied to the brep by the render mesh's
02340           //   m_T[] values and some users will want to see 
02341           //   the "picture" on the brep mapped to the 
02342           //   "picture" on the
02343           //   target.
02344         }
02345         break;
02346 
02347       case ON_TextureMapping::srf_mapping_primitive:
02348         {
02349           const ON_Surface* surface = ON_Surface::Cast(m_mapping_primitive);
02350           if ( 0 == surface )
02351             break;
02352           crc32 = surface->DataCRC(crc32);
02353         }
02354         break;
02355 
02356       case ON_TextureMapping::no_mapping:
02357       case ON_TextureMapping::srfp_mapping:
02358       case ON_TextureMapping::plane_mapping:
02359       case ON_TextureMapping::cylinder_mapping:
02360       case ON_TextureMapping::sphere_mapping:
02361       case ON_TextureMapping::box_mapping:
02362       case ON_TextureMapping::force_32bit_mapping_projection:
02363       default:
02364         break;
02365       }
02366     }
02367 
02368   }
02369 
02370   crc32 = ON_CRC32(crc32,sizeof(m_uvw), &m_uvw);
02371   return crc32;
02372 }
02373 
02374 bool ON_TextureMapping::RequiresVertexNormals() const
02375 {
02376   if ( ON_TextureMapping::srfp_mapping == m_type )
02377     return false;
02378 
02379         if(m_projection == ray_projection) 
02380     return true;
02381 
02382   if(m_type == box_mapping) 
02383     return true;
02384         if(m_type == cylinder_mapping && m_bCapped) 
02385     return true;
02386 
02387         return false;
02388 }
02389 
02390 bool ON_TextureMapping::IsPeriodic(void) const
02391 {
02392         return (m_type == sphere_mapping || m_type == cylinder_mapping);
02393 }
02394 
02395 bool ON_TextureMapping::HasMatchingTextureCoordinates( 
02396        const ON_Mesh& mesh,
02397        const ON_Xform* mesh_xform
02398        ) const
02399 {
02400   bool rc = (mesh.HasTextureCoordinates())
02401           ? HasMatchingTextureCoordinates(mesh.m_Ttag,mesh_xform)
02402           : false;
02403 
02404   return rc;
02405 }
02406 
02407 bool ON_TextureMapping::HasMatchingTextureCoordinates( 
02408        const ON_MappingTag& tag,
02409        const ON_Xform* mesh_xform
02410        ) const
02411 {
02412   bool rc = false;
02413 
02414   // DO NOT COMPARE m_mapping_id's in this function.
02415   // This function returns true if the tc values
02416   // calculated by the mapping will be the same
02417   // as the mapping that was used to set the tag.
02418   if ( tag.m_mapping_crc == MappingCRC() )
02419   {
02420     rc = true;
02421 
02422     // zero transformations indicate the mapping
02423     // values are independent of the 3d location
02424     // of the mesh.  The srfp_mapping != m_type
02425     // check is used because these mappings are
02426     // alwasy independent of 3d location but
02427     // the transformations are often set.
02428     if ( ON_TextureMapping::srfp_mapping != m_type
02429          && mesh_xform 
02430          && mesh_xform->IsValid()
02431          && !mesh_xform->IsZero() 
02432          && !tag.m_mesh_xform.IsZero()
02433        )
02434     {
02435       // compare xforms - these can have a bit of slop
02436       const double* a = &mesh_xform->m_xform[0][0];
02437       const double* b = &tag.m_mesh_xform.m_xform[0][0];
02438       for ( int i = 16; i--; /*empty*/ )
02439       {
02440         if ( fabs(*a++ - *b++) > ON_SQRT_EPSILON )
02441         {
02442           rc = false;
02443           break;
02444         }
02445       }
02446     }
02447   }
02448 
02449   return rc;
02450 }
02451 
02452 static
02453 bool GetSPTCHelper(
02454   const ON_Mesh& mesh,
02455   const ON_TextureMapping& mapping,
02456   float* tc,
02457   int tc_stride
02458   )
02459 {
02460   const int vcnt = mesh.m_V.Count();
02461   if ( vcnt <= 0 )
02462     return false;
02463   if ( !mesh.HasSurfaceParameters() )
02464     return false;
02465   const ON_2dPoint* S = mesh.m_S.Array();
02466   if ( !S )
02467     return false;
02468 
02469   int i;
02470   double u, v, a, b;
02471 
02472   // srf_udom and srf_vdom record the range
02473   // of parameters saved in the m_S[] array.
02474   ON_Interval srf_udom = mesh.m_srf_domain[0];
02475   ON_Interval srf_vdom = mesh.m_srf_domain[1];
02476   if ( !srf_udom.IsIncreasing() || !srf_vdom.IsIncreasing() )
02477   {
02478     // Attempt to calculate it from m_S[].
02479     srf_udom.m_t[0] = srf_udom.m_t[1] = S[0].x;
02480     srf_vdom.m_t[0] = srf_vdom.m_t[1] = S[0].y;
02481     for ( i = 1; i < vcnt; i++ )
02482     {
02483       u = S[i].x;
02484       if      (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u; 
02485       else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u; 
02486       v = S[i].y;
02487       if      (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v; 
02488       else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v; 
02489     }
02490     if (    !srf_udom.IsIncreasing() 
02491          || !srf_vdom.IsIncreasing() )
02492     {
02493       return false;
02494     }
02495   }
02496 
02497   bool bHaveUVWXform =   mapping.m_uvw.IsValid() 
02498                      && !mapping.m_uvw.IsIdentity() 
02499                      && !mapping.m_uvw.IsZero();
02500 
02501   if ( mesh.HasPackedTextureRegion() )
02502   {
02503     // Packed textures are not compatible with the use 
02504     // of m_uvw.  m_uvw is ignored in this block
02505     // of code on purpose.  //SEE BELOW
02506     const ON_Interval tex_udom = mesh.m_packed_tex_domain[0];
02507     const ON_Interval tex_vdom = mesh.m_packed_tex_domain[1];
02508     for ( i = 0; i < vcnt; i++)
02509     {
02510                 //ALB 2011.01.14
02511                 //Added support for m_uvw in packed textures.  Even though this conceptually makes
02512                 //very little sense, it's one of the most requested features for the texture mapping
02513                 //system, so I grudgingly add it.
02514                 if (bHaveUVWXform)
02515                 {
02516                         const ON_2dPoint si = mapping.m_uvw*S[i];
02517                         u = si.x;
02518                         v = si.y;
02519                 }
02520                 else
02521                 {
02522                         u = S[i].x;
02523                         v = S[i].y;
02524                 }
02525 
02526             // (u, v) = known surface parameter
02527             if ( mesh.m_packed_tex_rotate ) 
02528             {
02529         // verify this by checking with mesher
02530                a = 1.0 - srf_vdom.NormalizedParameterAt( v );
02531                b = srf_udom.NormalizedParameterAt( u );
02532             }
02533             else 
02534             {
02535               a = srf_udom.NormalizedParameterAt( u );
02536               b = srf_vdom.NormalizedParameterAt( v );
02537             }
02538 
02539       // When textures are packed, tex_udom and tex_vdom
02540       // are subintervals of (0,1).
02541             u = tex_udom.ParameterAt(a);
02542             v = tex_vdom.ParameterAt(b);
02543 
02544             tc[0] = (float)u;
02545             tc[1] = (float)v;
02546       tc += tc_stride;
02547     }
02548   }
02549   else if ( bHaveUVWXform )
02550   {
02551     const ON_Xform xform(mapping.m_uvw);
02552     ON_3dPoint P;
02553     for ( i = 0; i < vcnt; i++)
02554     {
02555             // normalize surface parameter
02556       P.x = srf_udom.NormalizedParameterAt( S[i].x );
02557       P.y = srf_vdom.NormalizedParameterAt( S[i].y );
02558       P.z = 0.0;
02559 
02560       // apply m_uvw transformation
02561       P = xform*P;
02562 
02563       tc[0] = (float)P.x;
02564       tc[1] = (float)P.y;
02565       tc += tc_stride;
02566     }
02567   }
02568   else
02569   {
02570     // m_srf_tex_rotate is ignored on purpose.
02571     // It only applies if the texture is packed.
02572     for ( i = 0; i < vcnt; i++)
02573     {
02574             // tc = normalized surface parameter
02575       a = srf_udom.NormalizedParameterAt( S[i].x );
02576       b = srf_vdom.NormalizedParameterAt( S[i].y );
02577 
02578             tc[0] = (float)a;
02579             tc[1] = (float)b;
02580       tc += tc_stride;
02581     }
02582   }
02583 
02584   return true;
02585 }
02586 
02587 
02588 bool ON_TextureMapping::GetTextureCoordinates(
02589           const ON_Mesh& mesh, 
02590           ON_SimpleArray<ON_3fPoint>& T,
02591           const ON_Xform* mesh_xform,
02592           bool bLazy,
02593           ON_SimpleArray<int>* Tside
02594           ) const
02595 {
02596   if ( Tside )
02597     Tside->SetCount(0);
02598 
02599   int i;
02600   const int vcnt = mesh.m_V.Count();
02601   if ( vcnt <= 0 )
02602     return false;
02603 
02604   if ( bLazy )
02605   {
02606     int tci, tccount = mesh.m_TC.Count();
02607     for ( tci = 0; tci < tccount; tci++ )
02608     {
02609       if ( vcnt == mesh.m_TC[tci].m_T.Count() )
02610       {
02611         if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
02612         {
02613           T = mesh.m_TC[tci].m_T;
02614           return true;
02615         }
02616       }
02617     }
02618 
02619     if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
02620     {
02621       T.Reserve(vcnt);
02622       T.SetCount(vcnt);
02623       const ON_2fPoint* f = mesh.m_T.Array();
02624       ON_3fPoint* d = T.Array();
02625       for ( i = vcnt; i--; f++, d++ )
02626       {
02627         d->x = f->x;
02628         d->y = f->y;
02629         d->z = 0.0f;
02630       }
02631       return true;
02632     }
02633   }
02634 
02635         bool rc = false;
02636 
02637   if ( ON_TextureMapping::srfp_mapping == m_type )
02638   {
02639     // uv textures from surface parameterization
02640     T.Reserve(vcnt);
02641     T.SetCount(vcnt);
02642     T.Zero();
02643     rc = GetSPTCHelper(mesh,*this,&T[0].x,3);
02644         }
02645   else
02646   {
02647     ON_3dPoint  P, tc;
02648                 ON_3dVector N(0.0,0.0,0.0);
02649 
02650                 const ON_3fPoint*  mesh_V = mesh.m_V.Array();
02651                 const ON_3fVector* mesh_N = mesh.HasVertexNormals()
02652                               ? mesh.m_N.Array()
02653                               : 0;
02654 
02655     T.Reserve(vcnt);
02656     T.SetCount(vcnt);
02657 
02658     int* Tsd = 0;
02659     if ( Tside )
02660     {
02661       Tside->Reserve(vcnt);
02662       Tside->SetCount(vcnt);
02663       Tsd = Tside->Array();
02664       memset(Tsd,0,vcnt*sizeof(Tsd[0]));
02665     }
02666 
02667     ON_Xform P_xform(1.0), N_xform(1.0);
02668     const double* PT = 0;
02669     const double* NT = 0;
02670     if ( mesh_xform )
02671     {
02672       if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
02673       {
02674         // ignore transformation
02675         mesh_xform = 0;
02676       }
02677       else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
02678       {
02679         PT = &P_xform[0][0];
02680         NT = &N_xform[0][0];
02681       }
02682       else
02683       {
02684         mesh_xform = 0;
02685       }
02686     }
02687 
02688     const float* f;
02689     double w;
02690     int sd;
02691 
02692                 if (clspt_projection == m_projection && ON_TextureMapping::mesh_mapping_primitive == m_type && NULL != m_mapping_primitive)
02693                 {
02694       rc = false;
02695                 }
02696                 else if ( mesh_N &&
02697           (   ray_projection == m_projection 
02698            || ON_TextureMapping::box_mapping == m_type 
02699            || ON_TextureMapping::cylinder_mapping == m_type
02700            || ON_TextureMapping::mesh_mapping_primitive == m_type
02701                    )
02702         )
02703         {
02704                         // calculation uses mesh vertex normal
02705       if ( PT && NT )
02706       {
02707         // need to transform vertex and normal
02708         // before calculating texture coordinates
02709                           for (i = 0; i < vcnt; i++)
02710                           {
02711           f = &mesh_V[i].x;
02712                                   w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
02713           w = (0.0 != w) ? 1.0/w : 1.0;
02714                                   P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
02715                                   P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
02716                                   P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
02717 
02718           f = &mesh_N[i].x;
02719           N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
02720                                   N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
02721                                   N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
02722           N.Unitize();
02723                                   sd = Evaluate(P,N,&tc);
02724                                   T[i] = tc;
02725           if ( Tsd ) Tsd[i] = sd;
02726                           }
02727       }
02728                         else
02729       {
02730         // mesh vertex and normal are ok
02731                           for (i = 0; i < vcnt; i++)
02732                           {
02733                                   P = mesh_V[i];
02734                                   N = mesh_N[i];
02735                                   sd = Evaluate(P,N,&tc);
02736                                   T[i] = tc;
02737           if ( Tsd ) Tsd[i] = sd;
02738                           }
02739       }
02740                 }
02741                 else if ( PT )
02742     {
02743       // normal is not used
02744       // mesh vertex needs to be transformed
02745       for ( i = 0; i < vcnt; i++ )
02746       {
02747         f = &mesh_V[i].x;
02748                           w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
02749         w = (0.0 != w) ? 1.0/w : 1.0;
02750                           P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
02751                           P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
02752                           P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
02753         sd = Evaluate(P,N,&tc);
02754                           T[i] = tc;
02755         if ( Tsd )
02756           Tsd[i] = sd;
02757                   }
02758     }
02759     else
02760                 {
02761                         // normal is not used and mesh vertex is ok
02762                         for ( i = 0; i < vcnt; i++ )
02763                         {
02764                                 P = mesh_V[i];
02765                                 sd = Evaluate(P,N,&tc);
02766                                 T[i] = tc;
02767                                 if ( Tsd )
02768                                         Tsd[i] = sd;
02769                         }
02770     }
02771     rc = true;
02772         }
02773 
02774         return rc;
02775 }
02776 
02777 static 
02778 void ThreeToTwoHelper( 
02779       const ON_SimpleArray<ON_3fPoint>& T3,
02780       ON_SimpleArray<ON_2fPoint>& T2
02781       )
02782 {
02783   int i = T3.Count();
02784   const ON_3fPoint* t3 = T3.Array();
02785 
02786   T2.Reserve(i);
02787   T2.SetCount(i);
02788   ON_2fPoint* t2 = T2.Array();
02789   while(i--)
02790   {
02791     t2->x = t3->x;
02792     t2->y = t3->y;
02793     t2++;
02794     t3++;
02795   }
02796 }
02797 
02798 bool ON_TextureMapping::GetTextureCoordinates(
02799             const ON_Mesh& mesh, 
02800             ON_SimpleArray<ON_2fPoint>& T, 
02801             const ON_Xform* mesh_xform,
02802             bool bLazy,
02803             ON_SimpleArray<int>* Tside
02804             ) const
02805 {
02806   bool rc = false;
02807   if ( Tside )
02808     Tside->SetCount(0);
02809   if ( bLazy )
02810   {
02811     if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
02812     {
02813       if ( T.Array() != mesh.m_T.Array() )
02814       {
02815         // different arrays - copy
02816         T = mesh.m_T;
02817       }
02818       return true;
02819     }
02820     else
02821     {
02822       int vcnt = mesh.m_V.Count();
02823       int tci, tccount = mesh.m_TC.Count();
02824       for ( tci = 0; tci < tccount; tci++ )
02825       {
02826         if ( vcnt == mesh.m_TC[tci].m_T.Count() )
02827         {
02828           if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
02829           {
02830             // copy T3d[] results to T[]
02831             ThreeToTwoHelper(mesh.m_TC[tci].m_T,T);
02832             return true;
02833           }
02834         }
02835       }
02836     }
02837   }
02838 
02839   if ( ON_TextureMapping::srfp_mapping == m_type )
02840   {
02841     // uv textures from surface parameterization
02842     T.Reserve(mesh.m_V.Count());
02843     T.SetCount(mesh.m_V.Count());
02844     T.Zero();
02845     rc = GetSPTCHelper(mesh,*this,&T[0].x,2);
02846   }
02847   else
02848   {
02849     T.SetCount(0);
02850           ON_SimpleArray<ON_3fPoint> T3;
02851     if ( GetTextureCoordinates(mesh, T3, mesh_xform, false, Tside ) )
02852     {
02853       // copy T3d[] results to T[]
02854       ThreeToTwoHelper(T3,T);
02855       rc = true;
02856           }
02857   }
02858         return rc;
02859 }
02860 
02861 
02862 //bool ON_Mesh::GetSurfaceParameterTextureXform( 
02863 //          class ON_Xform& StoT 
02864 //          ) const
02865 //
02866 //{
02867 //  bool rc = false;
02868 //  StoT.Identity();
02869 //
02870 //  // Gets default mesh mapping
02871 //  const ON_Interval surface_u_domain(m_srf_domain[0]);
02872 //  const ON_Interval surface_v_domain(m_srf_domain[1]);
02873 //  const ON_Interval texture_u_domain(m_tex_domain[0]);
02874 //  const ON_Interval texture_v_domain(m_tex_domain[1]);
02875 //  bool bRotateTexture = m_srf_tex_rotate;
02876 //  if (   surface_u_domain.IsInterval() 
02877 //      && surface_v_domain.IsInterval()
02878 //      && texture_u_domain.IsInterval()
02879 //      && texture_v_domain.IsInterval()
02880 //      )
02881 //  {
02882 //    double du = 1.0/surface_u_domain.Length();
02883 //    double dv = 1.0/surface_v_domain.Length();
02884 //    ON_Xform x1(1.0), x2(1.0), x3(1.0);
02885 //    x1.m_xform[0][0] = du; x1.m_xform[0][3] = -surface_u_domain[0]*du;
02886 //    x1.m_xform[1][1] = dv; x1.m_xform[1][3] = -surface_v_domain[0]*dv;
02887 //    if ( bRotateTexture )
02888 //    {
02889 //      x2.m_xform[0][0] =  0.0; x2.m_xform[0][1] = -1.0; x2.m_xform[0][3] = 1.0;
02890 //      x2.m_xform[1][0] =  1.0; x2.m_xform[1][1] =  0.0;
02891 //    }
02892 //    x3.m_xform[0][0] = texture_u_domain.Length(); x3.m_xform[0][3] = texture_u_domain[0];
02893 //    x3.m_xform[1][1] = texture_v_domain.Length(); x3.m_xform[1][3] = texture_v_domain[0];
02894 //
02895 //    // transforms surface(u,v) to texture(u,v)
02896 //    StoT = x3*x2*x1;
02897 //
02898 //    rc = true;
02899 //  }
02900 //  return rc;
02901 //}
02902 
02903 class ON__CMeshFaceTC
02904 {
02905   // DO NOT PUT THIS CLASS IN A HEADER FILE
02906   // IT IS A PRIVATE HELPER CLASS.
02907 public:
02908   int   fi;
02909   int   quad[4];
02910   float Tx[4];
02911   bool  bSetT[4];
02912 };
02913 
02914 class ON__CChangeTextureCoordinateHelper
02915 {
02916   // DO NOT PUT THIS CLASS IN A HEADER FILE
02917   // IT IS A PRIVATE HELPER CLASS.
02918 public:
02919   ON__CChangeTextureCoordinateHelper( ON_Mesh& mesh, int newvcnt, float*& mesh_T );
02920   ~ON__CChangeTextureCoordinateHelper();
02921 
02922   int DupVertex(int vi);
02923   void ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y, float* mesh_T, int mesh_T_stride );
02924 
02925   int m_tci;
02926 
02927   ON_Mesh& m_mesh;
02928   ON_3dPointArray* m_mesh_dV;
02929   bool m_bHasVertexNormals;
02930   bool m_bHasVertexTextures;
02931   bool m_bHasVertexColors;
02932   bool m_bHasSurfaceParameters;
02933   bool m_bHasPrincipalCurvatures;
02934   bool m_bHasHiddenVertices;
02935 
02936   bool m_bHasCachedTextures;  
02937   ON_SimpleArray< ON_TextureCoordinates* > m_TC;
02938 
02939   // m_vuse[] is an array of length = original number of 
02940   // vertices in m_mesh and m_vuse[vi] = number of faces 
02941   // that reference vertex vi. If this vertex needs to be
02942   // split, vuse[vi] is decremented.  The ultimate goal
02943   // is to split a few times as needed so we don't
02944   // bloat the mesh with repeated calls to changing
02945   // texture maps. m_vuse[] is set the first time
02946   // DupVertex() is called.
02947   int m_vuse_count;
02948   ON_SimpleArray< unsigned int > m_vuse;
02949 private:
02950   // no implementation
02951   ON__CChangeTextureCoordinateHelper(const ON__CChangeTextureCoordinateHelper&);
02952   ON__CChangeTextureCoordinateHelper& operator=(const ON__CChangeTextureCoordinateHelper&);
02953 };
02954 
02955 void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y,
02956                                                              float* mesh_T, int mesh_T_stride )
02957 {
02958   int oldvi = Fvi[fvi];
02959   float* T = mesh_T+(oldvi*mesh_T_stride);
02960   if ( x != T[0] || (y != ON_UNSET_FLOAT && y != T[1]) )
02961   {
02962     int newvi = DupVertex(oldvi);
02963     T = mesh_T + (newvi*mesh_T_stride);
02964     T[0] = x;
02965     if ( y != ON_UNSET_FLOAT )
02966      T[1] = y;
02967 
02968     if ( 2 == fvi && oldvi == Fvi[3] )
02969     {
02970       Fvi[2] = newvi;
02971       Fvi[3] = newvi;
02972     }
02973     else
02974     {
02975       Fvi[fvi] = newvi;
02976     }
02977   }
02978 }
02979 
02980 
02981 ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper( 
02982     ON_Mesh& mesh,
02983     int newvcnt,
02984     float*& mesh_T ) 
02985 : m_mesh(mesh)
02986 , m_mesh_dV(0)
02987 , m_vuse_count(0)
02988 {
02989   // adding vertices invalidates this cached information.
02990   m_mesh.DestroyTopology();
02991   m_mesh.DestroyPartition();
02992   m_mesh.DestroyTree();
02993 
02994   m_tci = -1;
02995 
02996   const int vcnt = m_mesh.m_V.Count();
02997 
02998   // It is critical to reserve enough room in the arrays
02999   // before duplication starts.  Otherwise, during duplication,
03000   // a dyanamic array can be reallocated, which will make
03001   // saved array base pointers will be invalid, and you crash
03002   // the next time they are used.
03003 
03004   m_mesh.m_V.Reserve(vcnt+newvcnt);
03005 
03006   if (    m_mesh.HasDoublePrecisionVertices() 
03007        && m_mesh.DoublePrecisionVerticesAreValid() 
03008      )
03009   {
03010     m_mesh_dV = &m_mesh.DoublePrecisionVertices();
03011     m_mesh_dV->Reserve(vcnt+newvcnt);
03012   }
03013   else
03014   {
03015     m_mesh.DestroyDoublePrecisionVertices();
03016   }
03017 
03018   m_bHasVertexNormals = m_mesh.HasVertexNormals();
03019   if ( m_bHasVertexNormals ) 
03020     m_mesh.m_N.Reserve(vcnt+newvcnt);
03021 
03022   m_bHasVertexTextures = m_mesh.HasTextureCoordinates();
03023   if ( m_bHasVertexTextures )
03024   {
03025     float* p = (float*)m_mesh.m_T.Array();
03026     m_mesh.m_T.Reserve(vcnt+newvcnt);
03027     if ( p == mesh_T )
03028       mesh_T = (float*)m_mesh.m_T.Array();
03029   }
03030 
03031   m_bHasVertexColors = m_mesh.HasVertexColors();
03032   if ( m_bHasVertexColors )
03033     m_mesh.m_C.Reserve(vcnt+newvcnt);
03034 
03035   m_bHasSurfaceParameters = m_mesh.HasSurfaceParameters();
03036   if ( m_bHasSurfaceParameters )
03037     m_mesh.m_S.Reserve(vcnt+newvcnt);
03038 
03039   m_bHasPrincipalCurvatures = m_mesh.HasPrincipalCurvatures();
03040   if ( m_bHasPrincipalCurvatures )
03041     m_mesh.m_K.Reserve(vcnt+newvcnt);
03042 
03043   m_bHasHiddenVertices = (0 != m_mesh.HiddenVertexArray());
03044   if ( m_bHasHiddenVertices )
03045     m_mesh.m_H.Reserve(vcnt+newvcnt);
03046 
03047   // Set m_TC[] to be the subset of m_mesh.m_TC[] that is
03048   // valid for duplication.
03049   m_bHasCachedTextures = false;
03050   int tci, tccount = m_mesh.m_TC.Count();
03051   m_TC.Reserve(tccount);
03052   for ( tci = 0; tci < tccount; tci++ )
03053   {
03054     ON_TextureCoordinates& tc = m_mesh.m_TC[tci];
03055     if ( vcnt == tc.m_T.Count() )
03056     {
03057       m_bHasCachedTextures = true;
03058       float* p = (float*)tc.m_T.Array();
03059       tc.m_T.Reserve(vcnt+newvcnt);
03060       if ( p == mesh_T )
03061         mesh_T = (float*)tc.m_T.Array();
03062       m_TC.Append( &tc );
03063     }
03064   }
03065 }
03066 
03067 
03068 ON__CChangeTextureCoordinateHelper::~ON__CChangeTextureCoordinateHelper()
03069 {
03070   if ( 0 != m_mesh_dV )
03071   {
03072     m_mesh.SetDoublePrecisionVerticesAsValid();
03073     m_mesh.SetSinglePrecisionVerticesAsValid();
03074     m_mesh_dV = 0;
03075   }
03076 }
03077 
03078 int ON__CChangeTextureCoordinateHelper::DupVertex(int vi)
03079 {
03080   if ( 0 == m_vuse_count )
03081   {
03082     // m_vuse[] is an array of length = original number of 
03083     // vertices in m_mesh and m_vuse[vi] = number of faces 
03084     // that reference vertex vi. If this vertex needs to be
03085     // split, vuse[vi] is decremented.  The ultimate goal
03086     // is to split a few times as needed so we don't
03087     // bloat the mesh with repeated calls to changing
03088     // texture maps. m_vuse[] is set the first time
03089     // DupVertex() is called.
03090     m_vuse_count = m_mesh.m_V.Count();
03091     m_vuse.Reserve(m_vuse_count);
03092     m_vuse.SetCount(m_vuse_count);
03093     m_vuse.Zero();
03094     for ( int fi = 0; fi < m_mesh.m_F.Count(); fi++ )
03095     {
03096       const int* Fvi = m_mesh.m_F[fi].vi;
03097       int i = Fvi[0];
03098       if ( i >= 0 && i < m_vuse_count )
03099         m_vuse[i]++;
03100       i = Fvi[1];
03101       if ( i >= 0 && i < m_vuse_count )
03102         m_vuse[i]++;
03103       i = Fvi[2];
03104       if ( i >= 0 && i < m_vuse_count )
03105         m_vuse[i]++;
03106       i = Fvi[3];
03107       if ( Fvi[2] != i && i >= 0 && i < m_vuse_count )
03108         m_vuse[i]++;
03109     }
03110   }
03111 
03112   if ( vi >= 0 && vi < m_vuse_count )
03113   {
03114     if ( m_vuse[vi] <= 1 )
03115       return vi; // only one face uses this vertex - no need to dup the vertex
03116 
03117     // otherwise we will duplicate this vertex, reducing its use count by 1.
03118     m_vuse[vi]--;
03119   }
03120 
03121 
03122   m_mesh.m_V.AppendNew();
03123   *m_mesh.m_V.Last() = m_mesh.m_V[vi];
03124   if ( 0 != m_mesh_dV )
03125   {
03126     m_mesh_dV->AppendNew();
03127     *(m_mesh_dV->Last()) = m_mesh_dV->operator[](vi);
03128   }
03129   if ( m_bHasVertexTextures )
03130   {
03131     m_mesh.m_T.AppendNew();
03132     *m_mesh.m_T.Last() = m_mesh.m_T[vi];
03133   }
03134   if ( m_bHasVertexNormals )
03135   {
03136     m_mesh.m_N.AppendNew();
03137     *m_mesh.m_N.Last() = m_mesh.m_N[vi];
03138   }
03139   if ( m_bHasVertexColors )
03140   {
03141     m_mesh.m_C.AppendNew();
03142     *m_mesh.m_C.Last() = m_mesh.m_C[vi];
03143   }
03144   if ( m_bHasSurfaceParameters )
03145   {
03146     m_mesh.m_S.AppendNew();
03147     *m_mesh.m_S.Last() = m_mesh.m_S[vi];
03148   }
03149   if ( m_bHasPrincipalCurvatures )
03150   {
03151     m_mesh.m_K.AppendNew();
03152     *m_mesh.m_K.Last() = m_mesh.m_K[vi];
03153   }
03154   if ( m_bHasHiddenVertices )
03155   {
03156     m_mesh.m_H.AppendNew();
03157     if ( 0 != (*m_mesh.m_H.Last() = m_mesh.m_H[vi]) )
03158       m_mesh.m_hidden_count++;
03159   }
03160 
03161   if ( m_bHasCachedTextures )
03162   {
03163     // Note:  This m_TC[] is the subset of m_mesh.m_TC[]
03164     //        that need to be duped.  The constructor
03165     //        insures that m_TC[i] is not NULL and
03166     //        has the right count and capacity.
03167     //
03168     //        DO NOT REFERENCE m_mesh.m_TC[] in this block.
03169     int tccount = m_TC.Count();
03170     for ( int i = 0; i < tccount; i++ )
03171     {
03172       ON_SimpleArray<ON_3fPoint>& T = m_TC[i]->m_T;
03173       T.AppendNew();
03174       *T.Last() = T[vi];
03175     }
03176   }
03177 
03178   return m_mesh.m_V.Count()-1;
03179 }
03180 
03181 
03182 static
03183 float PoleFix( float t0,  float t1 )
03184 {
03185   float t = ( ON_UNSET_FLOAT == t0 )
03186           ? t1
03187           : ((ON_UNSET_FLOAT == t1 ) ? t0 : (0.5f*(t0+t1)));
03188   return t;
03189 }
03190 
03191 static
03192 int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector& n, double* s)
03193 {
03194   /*
03195   returns:
03196     0 = ray parallel to sides
03197     1 = ray hit left side (x=-1)
03198     2 = ray hit right side (x=+1)
03199     3 = ray hit back side (y=-1)
03200     4 = ray hit front side (y=+1)
03201     5 = ray hit bottom side (z=-1)
03202     6 = ray hit top side (z=+1)
03203   */
03204   double nx;
03205   ON_3dPoint Q;
03206   double t,t0,t1;
03207   int dir;
03208 
03209 
03210   switch(side)
03211   {
03212   case 1: // =  left side (x=-1)
03213     t1 = -1.0;
03214     dir = 0;
03215     break;
03216   case 2: //   right side (x=+1)
03217     t1 = 1.0;
03218     dir = 0;
03219     break;
03220   case 3: //   back side (y=-1)
03221     t1 = -1.0;
03222     dir = 1;
03223     break;
03224   case 4: //   front side (y=+1)
03225     t1 = 1.0;
03226     dir = 1;
03227     break;
03228   case 5: //   bottom side (z=-1)
03229     t1 = -1.0;
03230     dir = 2;
03231     break;
03232   case 6: //   top side (z=+1)
03233     t1 = 1.0;
03234     dir = 2;
03235     break;
03236   default:
03237     *s = ON_UNSET_VALUE;
03238     return 0;
03239     break;
03240   }
03241 
03242   // protect against overflow
03243   nx = (&n.x)[dir];
03244   t0 = (t1 - (&rst.x)[dir]);
03245   if ( fabs(t0) >= fabs(nx)*on__overflow_tol )
03246   {
03247     *s = ON_UNSET_VALUE;
03248     return 0;
03249   }
03250 
03251   t0 /= nx;
03252   Q = rst + t0*n;
03253   if ( dir )
03254   {
03255     t = Q.x;
03256     Q.x = Q[dir];
03257     Q[dir] = t;
03258   }
03259   if ( fabs(Q.x-t1) > ON_SQRT_EPSILON || fabs(Q.y) > 1.0e8 || fabs(Q.z) > 1.0e8 )
03260   {
03261     *s = ON_UNSET_VALUE;
03262     return 0;
03263   }
03264 
03265 
03266   *s = t0;
03267   return side;
03268 }
03269 
03270 static
03271 bool EvBoxSideTextureCoordinateHelper2( 
03272                        int side,
03273                        const ON_TextureMapping& box_mapping,
03274                                                                                    const ON_3dPoint& P,
03275                                                                                    const ON_3dVector& N,
03276                                                                                    ON_3dPoint* T
03277                                                                                    )
03278 {
03279   // side flag
03280   //  1 =  left side (x=-1)
03281   //  2 =  right side (x=+1)
03282   //  3 =  back side (y=-1)
03283   //  4 =  front side (y=+1)
03284   //  5 =  bottom side (z=-1)
03285   //  6 =  top side (z=+1)
03286   // The matrix m_Pxyz transforms the world coordinate
03287   // "mapping cylinder" into the cylinder centered at
03288   // rst = (0,0,0) with radius 1.0.  The axis runs
03289   // from rst = (0,0,-1) to rst = (0,0,+1).
03290 
03291   ON_3dPoint rst(box_mapping.m_Pxyz*P);
03292 
03293         ON_3dVector n(box_mapping.m_Nxyz*N);
03294   n.Unitize();
03295 
03296   // side flag
03297   //  1 =  left side (x=-1)
03298   //  2 =  right side (x=+1)
03299   //  3 =  back side (y=-1)
03300   //  4 =  front side (y=+1)
03301   //  5 =  bottom side (z=-1)
03302   //  6 =  top side (z=+1)
03303         
03304   if ( ON_TextureMapping::ray_projection == box_mapping.m_projection )
03305         {
03306     double s;
03307     if ( side == IntersectBoxSideRayHelper(side, rst, n, &s) )
03308     {
03309                   // ray hit the box side
03310                   rst = rst + s*n;
03311     }
03312         } 
03313 
03314         double shift = 0.0;
03315         
03316   // side flag
03317   //  1 =  left side (x=-1)
03318   //  2 =  right side (x=+1)
03319   //  3 =  back side (y=-1)
03320   //  4 =  front side (y=+1)
03321   //  5 =  bottom side (z=-1)
03322   //  6 =  top side (z=+1)
03323 
03324         switch(side)
03325         {
03326         case 1: // x = -1 
03327                 rst.x = -rst.y; 
03328                 rst.y =  rst.z; 
03329                 shift =  3.0;
03330                 break;
03331         case 2: // x = +1
03332                 rst.x =  rst.y;     
03333                 rst.y =  rst.z; 
03334                 shift =  1.0;
03335                 break;
03336         case 3: // y = -1
03337                 rst.y =  rst.z; 
03338                 shift =  0.0;
03339                 break;
03340         case 4: // y = +1
03341                 rst.x = -rst.x; 
03342                 rst.y =  rst.z; 
03343                 shift =  2.0;
03344                 break;
03345         case 5: // z = -1
03346                 rst.x = -rst.x; 
03347                 shift =  4.0;
03348                 break;
03349         case 6: // z = +1
03350                 shift =  5.0;
03351                 break;
03352   default:
03353     return 0;
03354     break;
03355         }
03356 
03357   // normalize texture coordinates
03358   rst.x = 0.5*rst.x + 0.5;
03359   rst.y = 0.5*rst.y + 0.5;
03360         rst.z = 0.0;
03361         
03362         if( ON_TextureMapping::divided == box_mapping.m_texture_space)
03363         {
03364     rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0);
03365         }
03366 
03367         *T = box_mapping.m_uvw*rst;
03368   
03369   return true;
03370 }
03371 
03372 static
03373 bool EvBoxSideTextureCoordinateHelper1(
03374           const ON_Mesh& mesh, 
03375           const ON_Xform* mesh_xform,
03376           int vi,
03377           int side,
03378           const ON_TextureMapping& box_mapping,
03379           float* Tx,
03380           float* Ty
03381           )
03382 {
03383         bool rc = false;
03384   ON_3dPoint  P, tc;
03385         ON_3dVector N(0.0,0.0,0.0);
03386 
03387         const ON_3fPoint*  mesh_V = mesh.m_V.Array();
03388         const ON_3fVector* mesh_N = mesh.HasVertexNormals()
03389                             ? mesh.m_N.Array()
03390                             : 0;
03391 
03392   ON_Xform P_xform(1.0), N_xform(1.0);
03393   const double* PT = 0;
03394   const double* NT = 0;
03395   if ( mesh_xform )
03396   {
03397     if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
03398     {
03399       // ignore transformation
03400       mesh_xform = 0;
03401     }
03402     else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
03403     {
03404       PT = &P_xform[0][0];
03405       NT = &N_xform[0][0];
03406     }
03407     else
03408     {
03409       mesh_xform = 0;
03410     }
03411   }
03412 
03413   const float* f;
03414   double w;
03415 
03416   if ( mesh_N && ON_TextureMapping::ray_projection == box_mapping.m_projection )
03417         {
03418                 // calculation uses mesh vertex normal
03419     if ( PT && NT )
03420     {
03421       // need to transform vertex and normal
03422       // before calculating texture coordinates
03423       f = &mesh_V[vi].x;
03424                   w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
03425       w = (0.0 != w) ? 1.0/w : 1.0;
03426                   P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
03427                   P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
03428                   P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
03429 
03430       f = &mesh_N[vi].x;
03431       N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
03432                   N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
03433                   N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
03434       N.Unitize();
03435     }
03436     else
03437     {
03438       // mesh vertex and normal are ok
03439                   P = mesh_V[vi];
03440                   N = mesh_N[vi];
03441     }
03442         }
03443         else if ( PT )
03444   {
03445     // normal is not used
03446     // mesh vertex needs to be transformed
03447     f = &mesh_V[vi].x;
03448           w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
03449     w = (0.0 != w) ? 1.0/w : 1.0;
03450           P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
03451           P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
03452           P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
03453   }
03454   else
03455   {
03456     // normal is not used and mesh vertex is ok
03457     P = mesh_V[vi];
03458   }
03459 
03460 
03461   rc = EvBoxSideTextureCoordinateHelper2(side,box_mapping,P,N,&tc);
03462   if (rc)
03463   {
03464     rc = tc.IsValid();
03465     if (rc)
03466     {
03467       *Tx = (float)tc.x;
03468       *Ty = (float)tc.y;
03469     }
03470   }
03471         return rc;
03472 }
03473 
03474 
03475 class ON__CNewMeshFace
03476 {
03477 public:
03478   int fi;
03479   int newvcnt;
03480   bool bNewV[4];
03481   ON_2fPoint tc[4];
03482 };
03483 
03484 static
03485 float TcDistanceHelper(const ON_2fPoint& tc)
03486 {
03487   float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x;
03488   if ( dx < 0.0f) 
03489     return 0.0f;
03490   float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y;
03491   if ( dy < 0.0f) 
03492     return 0.0f;
03493   return (dx < dy) ? dx : dy;
03494 }
03495 
03496 static
03497 void AdjustSingleBoxTextureCoordinatesHelper( 
03498           ON_Mesh& mesh, 
03499           const ON_Xform* mesh_xform,
03500           float* mesh_T,
03501           int    mesh_T_stride,
03502           const int* Tsd,
03503           const ON_TextureMapping& box_mapping
03504           )
03505 {
03506   const int vcnt = mesh.m_V.Count();
03507   const int fcnt = mesh.m_F.Count();
03508   if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() || !Tsd )
03509     return;
03510   const ON_MeshFace* mesh_F = mesh.m_F.Array();
03511   const int* Fvi;
03512   int j, k, fi, sd[4], fvicnt, side, newvcnt=0;
03513   ON__CNewMeshFace mf;
03514   ON_2fPoint tc;
03515   ON_SimpleArray<ON__CNewMeshFace> mflist(512);
03516   float d;
03517   for ( fi = 0; fi < fcnt; fi++ )
03518   {
03519     Fvi = mesh_F[fi].vi;
03520     sd[0] = Tsd[Fvi[0]];
03521     sd[1] = Tsd[Fvi[1]];
03522     sd[2] = Tsd[Fvi[2]];
03523     sd[3] = Tsd[Fvi[3]];
03524     if ( sd[0] == sd[1] && sd[0] == sd[2] && sd[0] == sd[3] )
03525     {
03526       // all texture coords are on same side of box
03527       continue;
03528     }
03529     fvicnt = (Fvi[2] != Fvi[3]) ? 4 : 3;
03530 
03531     memset(&mf,0,sizeof(mf));
03532     mf.tc[0] = mesh_T + (Fvi[0]*mesh_T_stride);
03533     mf.tc[1] = mesh_T + (Fvi[1]*mesh_T_stride);
03534     mf.tc[2] = mesh_T + (Fvi[2]*mesh_T_stride);
03535     mf.tc[3] = mesh_T + (Fvi[3]*mesh_T_stride);
03536 
03537     // find the side we will use for this face
03538     side = sd[0];
03539     d = TcDistanceHelper(mf.tc[0]);
03540     for ( j = 1; j < fvicnt; j++ )
03541     {
03542       float d1 = TcDistanceHelper(mf.tc[j]);
03543       if (d1 > d)
03544       {
03545         side = sd[j];
03546         d = d1;
03547       }
03548     }
03549 
03550     // Jussi, 5th September 2011:
03551     // This 'continue' only works for faces having one or more of its tc's in (0,1)x(0,1).
03552     // I have commented it out as a fix to RR 90329.
03553     //if ( d <= 0.0f )
03554     //  continue;
03555 
03556     for ( j = 0; j < fvicnt; j++ )
03557     {
03558       if ( sd[j] != side )
03559       {
03560         // calculate new tc for this side
03561         if ( EvBoxSideTextureCoordinateHelper1(
03562           mesh,
03563           mesh_xform,
03564           Fvi[j],
03565           side,
03566           box_mapping,
03567           &tc.x,&tc.y) )
03568         {
03569           if ( tc.x != mf.tc[j].x || tc.y != mf.tc[j].y )
03570           {
03571             mf.tc[j] = tc;
03572             mf.bNewV[j] = true;
03573             mf.newvcnt++;
03574           }
03575         }
03576         else
03577           break;
03578       }
03579     }
03580     if ( j >= fvicnt && mf.newvcnt > 0 )
03581     {
03582       mf.fi = fi;
03583       newvcnt += mf.newvcnt;
03584       mflist.Append(mf);
03585     }
03586   }
03587 
03588   if ( newvcnt <= 0 )
03589     return;
03590 
03591   ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T);
03592   
03593   const int mflist_count = mflist.Count();
03594 
03595   for ( k = 0; k < mflist_count; k++ )
03596   {
03597     mf = mflist[k];
03598     int* fvi = mesh.m_F[mf.fi].vi;
03599     fvicnt = (fvi[2]!=fvi[3]) ? 4 : 3;
03600     for ( j = 0; j < fvicnt; j++ )
03601     {
03602       if ( mf.bNewV[j] )
03603       {
03604         helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride);
03605       }
03606     }    
03607   }
03608 }
03609 
03610 static 
03611 void AdjustMeshPeriodicTextureCoordinatesHelper( 
03612           ON_Mesh& mesh, 
03613           const ON_Xform* mesh_xform,
03614           float* mesh_T,
03615           int    mesh_T_stride,
03616           const int* Tsd,
03617           double two_pi_tc,
03618           const ON_TextureMapping& mapping
03619           )
03620 {
03621   // This helper adjusts texture coordinates on faces that
03622   // span the seam on mapping spheres and cylinders and
03623   // resolves the mulitiple valued problem that
03624   // exists at the poles of sphere mappings.
03625 
03626   const int vcnt = mesh.m_V.Count();
03627   const int fcnt = mesh.m_F.Count();
03628   if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() )
03629     return;
03630 
03631   // see if any texture coordinate adjustment is necessary
03632   const ON_TextureMapping::TYPE mapping_type = mapping.m_type;
03633   const bool bSphereCheck = ( ON_TextureMapping::sphere_mapping == mapping_type );
03634   const bool bCylinderCheck = (Tsd && ON_TextureMapping::cylinder_mapping == mapping_type);
03635   const bool bBoxCheck = (Tsd && ON_TextureMapping::box_mapping == mapping_type);
03636 
03637   if ( bBoxCheck && ON_TextureMapping::single == mapping.m_texture_space )
03638   {
03639     AdjustSingleBoxTextureCoordinatesHelper( mesh, mesh_xform, mesh_T, mesh_T_stride, Tsd, mapping );
03640     return;
03641   }
03642 
03643   ON_Workspace ws;
03644   int* quad = ws.GetIntMemory(vcnt); // ~ws will free quad memory
03645   float* Tx = (float*)ws.GetMemory(vcnt*sizeof(Tx[0]));
03646   float t;
03647   int vi, ti, q=0;
03648   int ftc_count = 0;
03649 
03650   const float ang0 = (float)(0.25*two_pi_tc);
03651   const float ang1 = (float)(0.75*two_pi_tc);
03652 
03653 
03654   for ( vi = ti = 0; vi < vcnt; vi++, ti += mesh_T_stride )
03655   {
03656     quad[vi] = 0;
03657     Tx[vi] = mesh_T[ti];
03658     if ( bCylinderCheck )
03659     {
03660       if ( 1 != Tsd[vi] )
03661         continue;
03662     }
03663     else if ( bBoxCheck )
03664     {
03665       if ( 1 != Tsd[vi] && 3 != Tsd[vi] )
03666         continue;
03667     }
03668     else if ( bSphereCheck )
03669     {
03670       t = mesh_T[ti+1]; // t = "v" texture coordinate
03671       if ( t < 0.001f )
03672       {
03673         quad[vi] = 8; q |= 8; // south pole point
03674         ftc_count++;
03675         continue;
03676       }
03677       if ( t > 0.999f )
03678       {
03679         quad[vi] = 8; q |= 8; // north pole point
03680         ftc_count++;
03681         continue;
03682       }
03683     }
03684 
03685     t = Tx[vi]; // t = "u" texture coordinate
03686     if ( t < ang0 )
03687     {      
03688       quad[vi] = 1; q |= 1; // longitude < pi/2
03689       ftc_count++;
03690     }
03691     else if ( t > ang1 )
03692     {
03693       quad[vi] = 4; q |= 4; // longitude > 3pi/2
03694       ftc_count++;
03695     }
03696   }
03697 
03698   if ( 0 == q || 1 == q || 4 == q )
03699   {
03700     // nothing needs to be adjusted
03701     return;
03702   }
03703 
03704   // 4*ftc_count = (over) estimate of the number of faces that
03705   // will be changed.
03706   ON_SimpleArray<ON__CMeshFaceTC> ftc_list(ftc_count*4 + 128);
03707   ftc_count = 0;
03708   const ON_MeshFace* F = mesh.m_F.Array();
03709   const int* Fvi;
03710   int fi;
03711   ON__CMeshFaceTC ftc;
03712   memset(&ftc,0,sizeof(ftc));
03713   float t0, t1;
03714 
03715   for ( fi = 0; fi < fcnt; fi++ )
03716   {
03717     Fvi = F[fi].vi;
03718 
03719     ftc.quad[0] = quad[Fvi[0]];
03720     ftc.quad[1] = quad[Fvi[1]];
03721     ftc.quad[2] = quad[Fvi[2]];
03722     ftc.quad[3] = quad[Fvi[3]];
03723 
03724     q = (ftc.quad[0] | ftc.quad[1] | ftc.quad[2] | ftc.quad[3]);
03725     if ( 0 == q || 1 == q || 4 == q )
03726     {
03727       // no adjustments need to be made
03728       continue;
03729     }
03730 
03731     // ftc.fi will be set to fi if a texture coordinate needs to be adjusted
03732     ftc.fi = -1; 
03733 
03734     ftc.Tx[0] = Tx[Fvi[0]];
03735     ftc.Tx[1] = Tx[Fvi[1]];
03736     ftc.Tx[2] = Tx[Fvi[2]];
03737     ftc.Tx[3] = Tx[Fvi[3]];
03738 
03739     if ( 0 != (8&q) )
03740     {
03741       // see if check for north/south sphere mapping poles and fix them
03742       if ( 8 == ftc.quad[0] ) 
03743       {
03744         t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3];
03745         t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
03746         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
03747         {
03748           ftc.Tx[0] = PoleFix(t0,t1);
03749           ftc.quad[0] = ((ftc.Tx[0] < ang0) ? 1 : ((ftc.Tx[0] > ang1) ? 4 : 0));
03750           q |= ftc.quad[0];
03751           ftc.fi = fi;
03752         }
03753       }
03754       if ( 8 == ftc.quad[1] ) 
03755       {
03756         t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
03757         t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
03758         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
03759         {
03760           ftc.Tx[1] = PoleFix(t0,t1);
03761           ftc.quad[1] = ((ftc.Tx[1] < ang0) ? 1 : ((ftc.Tx[1] > ang1) ? 4 : 0));
03762           q |= ftc.quad[1];
03763           ftc.fi = fi;
03764         }
03765       }
03766       if ( 8 == ftc.quad[2] ) 
03767       {
03768         int k = (Fvi[2] == Fvi[3]) ? 0 : 3;
03769         t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
03770         t1 = (8 == ftc.quad[k]) ? ON_UNSET_FLOAT : ftc.Tx[k];
03771         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
03772         {
03773           ftc.Tx[2] = PoleFix(t0,t1);
03774           ftc.quad[2] = ((ftc.Tx[2] < ang0) ? 1 : ((ftc.Tx[2] > ang1) ? 4 : 0));
03775           if ( !k )
03776           {
03777             ftc.Tx[3] = ftc.Tx[2];
03778             ftc.quad[3] = ftc.quad[2];
03779           }
03780           q |= ftc.quad[2];
03781           ftc.fi = fi;
03782         }
03783       }
03784       if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] ) 
03785       {
03786         t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
03787         t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
03788         if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
03789         {
03790           ftc.Tx[3] = PoleFix(t0,t1);
03791           ftc.quad[3] = ((ftc.Tx[3] < ang0) ? 1 : ((ftc.Tx[3] > ang1) ? 4 : 0));
03792           q |= ftc.quad[3];
03793           ftc.fi = fi;
03794         }
03795       }
03796     }
03797 
03798     if ( 5 == (5&q) )
03799     {
03800       // The face has corners on both sides of the seam
03801       if ( two_pi_tc == 1.0 )
03802       {
03803         if ( 1 == ftc.quad[0] ) {ftc.Tx[0] += 1.0f; ftc.fi = fi;}
03804         if ( 1 == ftc.quad[1] ) {ftc.Tx[1] += 1.0f; ftc.fi = fi;}
03805         if ( 1 == ftc.quad[2] ) {ftc.Tx[2] += 1.0f; ftc.fi = fi;}
03806         if ( 1 == ftc.quad[3] ) {ftc.Tx[3] += 1.0f; ftc.fi = fi;}
03807       }
03808       else
03809       {
03810         // With divided textures, wrapping the texture coordinate
03811         // does not work because it wraps into a region of the
03812         // texture not use by this "side".  In this case, the
03813         // only thing to do is to pick the best end of the texture
03814         // map and clamp the tcs that hang over.  If the mesh
03815         // has edges near the texture seam, the picture will
03816         // still look ok.
03817         float f0=0.0f, f1=0.0f, twopitc = (float)two_pi_tc;;
03818         //int f0cnt=0, f1cnt=0;
03819         if ( 1 == ftc.quad[0] ) f0 += ftc.Tx[0]; else if ( 4 == ftc.quad[0] ) f1 += twopitc-ftc.Tx[0];
03820         if ( 1 == ftc.quad[1] ) f0 += ftc.Tx[1]; else if ( 4 == ftc.quad[1] ) f1 += twopitc-ftc.Tx[1];
03821         if ( 1 == ftc.quad[2] ) f0 += ftc.Tx[2]; else if ( 4 == ftc.quad[2] ) f1 += twopitc-ftc.Tx[2];
03822         if (Fvi[2] != Fvi[3])
03823         {
03824           if ( 1 == ftc.quad[3] ) f0 += ftc.Tx[3]; else if ( 4 == ftc.quad[3] ) f1 += twopitc-ftc.Tx[3];
03825         }
03826         if (f0 >= f1 )
03827         {
03828           // "most" of the face is on the left side of the texture 
03829           // If a vertex is on the right side, clamp its tc to 0.
03830           if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;}
03831           if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;}
03832           if ( 4 == ftc.quad[2] ) {ftc.Tx[2] = 0.0f; ftc.fi = fi;}
03833           if ( 4 == ftc.quad[3] ) {ftc.Tx[3] = 0.0f; ftc.fi = fi;}
03834         }
03835         else
03836         {
03837           // "most" of the face is on the right side of the texture 
03838           // If a vertex is on the left side, clamp its tc to two_pi_tc.
03839           if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;}
03840           if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;}
03841           if ( 1 == ftc.quad[2] ) {ftc.Tx[2] = twopitc; ftc.fi = fi;}
03842           if ( 1 == ftc.quad[3] ) {ftc.Tx[3] = twopitc; ftc.fi = fi;}
03843         }
03844       }
03845     }
03846 
03847     if ( ftc.fi >= 0 )
03848     {
03849       // face will require special handling
03850       ftc_list.Append(ftc);    
03851     }
03852   }
03853 
03854   ftc_count = ftc_list.Count();
03855   if ( ftc_count <= 0 )
03856     return;
03857 
03858   // Count the number of new vertices that will be added.
03859   int ftci;
03860   int newvcnt = 0;
03861   for ( ftci = 0; ftci < ftc_count; ftci++ )
03862   {
03863     ON__CMeshFaceTC& ftc = ftc_list[ftci];
03864     Fvi = F[ftc.fi].vi;
03865     if ( ftc.Tx[0] != Tx[Fvi[0]] )
03866     {
03867       ftc.bSetT[0] = true;
03868       newvcnt++;
03869     }
03870     if ( ftc.Tx[1] != Tx[Fvi[1]] )
03871     {
03872       ftc.bSetT[1] = true;
03873       newvcnt++;
03874     }
03875     if ( ftc.Tx[2] != Tx[Fvi[2]] )
03876     {
03877       ftc.bSetT[2] = true;
03878       newvcnt++;
03879     }
03880     if ( Fvi[2] != Fvi[3] )
03881     {
03882       if ( ftc.Tx[3] != Tx[Fvi[3]] )
03883       {
03884         ftc.bSetT[3] = true;
03885         newvcnt++;
03886       }
03887     }
03888   }
03889 
03890   if ( newvcnt <= 0 )
03891     return;
03892 
03893 
03894   F = 0; // Setting them to NULL makes sure anybody who
03895          // tries to use them below will crash.
03896 
03897   // reserve room for new vertex information
03898   ON__CChangeTextureCoordinateHelper helper(mesh,newvcnt,mesh_T);
03899 
03900   // add vertices and update mesh faces
03901   for ( ftci = 0; ftci < ftc_count; ftci++ )
03902   {
03903     const ON__CMeshFaceTC& ftc = ftc_list[ftci];
03904     int* meshFvi = mesh.m_F[ftc.fi].vi;
03905 
03906     if ( ftc.bSetT[0] )
03907     {
03908       helper.ChangeTextureCoordinate(meshFvi,0,ftc.Tx[0],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
03909     }
03910     if ( ftc.bSetT[1] )
03911     {
03912       helper.ChangeTextureCoordinate(meshFvi,1,ftc.Tx[1],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
03913     }
03914     if ( ftc.bSetT[2] )
03915     {
03916       helper.ChangeTextureCoordinate(meshFvi,2,ftc.Tx[2],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
03917     }
03918     if ( ftc.bSetT[3] )
03919     {
03920       helper.ChangeTextureCoordinate(meshFvi,3,ftc.Tx[3],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
03921     }
03922   }
03923 }
03924 
03925 static
03926 bool SeamCheckHelper( const ON_TextureMapping& mp, 
03927                       double& two_pi_tc, 
03928                       ON_SimpleArray<int>& Tside,
03929                       ON_SimpleArray<int>*& Tsd )
03930 {
03931   bool bSeamCheck = false;
03932   switch(mp.m_type)
03933   {
03934     case ON_TextureMapping::box_mapping:
03935       if ( ON_TextureMapping::divided == mp.m_texture_space )
03936       {
03937         if ( mp.m_bCapped )
03938           two_pi_tc = 2.0/3.0;
03939         Tsd = &Tside;
03940         bSeamCheck = true;
03941       }
03942       else if ( ON_TextureMapping::single == mp.m_texture_space )
03943       {
03944         Tsd = &Tside;
03945         bSeamCheck = true;
03946       }
03947       break;
03948 
03949     case ON_TextureMapping::cylinder_mapping:
03950       if ( ON_TextureMapping::divided == mp.m_texture_space )
03951       {
03952         two_pi_tc = 2.0/3.0;
03953         Tsd = &Tside;
03954       }
03955       bSeamCheck = true;
03956       break;
03957 
03958     case ON_TextureMapping::sphere_mapping:
03959       bSeamCheck = true;
03960       break;
03961 
03962     default:
03963       // intentionally skip other enum values
03964       break;
03965   }
03966 
03967   return bSeamCheck;
03968 }
03969 
03970 //If there are unused vertices, this may not work correctly - but it is very fast.
03971 static inline bool HasSharedVertices(const ON_Mesh& mesh)
03972 {
03973   return mesh.m_V.Count() < ((mesh.TriangleCount() * 3) + (mesh.QuadCount() * 4));
03974 }
03975 
03976 
03977 const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates( 
03978         const class ON_TextureMapping& mapping,
03979                                 const class ON_Xform* mesh_xform,
03980         bool bLazy
03981         )
03982 {
03983   if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
03984     ComputeVertexNormals();
03985 
03986   ON_TextureMapping mp = mapping;
03987   double two_pi_tc = 1.0;
03988   ON_SimpleArray<int> Tside;
03989   ON_SimpleArray<int>* Tsd = 0;
03990   bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
03991   if ( bSeamCheck )
03992     mp.m_uvw.Identity();
03993 
03994   ON_TextureCoordinates* TC = 0;
03995   {
03996     for ( int i = 0; i < m_TC.Count(); i++ )
03997     {
03998       if ( m_TC[i].m_tag.m_mapping_id == mapping.m_mapping_id )
03999       {
04000         TC = &m_TC[i];
04001         break;
04002       }
04003     }
04004   }
04005   if ( bLazy && TC && mapping.HasMatchingTextureCoordinates( TC->m_tag, mesh_xform ) )
04006     return TC;
04007 
04008   if ( !TC )
04009   {
04010     m_TC.AppendNew();
04011     TC = m_TC.Last();
04012   }
04013 
04014   // Use mp instead of mapping to call GetTextureCoordinates()
04015   // because m_uvw must be the identity if we have seams.
04016   if ( !mp.GetTextureCoordinates( *this,TC->m_T,mesh_xform,false,Tsd) )
04017   {
04018     int tci = (int)(TC - m_TC.Array());
04019     m_TC.Remove(tci);
04020     return 0;
04021   }
04022 
04023   TC->m_tag.Set(mapping);
04024   if (    mesh_xform && mesh_xform->IsValid()
04025        && !mesh_xform->IsIdentity() 
04026        && !mesh_xform->IsZero()
04027      )
04028   {
04029     TC->m_tag.m_mesh_xform = *mesh_xform;
04030   }
04031 
04032   TC->m_dim = 2;
04033 
04034   if ( bSeamCheck &&  m_F.Count() > 0 && TC->m_T.Count() == m_V.Count() )
04035   {
04036     float* mesh_T = (float*)TC->m_T.Array();
04037     int mesh_T_stride = sizeof(TC->m_T[0])/sizeof(mesh_T[0]);
04038     if ( Tsd && Tside.Count() != m_V.Count() )
04039       Tsd = 0;
04040     AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
04041     mesh_T = 0; // when the array is grown, the pointer may become invalid
04042     if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
04043     {
04044       // Apply the uvw transformation that is on mapping
04045       // to the texture coordinates.
04046       ON_3dPoint T;
04047       int vi, vcnt = TC->m_T.Count();
04048       ON_3fPoint* meshT = TC->m_T.Array();
04049       for ( vi = 0; vi < vcnt; vi++ )
04050       {
04051         T = meshT[vi];
04052         T = mapping.m_uvw*T;
04053         meshT[vi] = T;
04054       }
04055     }
04056   }
04057 
04058   return TC;
04059 }
04060 
04061 bool ON_Mesh::SetTextureCoordinates(
04062                   const class ON_TextureMapping& mapping, 
04063                   const class ON_Xform* mesh_xform,
04064                   bool bLazy
04065                   )
04066 {
04067   if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
04068     ComputeVertexNormals();
04069 
04070   InvalidateTextureCoordinateBoundingBox();
04071 
04072   ON_SimpleArray<int> Tside;
04073   ON_SimpleArray<int>* Tsd = 0;
04074   ON_TextureMapping mp = mapping;
04075   
04076   double two_pi_tc = 1.0;
04077 
04078   bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
04079   if ( bSeamCheck )
04080     mp.m_uvw.Identity();
04081 
04082   // Use mp instead of mapping to call GetTextureCoordinates()
04083   // because m_uvw must be the identity if we have seams.
04084   bool rc = mp.GetTextureCoordinates(*this,m_T,mesh_xform,bLazy,Tsd);
04085   
04086   if (rc)
04087   {
04088     // update the texture coordinate tag
04089     m_Ttag.Set(mapping);
04090     if (    mesh_xform 
04091          && mesh_xform->IsValid() 
04092          && !mesh_xform->IsIdentity() 
04093          && !mesh_xform->IsZero() 
04094        )
04095     {
04096       m_Ttag.m_mesh_xform  = *mesh_xform;
04097     }
04098   }
04099 
04100   if ( rc && bSeamCheck && HasTextureCoordinates() && m_F.Count() > 0 )
04101   {
04102     float* mesh_T = (float*)m_T.Array();
04103     int mesh_T_stride = sizeof(m_T[0])/sizeof(mesh_T[0]);
04104     if ( Tsd && Tside.Count() != m_V.Count() )
04105       Tsd = 0;
04106     AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
04107     mesh_T = 0; // when the array is grown, the pointer may become invalid
04108     if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
04109     {
04110       // Apply the uvw transformation that is on mapping
04111       // to the texture coordinates.
04112       ON_2fPoint* meshT = m_T.Array();
04113       ON_3dPoint T;
04114       int vi, vcnt = m_T.Count();
04115       for ( vi = 0; vi < vcnt; vi++ )
04116       {
04117         T.x = meshT[vi].x;
04118         T.y = meshT[vi].y;
04119         T.z = 0.0;
04120         T = mapping.m_uvw*T;
04121         meshT[vi].x = (float)T.x;
04122         meshT[vi].y = (float)T.y;
04123       }
04124     }
04125   }
04126 
04127   return rc;
04128 }
04129 
04130 ON_MappingChannel::ON_MappingChannel()
04131 {
04132   Default();
04133 }
04134 
04135 void ON_MappingChannel::Default()
04136 {
04137   memset(this,0,sizeof(*this));
04138   m_mapping_channel_id = 1;
04139   m_mapping_index = -1;
04140   m_object_xform.Identity();
04141 }
04142 
04143 int ON_MappingChannel::Compare( const ON_MappingChannel& other ) const
04144 {
04145   int rc = m_mapping_channel_id - other.m_mapping_channel_id;
04146   if (!rc)
04147     rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id);
04148   return rc;
04149 }
04150 
04151 bool ON_MappingChannel::Write( ON_BinaryArchive& archive ) const
04152 {
04153   bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
04154   if (rc)
04155   {
04156     rc = archive.WriteInt(m_mapping_channel_id);
04157     if (rc) rc = archive.WriteUuid(m_mapping_id);
04158 
04159     // 1.1 field added 6 June 2006
04160     if (rc) rc = archive.WriteXform(m_object_xform);
04161 
04162     if ( !archive.EndWrite3dmChunk() )
04163       rc = false;
04164   }
04165   return rc;
04166 }
04167 
04168 bool ON_MappingChannel::Read( ON_BinaryArchive& archive )
04169 {
04170   Default();
04171   int major_version = 0;
04172   int minor_version = 0;
04173   bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
04174   if (rc)
04175   {
04176     rc = (1 == major_version);
04177     if (rc) rc = archive.ReadInt(&m_mapping_channel_id);
04178     if (rc) rc = archive.ReadUuid(m_mapping_id);
04179 
04180     if ( rc && minor_version >= 1 )
04181     {
04182       // 1.1 field added 6 June 2006
04183       if (rc) rc = archive.ReadXform(m_object_xform);
04184       if (rc 
04185           && archive.ArchiveOpenNURBSVersion() < 200610030 
04186           && m_object_xform.IsZero()
04187           )
04188       {
04189         // Between versions 200606060 and 200610030,
04190         // there was a bug that created some mapping
04191         // channels with zero transformations.  This
04192         // if clause finds those and sets them to the
04193         // identity.
04194         m_object_xform.Identity();
04195       }
04196     }
04197 
04198     if ( !archive.EndRead3dmChunk() )
04199       rc = false;
04200   }
04201   return rc;
04202 }
04203 
04204 
04205 ON_MaterialRef::ON_MaterialRef()
04206 {
04207   Default();
04208 }
04209 
04210 ON_MappingRef::ON_MappingRef()
04211 {
04212   Default();
04213 }
04214 
04215 void ON_MaterialRef::Default()
04216 {
04217   memset(this,0,sizeof(*this));
04218   // runtme index value of -1 means not set
04219   m_material_index          = -1;
04220   m_material_backface_index = -1;
04221   m_material_source = ON::material_from_layer;
04222 }
04223 
04224 void ON_MappingRef::Default()
04225 {
04226   m_plugin_id = ON_nil_uuid;
04227   m_mapping_channels.Destroy();
04228 }
04229 
04230 int ON_MaterialRef::Compare( const ON_MaterialRef& other ) const
04231 {
04232   int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
04233   if (rc)
04234     rc = ((int)m_material_source) - ((int)other.m_material_source);
04235   if (!rc)
04236     rc = ON_UuidCompare(m_material_id,other.m_material_id);
04237   if (!rc)
04238     rc = ON_UuidCompare(m_material_backface_id,other.m_material_backface_id);
04239   return rc;
04240 }
04241 
04242 int ON_MappingRef::Compare( const ON_MappingRef& other ) const
04243 {
04244   int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
04245   if ( !rc)
04246   {
04247     const int count = m_mapping_channels.Count();
04248     rc = count - other.m_mapping_channels.Count();
04249     if (!rc)
04250     {
04251       for ( int i = 0; i < count && !rc; i++ )
04252       {
04253         rc = m_mapping_channels[i].Compare(other.m_mapping_channels[i]);
04254       }
04255     }
04256   }
04257   return rc;
04258 }
04259 
04260 
04261 bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const
04262 {
04263   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 );
04264   if (rc)
04265   {
04266     if (rc) rc = archive.WriteUuid( m_plugin_id );
04267     if (rc) rc = archive.WriteUuid( m_material_id );
04268 
04269     // 23 May 2006 Dale lear
04270     //   m_mapping_channels[] was removed from ON_MaterialRef.
04271     //   To keep from breaking the file format, I need to
04272     //   write a zero as the array length.
04273     //
04274     //if (rc) rc = archive.WriteArray( m_mapping_channels );
04275     if (rc) rc = archive.WriteInt(0);
04276 
04277     // 23 May 2006 added 
04278     if (rc) rc = archive.WriteUuid( m_material_backface_id );
04279     if (rc) rc = archive.WriteInt( m_material_source );
04280 
04281 
04282     if ( !archive.EndWrite3dmChunk() )
04283       rc = false;
04284   }
04285   return rc;
04286 }
04287 
04288 bool ON_MaterialRef::Read( ON_BinaryArchive& archive )
04289 {
04290   Default();
04291   int major_version = 0;
04292   int minor_version = 0;
04293   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
04294   if (rc)
04295   {
04296     rc = (1 == major_version);
04297 
04298     if (rc) rc = archive.ReadUuid( m_plugin_id );
04299     if (rc) rc = archive.ReadUuid( m_material_id );
04300 
04301     // 23 May 2006 Dale lear
04302     //   m_mapping_channels[] was removed from ON_MaterialRef.
04303     //   To keep from breaking the file format, I need to
04304     //   write a zero as the array length.
04305     ON_SimpleArray<ON_MappingChannel> obsolete_mapping_channels;
04306     if (rc) rc = archive.ReadArray( obsolete_mapping_channels );
04307 
04308     if ( minor_version >= 1 )
04309     {
04310       if (rc) rc = archive.ReadUuid( m_material_backface_id );
04311       int i = m_material_source;
04312       if (rc) rc = archive.ReadInt( &i );
04313       if (rc) m_material_source = (unsigned char)ON::ObjectMaterialSource(i);
04314     }
04315 
04316     if ( !archive.EndRead3dmChunk() )
04317       rc = false;
04318   }
04319   return rc;
04320 }
04321 
04322 ON::object_material_source ON_MaterialRef::MaterialSource() const
04323 {
04324   return ON::ObjectMaterialSource(m_material_source);
04325 }
04326 
04327 bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const
04328 {
04329   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
04330   if (rc)
04331   {
04332     if (rc) rc = archive.WriteUuid( m_plugin_id );
04333     if (rc) rc = archive.WriteArray( m_mapping_channels );
04334     
04335     if ( !archive.EndWrite3dmChunk() )
04336       rc = false;
04337   }
04338   return rc;
04339 }
04340 
04341 bool ON_MappingRef::Read( ON_BinaryArchive& archive )
04342 {
04343   Default();
04344   int major_version = 0;
04345   int minor_version = 0;
04346   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
04347   if (rc)
04348   {
04349     rc = (1 == major_version);
04350 
04351     if (rc) rc = archive.ReadUuid( m_plugin_id );
04352     if (rc) rc = archive.ReadArray( m_mapping_channels );
04353 
04354     if ( !archive.EndRead3dmChunk() )
04355       rc = false;
04356   }
04357   return rc;
04358 }
04359 
04360 bool ON_MappingRef::Transform( const ON_Xform& xform )
04361 {
04362   int count = m_mapping_channels.Count();
04363   if ( count > 0 )
04364   {
04365     for ( ON_MappingChannel* mapping_channel = m_mapping_channels.Array();
04366           count--;
04367           mapping_channel++ )
04368     {
04369       mapping_channel->m_object_xform = xform*mapping_channel->m_object_xform;
04370     }
04371   }
04372   return true;
04373 }
04374 
04375 ON_ObjectRenderingAttributes::ON_ObjectRenderingAttributes()
04376 {
04377   Default();
04378 }
04379 
04380 ON_RenderingAttributes::ON_RenderingAttributes()
04381 {
04382   Default();
04383 }
04384 
04385 void ON_ObjectRenderingAttributes::Default()
04386 {
04387   ON_RenderingAttributes::Default();
04388   m_mappings.Destroy();
04389   m_bCastsShadows = true;
04390   m_bReceivesShadows = true;
04391   m_bits = 0;
04392   m_reserved1 = 0;
04393 }
04394 
04395 void ON_RenderingAttributes::Default()
04396 {
04397   m_materials.Destroy();
04398 }
04399 
04400 void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b)
04401 {
04402   if ( b )
04403     m_bits |= 1;    // set bit 1
04404   else 
04405     m_bits &= 0xFE; // clear bit 1
04406 }
04407 
04408 bool ON_ObjectRenderingAttributes::AdvancedTexturePreview() const
04409 {
04410   return (0 != (1 & m_bits)) ? true : false;
04411 }
04412 
04413 bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const
04414 {
04415   // plug-in uuids must be unique
04416   int count;
04417   if( (count = m_materials.Count()) > 1 )
04418   {
04419     const ON_MaterialRef* mr = m_materials.Array();
04420     ON_UUID plugin_id;
04421     int i, j;
04422     for ( i = 0; i < count-1; i++ )
04423     {
04424       plugin_id = mr[i].m_plugin_id;
04425       for ( j = i+1; j < count; j++ )
04426       {
04427         if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
04428         {
04429           if( text_log )
04430           {
04431             text_log->Print("ON_RenderingAttributes error: m_materials[%d] and m_materials[%d] have the same plug-in id.\n",i,j);
04432           }
04433           return false;
04434         }
04435       }
04436     }    
04437   }
04438   return true;
04439 }
04440 
04441 bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const
04442 {
04443   if ( !ON_RenderingAttributes::IsValid(text_log) )
04444     return false;
04445 
04446   // plug-in uuids must be unique
04447   int count;
04448   if( (count = m_mappings.Count()) > 1 )
04449   {
04450     const ON_MappingRef* mr = m_mappings.Array();
04451     ON_UUID plugin_id;
04452     int i, j;
04453     for ( i = 0; i < count-1; i++ )
04454     {
04455       plugin_id = mr[i].m_plugin_id;
04456       for ( j = i+1; j < count; j++ )
04457       {
04458         if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
04459         {
04460           if( text_log )
04461           {
04462             text_log->Print("ON_ObjectRenderingAttributes error: m_mappings[%d] and m_mappings[%d] have the same plug-in id.\n",i,j);
04463           }
04464           return false;
04465         }
04466       }
04467     }    
04468   }
04469 
04470   return true;
04471 }
04472 
04473 int ON_RenderingAttributes::Compare( const ON_RenderingAttributes& other ) const
04474 {
04475   const int count = m_materials.Count();
04476   int rc = count - other.m_materials.Count();
04477   if (!rc)
04478   {
04479     int i;
04480     for ( i = 0; i < count && !rc; i++ )
04481     {
04482       rc = m_materials[i].Compare(other.m_materials[i]);
04483     }
04484   }
04485   return rc;
04486 }
04487 
04488 const ON_MaterialRef* ON_RenderingAttributes::MaterialRef( const ON_UUID& plugin_id ) const
04489 {
04490   int count;
04491   if ( (count = m_materials.Count()) > 0 )
04492   {
04493     for ( const ON_MaterialRef* mr = m_materials.Array(); count--; mr++ )
04494     {
04495       if ( plugin_id == mr->m_plugin_id )
04496         return mr;
04497     }
04498   }
04499   return 0;
04500 }
04501 
04502 int ON_ObjectRenderingAttributes::Compare( const ON_ObjectRenderingAttributes& other ) const
04503 {
04504   int rc = ON_RenderingAttributes::Compare(other);
04505   if (!rc)
04506   {
04507     int i;
04508     const int count = m_mappings.Count();
04509     rc = other.m_mappings.Count() - count;
04510     for ( i = 0; i < count && !rc; i++ )
04511     {
04512       rc = m_mappings[i].Compare(other.m_mappings[i]);
04513     }
04514     if ( !rc )
04515     {
04516       rc = ((int)(m_bCastsShadows?1:0)) - ((int)(other.m_bCastsShadows?1:0));
04517       if ( !rc )
04518       {
04519         rc = ((int)(m_bReceivesShadows?1:0)) - ((int)(other.m_bReceivesShadows?1:0));
04520       }
04521           if ( !rc )
04522           {
04523             rc = ((int)(AdvancedTexturePreview()?1:0)) - ((int)(other.AdvancedTexturePreview()?1:0));
04524           }
04525     }
04526   }
04527   return rc;
04528 }
04529 
04530 bool ON_ObjectRenderingAttributes::Transform( const ON_Xform& xform )
04531 {
04532   int i;
04533   if ( (i = m_mappings.Count()) > 0 )
04534   {
04535     for( ON_MappingRef* mr = m_mappings.Array(); i--; mr++ )
04536       mr->Transform(xform);
04537   }
04538   return true;
04539 }
04540 
04541 const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef(
04542   const ON_UUID& plugin_id ) const
04543 {
04544   int count;
04545   if ( (count = m_mappings.Count()) > 0 )
04546   {
04547     for ( const ON_MappingRef* mr = m_mappings.Array(); count--; mr++ )
04548     {
04549       if ( plugin_id == mr->m_plugin_id )
04550         return mr;
04551     }    
04552   }
04553   return 0;
04554 }
04555 
04556 ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef( 
04557   const ON_UUID& plugin_id 
04558   )
04559 {
04560   ON_MappingRef* mr = 0;
04561   int count;
04562   if ( (count = m_mappings.Count()) > 0 )
04563   {
04564     for ( mr = const_cast<ON_MappingRef*>(m_mappings.Array()); count--; mr++ )
04565     {
04566       if ( plugin_id == mr->m_plugin_id )
04567         break;
04568     }    
04569   }
04570 
04571   if ( !mr )
04572   {
04573     mr = &m_mappings.AppendNew();
04574     mr->m_plugin_id = plugin_id;
04575   }
04576 
04577   return mr;
04578 }
04579 
04580 bool ON_ObjectRenderingAttributes::DeleteMappingRef( 
04581   const ON_UUID& plugin_id 
04582   )
04583 {
04584   const ON_MappingRef* mr = MappingRef(plugin_id);
04585   if ( mr ) 
04586     m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion
04587   return (0 != mr);  
04588 }
04589 
04590 const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( 
04591   const ON_UUID& plugin_id, 
04592   const ON_UUID& mapping_id
04593   ) const
04594 {
04595   const ON_MappingRef* mr = MappingRef(plugin_id);
04596   if ( mr )
04597   {
04598     int count;
04599     if ( (count = mr->m_mapping_channels.Count()) > 0 )
04600     {
04601       for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
04602       {
04603         if ( mapping_id == mc->m_mapping_id )
04604           return mc;
04605       }
04606     }
04607   }
04608   return 0;
04609 }
04610 
04611 const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel( 
04612   const ON_UUID& plugin_id, 
04613   int mapping_channel_id
04614   ) const
04615 {
04616   const ON_MappingRef* mr = MappingRef(plugin_id);
04617   if ( mr )
04618   {
04619     int count;
04620     if ( (count = mr->m_mapping_channels.Count()) > 0 )
04621     {
04622       for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
04623       {
04624         if ( mapping_channel_id == mc->m_mapping_channel_id )
04625           return mc;
04626       }
04627     }
04628   }
04629   return 0;
04630 }
04631 
04632 
04633 
04634 bool ON_ObjectRenderingAttributes::AddMappingChannel(
04635         const ON_UUID& plugin_id, 
04636         int mapping_channel_id,
04637         const ON_UUID& mapping_id
04638         )
04639 {
04640   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
04641   if ( !mr )
04642   {
04643     mr = &m_mappings.AppendNew();
04644     mr->m_plugin_id = plugin_id;
04645     ON_MappingChannel& mc = mr->m_mapping_channels.AppendNew();
04646     mc.m_mapping_channel_id = mapping_channel_id;
04647     mc.m_mapping_id = mapping_id;
04648     mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
04649     mc.m_object_xform.Identity();
04650     return true;
04651   }
04652 
04653   return mr->AddMappingChannel(mapping_channel_id,mapping_id);
04654 }
04655 
04656 bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
04657   const ON_UUID& plugin_id, 
04658   int mapping_channel_id
04659   )
04660 {
04661   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
04662   return mr ? mr->DeleteMappingChannel(mapping_channel_id) : false;
04663 }
04664 
04665 bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
04666   const ON_UUID& plugin_id, 
04667   const ON_UUID& mapping_id
04668   )
04669 {
04670   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
04671   return mr ? mr->DeleteMappingChannel(mapping_id) : false;
04672 }
04673 
04674 bool ON_ObjectRenderingAttributes::ChangeMappingChannel(
04675   const ON_UUID& plugin_id, 
04676   int old_mapping_channel_id,
04677   int new_mapping_channel_id
04678   )
04679 {
04680   ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
04681   return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false;
04682 }
04683 
04684 const ON_MappingChannel* ON_MappingRef::MappingChannel( 
04685   const ON_UUID& mapping_id
04686   ) const
04687 {
04688   int count;
04689   if ( (count = m_mapping_channels.Count()) > 0 )
04690   {
04691     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
04692     {
04693       if ( mapping_id == mc->m_mapping_id )
04694         return mc;
04695     }
04696   }
04697   return 0;
04698 }
04699 
04700 const ON_MappingChannel* ON_MappingRef::MappingChannel( 
04701   int mapping_channel_id
04702   ) const
04703 {
04704   int count;
04705   if ( (count = m_mapping_channels.Count()) > 0 )
04706   {
04707     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
04708     {
04709       if ( mapping_channel_id == mc->m_mapping_channel_id )
04710         return mc;
04711     }
04712   }
04713   return 0;
04714 }
04715 
04716 
04717 
04718 bool ON_MappingRef::AddMappingChannel(
04719         int mapping_channel_id,
04720         const ON_UUID& mapping_id
04721         )
04722 {
04723   int i;
04724   if ( (i = m_mapping_channels.Count()) > 0 )
04725   {
04726     for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); i--; mc++ )
04727     {
04728       if ( mapping_channel_id == mc->m_mapping_channel_id )
04729       {
04730         // a matching mapping channel id exists
04731         // return true if mapping_id matches
04732         return ( 0 == ON_UuidCompare(&mapping_id,&mc->m_mapping_id) );
04733       }
04734     }
04735   }
04736 
04737   ON_MappingChannel& mc   = m_mapping_channels.AppendNew();
04738   mc.m_mapping_channel_id = mapping_channel_id;
04739   mc.m_mapping_id         = mapping_id;
04740   mc.m_mapping_index      = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
04741   mc.m_object_xform.Identity();
04742 
04743   return true;
04744 }
04745 
04746 bool ON_MappingRef::DeleteMappingChannel(int mapping_channel_id)
04747 {
04748   const ON_MappingChannel* mc = MappingChannel(mapping_channel_id);
04749   if ( mc )
04750   {
04751     m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
04752   }
04753   return ( 0 != mc);
04754 }
04755 
04756 bool ON_MappingRef::DeleteMappingChannel(const ON_UUID& mapping_id)
04757 {
04758   const ON_MappingChannel* mc = MappingChannel(mapping_id);
04759   if ( mc )
04760   {
04761     m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
04762   }
04763   return ( 0 != mc);
04764 }
04765 
04766 bool ON_MappingRef::ChangeMappingChannel(
04767   int old_mapping_channel_id,
04768   int new_mapping_channel_id
04769   )
04770 {
04771   ON_MappingChannel* mc = const_cast<ON_MappingChannel*>(MappingChannel(old_mapping_channel_id));
04772   if ( mc )
04773   {
04774     mc->m_mapping_channel_id = new_mapping_channel_id;
04775   }
04776   return ( 0 != mc );
04777 }
04778 
04779 bool ON_RenderingAttributes::Write( ON_BinaryArchive& archive ) const
04780 {
04781   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
04782   if ( !rc )
04783     return false;
04784   for(;;)
04785   {
04786     rc = archive.WriteArray(m_materials);
04787     if ( !rc ) break;
04788 
04789     break;
04790   }
04791   if ( !archive.EndWrite3dmChunk() )
04792     rc = false;
04793   return rc;
04794 }
04795 
04796 bool ON_RenderingAttributes::Read( ON_BinaryArchive& archive )
04797 {
04798   Default();
04799   int major_version = 0;
04800   int minor_version = 0;
04801   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
04802   if (!rc) 
04803     return false;
04804   for(;;)
04805   {
04806     rc = ( 1 == major_version );
04807     if (!rc) break;
04808     rc = archive.ReadArray(m_materials);
04809     if (!rc) break;
04810 
04811     break;
04812   }
04813   if ( !archive.EndRead3dmChunk() )
04814     rc = false;
04815   return rc;
04816 }
04817 
04818 bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const
04819 {
04820   bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 );
04821   if ( !rc )
04822     return false;
04823   for(;;)
04824   {
04825     // DO NOT CALL ON_RenderingAttributes::Write 
04826     rc = archive.WriteArray(m_materials);
04827     if ( !rc ) break;
04828     rc = archive.WriteArray(m_mappings);
04829     if ( !rc ) break;
04830 
04831     // version 1.2 fields added 20061129
04832     rc = archive.WriteBool(m_bCastsShadows);
04833     if ( !rc ) break;
04834     rc = archive.WriteBool(m_bReceivesShadows);
04835     if ( !rc ) break;
04836 
04837     // version 1.3 fields added 20101019
04838     bool b = AdvancedTexturePreview();
04839     rc = archive.WriteBool(b);
04840     if ( !rc ) break;
04841 
04842     break;
04843   }
04844   if ( !archive.EndWrite3dmChunk() )
04845     rc = false;
04846   return rc;
04847 }
04848 
04849 bool ON_ObjectRenderingAttributes::Read( ON_BinaryArchive& archive )
04850 {
04851   Default();
04852   int major_version = 0;
04853   int minor_version = 0;
04854   bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
04855   if (!rc) 
04856     return false;
04857   for(;;)
04858   {
04859     rc = ( 1 == major_version && minor_version >= 1 );
04860     if (!rc) break;
04861 
04862     // DO NOT CALL ON_RenderingAttributes::Read 
04863     if (rc) rc = archive.ReadArray(m_materials);
04864     if (!rc) break;
04865     if (rc) rc = archive.ReadArray(m_mappings);
04866     if (!rc) break;
04867 
04868     if ( minor_version <= 1 )
04869       break;
04870 
04871     // version 1.2 fields added 20061129
04872     rc = archive.ReadBool(&m_bCastsShadows);
04873     if ( !rc ) break;
04874     rc = archive.ReadBool(&m_bReceivesShadows);
04875     if ( !rc ) break;
04876 
04877     if ( minor_version <= 2 )
04878       break;
04879 
04880     // version 1.3 fields added 20101019
04881     bool b = AdvancedTexturePreview();
04882     rc = archive.ReadBool(&b);
04883     if ( !rc ) break;
04884     // Jussi 20120430: We don't want to enable advanced texture preview by default. It will be
04885     //                 turned on when needed (depending on active render plug-in etc).
04886     //EnableAdvancedTexturePreview(b);
04887 
04888     break;
04889   }
04890   if ( !archive.EndRead3dmChunk() )
04891     rc = false;
04892 
04893   return rc;
04894 }
04895 
04896 
04897 bool ON_TextureMapping::SetSurfaceParameterMapping(void)
04898 {
04899   Default();
04900         m_type = srfp_mapping;
04901   ON_CreateUuid(m_mapping_id);
04902         return true;
04903 }
04904 
04905 
04906 bool ON_TextureMapping::SetPlaneMapping(
04907           const ON_Plane& plane,
04908           const ON_Interval& dx,
04909           const ON_Interval& dy,
04910           const ON_Interval& dz
04911           )
04912 {
04913   Default();
04914 
04915   // Don't call plane.IsValid(), because the plane
04916   // equation does not matter and many developers
04917   // forget to set it correctly.
04918   if ( !plane.origin.IsValid() )
04919     return false;
04920   if ( !ON_IsRightHandFrame( plane.xaxis, plane.yaxis, plane.zaxis ) )
04921     return false;
04922   if ( !dx.IsValid() || !dy.IsValid() || !dz.IsValid() )
04923     return false;
04924 
04925   ON_3dPoint C = plane.PointAt(dx.Mid(),dy.Mid(),dz.Mid());
04926   C.x = (0.0 == C.x) ? 0.0 : -C.x;
04927   C.y = (0.0 == C.y) ? 0.0 : -C.y;
04928   C.z = (0.0 == C.z) ? 0.0 : -C.z;
04929   ON_3dVector xaxis = plane.xaxis;
04930   ON_3dVector yaxis = plane.yaxis;
04931   ON_3dVector zaxis = plane.zaxis;
04932 
04933   // Any "cleanup" needs to be done here 
04934   // to xaxis, yaxis, zaxis.
04935   
04936   double sx,sy,sz;
04937   if ( 0.0 == (sx = dx.Length())) sx = 2.0;
04938   if ( 0.0 == (sy = dy.Length())) sy = 2.0;
04939   if ( 0.0 == (sz = dz.Length())) sz = 2.0;
04940 
04941   // The plane mapping matrix m_Pxyz transforms the
04942   // world coordinate rectangle to a (-1<=r<=1,
04943   // on plane to a 
04944   // 1 X 1 square in the xy plane centered at the
04945   // origin.  
04946 
04947   // m_Pxyz = surface point transformation
04948   ON_3dVector X = (2.0/sx)*xaxis;
04949   ON_3dVector Y = (2.0/sy)*yaxis;
04950   ON_3dVector Z = (2.0/sz)*zaxis;
04951 
04952   m_Pxyz.m_xform[0][0] = X.x;
04953   m_Pxyz.m_xform[0][1] = X.y;
04954   m_Pxyz.m_xform[0][2] = X.z;
04955   m_Pxyz.m_xform[0][3] = (X.x*C.x + X.y*C.y + X.z*C.z);
04956 
04957   m_Pxyz.m_xform[1][0] = Y.x;
04958   m_Pxyz.m_xform[1][1] = Y.y;
04959   m_Pxyz.m_xform[1][2] = Y.z;
04960   m_Pxyz.m_xform[1][3] = (Y.x*C.x + Y.y*C.y + Y.z*C.z);
04961 
04962   m_Pxyz.m_xform[2][0] = Z.x;
04963   m_Pxyz.m_xform[2][1] = Z.y;
04964   m_Pxyz.m_xform[2][2] = Z.z;
04965   m_Pxyz.m_xform[2][3] = (Z.x*C.x + Z.y*C.y + Z.z*C.z);
04966 
04967   m_Pxyz.m_xform[3][0] = 0.0;
04968   m_Pxyz.m_xform[3][1] = 0.0;
04969   m_Pxyz.m_xform[3][2] = 0.0;
04970   m_Pxyz.m_xform[3][3] = 1.0;
04971 
04972   // m_Nxyz = surface normal transformation
04973   //        = inverse transpose of upper 3x3 of m_Pxyz
04974   X = (0.5*sx)*xaxis;
04975   Y = (0.5*sy)*yaxis;
04976   Z = (0.5*sz)*zaxis;
04977   m_Nxyz.m_xform[0][0] = X.x;
04978   m_Nxyz.m_xform[0][1] = X.y;
04979   m_Nxyz.m_xform[0][2] = X.z;
04980   m_Nxyz.m_xform[0][3] = 0.0;
04981 
04982   m_Nxyz.m_xform[1][0] = Y.x;
04983   m_Nxyz.m_xform[1][1] = Y.y;
04984   m_Nxyz.m_xform[1][2] = Y.z;
04985   m_Nxyz.m_xform[1][3] = 0.0;
04986 
04987   m_Nxyz.m_xform[2][0] = Z.x;
04988   m_Nxyz.m_xform[2][1] = Z.y;
04989   m_Nxyz.m_xform[2][2] = Z.z;
04990   m_Nxyz.m_xform[2][3] = 0.0;
04991 
04992   m_Nxyz.m_xform[3][0] = 0.0;
04993   m_Nxyz.m_xform[3][1] = 0.0;
04994   m_Nxyz.m_xform[3][2] = 0.0;
04995   m_Nxyz.m_xform[3][3] = 1.0;
04996 
04997   m_type = plane_mapping;
04998   ON_CreateUuid(m_mapping_id);
04999 
05000 #if defined(ON_DEBUG)
05001   {
05002     ON_Plane p;
05003     p.xaxis = (2.0/sx)*plane.xaxis;
05004     p.yaxis = (2.0/sy)*plane.yaxis;
05005     p.zaxis = (2.0/sz)*plane.zaxis;
05006     p.origin.Set(-C.x,-C.y,-C.z);
05007     p.UpdateEquation();
05008     ON_Xform P_dbg, N_dbg;
05009     P_dbg.Rotation(p,ON_xy_plane);
05010     P_dbg.GetSurfaceNormalXform(N_dbg);
05011 
05012     for ( int i = 0; i < 4; i++ )
05013     {
05014       for ( int j = 0; j < 4; j++ )
05015       {
05016         if ( fabs(m_Pxyz[i][j] - P_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Pxyz[i][j])+128.0) )
05017         {
05018           ON_ERROR("m_Pxyz is nor right\n");
05019           break;
05020         }
05021         if ( fabs(m_Nxyz[i][j] - N_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Nxyz[i][j])+128.0) )
05022         {
05023           ON_ERROR("m_Nxyz is nor right\n");
05024           break;
05025         }
05026       }
05027     }
05028   }
05029 #endif
05030         return true;
05031 }
05032 
05033 bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane,
05034                                       ON_Interval dx,
05035                                       ON_Interval dy,
05036                                       ON_Interval dz,
05037                                       bool bCapped 
05038                                       )
05039 {
05040   bool rc = SetPlaneMapping(plane,dx,dy,dz);
05041   if (rc)
05042   {
05043     m_bCapped = bCapped;
05044     m_type = ON_TextureMapping::box_mapping;
05045   }
05046   return rc;
05047 }
05048 
05049 bool ON_TextureMapping::SetCylinderMapping(const ON_Cylinder& cylinder, bool bIsCapped)
05050 {
05051   ON_Interval dr, dh;
05052   if ( !ON_IsValid(cylinder.circle.radius ) )
05053     return false;
05054   double r = cylinder.circle.radius;
05055   if ( 0.0 == r )
05056     r = 1.0;
05057   dr.Set(-r,r);
05058   dh.Set(cylinder.height[0],cylinder.height[1]);
05059   if ( dh[0] == dh[1] )
05060   {
05061     if ( ON_UNSET_VALUE == dh[0] )
05062     {
05063       dh.Set(-1.0,1.0);
05064     }
05065     else
05066     {
05067       dh.m_t[0] -= 1.0;
05068       dh.m_t[0] += 1.0;
05069     }
05070   }
05071   if ( !dh.IsValid() )
05072     return false;
05073 
05074   bool rc = SetBoxMapping(cylinder.circle.plane,dr,dr,dh,bIsCapped);
05075   if (rc)
05076   {
05077           m_type = cylinder_mapping;
05078   }
05079 
05080         return rc;
05081 }
05082 
05083 bool ON_TextureMapping::SetSphereMapping(const ON_Sphere& sphere)
05084 {
05085   ON_Interval dr(-sphere.radius,sphere.radius);
05086   bool rc = SetBoxMapping(sphere.plane,dr,dr,dr,false);
05087   if (rc)
05088   {
05089           m_type = sphere_mapping;
05090   }
05091         return rc;
05092 }
05093 
05094 
05095 
05096 bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane,
05097                                         ON_Interval& dx,
05098                                         ON_Interval& dy,
05099                                         ON_Interval& dz
05100                                         ) const
05101 {
05102   ON_Xform xform(m_Pxyz);
05103 
05104   ON_3dVector S(((ON_3dVector*)&xform.m_xform[0])->Length(),
05105                 ((ON_3dVector*)&xform.m_xform[1])->Length(),
05106                 ((ON_3dVector*)&xform.m_xform[2])->Length());
05107 
05108   if ( 0.0 == S.x )
05109     return false;
05110   S.x = 1.0/S.x;
05111   if ( 0.0 == S.y )
05112     return false;
05113   S.y = 1.0/S.y;
05114   if ( 0.0 == S.z )
05115     return false;
05116   S.z = 1.0/S.z;
05117 
05118   xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x;
05119   xform.m_xform[0][3] *= S.x;  
05120   
05121   xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y;
05122   xform.m_xform[1][3] *= S.y;  
05123 
05124   xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z;
05125   xform.m_xform[2][3] *= S.z;
05126 
05127   xform.m_xform[3][0] = 0.0;
05128   xform.m_xform[3][1] = 0.0;
05129   xform.m_xform[3][2] = 0.0;
05130   xform.m_xform[3][3] = 1.0;
05131 
05132   ON_Xform inv(xform);
05133   if ( !inv.Invert() )
05134     return false;
05135 
05136   plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]);
05137   xform.m_xform[0][3] = 0.0; 
05138   xform.m_xform[1][3] = 0.0;
05139   xform.m_xform[2][3] = 0.0;
05140   plane.xaxis = &xform.m_xform[0][0];
05141   plane.yaxis = &xform.m_xform[1][0];
05142   plane.zaxis = &xform.m_xform[2][0];
05143 
05144         plane.UpdateEquation();
05145 
05146   dx.Set(-S.x,S.x);
05147   dy.Set(-S.y,S.y);
05148   dz.Set(-S.z,S.z);
05149 
05150   return plane.IsValid();
05151 }
05152 
05153 bool ON_TextureMapping::GetMappingBox(ON_Plane& plane,
05154                                       ON_Interval& dx,
05155                                       ON_Interval& dy,
05156                                       ON_Interval& dz) const
05157 {
05158         return GetMappingPlane(plane, dx, dy, dz);
05159 }
05160 
05161 bool ON_TextureMapping::GetMappingCylinder(ON_Cylinder& cylinder) const
05162 {
05163   ON_Interval dx, dy, dz;
05164   ON_Plane plane;
05165   bool rc = GetMappingPlane(cylinder.circle.plane, dx, dy, dz);
05166   if (rc)
05167   {
05168     double r0 = 0.5*dx.Length();
05169     double r1 = 0.5*dy.Length();
05170     cylinder.circle.radius = (r0 == r1) ? r0 : 0.5*(r0+r1);
05171     cylinder.height[0] = dz[0];
05172     cylinder.height[1] = dz[1];
05173   }
05174 
05175   return rc && cylinder.IsValid();
05176 }
05177 
05178 bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const
05179 {
05180   ON_Interval dx, dy, dz;
05181   bool rc = GetMappingPlane(sphere.plane, dx, dy, dz);
05182   if (rc)
05183   {
05184     double r0 = 0.5*dx.Length();
05185     double r1 = 0.5*dy.Length();
05186     double r2 = 0.5*dz.Length();
05187     sphere.radius = (r0 == r1 && r0 == r2) ? r0 : (r0+r1+r2)/3.0;
05188   }
05189   return rc && sphere.IsValid();
05190 }
05191 


pcl
Author(s): Open Perception
autogenerated on Wed Aug 26 2015 15:27:01