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