Files
opennurbs/opennurbs_material.cpp
2020-01-16 10:59:30 -08:00

7111 lines
189 KiB
C++

/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2012 Robert McNeel & Associates. All rights reserved.
// OpenNURBS, Rhinoceros, and Rhino3D are registered trademarks of Robert
// McNeel & Associates.
//
// THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY.
// ALL IMPLIED WARRANTIES OF FITNESS FOR ANY PARTICULAR PURPOSE AND OF
// MERCHANTABILITY ARE HEREBY DISCLAIMED.
//
// For complete openNURBS copyright information see <http://www.opennurbs.org>.
//
////////////////////////////////////////////////////////////////
*/
#include "opennurbs.h"
#if !defined(ON_COMPILING_OPENNURBS)
// This check is included in all opennurbs source .c and .cpp files to insure
// ON_COMPILING_OPENNURBS is defined when opennurbs source is compiled.
// When opennurbs source is being compiled, ON_COMPILING_OPENNURBS is defined
// and the opennurbs .h files alter what is declared and how it is declared.
#error ON_COMPILING_OPENNURBS must be defined when compiling opennurbs
#endif
////////////////////////////////////////////////////////////////
// Class ON_Material
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT(ON_Material,ON_ModelComponent,"60B5DBBC-E660-11d3-BFE4-0010830122F0");
const ON_Material* ON_Material::FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
const ON_Material* none_return_value
)
{
const ON_Material* p = ON_Material::Cast(model_component_reference.ModelComponent());
return (nullptr != p) ? p : none_return_value;
}
// Default constructor
ON_Material::ON_Material() ON_NOEXCEPT
: ON_ModelComponent(ON_ModelComponent::Type::RenderMaterial)
{}
ON_Material::ON_Material( const ON_Material& src)
: ON_ModelComponent(ON_ModelComponent::Type::RenderMaterial,src)
{
Internal_CopyFrom(src);
}
void ON_Material::Internal_CopyFrom(
const ON_Material& src
)
{
#define ON_COPY_SRC(m) m=src.m
ON_COPY_SRC(m_rdk_material_instance_id);
ON_COPY_SRC(m_ambient);
ON_COPY_SRC(m_diffuse);
ON_COPY_SRC(m_emission);
ON_COPY_SRC(m_specular);
ON_COPY_SRC(m_reflection);
ON_COPY_SRC(m_transparent);
ON_COPY_SRC(m_bShareable);
ON_COPY_SRC(m_bDisableLighting);
ON_COPY_SRC(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture);
ON_COPY_SRC(m_bFresnelReflections);
ON_COPY_SRC(m_reflectivity);
ON_COPY_SRC(m_shine);
ON_COPY_SRC(m_transparency);
ON_COPY_SRC(m_reflection_glossiness);
ON_COPY_SRC(m_refraction_glossiness);
ON_COPY_SRC(m_index_of_refraction);
ON_COPY_SRC(m_fresnel_index_of_refraction);
ON_COPY_SRC(m_textures);
ON_COPY_SRC(m_material_channel);
ON_COPY_SRC(m_plugin_id);
#undef ON_COPY_SRC
}
bool ON_Material::IsValid( ON_TextLog* text_log ) const
{
return true;
}
void
ON_Material::Dump( ON_TextLog& dump ) const
{
ON_ModelComponent::Dump(dump);
dump.Print("ambient rgb = "); dump.PrintRGB( m_ambient ); dump.Print("\n");
dump.Print("diffuse rgb = "); dump.PrintRGB( m_diffuse ); dump.Print("\n");
dump.Print("emmisive rgb = "); dump.PrintRGB( m_emission ); dump.Print("\n");
dump.Print("specular rgb = "); dump.PrintRGB( m_specular ); dump.Print("\n");
dump.Print("reflection rgb = "); dump.PrintRGB( m_reflection ); dump.Print("\n");
dump.Print("transparent rgb = "); dump.PrintRGB( m_transparent ); dump.Print("\n");
dump.Print("shine = %g%%\n",100.0*m_shine/ON_Material::MaxShine );
dump.Print("transparency = %g%%\n",100.0*m_transparency);
dump.Print("reflectivity = %g%%\n",100.0*m_reflectivity);
dump.Print("index of refraction = %g\n",m_index_of_refraction);
dump.Print("plug-in id = "); dump.Print(m_plugin_id); dump.Print("\n");
int i;
for( i = 0; i < m_textures.Count(); i++ )
{
dump.Print("texture[%d]:\n",i);
dump.PushIndent();
m_textures[i].Dump(dump);
dump.PopIndent();
}
}
ON_UUID ON_Material::MaterialPlugInId() const
{
return m_plugin_id;
}
void ON_Material::SetMaterialPlugInId(
ON_UUID plugin_id
)
{
if (m_plugin_id != plugin_id)
{
m_plugin_id = plugin_id;
IncrementContentVersionNumber();
}
}
bool ON_Material::Write( ON_BinaryArchive& archive ) const
{
if (archive.Archive3dmVersion() < 60)
return Internal_WriteV5(archive);
const int major_version = 1;
const int minor_version = 0;
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version))
return false;
bool rc = false;
for (;;)
{
const unsigned int attributes_filter
= ON_ModelComponent::Attributes::IndexAttribute
| ON_ModelComponent::Attributes::IdAttribute
| ON_ModelComponent::Attributes::NameAttribute;
if ( !WriteModelComponentAttributes(archive,attributes_filter)) break;
if ( !archive.WriteUuid(m_plugin_id) ) break;
if ( !archive.WriteColor( m_ambient ) ) break;
if ( !archive.WriteColor( m_diffuse ) ) break;
if ( !archive.WriteColor( m_emission ) ) break;
if ( !archive.WriteColor( m_specular ) ) break;
if ( !archive.WriteColor( m_reflection ) ) break;
if ( !archive.WriteColor( m_transparent ) ) break;
if ( !archive.WriteDouble( m_index_of_refraction ) ) break;
if ( !archive.WriteDouble( m_reflectivity ) ) break;
if ( !archive.WriteDouble( m_shine ) ) break;
if (!archive.WriteDouble(m_transparency)) break;
// array of textures written in a way that user data persists
{
const int textures_major_version = 1;
const int textures_minor_version = 0;
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, textures_major_version, textures_minor_version))
break;
bool textures_chunk_rc = false;
for (;;)
{
const unsigned int count = m_textures.Count();
if (!archive.WriteInt(count))
break;
unsigned int i;
for (i = 0; i < count; i++)
{
if (!archive.WriteObject(&m_textures[i]))
break;
}
if (i < count)
break;
textures_chunk_rc = true;
break;
}
if (!archive.EndWrite3dmChunk())
textures_chunk_rc = false;
if ( !textures_chunk_rc )
break;
}
if ( !archive.WriteArray(m_material_channel) )
break;
if ( !archive.WriteBool(m_bShareable) )
break;
if ( !archive.WriteBool(m_bDisableLighting))
break;
if ( !archive.WriteBool(m_bFresnelReflections))
break;
if ( !archive.WriteDouble(m_reflection_glossiness))
break;
if ( !archive.WriteDouble(m_refraction_glossiness))
break;
if ( !archive.WriteDouble(m_fresnel_index_of_refraction))
break;
if ( !archive.WriteUuid(m_rdk_material_instance_id))
break;
if ( !archive.WriteBool(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture))
break;
rc = true;
break;
}
if (!archive.EndWrite3dmChunk())
rc = false;
return rc;
}
bool ON_Material::Read( ON_BinaryArchive& archive )
{
*this = ON_Material::Unset;
if (archive.Archive3dmVersion() < 60)
return Internal_ReadV5(archive);
if (archive.ArchiveOpenNURBSVersion() < 2348833910)
return Internal_ReadV5(archive);
bool rc = false;
int major_version = 0;
int minor_version = 0;
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
return false;
for (;;)
{
if (1 != major_version)
break;
if ( !ReadModelComponentAttributes(archive)) break;
if ( !archive.ReadUuid(m_plugin_id) ) break;
if ( !archive.ReadColor( m_ambient ) ) break;
if ( !archive.ReadColor( m_diffuse ) ) break;
if ( !archive.ReadColor( m_emission ) ) break;
if ( !archive.ReadColor( m_specular ) ) break;
if ( !archive.ReadColor( m_reflection ) ) break;
if ( !archive.ReadColor( m_transparent ) ) break;
if ( !archive.ReadDouble( &m_index_of_refraction ) ) break;
if ( !archive.ReadDouble( &m_reflectivity ) ) break;
if ( !archive.ReadDouble( &m_shine ) ) break;
if (!archive.ReadDouble(&m_transparency)) break;
// array of textures read in a way that user data persists
{
int textures_major_version = 0;
int textures_minor_version = 0;
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &textures_major_version, &textures_minor_version))
break;
bool textures_chunk_rc = false;
for (;;)
{
if (1 != textures_major_version)
break;
unsigned int count = 0;
if ( !archive.ReadInt(&count) )
break;
m_textures.SetCount(0);
m_textures.Reserve(count);
unsigned int i;
for (i = 0; i < count; i++)
{
if (!archive.ReadObject(m_textures.AppendNew()))
break;
}
if (i < count)
break;
textures_chunk_rc = true;
break;
}
if (!archive.EndRead3dmChunk())
textures_chunk_rc = false;
if (!textures_chunk_rc)
break;
}
if ( !archive.ReadArray(m_material_channel) )
break;
if ( !archive.ReadBool(&m_bShareable) )
break;
if ( !archive.ReadBool(&m_bDisableLighting))
break;
if ( !archive.ReadBool(&m_bFresnelReflections))
break;
if ( !archive.ReadDouble(&m_reflection_glossiness))
break;
if ( !archive.ReadDouble(&m_refraction_glossiness))
break;
if ( !archive.ReadDouble(&m_fresnel_index_of_refraction))
break;
if ( !archive.ReadUuid(m_rdk_material_instance_id))
break;
if ( !archive.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture))
break;
rc = true;
break;
}
if (!archive.EndRead3dmChunk())
rc = false;
return rc;
}
bool ON_Material::Internal_WriteV5( ON_BinaryArchive& file ) const
{
bool rc = false;
if ( file.Archive3dmVersion() <= 3 )
{
// V2 or V3 file format
rc = Internal_WriteV3(file);
}
else
{
// V4 file format
// The chunk version 2.0 prevents old V3 IO code from attempting
// to read this material
rc = file.Write3dmChunkVersion(2,0); // never change the 2,0
// version 1.2 field (20061113*)
// version 1.3 fields (20100917*)
// version 1.4 fields 6.0.2013-11-6
// version 1.5 fields 6.0.2014-05-16 (RDK material instance id)
// version 1.6 fields 6.0.2014-07-11 (m_bUseDiffuseTextureAlphaForObjectTransparencyTexture)
const int minor_version = (file.Archive3dmVersion() >= 60) ? 6 : 3;
if (rc) rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,minor_version);
if (rc)
{
for(;;)
{
if ( rc ) rc = file.WriteUuid(Id());
if ( rc ) rc = file.Write3dmReferencedComponentIndex(*this);
if ( rc ) rc = file.WriteString(Name());
if ( rc ) rc = file.WriteUuid(m_plugin_id);
if ( rc ) rc = file.WriteColor( m_ambient );
if ( rc ) rc = file.WriteColor( m_diffuse );
if ( rc ) rc = file.WriteColor( m_emission );
if ( rc ) rc = file.WriteColor( m_specular );
if ( rc ) rc = file.WriteColor( m_reflection );
if ( rc ) rc = file.WriteColor( m_transparent );
if ( rc ) rc = file.WriteDouble( m_index_of_refraction );
if ( rc ) rc = file.WriteDouble( m_reflectivity );
if ( rc ) rc = file.WriteDouble( m_shine );
if ( rc ) rc = file.WriteDouble( m_transparency );
if ( !rc )
break;
// array of textures written in a way that user data persists
rc = file.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,0);
if (rc)
{
int i, count = m_textures.Count();
rc = file.WriteInt(count);
for ( i = 0; i < count && rc; i++ )
{
rc = file.WriteObject(&m_textures[i]);
}
if ( !file.EndWrite3dmChunk() )
rc = false;
}
//version 1.1 field
ON_wString obsolete_flamingo_library = ON_wString::EmptyString;
if (rc) rc = file.WriteString(obsolete_flamingo_library);
// version 1.2 field (20061113)
if (rc) rc = file.WriteArray(m_material_channel);
// version 1.3 fields (20100917*)
rc = file.WriteBool(m_bShareable);
if (!rc) break;
rc = file.WriteBool(m_bDisableLighting);
if (!rc) break;
if ( minor_version >= 4 )
{
rc = file.WriteBool(m_bFresnelReflections);
if (!rc) break;
rc = file.WriteDouble(m_reflection_glossiness);
if (!rc) break;
rc = file.WriteDouble(m_refraction_glossiness);
if (!rc) break;
rc = file.WriteDouble(m_fresnel_index_of_refraction);
if (!rc) break;
if (minor_version >= 5)
{
rc = file.WriteUuid(m_rdk_material_instance_id);
if (!rc) break;
}
if (minor_version >= 6)
{
rc = file.WriteBool(m_bUseDiffuseTextureAlphaForObjectTransparencyTexture);
if (!rc) break;
}
}
break;
}
if (!file.EndWrite3dmChunk() )
rc = false;
}
}
if (rc && file.Archive3dmVersion() < 60)
{
if (RdkMaterialInstanceIdIsNotNil())
{
// For V5 files and earlier, we need to save
// the RDK material instance id as user data.
// Because ON_RdkMaterialInstanceIdObsoleteUserData.DeleteAfterWrite()
// returns true, the user data we are attaching here will be deleted
// after it is written.
ON_RdkMaterialInstanceIdObsoleteUserData* ud = new ON_RdkMaterialInstanceIdObsoleteUserData();
ud->m_rdk_material_instance_id = RdkMaterialInstanceId();
const_cast<ON_Material*>(this)->AttachUserData(ud);
}
}
return rc;
}
bool ON_Material::Internal_ReadV5( ON_BinaryArchive& file )
{
int major_version = 0;
int minor_version = 0;
bool rc = file.Read3dmChunkVersion(&major_version,&minor_version);
if (rc)
{
if ( 1 == major_version )
{
rc = Internal_ReadV3(file,minor_version);
}
else if ( 2 == major_version )
{
// fancy V4 material
// V4 file format
// The chunk version 2.0 prevents old V3 IO code from attempting
// to read this material
rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
if (rc)
{
for(;;)
{
ON_UUID material_id = Id();
if ( rc ) rc = file.ReadUuid(material_id);
if (rc) SetId(material_id);
int material_index = Index();
if ( rc ) rc = file.ReadInt(&material_index);
if (rc) SetIndex(material_index);
ON_wString material_name;
if ( rc ) rc = file.ReadString(material_name);
if (rc) SetName(material_name);
if ( rc ) rc = file.ReadUuid(m_plugin_id);
if ( rc ) rc = file.ReadColor( m_ambient );
if ( rc ) rc = file.ReadColor( m_diffuse );
if ( rc ) rc = file.ReadColor( m_emission );
if ( rc ) rc = file.ReadColor( m_specular );
if ( rc ) rc = file.ReadColor( m_reflection );
if ( rc ) rc = file.ReadColor( m_transparent );
if ( rc
&& file.ArchiveOpenNURBSVersion() < 200912010
&& 128 == m_transparent.Red()
&& 128 == m_transparent.Green()
&& 128 == m_transparent.Blue()
)
{
// Prior to 1 Dec 2009 the ON_Material::Defaults() set
// m_transparent to 128,128,128. This was the wrong
// value for the default. This "hack" is here to
// make it appear that the default was always white.
m_transparent = m_diffuse;
}
if ( rc ) rc = file.ReadDouble( &m_index_of_refraction );
if ( rc ) rc = file.ReadDouble( &m_reflectivity );
if ( rc ) rc = file.ReadDouble( &m_shine );
if ( rc ) rc = file.ReadDouble( &m_transparency );
if ( !rc )
break;
// array of textures read in a way that user data persists
int texmajver = 0;
int texminver = 0;
rc = file.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&texmajver,&texminver);
if (rc)
{
if ( 1 == texmajver )
{
int i, count = 0;
rc = file.ReadInt(&count);
if (rc) m_textures.Reserve(count);
for ( i = 0; i < count && rc; i++ )
{
int trc = file.ReadObject(m_textures.AppendNew());
if (trc <= 0)
{
// http://mcneel.myjetbrains.com/youtrack/issue/RH-30204
// Part of fixing crash reading corrupt file sem2observe copy.3dm
rc = false;
m_textures.Remove();
}
else if ( trc > 1 )
m_textures.Remove();
}
}
if ( !file.EndRead3dmChunk() )
rc = false;
}
if ( rc && minor_version >= 1 )
{
ON_wString obsolete_flamingo_library;
rc = file.ReadString(obsolete_flamingo_library);
if ( !rc ) break;
if ( minor_version >= 2 )
{
// version 1.2 field (20061113)
rc = file.ReadArray(m_material_channel);
if (!rc) break;
if ( minor_version >= 3 )
{
// version 1.3 fields (20100917*)
rc = file.ReadBool(&m_bShareable);
if (!rc) break;
rc = file.ReadBool(&m_bDisableLighting);
if (!rc) break;
if ( minor_version >= 4 )
{
rc = file.ReadBool(&m_bFresnelReflections);
if (!rc) break;
rc = file.ReadDouble(&m_reflection_glossiness);
if (!rc) break;
rc = file.ReadDouble(&m_refraction_glossiness);
if (!rc) break;
rc = file.ReadDouble(&m_fresnel_index_of_refraction);
if (!rc) break;
if (minor_version >= 5)
{
// version 1.5 field 6.0.2014-05-16
rc = file.ReadUuid(m_rdk_material_instance_id);
if (!rc) break;
}
if (minor_version >= 6)
{
// version 1.6 field 6.0.2014-07-11
rc = file.ReadBool(&m_bUseDiffuseTextureAlphaForObjectTransparencyTexture);
if (!rc) break;
}
}
}
}
}
break;
}
if (!file.EndRead3dmChunk() )
rc = false;
}
}
}
return rc;
}
bool ON_Material::Internal_WriteV3( ON_BinaryArchive& file ) const
{
int i;
// V2 and V3 file format
bool rc = file.Write3dmChunkVersion(1,1);
if ( rc ) rc = file.WriteColor( m_ambient );
if ( rc ) rc = file.WriteColor( m_diffuse );
if ( rc ) rc = file.WriteColor( m_emission );
if ( rc ) rc = file.WriteColor( m_specular );
if ( rc ) rc = file.WriteDouble( Shine() );
if (rc) rc = file.WriteDouble(m_transparency);
if (rc) rc = file.WriteChar((unsigned char)1); // OBSOLETE // m_casts_shadows
if (rc) rc = file.WriteChar((unsigned char)1); // OBSOLETE // m_shows_shadows
if (rc) rc = file.WriteChar((unsigned char)0); // OBSOLETE // m_wire_mode
if (rc) rc = file.WriteChar((unsigned char)2); // OBSOLETE // m_wire_density
if (rc) rc = file.WriteColor(ON_Color(0, 0, 0)); // OBSOLETE // m_wire_color
if (rc)
{
// OBSOLETE - line style info never used
short s = 0;
if (rc) rc = file.WriteShort(s);
if (rc) rc = file.WriteShort(s);
if (rc) rc = file.WriteDouble(0.0);
if (rc) rc = file.WriteDouble(1.0);
}
ON_wString filename;
int j = 0;
i = FindTexture(nullptr, ON_Texture::TYPE::bitmap_texture);
if (i >= 0)
{
const ON_Texture& tmap = m_textures[i];
const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath();
if (tmap_filename.Length() > 0)
{
filename = tmap_filename;
j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1;
}
}
// OBSOLETE // if ( rc ) rc = file.WriteString( TextureBitmapFileName() );
// OBSOLETE // i = TextureMode();
// OBSOLETE // if ( rc ) rc = file.WriteInt( i );
// OBSOLETE // if ( rc ) rc = file.WriteInt( m_texture_bitmap_index );
if (rc) rc = file.WriteString(filename);
if (rc) rc = file.WriteInt(j);
if (rc) rc = file.WriteInt(0);
filename.Destroy();
j = 0;
double bump_scale = 1.0;
i = FindTexture(nullptr, ON_Texture::TYPE::bump_texture);
if (i >= 0)
{
const ON_Texture& tmap = m_textures[i];
const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath();
if (tmap_filename.Length() > 0)
{
filename = tmap_filename;
j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1;
bump_scale = tmap.m_bump_scale[1];
}
}
// OBSOLETE //if ( rc ) rc = file.WriteString( BumpBitmapFileName() );
// OBSOLETE //i = BumpMode();
// OBSOLETE //if ( rc ) rc = file.WriteInt( i );
// OBSOLETE //if ( rc ) rc = file.WriteInt( m_bump_bitmap_index );
// OBSOLETE //if ( rc ) rc = file.WriteDouble( m_bump_scale );
if (rc) rc = file.WriteString(filename);
if (rc) rc = file.WriteInt(j);
if (rc) rc = file.WriteInt(0);
if (rc) rc = file.WriteDouble(bump_scale);
filename.Destroy();
j = 0;
i = FindTexture(nullptr, ON_Texture::TYPE::emap_texture);
if (i >= 0)
{
const ON_Texture& tmap = m_textures[i];
const ON_wString tmap_filename = tmap.m_image_file_reference.FullPath();
if (tmap_filename.Length() > 0)
{
filename = tmap_filename;
j = (ON_Texture::MODE::decal_texture == tmap.m_mode) ? 2 : 1;
}
}
// OBSOLETE //if ( rc ) rc = file.WriteString( EmapBitmapFileName() );
// OBSOLETE //i = EmapMode();
// OBSOLETE //if ( rc ) rc = file.WriteInt( i );
// OBSOLETE //if ( rc ) rc = file.WriteInt( m_emap_bitmap_index );
if (rc) rc = file.WriteString(filename);
if (rc) rc = file.WriteInt(j);
if (rc) rc = file.WriteInt(0);
if (rc) rc = file.Write3dmReferencedComponentIndex(*this);
if (rc) rc = file.WriteUuid(m_plugin_id);
const ON_wString obsolete_flamingo_library = ON_wString::EmptyString;
if (rc) rc = file.WriteString(obsolete_flamingo_library);
if (rc) rc = file.WriteString(Name());
// 1.1 fields
if (rc) rc = file.WriteUuid(Id());
if (rc) rc = file.WriteColor(m_reflection);
if (rc) rc = file.WriteColor(m_transparent);
if (rc) rc = file.WriteDouble(m_index_of_refraction);
return rc;
}
bool ON_Material::Internal_ReadV3( ON_BinaryArchive& file, int minor_version )
{
double shine = 0.0, transparency = 0.0;
int i, j;
bool rc = true;
{
// common to all version 1.x formats
if ( rc ) rc = file.ReadColor( m_ambient );
if ( rc ) rc = file.ReadColor( m_diffuse );
if ( rc ) rc = file.ReadColor( m_emission );
if ( rc ) rc = file.ReadColor( m_specular );
if ( rc ) rc = file.ReadDouble( &shine );
if ( rc ) SetShine(shine);
if ( rc ) rc = file.ReadDouble( &transparency );
if ( rc ) SetTransparency(transparency);
unsigned char obsolete_uc;
if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_casts_shadows
if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_shows_shadows
if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_mode
if ( rc ) rc = file.ReadChar( &obsolete_uc ); // m_wire_density
ON_Color obsolete_color;
if ( rc ) rc = file.ReadColor( obsolete_color ); // m_wire_color
if (rc)
{
// OBSOLETE if ( rc ) rc = file.ReadLineStyle( m_wire_style );
short s;
double x;
if (rc) rc = file.ReadShort(&s);
if (rc) rc = file.ReadShort(&s);
if (rc) rc = file.ReadDouble(&x);
if (rc) rc = file.ReadDouble(&x);
}
ON_wString str;
if ( rc ) rc = file.ReadString( str ); //sTextureBitmapFileName
i = 0;
j = 0;
if ( rc ) rc = file.ReadInt( &i );
// OBSOLETE // if ( rc ) SetTextureMode( ON::TextureMode(i) );
if ( rc ) rc = file.ReadInt( &j );//&m_texture_bitmap_index
if ( rc && !str.IsEmpty() )
{
ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::bitmap_texture)];
if ( 2 == i )
{
texture.m_mode = ON_Texture::MODE::decal_texture;
}
else
{
texture.m_mode = ON_Texture::MODE::modulate_texture;
}
}
if ( rc ) rc = file.ReadString( str ); // sBumpBitmapFileName
if ( rc ) rc = file.ReadInt( &i );
// OBSOLETE // if ( rc ) SetBumpMode( ON::TextureMode(i) );
if ( rc ) rc = file.ReadInt( &j );//&m_bump_bitmap_index );
double bump_scale = 0.0;
if ( rc ) rc = file.ReadDouble( &bump_scale );
if ( rc && !str.IsEmpty() )
{
ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::bump_texture)];
if ( 2 == i )
{
texture.m_mode = ON_Texture::MODE::decal_texture;
}
else
{
texture.m_mode = ON_Texture::MODE::modulate_texture;
}
texture.m_bump_scale.Set(0.0,bump_scale);
}
if ( rc ) rc = file.ReadString( str ); // sEmapBitmapFileName
if ( rc ) rc = file.ReadInt( &i );
// OBSOLETE // if ( rc ) SetEmapMode( ON::TextureMode(i) );
if ( rc ) rc = file.ReadInt( &j ); //&m_emap_bitmap_index;
if ( rc && !str.IsEmpty() )
{
ON_Texture& texture = m_textures[AddTexture(static_cast< const wchar_t* >(str),ON_Texture::TYPE::emap_texture)];
if ( 2 == i )
{
texture.m_mode = ON_Texture::MODE::decal_texture;
}
else
{
texture.m_mode = ON_Texture::MODE::modulate_texture;
}
}
int material_index = Index();
if ( rc ) rc = file.ReadInt( &material_index );
if (rc) SetIndex(material_index);
if ( rc ) rc = file.ReadUuid( m_plugin_id );
ON_wString obsolete_flamingo_library;
if ( rc ) rc = file.ReadString( obsolete_flamingo_library );
ON_wString material_name;
if ( rc ) rc = file.ReadString( material_name );
if (rc) SetName(material_name);
if ( minor_version >= 1 )
{
// 1.1 fields
ON_UUID material_id = Id();
if (rc) rc = file.ReadUuid( material_id );
SetId(material_id);
if (rc) rc = file.ReadColor( m_reflection);
if (rc) rc = file.ReadColor( m_transparent);
if (rc) rc = file.ReadDouble( &m_index_of_refraction );
}
else
{
// old material needs a valid non-nil id.
SetId();
}
}
return rc;
}
ON::object_type ON_Material::ObjectType() const
{
return ON::material_object;
}
int ON_Material::FindTexture( const wchar_t* filename,
ON_Texture::TYPE type,
int i0
) const
{
int i, count = m_textures.Count();
for (i = ((i0 < 0) ? 0 : (i0+1)); i < count; i++ )
{
if ( type != m_textures[i].m_type
&& type != ON_Texture::TYPE::no_texture_type )
{
continue;
}
const ON_wString texture_file_name = m_textures[i].m_image_file_reference.FullPath();
if ( filename && texture_file_name.ComparePath(filename) )
{
continue;
}
return i;
}
return -1;
}
double ON_Material::FresnelReflectionCoefficient(
double fresnel_index_of_refraction,
const double N[3],
const double R[3]
)
{
double x,y,z,c,g;
for(;;)
{
x = N[0]-R[0];
y = N[1]-R[1];
z = N[2]-R[2];
c = ON_Length3d(x,y,z);
if ( !(c > ON_DBL_MIN) )
break;
c = (N[0]*x + N[1]*y + N[2]*z)/c; // c = N o (N - R) / |N - R|
g = fresnel_index_of_refraction*fresnel_index_of_refraction + c*c - 1.0;
g = (g > 0.0) ? sqrt(g) : 0.0;
// unsafe (potiential divide by zero, overflow, ...) and inefficient
// return ( ((g-c)*(g-c))/(2*(g+c)*(g+c)) ) * (1.0f + ( ((c*(g+c)-1.0)*(c*(g+c)-1.0))/((c*(g+c)+1.0)*(c*(g+c)+1.0)) ) );
x = g+c;
if ( !(x != 0.0) )
break; // x is NAN or zero
z = (g-c)/x; // z = (g-c)/(g+c)
if ( fabs(z) <= 1.0e-154 )
return 0.0; // z*z below will be zero or denormalized
x *= c; // x = c*(g+c)
y = x + 1.0; // y = c*(g+c) + 1.0
if ( !(y != 0.0) )
break; // y is NAN or zero
y = (x - 1.0)/y; // y = (c*(g+c) - 1.0)/(c*(g+c) + 1.0)
x = 0.5*z*z*(1.0 + y*y);
if ( !(x == x) )
break; // x is NAN
if ( !ON_IS_FINITE(x) )
break; // x is infinity
return x; // x is
}
return 1.0; // error occured
}
double ON_Material::FresnelReflectionCoefficient(
ON_3dVector N,
ON_3dVector R
) const
{
return m_bFresnelReflections
? ON_Material::FresnelReflectionCoefficient(m_fresnel_index_of_refraction,&N.x,&R.x)
: 1.0;
}
int ON_Material::FindTexture( ON_UUID texture_id ) const
{
int i, count = m_textures.Count();
for (i = 0; i < count; i++ )
{
if ( !ON_UuidCompare(&texture_id,&m_textures[i].m_texture_id) )
return i;
}
return -1;
}
int ON_Material::DeleteTexture(const wchar_t* filename,ON_Texture::TYPE type )
{
int deleted_count = 0;
int i;
if ( !filename && type == ON_Texture::TYPE::no_texture_type )
{
deleted_count = m_textures.Count();
m_textures.Destroy();
}
else
{
for ( i = m_textures.Count()-1; i >= 0; i--)
{
if ( type != ON_Texture::TYPE::no_texture_type && type != m_textures[i].m_type )
continue;
if ( filename && m_textures[i].m_image_file_reference.FullPath().ComparePath(filename) )
continue;
m_textures.Remove(i);
deleted_count++;
}
}
return deleted_count;
}
int ON_Material::AddTexture( const ON_Texture& tx )
{
// has to copy user data
int i = FindTexture( static_cast< const wchar_t* >(tx.m_image_file_reference.FullPath()), tx.m_type );
if ( i < 0 )
{
i = m_textures.Count();
m_textures.Append(tx);
}
else
{
m_textures[i] = tx;
}
if ( ON_UuidIsNil(m_textures[i].m_texture_id) )
{
ON_CreateUuid(m_textures[i].m_texture_id);
}
return i;
}
int ON_Material::AddTexture(const wchar_t* filename,ON_Texture::TYPE type)
{
int ti = FindTexture(nullptr,type);
if ( ti < 0 )
{
ti = m_textures.Count();
m_textures.AppendNew();
}
if (ti >= 0 )
{
m_textures[ti].m_image_file_reference.SetFullPath(filename,false);
m_textures[ti].m_type = type;
m_textures[ti].m_mode = ON_Texture::MODE::modulate_texture;
m_textures[ti].m_magfilter = ON_Texture::FILTER::linear_filter;
ON_CreateUuid(m_textures[ti].m_texture_id);
}
return ti;
}
// Shine values are in range 0.0 to ON_Material::GetMaxShine()
double ON_Material::Shine() const
{
return m_shine;
}
void ON_Material::SetShine( double shine )
{
if (shine == shine) // NANs fail this test
{
if (shine < 0.0)
m_shine = 0.0;
else if (shine > ON_Material::MaxShine)
m_shine = ON_Material::MaxShine;
else
m_shine = (float)shine;
}
}
// Transparency values are in range 0.0 = opaque to 1.0 = transparent
double ON_Material::Transparency( ) const
{
return m_transparency;
}
void ON_Material::SetTransparency( double transparency )
{
if ( transparency < 0.0 )
m_transparency = 0.0;
else if ( transparency > 1.0)
m_transparency = 1.0;
else
m_transparency = transparency;
}
// Transparency values are in range 0.0 = opaque to 1.0 = transparent
double ON_Material::Reflectivity( ) const
{
return m_reflectivity;
}
void ON_Material::SetReflectivity( double reflectivity )
{
if ( reflectivity < 0.0 )
m_reflectivity = 0.0;
else if ( reflectivity > 1.0)
m_reflectivity = 1.0;
else
m_reflectivity = reflectivity;
}
bool operator==( const ON_Material& a, const ON_Material& b )
{
return (0 == ON_Material::Compare(a,b));
}
bool operator!=( const ON_Material& a, const ON_Material& b )
{
return (0 != ON_Material::Compare(a,b));
}
static int CompareNans(double a, double b)
{
if (a == a)
{
if (b == b)
{
return ( ( a < b ) ? -1 : ((a > b) ? 1 : 0 ) );
}
// a is not a NAN, b is a NAN
return -1; // a < b - NAN's are last
}
else if (b == b)
{
// a is a NAN, b is not a NAN
return -1; // b < a - NAN's are last
}
return 0; // a and b are both NaNs
}
static int CompareDouble( double a, double b )
{
return ( ( a < b ) ? -1 : ((a > b) ? 1 : (a==b ? 0 : CompareNans(a,b)) ) );
}
static int CompareXform( const ON_Xform& a, const ON_Xform& b )
{
int i,j;
const double* da = &a.m_xform[0][0];
const double* db = &b.m_xform[0][0];
i = 16;
j = 0;
while ( i-- && !j)
{
j = CompareDouble(*da++,*db++);
}
return j;
}
int ON_Texture::Compare(const ON_Texture& a, const ON_Texture& b)
{
int rc = ON_UuidCompare(&a.m_texture_id, &b.m_texture_id);
while (!rc)
{
if (a.m_mapping_channel_id < b.m_mapping_channel_id)
rc = -1;
else if (a.m_mapping_channel_id > b.m_mapping_channel_id)
rc = 1;
if (rc) break;
rc = a.m_image_file_reference.FullPath().ComparePath(static_cast< const wchar_t* >(b.m_image_file_reference.FullPath()));
if (rc) break;
rc = ((int)(a.m_bOn ? 1 : 0)) - ((int)(b.m_bOn ? 1 : 0));
if (rc) break;
rc = ((int)a.m_type) - ((int)b.m_type);
if (rc) break;
rc = ((int)a.m_mode) - ((int)b.m_mode);
if (rc) break;
rc = ((int)a.m_minfilter) - ((int)b.m_minfilter);
if (rc) break;
rc = ((int)a.m_magfilter) - ((int)b.m_magfilter);
if (rc) break;
rc = ((int)a.m_wrapu) - ((int)b.m_wrapu);
if (rc) break;
rc = ((int)a.m_wrapv) - ((int)b.m_wrapv);
if (rc) break;
rc = ((int)a.m_wrapw) - ((int)b.m_wrapw);
if (rc) break;
rc = CompareXform(a.m_uvw, b.m_uvw);
if (rc) break;
rc = a.m_border_color.Compare(b.m_border_color);
if (rc) break;
rc = a.m_transparent_color.Compare(b.m_transparent_color);
if (rc) break;
rc = ON_Interval::Compare(a.m_bump_scale, b.m_bump_scale);
if (rc) break;
rc = CompareDouble(a.m_blend_constant_A, b.m_blend_constant_A);
if (rc) break;
rc = CompareDouble(a.m_blend_A0, b.m_blend_A0);
if (rc) break;
rc = CompareDouble(a.m_blend_A1, b.m_blend_A1);
if (rc) break;
rc = CompareDouble(a.m_blend_A2, b.m_blend_A2);
if (rc) break;
rc = CompareDouble(a.m_blend_A3, b.m_blend_A3);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB0, b.m_blend_RGB0);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB1, b.m_blend_RGB1);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB2, b.m_blend_RGB2);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3);
if (rc) break;
rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0));
if (rc) break;
break;
}
return rc;
}
int ON_Texture::CompareAppearance(const ON_Texture& a, const ON_Texture& b)
{
int rc = 0;
while (!rc)
{
if (a.m_mapping_channel_id < b.m_mapping_channel_id)
rc = -1;
else if (a.m_mapping_channel_id > b.m_mapping_channel_id)
rc = 1;
if (rc) break;
rc = a.m_image_file_reference.FullPath().ComparePath(static_cast< const wchar_t* >(b.m_image_file_reference.FullPath()));
if (rc) break;
rc = ((int)(a.m_bOn ? 1 : 0)) - ((int)(b.m_bOn ? 1 : 0));
if (rc) break;
rc = ((int)a.m_type) - ((int)b.m_type);
if (rc) break;
rc = ((int)a.m_mode) - ((int)b.m_mode);
if (rc) break;
rc = ((int)a.m_minfilter) - ((int)b.m_minfilter);
if (rc) break;
rc = ((int)a.m_magfilter) - ((int)b.m_magfilter);
if (rc) break;
rc = ((int)a.m_wrapu) - ((int)b.m_wrapu);
if (rc) break;
rc = ((int)a.m_wrapv) - ((int)b.m_wrapv);
if (rc) break;
rc = ((int)a.m_wrapw) - ((int)b.m_wrapw);
if (rc) break;
rc = CompareXform(a.m_uvw, b.m_uvw);
if (rc) break;
rc = a.m_border_color.Compare(b.m_border_color);
if (rc) break;
rc = a.m_transparent_color.Compare(b.m_transparent_color);
if (rc) break;
rc = ON_Interval::Compare(a.m_bump_scale, b.m_bump_scale);
if (rc) break;
rc = CompareDouble(a.m_blend_constant_A, b.m_blend_constant_A);
if (rc) break;
rc = CompareDouble(a.m_blend_A0, b.m_blend_A0);
if (rc) break;
rc = CompareDouble(a.m_blend_A1, b.m_blend_A1);
if (rc) break;
rc = CompareDouble(a.m_blend_A2, b.m_blend_A2);
if (rc) break;
rc = CompareDouble(a.m_blend_A3, b.m_blend_A3);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB0, b.m_blend_RGB0);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB1, b.m_blend_RGB1);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB2, b.m_blend_RGB2);
if (rc) break;
rc = CompareDouble(a.m_blend_RGB3, b.m_blend_RGB3);
if (rc) break;
rc = ((int)(a.m_bTreatAsLinear ? 1 : 0)) - ((int)(b.m_bTreatAsLinear ? 1 : 0));
if (rc) break;
break;
}
return rc;
}
int ON_Material::Compare( const ON_Material& a, const ON_Material& b )
{
int rc = CompareNameAndIds(a,b);
if (0 == rc)
rc = CompareAppearance(a,b);
return rc;
}
int ON_Material::CompareNameAndIds( const ON_Material& a, const ON_Material& b )
{
// do NOT test index or id
const ON_UUID aid = a.Id();
const ON_UUID bid = b.Id();
int rc = ON_UuidCompare( &aid, &bid );
if (rc) return rc;
rc = a.Name().CompareOrdinal( static_cast< const wchar_t* >(b.Name()), false );
if (rc) return rc;
rc = ON_UuidCompare(&a.m_rdk_material_instance_id, &b.m_rdk_material_instance_id);
if (rc) return rc;
const int tcount = a.m_textures.Count();
rc = tcount - b.m_textures.Count();
for (int i = 0; i < tcount && 0 == rc; i++)
{
rc = ON_UuidCompare(a.m_textures[i].m_texture_id, b.m_textures[i].m_texture_id);
}
return rc;
}
int ON_Material::CompareColorAttributes( const ON_Material& a, const ON_Material& b )
{
const auto a_pbr = a.PhysicallyBased();
const auto b_pbr = b.PhysicallyBased();
if (a_pbr.Supported() && !b_pbr.Supported())
return -1;
if (!a_pbr.Supported() && b_pbr.Supported())
return 1;
if (a_pbr.Supported() && b_pbr.Supported())
{
int rc = a_pbr.BaseColor().Compare(a_pbr.BaseColor());
if (rc) return rc;
rc = ((int)a_pbr.BRDF()) - ((int)b_pbr.BRDF());
if (rc) return rc;
rc = CompareDouble(a_pbr.Subsurface(), b_pbr.Subsurface());
if (0 != rc) return rc;
rc = a_pbr.SubsurfaceScatteringColor().Compare(b_pbr.SubsurfaceScatteringColor());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.SubsurfaceScatteringRadius(), b_pbr.SubsurfaceScatteringRadius());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Sheen(), b_pbr.Sheen());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.SheenTint(), b_pbr.SheenTint());
if (0 != rc) return rc;
//rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR());
//if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Opacity(), b_pbr.Opacity());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.OpacityIOR(), b_pbr.OpacityIOR());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.OpacityRoughness(), b_pbr.OpacityRoughness());
if (0 != rc) return rc;
rc = a_pbr.Emission().Compare(b_pbr.Emission());
return rc;
}
int rc = a.m_ambient.Compare(b.m_ambient);
if (rc) return rc;
rc = a.m_diffuse.Compare( b.m_diffuse );
if (rc) return rc;
rc = a.m_emission.Compare( b.m_emission );
if (rc) return rc;
rc = a.m_specular.Compare( b.m_specular );
if (rc) return rc;
rc = a.m_reflection.Compare( b.m_reflection );
if (rc) return rc;
rc = a.m_transparent.Compare( b.m_transparent );
if (rc) return rc;
rc = CompareDouble(a.m_transparency,b.m_transparency);
if (rc) return rc;
rc = ((int)a.m_bDisableLighting) - ((int)b.m_bDisableLighting);
return rc;
}
int ON_Material::CompareReflectionAttributes(const ON_Material& a, const ON_Material& b)
{
const auto a_pbr = a.PhysicallyBased();
const auto b_pbr = b.PhysicallyBased();
if (a_pbr.Supported() && !b_pbr.Supported())
return -1;
if (!a_pbr.Supported() && b_pbr.Supported())
return 1;
if (a_pbr.Supported() && b_pbr.Supported())
{
int rc = CompareDouble(a_pbr.Metallic(), b_pbr.Metallic());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Specular(), b_pbr.Specular());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.SpecularTint(), b_pbr.SpecularTint());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Roughness(), b_pbr.Roughness());
if (0 != rc) return rc;
//rc = CompareDouble(a_pbr.ReflectiveIOR(), b_pbr.ReflectiveIOR());
//if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Anisotropic(), b_pbr.Anisotropic());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.AnisotropicRotation(), b_pbr.AnisotropicRotation());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.Clearcoat(), b_pbr.Clearcoat());
if (0 != rc) return rc;
rc = CompareDouble(a_pbr.ClearcoatRoughness(), b_pbr.ClearcoatRoughness());
return rc;
}
int rc = a.m_reflection.Compare(b.m_reflection);
if (0 != rc) return rc;
rc = CompareDouble(a.m_index_of_refraction, b.m_index_of_refraction);
if (0 != rc) return rc;
rc = CompareDouble(a.m_reflectivity, b.m_reflectivity);
if (0 != rc) return rc;
rc = CompareDouble(a.m_shine, b.m_shine);
if (0 != rc) return rc;
rc = (a.m_bFresnelReflections ? 1 : 0) - (b.m_bFresnelReflections ? 1 : 0);
if (0 != rc) return rc;
if (a.m_bFresnelReflections)
{
rc = CompareDouble(a.m_fresnel_index_of_refraction, b.m_fresnel_index_of_refraction);
if (0 != rc) return rc;
}
rc = CompareDouble(a.m_reflection_glossiness, b.m_reflection_glossiness);
if (0 != rc) return rc;
rc = CompareDouble(a.m_refraction_glossiness, b.m_refraction_glossiness);
return rc;
}
int ON_Material::CompareTextureAttributes(const ON_Material& a, const ON_Material& b)
{
// do NOT test index or id
const int tcount = a.m_textures.Count();
int rc = tcount - b.m_textures.Count();
for (int i = 0; i < tcount && 0 == rc; i++)
{
rc = ON_Texture::Compare(a.m_textures[i], b.m_textures[i]);
}
if (0 == rc)
rc = ((int)a.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture) - ((int)b.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture);
return rc;
}
int ON_Material::CompareTextureAttributesAppearance(const ON_Material& a, const ON_Material& b)
{
// do NOT test index or id
const int tcount = a.m_textures.Count();
int rc = tcount - b.m_textures.Count();
for (int i = 0; i < tcount && 0 == rc; i++)
{
rc = ON_Texture::CompareAppearance(a.m_textures[i], b.m_textures[i]);
}
if (0 == rc)
rc = ((int)a.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture) - ((int)b.m_bUseDiffuseTextureAlphaForObjectTransparencyTexture);
return rc;
}
int ON_Material::CompareAppearance( const ON_Material& a, const ON_Material& b )
{
int rc = CompareColorAttributes(a,b);
if ( 0 == rc )
rc = CompareReflectionAttributes(a,b);
if ( 0 == rc )
rc = CompareTextureAttributesAppearance(a,b);
if ( 0 == rc )
rc = ON_UuidCompare( &a.m_plugin_id, &b.m_plugin_id );
return rc;
}
ON_UUID ON_Material::RdkMaterialInstanceId() const
{
return m_rdk_material_instance_id;
}
/*
Description:
Set this material's RDK material id.
Parameters:
rdk_material_id - [in]
RDK material id value.
Remarks:
The RDK material id identifies a material definition managed by
the RDK (rendering development kit). Multiple materials in
a Rhino or opennurbs model can reference the same RDK material.
*/
void ON_Material::SetRdkMaterialInstanceId(
ON_UUID rdk_material_instance_id
)
{
m_rdk_material_instance_id = rdk_material_instance_id;
}
bool ON_Material::RdkMaterialInstanceIdIsNotNil() const
{
return (!(ON_nil_uuid == m_rdk_material_instance_id));
}
bool ON_Material::RdkMaterialInstanceIdIsNil() const
{
return (ON_nil_uuid == m_rdk_material_instance_id);
}
// NO ON_OBJECT_IMPLEMENT(ON_RdkMaterialInstanceIdObsoleteUserData, ON_ObsoleteUserData, ...)
// because ON_RdkMaterialInstanceIdObsoleteUserData is derived from ON_ObsoleteUserData.
// CRhRdkUserData class id valud
// = ON_RdkMaterialInstanceIdObsoleteUserData::ClassId()->Uuid()
// = AFA82772-1525-43dd-A63C-C84AC5806911
// From CRhRdkUserData ON_OBJECT_IMPLEMENT macro
// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmUserData.cpp
// revision: 102101
//
// {AFA82772-1525-43dd-A63C-C84AC5806911}
const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor =
{ 0xAFA82772, 0x1525, 0x43dd, { 0xA6, 0x3C, 0xC8, 0x4A, 0xC5, 0x80, 0x69, 0x11 } };
// CRhRdkUserData::m_userdata_uuid value
// = CRhRdkUserData::Uuid()
// = CRhRdkUserData::m_Uuid
// = B63ED079-CF67-416c-800D-22023AE1BE21
// From
// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmUserData.cpp
// revision: 102101
//
// {B63ED079-CF67-416c-800D-22023AE1BE21}
const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor =
{ 0xb63ed079, 0xcf67, 0x416c, { 0x80, 0xd, 0x22, 0x2, 0x3a, 0xe1, 0xbe, 0x21 } };
// CRhRdkUserData::m_application_uuid value
// = CRhRdkRhinoPlugIn::RhinoPlugInUuid()
// = CRhRdkRhinoPlugIn::m_uuidRhinoPlugIn
// = 16592D58-4A2F-401D-BF5E-3B87741C1B1B
// From
// file: http://subversion.mcneel.com/rhino/6/trunk/src4/rhino4/Plug-ins/RDK/RDK/RhRcmRhinoPlugIn.cpp
// revision: 102101
//
// {16592D58-4A2F-401D-BF5E-3B87741C1B1B}
const ON_UUID ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor =
{ 0x16592D58, 0x4A2F, 0x401D, { 0xBF, 0x5E, 0x3B, 0x87, 0x74, 0x1C, 0x1B, 0x1B } };
const unsigned int ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_copycount_ctor = 1;
const ON_Xform ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_xform_ctor(ON_Xform::IdentityTransformation);
ON_RdkMaterialInstanceIdObsoleteUserData::ON_RdkMaterialInstanceIdObsoleteUserData()
: m_rdk_material_instance_id(ON_nil_uuid)
{
///////////////////////////////////////////////////////////
// ON_UserData field initialization:
// The values of m_userdata_uuid and m_application_uuid match those used by CRhRdkUserData.
m_userdata_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor;
m_application_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor;
// Values for the 3dm archive.
m_userdata_copycount = ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_copycount_ctor;
m_userdata_xform = ON_RdkMaterialInstanceIdObsoleteUserData::m_userdata_xform_ctor;
///////////////////////////////////////////////////////////
// ON_ObsoleteUserData field initialization:
m_archive_class_uuid = ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor;
}
ON_RdkMaterialInstanceIdObsoleteUserData::~ON_RdkMaterialInstanceIdObsoleteUserData()
{}
ON_RdkMaterialInstanceIdObsoleteUserData::ON_RdkMaterialInstanceIdObsoleteUserData(const ON_RdkMaterialInstanceIdObsoleteUserData& src)
: ON_ObsoleteUserData(src)
, m_rdk_material_instance_id(src.m_rdk_material_instance_id)
{}
ON_RdkMaterialInstanceIdObsoleteUserData& ON_RdkMaterialInstanceIdObsoleteUserData::operator=(const ON_RdkMaterialInstanceIdObsoleteUserData& src)
{
if (this != &src)
{
ON_ObsoleteUserData::operator=(src);
m_rdk_material_instance_id = src.m_rdk_material_instance_id;
}
return *this;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::GetDescription(ON_wString& description)
{
description = "RDK material instance id as user data for writing pre V6 file formats.";
return true;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::WriteToArchive(
const class ON_BinaryArchive& archive,
const class ON_Object* parent_object
) const
{
return (ON_UuidIsNotNil(m_rdk_material_instance_id) && archive.Archive3dmVersion() <= 50);
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterWrite(
const ON_BinaryArchive& archive,
const ON_Object* parent_object
) const
{
// Returning true will cause the ON_BinaryArchive::WriteObject()
// plumbing to delete this user data.
return true;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::DeleteAfterRead(
const ON_BinaryArchive& archive,
ON_Object* parent_object
) const
{
// move the RDK material id value from the V5 user data to
// the V6 ON_Material.m_rdk_material_instance_id field.
ON_Material* mat = ON_Material::Cast(parent_object);
if (mat && mat->RdkMaterialInstanceIdIsNil())
mat->SetRdkMaterialInstanceId(m_rdk_material_instance_id);
// Returning true will cause the ON_BinaryArchive::ReadObject()
// plumbing to delete this user data.
return true;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::IsRdkMaterialInstanceIdUserData(
ON_UUID class_id,
ON_UUID userdata_id,
ON_UUID app_id,
ON_Object* object
)
{
return (
nullptr != ON_Material::Cast(object)
&& class_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_class_id_ctor
&& userdata_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_userdata_uuid_ctor
&& app_id == ON_RdkMaterialInstanceIdObsoleteUserData::m_archive_application_uuid_ctor
);
}
static const char* ParsePastWhiteSpace(const char* s)
{
for (char c = s ? *s++ : 0; 0 != c; c = *s++)
{
if (32 == c)
continue;
if (c >= 9 && c <= 13)
continue;
break;
}
return s;
}
static char ToUpper(char c)
{
if (c >= 'a' && c <= 'z')
c -= 0x20;
return c;
}
static const char* ParsePast(const char* token, const char* s)
{
if (0 == token || token[0] <= 32)
return 0;
s = ParsePastWhiteSpace(s);
if (0 == s || s[0] <= 32)
return 0;
for (/*empty init*/; ToUpper(*s) == ToUpper(*token); s++, token++)
{
if (0 == *s)
return s;
}
return 0;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::Read(
ON_BinaryArchive& archive
)
{
m_rdk_material_instance_id = ON_nil_uuid;
bool rc = false;
for (;;)
{
int version = 0;
if (!archive.ReadInt(&version))
break;
if (2 != version)
break;
int s_length = 0;
if (!archive.ReadInt(&s_length))
break;
if (s_length < 0 || s_length > 1024)
break;
if (0 == s_length)
{
rc = true;
break;
}
ON_String str((char)0, (int)s_length);
if (str.Length() < s_length)
break;
const char* s = str.Array();
if (0 == s)
break;
if (!archive.ReadByte(s_length, (void*)s))
break;
// parse the xml the CRhRdkUserData::Write() function
// put into V5 files and V6 files written before May 16, 2014.
s = ParsePast("<", s);
s = ParsePast("xml", s);
s = ParsePast(">", s);
s = ParsePast("<", s);
s = ParsePast("render-content-manager-data", s);
s = ParsePast(">", s);
s = ParsePast("<", s);
s = ParsePast("material", s);
s = ParsePast("instance-id", s);
s = ParsePast("=", s);
s = ParsePast("\"", s);
s = ParsePastWhiteSpace(s);
s = ON_ParseUuidString(s, &m_rdk_material_instance_id);
s = ParsePast("\"", s);
rc = (0 != s);
break;
}
return rc;
}
bool ON_RdkMaterialInstanceIdObsoleteUserData::Write(
ON_BinaryArchive& archive
) const
{
// write xml the CRhRdkUserData::Write() function
// put into V5 files and V6 files written before May 16, 2014.
char s__rdk_material_instance_id[37];
ON_String s =
"<xml>\n"
"<render-content-manager-data>\n"
"<material instance-id=\"";
s += ON_UuidToString(m_rdk_material_instance_id, s__rdk_material_instance_id);
s +=
"\" name=\"\" />\n"
"</render-content-manager-data>\n"
"</xml>";
const int s_length = (int)s.Length();
const char* s_array = s.Array();
bool rc
= archive.WriteInt(2) // "2" was a version number used in V5 files
&& archive.WriteInt(s_length)
&& archive.WriteByte(s_length, s_array);
return rc;
}
ON_Color ON_Material::Ambient() const
{
return m_ambient & 0x00FFFFFF;
}
ON_Color ON_Material::Diffuse( ) const
{
return m_diffuse & 0x00FFFFFF;
}
ON_Color ON_Material::Emission( ) const
{
return m_emission & 0x00FFFFFF;
}
ON_Color ON_Material::Specular() const
{
return m_specular & 0x00FFFFFF;
}
void ON_Material::SetAmbient( ON_Color c )
{
if (m_ambient != c)
{
m_ambient = c;
IncrementContentVersionNumber();
}
}
void ON_Material::SetDiffuse( ON_Color c )
{
if (m_diffuse != c)
{
m_diffuse = c;
IncrementContentVersionNumber();
}
}
void ON_Material::SetEmission( ON_Color c )
{
if (m_emission != c)
{
m_emission = c;
IncrementContentVersionNumber();
}
}
void ON_Material::SetSpecular( ON_Color c )
{
if (m_specular != c)
{
m_specular = c;
IncrementContentVersionNumber();
}
}
bool ON_Material::Shareable() const
{
return m_bShareable;
}
void ON_Material::SetShareable(
bool bShareable
)
{
if (bShareable != m_bShareable)
{
m_bShareable = bShareable?true:false;
IncrementContentVersionNumber();
}
}
bool ON_Material::DisableLighting() const
{
return m_bDisableLighting;
}
void ON_Material::SetDisableLighting(
bool bDisableLighting
)
{
if (bDisableLighting != m_bDisableLighting)
{
m_bDisableLighting = bDisableLighting?true:false;
IncrementContentVersionNumber();
}
}
//Very simple preview color function that better supports metals and dialectrics.
ON_Color ON_Material::PreviewColor(void) const
{
if (Transparency() > 0.5)
{
return m_transparent;
}
if (!FresnelReflections())
{
if (Reflectivity() > 0.5)
{
return m_reflection;
}
}
return Diffuse( );
}
bool ON_Material::UseDiffuseTextureAlphaForObjectTransparencyTexture() const
{
//Physically based materials do not support alpha transparency (at the moment).
if (PhysicallyBased().Supported())
return false;
return m_bUseDiffuseTextureAlphaForObjectTransparencyTexture;
}
void ON_Material::SetUseDiffuseTextureAlphaForObjectTransparencyTexture(
bool bUseDiffuseTextureAlphaForObjectTransparencyTexture
)
{
if (bUseDiffuseTextureAlphaForObjectTransparencyTexture != m_bUseDiffuseTextureAlphaForObjectTransparencyTexture)
{
m_bUseDiffuseTextureAlphaForObjectTransparencyTexture = bUseDiffuseTextureAlphaForObjectTransparencyTexture?true:false;
IncrementContentVersionNumber();
}
}
bool ON_Material::FresnelReflections() const
{
return m_bFresnelReflections;
}
void ON_Material::SetFresnelReflections(
bool bFresnelReflections
)
{
if (bFresnelReflections != m_bFresnelReflections)
{
m_bFresnelReflections = bFresnelReflections?true:false;
IncrementContentVersionNumber();
}
}
////////////////////////////////////////////////////////////////
// Class ON_Texture
////////////////////////////////////////////////////////////////
ON_OBJECT_IMPLEMENT(ON_Texture,ON_Object,"D6FF106D-329B-4f29-97E2-FD282A618020");
bool ON_Texture::IsValid( ON_TextLog* text_log ) const
{
if ( ON_Texture::TYPE::no_texture_type == m_type )
{
if ( text_log )
{
text_log->Print("ON_Texture m_type has invalid value.\n");
}
return false;
}
// TODO ...
return true;
}
// overrides virtual ON_Object::Dump
void ON_Texture::Dump( ON_TextLog& ) const
{
}
// overrides virtual ON_Object::SizeOf
unsigned int ON_Texture::SizeOf() const
{
unsigned int sz = ON_Object::SizeOf();
sz += sizeof(*this) - sizeof(ON_Object);
return sz;
}
// overrides virtual ON_Object::Write
bool ON_Texture::Write(
ON_BinaryArchive& binary_archive
) const
{
const int major_version = 1;
const int minor_version
= binary_archive.Archive3dmVersion() >= 60
? (binary_archive.Archive3dmVersion() >= 70 ? 2 : 1)
: 0;
bool rc = binary_archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,major_version,minor_version);
if (rc)
{
for(;;)
{
// 1.0 values
rc = binary_archive.WriteUuid(m_texture_id);
if (!rc) break;
rc = binary_archive.WriteInt(m_mapping_channel_id);
if (!rc) break;
rc =
m_image_file_reference.FullPath().IsNotEmpty()
? binary_archive.WriteString(m_image_file_reference.FullPath())
: binary_archive.WriteString(m_image_file_reference.RelativePath());
if (!rc) break;
rc = binary_archive.WriteBool(m_bOn);
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_type));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_mode));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_minfilter));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_magfilter));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapu));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapv));
if (!rc) break;
rc = binary_archive.WriteInt(static_cast<unsigned int>(m_wrapw));
if (!rc) break;
rc = binary_archive.WriteXform(m_uvw);
if (!rc) break;
rc = binary_archive.WriteColor(m_border_color);
if (!rc) break;
rc = binary_archive.WriteColor(m_transparent_color);
if (!rc) break;
rc = binary_archive.WriteUuid(m_transparency_texture_id);
if (!rc) break;
rc = binary_archive.WriteInterval(m_bump_scale);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_constant_A);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_A0);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_A1);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_A2);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_A3);
if (!rc) break;
rc = binary_archive.WriteColor(m_blend_constant_RGB);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_RGB0);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_RGB1);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_RGB2);
if (!rc) break;
rc = binary_archive.WriteDouble(m_blend_RGB3);
if (!rc) break;
rc = binary_archive.WriteInt(m_blend_order);
if (!rc) break;
if ( minor_version <= 0 )
break;
// version 1.1 added m_image_file_reference
rc = m_image_file_reference.Write(true,binary_archive);
if (!rc) break;
if ( minor_version <= 1 )
break;
// version 1.2 added m_bTreatAsLinear
rc = binary_archive.WriteBool(m_bTreatAsLinear);
if (!rc) break;
break;
}
if ( !binary_archive.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
static ON_Texture::MAPPING_CHANNEL Internal_BuiltInMappingChannelFromUnsigned(
unsigned int built_in_mapping_channel_as_unsigned,
bool bEnableErrorMessage
)
{
switch (built_in_mapping_channel_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::tc_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::default_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::screen_based_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::wcs_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::wcs_box_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_box_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_light_probe_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_spherical_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_cube_map_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_vcross_cube_map_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_hcross_cube_map_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_hemispherical_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::environment_map_emap_channel);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::srfp_channel);
//ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MAPPING_CHANNEL::emap_channel);
}
if (bEnableErrorMessage)
{
ON_ERROR("Invalid built_in_mapping_channel_as_unsigned value.");
}
return ON_Texture::MAPPING_CHANNEL::tc_channel;
}
ON_Texture::MAPPING_CHANNEL ON_Texture::BuiltInMappingChannelFromUnsigned(
unsigned int built_in_mapping_channel_as_unsigned
)
{
return Internal_BuiltInMappingChannelFromUnsigned(
built_in_mapping_channel_as_unsigned,
true
);
}
const ON_SHA1_Hash ON_Texture::ContentHash() const
{
ON_SHA1 sha1;
sha1.AccumulateSubHash(m_image_file_reference.FullPathHash());
sha1.AccumulateBool(m_bOn);
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_type));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_mode));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_minfilter));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_magfilter));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapu));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapv));
sha1.AccumulateUnsigned32(static_cast<unsigned int>(m_wrapw));
sha1.AccumulateTransformation(m_uvw);
sha1.AccumulateUnsigned32(m_border_color);
sha1.AccumulateUnsigned32(m_transparent_color);
sha1.AccumulateId(m_transparency_texture_id);
sha1.AccumulateDouble(m_bump_scale.m_t[0]);
sha1.AccumulateDouble(m_bump_scale.m_t[1]);
sha1.AccumulateDouble(m_blend_constant_A);
sha1.AccumulateDouble(m_blend_A0);
sha1.AccumulateDouble(m_blend_A1);
sha1.AccumulateDouble(m_blend_A2);
sha1.AccumulateDouble(m_blend_A3);
sha1.AccumulateDouble(m_blend_constant_RGB);
sha1.AccumulateDouble(m_blend_RGB0);
sha1.AccumulateDouble(m_blend_RGB1);
sha1.AccumulateDouble(m_blend_RGB2);
sha1.AccumulateDouble(m_blend_RGB3);
sha1.AccumulateInteger32(m_blend_order);
sha1.AccumulateBool(m_bTreatAsLinear);
return sha1.Hash();
}
bool ON_Texture::IsBuiltInMappingChannel(
unsigned int mapping_channel_id
)
{
ON_Texture::MAPPING_CHANNEL mc = Internal_BuiltInMappingChannelFromUnsigned(mapping_channel_id,false);
return (static_cast<unsigned int>(mc) == mapping_channel_id);
}
void ON_Texture::SetBuiltInMappingChannel(
ON_Texture::MAPPING_CHANNEL built_in_mapping_channel_as_unsigned
)
{
SetMappingChannel( static_cast<unsigned int>(built_in_mapping_channel_as_unsigned) );
}
void ON_Texture::SetMappingChannel(
unsigned int mapping_channel_id
)
{
m_mapping_channel_id = mapping_channel_id;
}
ON_Texture::TYPE ON_Texture::TypeFromUnsigned(unsigned int type_as_unsigned)
{
switch (type_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::no_texture_type);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::diffuse_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::bump_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::emap_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::opacity_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_subsurface_scattering_radius_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_metallic_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_specular_tint_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_roughness_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_anisotropic_rotation_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_sheen_tint_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_roughness_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_clearcoat_bump_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_ior_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_opacity_roughness_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_emission_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_ambient_occlusion_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::TYPE::pbr_displacement_texture);
}
ON_ERROR("Invalid type_as_unsigned value.");
return ON_Texture::TYPE::no_texture_type;
}
ON_Texture::MODE ON_Texture::ModeFromUnsigned( unsigned int mode_as_unsigned )
{
switch(mode_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::no_texture_mode);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::modulate_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::decal_texture);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::MODE::blend_texture);
}
ON_ERROR("Invalid mode_as_unsigned value.");
return ON_Texture::MODE::no_texture_mode;
}
ON_Texture::FILTER ON_Texture::FilterFromUnsigned( unsigned int filter_as_unsigned )
{
switch(filter_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::FILTER::nearest_filter);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::FILTER::linear_filter);
}
ON_ERROR("Invalid filter_as_unsigned value.");
return ON_Texture::FILTER::linear_filter;
}
ON_Texture::WRAP ON_Texture::WrapFromUnsigned( unsigned int wrap_as_unsigned )
{
switch(wrap_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::WRAP::repeat_wrap);
ON_ENUM_FROM_UNSIGNED_CASE(ON_Texture::WRAP::clamp_wrap);
}
ON_ERROR("Invalid wrap_as_unsigned value.");
return ON_Texture::WRAP::repeat_wrap;
}
bool ON_Texture::Read(
ON_BinaryArchive& binary_archive
)
{
*this = ON_Texture::Default;
int major_version = 0;
int minor_version = 0;
bool rc = binary_archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
if (rc)
{
if ( 1 != major_version )
{
rc = false;
}
else
{
unsigned int i;
for(;;)
{
// 1.0 values
rc = binary_archive.ReadUuid( m_texture_id );
if (!rc) break;
rc = binary_archive.ReadInt( &m_mapping_channel_id );
if (!rc) break;
ON_wString filename;
rc = binary_archive.ReadString(filename);
if (!rc) break;
if (ON_FileSystemPath::IsRelativePath(filename))
m_image_file_reference.SetRelativePath(filename);
else
m_image_file_reference.SetFullPath(filename,false);
rc = binary_archive.ReadBool(&m_bOn);
if (!rc) break;
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_type = ON_Texture::TypeFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_mode = ON_Texture::ModeFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_minfilter = ON_Texture::FilterFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_magfilter = ON_Texture::FilterFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_wrapu = ON_Texture::WrapFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_wrapv = ON_Texture::WrapFromUnsigned(i);
rc = binary_archive.ReadInt(&i);
if (!rc) break;
m_wrapw = ON_Texture::WrapFromUnsigned(i);
rc = binary_archive.ReadXform(m_uvw);
if (!rc) break;
rc = binary_archive.ReadColor(m_border_color);
if (!rc) break;
rc = binary_archive.ReadColor(m_transparent_color);
if (!rc) break;
rc = binary_archive.ReadUuid(m_transparency_texture_id);
if (!rc) break;
rc = binary_archive.ReadInterval(m_bump_scale);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_constant_A);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_A0);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_A1);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_A2);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_A3);
if (!rc) break;
rc = binary_archive.ReadColor(m_blend_constant_RGB);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_RGB0);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_RGB1);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_RGB2);
if (!rc) break;
rc = binary_archive.ReadDouble(&m_blend_RGB3);
if (!rc) break;
rc = binary_archive.ReadInt(&m_blend_order);
if (!rc) break;
if ( minor_version <= 0 )
break;
rc = m_image_file_reference.Read(binary_archive);
if (!rc) break;
if (minor_version <= 1)
break;
rc = binary_archive.ReadBool(&m_bTreatAsLinear);
if (!rc) break;
break;
}
}
if ( !binary_archive.EndRead3dmChunk() )
rc = false;
}
return rc;
}
ON_OBJECT_IMPLEMENT(ON_TextureMapping,ON_ModelComponent,"32EC997A-C3BF-4ae5-AB19-FD572B8AD554");
const ON_TextureMapping* ON_TextureMapping::FromModelComponentRef(
const class ON_ModelComponentReference& model_component_reference,
const ON_TextureMapping* none_return_value
)
{
const ON_TextureMapping* p = ON_TextureMapping::Cast(model_component_reference.ModelComponent());
return (nullptr != p) ? p : none_return_value;
}
ON_TextureMapping::TYPE ON_TextureMapping::TypeFromUnsigned(
unsigned int type_as_unsigned
)
{
switch (type_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::no_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::srfp_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::plane_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::cylinder_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::sphere_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::box_mapping);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::mesh_mapping_primitive);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive);
}
ON_ERROR("Invalid type_as_unsigned value.");
return ON_TextureMapping::TYPE::no_mapping;
}
const ON_wString ON_TextureMapping::TypeToString(ON_TextureMapping::TYPE texture_mapping_type)
{
switch (texture_mapping_type)
{
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::no_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::srfp_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::plane_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::cylinder_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::sphere_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::box_mapping);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::mesh_mapping_primitive);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::srf_mapping_primitive);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TYPE::brep_mapping_primitive);
}
ON_ERROR("Invalid texture_mapping_type value.");
return ON_wString::EmptyString;
}
ON_TextureMapping::PROJECTION ON_TextureMapping::ProjectionFromUnsigned(
unsigned int projection_as_unsigned
)
{
switch (projection_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::no_projection);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::clspt_projection);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::PROJECTION::ray_projection);
}
ON_ERROR("Invalid projection_as_unsigned value.");
return ON_TextureMapping::PROJECTION::no_projection;
}
const ON_wString ON_TextureMapping::ProjectionToString(ON_TextureMapping::PROJECTION texture_mapping_projection)
{
switch (texture_mapping_projection)
{
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::no_projection);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::clspt_projection);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::PROJECTION::ray_projection);
}
ON_ERROR("Invalid texture_mapping_projection value.");
return ON_wString::EmptyString;
}
ON_TextureMapping::TEXTURE_SPACE ON_TextureMapping::TextureSpaceFromUnsigned(
unsigned int texture_space_as_unsigned
)
{
switch (texture_space_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::single);
ON_ENUM_FROM_UNSIGNED_CASE(ON_TextureMapping::TEXTURE_SPACE::divided);
}
ON_ERROR("Invalid texture_space_as_unsigned value.");
return ON_TextureMapping::TEXTURE_SPACE::single;
}
const ON_wString ON_TextureMapping::SpaceToString(ON_TextureMapping::TEXTURE_SPACE texture_mapping_space)
{
switch (texture_mapping_space)
{
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::single);
ON_ENUM_TO_WIDE_STRING_CASE(ON_TextureMapping::TEXTURE_SPACE::divided);
}
ON_ERROR("Invalid texture_mapping_space value.");
return ON_wString::EmptyString;
}
ON_TextureMapping::ON_TextureMapping() ON_NOEXCEPT
: ON_ModelComponent(ON_ModelComponent::Type::TextureMapping)
{}
ON_TextureMapping::~ON_TextureMapping()
{
Internal_Destroy();
}
ON_TextureMapping::ON_TextureMapping( const ON_TextureMapping& src)
: ON_ModelComponent(ON_ModelComponent::Type::TextureMapping,src)
{
Internal_CopyFrom(src);
}
ON_TextureMapping& ON_TextureMapping::operator=(const ON_TextureMapping& src)
{
if ( this != &src )
{
Internal_Destroy();
ON_ModelComponent::operator=(src);
Internal_CopyFrom(src);
}
return *this;
}
void ON_TextureMapping::Internal_Destroy()
{
PurgeUserData();
m_mapping_primitive.reset();
}
void ON_TextureMapping::Internal_CopyFrom(const ON_TextureMapping& src)
{
m_type = src.m_type;
m_projection = src.m_projection;
m_bCapped = src.m_bCapped;
m_texture_space = src.m_texture_space;
m_Pxyz = src.m_Pxyz;
m_Nxyz = src.m_Nxyz;
m_uvw = src.m_uvw;
m_mapping_primitive = src.m_mapping_primitive;
}
bool ON_TextureMapping::IsValid( ON_TextLog* text_log ) const
{
if ( m_type != ON_TextureMapping::TypeFromUnsigned( static_cast<unsigned int>(m_type) ) )
{
if ( text_log )
{
text_log->Print("ON_TextureMapping m_type = %d is not a valid value.\n",m_type);
}
return false;
}
if ( m_projection != ON_TextureMapping::ProjectionFromUnsigned( static_cast<unsigned int>(m_projection) ) )
{
if ( text_log )
{
text_log->Print("ON_TextureMapping m_projection = %d is not a valid value.\n",m_projection);
}
return false;
}
if (m_texture_space != ON_TextureMapping::TextureSpaceFromUnsigned( static_cast<unsigned int>(m_texture_space)))
{
if (text_log)
{
text_log->Print("ON_TextureMapping m_texture_space = %d is not a valid value.\n",m_texture_space);
}
return false;
}
return true;
}
void ON_TextureMapping::Dump( ON_TextLog& text_log ) const
{
ON_ModelComponent::Dump(text_log);
text_log.PushIndent();
const ON_wString type(ON_TextureMapping::TypeToString(m_type));
text_log.Print("m_type = %ls\n",static_cast<const wchar_t*>(type));
const ON_wString projection(ON_TextureMapping::ProjectionToString(m_projection));
text_log.Print("m_projection = %ls\n",static_cast<const wchar_t*>(projection));
const ON_wString texture_space(ON_TextureMapping::SpaceToString(m_texture_space));
text_log.Print("m_texture_space = %ls\n",static_cast<const wchar_t*>(texture_space));
text_log.Print("m_Pxyz =\n");
text_log.PushIndent();
text_log.Print(m_Pxyz);
text_log.PopIndent();
text_log.Print("m_Nxyz =\n");
text_log.PushIndent();
text_log.Print(m_Nxyz);
text_log.PopIndent();
text_log.Print("m_uvw =\n");
text_log.PushIndent();
text_log.Print(m_uvw);
text_log.PopIndent();
text_log.PopIndent();
}
unsigned int ON_TextureMapping::SizeOf() const
{
unsigned int sz = ON_Object::SizeOf();
sz = sizeof(*this) - sizeof(ON_Object);
return sz;
}
ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation(
const class ON_Brep* original,
const class ON_Brep* shrunk,
const ON_Xform* error_return
)
{
const class ON_Surface* original_srf
= (nullptr != original && 1 == original->m_F.Count())
? original->m_F[0].SurfaceOf()
: nullptr;
const class ON_Surface* shrunk_srf
= (nullptr != shrunk && 1 == shrunk->m_F.Count())
? shrunk->m_F[0].SurfaceOf()
: nullptr;
return ON_Texture::GetPictureShrinkSurfaceTransformation(
original_srf,
shrunk_srf,
error_return
);
}
ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation(
const class ON_Surface* original,
const class ON_Surface* shrunk,
const ON_Xform* error_return
)
{
ON_Interval original_udomain;
ON_Interval original_vdomain;
ON_Interval shrunk_udomain;
ON_Interval shrunk_vdomain;
if (nullptr != original)
{
original_udomain = original->Domain(0);
original_vdomain = original->Domain(1);
}
if (nullptr != shrunk)
{
shrunk_udomain = shrunk->Domain(0);
shrunk_vdomain = shrunk->Domain(1);
}
return ON_Texture::GetPictureShrinkSurfaceTransformation(
original_udomain, original_vdomain,
shrunk_udomain, shrunk_vdomain,
error_return
);
}
ON_Xform ON_Texture::GetPictureShrinkSurfaceTransformation(
const class ON_Interval& original_udomain,
const class ON_Interval& original_vdomain,
const class ON_Interval& shrunk_udomain,
const class ON_Interval& shrunk_vdomain,
const ON_Xform* error_return
)
{
if (nullptr == error_return)
error_return = &ON_Xform::Nan;
if (false == original_udomain.IsIncreasing())
return *error_return;
if (false == original_vdomain.IsIncreasing())
return *error_return;
if (false == shrunk_udomain.IsIncreasing())
return *error_return;
if (false == shrunk_vdomain.IsIncreasing())
return *error_return;
if (false == original_udomain.Includes(shrunk_udomain, false))
return *error_return;
if (false == original_vdomain.Includes(shrunk_vdomain, false))
return *error_return;
if (false == original_udomain.Includes(shrunk_udomain, true) && false == original_vdomain.Includes(shrunk_vdomain, true))
return *error_return;
const ON_3dPoint p0(original_udomain.NormalizedParameterAt(shrunk_udomain[0]), original_vdomain.NormalizedParameterAt(shrunk_vdomain[0]),0.0);
const ON_3dPoint p1(original_udomain.NormalizedParameterAt(shrunk_udomain[1]), original_vdomain.NormalizedParameterAt(shrunk_vdomain[1]),0.0);
if (!(0.0 <= p0.x && p0.x < p1.x && p1.x <= 1.0))
return *error_return;
if (!(0.0 <= p0.y && p0.y < p1.y && p1.y <= 1.0))
return *error_return;
const double sx = shrunk_udomain.Length() / original_udomain.Length();
if (!(sx > 0.0 && sx <= 1.0))
return *error_return;
const double sy = shrunk_vdomain.Length() / original_vdomain.Length();
if (!(sx > 0.0 && sx <= 1.0))
return *error_return;
// The new brep has a smaller surface.
// Adust the texture transform to use the proper subset of the old picture image texture.
ON_Xform x
= ON_Xform::TranslationTransformation(p0 - ON_3dPoint::Origin)
* ON_Xform::ScaleTransformation(ON_3dPoint::Origin, sx, sy, 1.0);
return x;
}
bool ON_TextureMapping::ReverseTextureCoordinate( int dir )
{
bool rc = false;
if ( 0 <= dir && dir <= 3 )
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[dir][dir] = -1.0;
x.m_xform[dir][3] = 1.0;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_TextureMapping::SwapTextureCoordinate( int i, int j )
{
bool rc = false;
if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_TextureMapping::TileTextureCoordinate( int dir, double count, double offset )
{
bool rc = false;
if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[dir][dir] = count;
x.m_xform[dir][3] = offset;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_Texture::ReverseTextureCoordinate( int dir )
{
bool rc = false;
if ( 0 <= dir && dir <= 3 )
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[dir][dir] = -1.0;
x.m_xform[dir][3] = 1.0;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_Texture::SwapTextureCoordinate( int i, int j )
{
bool rc = false;
if (i!=j && 0 <= i && i <= 3 && 0 <= j && j <= 3)
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[i][i] = x.m_xform[j][j] = 0.0;
x.m_xform[i][j] = x.m_xform[j][i] = 1.0;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_Texture::TileTextureCoordinate( int dir, double count, double offset )
{
bool rc = false;
if ( 0 <= dir && dir <= 3 && 0.0 != count && ON_IsValid(count) && ON_IsValid(offset) )
{
ON_Xform x(ON_Xform::IdentityTransformation);
x.m_xform[dir][dir] = count;
x.m_xform[dir][3] = offset;
m_uvw = x*m_uvw;
rc = true;
}
return rc;
}
bool ON_Texture::IsTiled( int dir, double* count, double* offset ) const
{
if ( count )
*count = 1.0;
if ( offset )
*offset = 0.0;
if ( 0 <= dir && dir <= 3 )
{
int row0=-1, row, col;
for ( row = 0; row < 3; row++ )
{
for ( col = 0; col < 3; col++ )
{
if ( col != dir && 0.0 != m_uvw.m_xform[row][col] )
break;
}
if ( 3 == col )
{
if ( -1 == row0 )
{
row0 = row;
}
else
return false;
}
}
if ( row0 >= 0 )
{
if (count)
*count = m_uvw.m_xform[row0][dir];
if ( offset )
*offset = m_uvw.m_xform[row0][3];
return true;
}
}
return false;
}
static const double on__overflow_tol = 1.0e100;
static
int BestHitHelper(double t0, double t1)
{
return ((t0 < 0.0 && t1 > t0) || (0.0 <= t1 && t1 < t0)) ? 1 : 0;
}
static
int IntersectBoxRayHelper(const ON_3dPoint& rst, const ON_3dVector& n, int dir, double* s)
{
/*
returns:
0 = ray parallel to sides
1 = ray hit left side (x=-1)
2 = ray hit right side (x=+1)
3 = ray hit back side (y=-1)
4 = ray hit front side (y=+1)
5 = ray hit bottom side (z=-1)
6 = ray hit top side (z=+1)
*/
double nx = (&n.x)[dir];
ON_3dPoint Q;
double t,t0,t1;
// protect against overflow
t = fabs(nx)*on__overflow_tol;
t0 = (-1.0 - (&rst.x)[dir]);
t1 = ( 1.0 - (&rst.x)[dir]);
if ( fabs(t0) >= t || fabs(t1) >= t )
{
*s = ON_UNSET_VALUE;
return 0;
}
t0 /= nx;
Q = rst + t0*n;
if ( dir )
{
t = Q.x;
Q.x = Q[dir];
Q[dir] = t;
}
if ( fabs(Q.x+1.0) > ON_SQRT_EPSILON
|| Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
|| Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
)
{
// The ray's intersection with the plane missed the
// (-1,+1)x(-1,+1) square that is the side of the box.
t0 = ON_UNSET_VALUE;
}
t1 /= nx;
Q = rst + t1*n;
if ( dir )
{
t = Q.x;
Q.x = Q[dir];
Q[dir] = t;
}
if ( fabs(Q.x-1.0) > ON_SQRT_EPSILON
|| Q.y < -(1.0+ON_SQRT_EPSILON) || Q.y > (1.0+ON_SQRT_EPSILON)
|| Q.z < -(1.0+ON_SQRT_EPSILON) || Q.z > (1.0+ON_SQRT_EPSILON)
)
{
// The ray's intersection with the plane missed the
// (-1,+1)x(-1,+1) square that is the side of the box.
t1 = ON_UNSET_VALUE;
if ( ON_UNSET_VALUE == t0 )
{
*s = ON_UNSET_VALUE;
return 0;
}
}
int rc;
if ( ON_UNSET_VALUE == t0 || 1 == BestHitHelper(t0,t1) )
{
rc = 2 + 2*dir;
*s = t1;
}
else
{
rc = 1 + 2*dir;
*s = t0;
}
return rc;
}
int ON_TextureMapping::EvaluatePlaneMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
) const
{
// The matrix m_Pxyz transforms the world coordinate
// "mapping rectangle" into the rectangle
// -1 <= r <= 1, -1 <= s <= 1, and (-1 <= t <= 1)
ON_3dPoint rst(m_Pxyz*P);
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
ON_3dVector n(m_Nxyz*N);
if ( fabs(rst.z) < fabs(n.z)*on__overflow_tol )
{
double t = -rst.z/n.z;
rst.x = rst.x + t*n.x;
rst.y = rst.y + t*n.y;
}
}
// convert -1 <= r <= 1, -1 <= s <= 1
// to normalized texture coordinate
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
// Apply texture coordinate transformation
*T = m_uvw*rst;
//See docs - if m_bCapped is false, then planar is truely flat.
if (!m_bCapped)
T->z = 0.0;
return 1;
}
int ON_TextureMapping::EvaluateSphereMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
) const
{
// The matrix m_Pxyz transforms the world coordinate
// "mapping sphere" into the sphere centered at
// rst = (0,0,0) with radius 1.0.
ON_3dPoint rst(m_Pxyz*P);
const double r = ((const ON_3dVector*)(&rst.x))->Length();
double t0, t1;
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
ON_3dVector n(m_Nxyz*N);
// Shoot a ray from P in the direction N and see if it
// hits the sphere.
int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y+n.z*n.z),
2.0*(rst.x*n.x+rst.y*n.y+rst.z*n.z),
(rst.x*rst.x+rst.y*rst.y+rst.z*rst.z) - 1.0,
&t0, &t1 );
if (rc >= 0 )
{
if ( 2 != rc && 1 == BestHitHelper(t0,t1) )
{
t0 = t1;
}
rst = rst + t0*n;
}
}
// convert sphere 3d location to longitude, latitude, radius
double longitude = (0.0 != rst.y || 0.0 != rst.x)
? atan2(rst.y,rst.x)
: 0.0;
double latitude = (0.0 != rst.z)
? atan2(rst.z,((const ON_2dVector*)(&rst.x))->Length())
: 0.0;
if ( latitude > ON_PI )
latitude -= 2.0*ON_PI;
// convert longitude to normalized texture coordinate
rst.x = 0.5*longitude/ON_PI;
if ( rst.x < -ON_EPSILON )
rst.x += 1.0;
else if (rst.x < 0.0)
rst.x = 0.0;
else if (rst.x > 1.0)
rst.x = 1.0;
// convert longitude to normalized texture coordinate
rst.y = latitude/ON_PI + 0.5;
if ( rst.y <= 0.0 )
rst.y = 0.0;
else if ( rst.y > 1.0 )
rst.y = 1.0;
// radius is already normalized
rst.z = r;
// apply texture coordinate transformation
*T = m_uvw*rst;
return 1;
}
int ON_TextureMapping::EvaluateCylinderMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
) const
{
// The matrix m_Pxyz transforms the world coordinate
// "mapping cylinder" into the cylinder centered at
// rst = (0,0,0) with radius 1.0. The axis runs
// from rst = (0,0,-1) to rst = (0,0,+1).
ON_3dPoint rst(m_Pxyz*P);
ON_3dPoint Q;
const double r = ((const ON_2dVector*)(&rst.x))->Length();
double t, t0, t1;
int side0, side1;
PROJECTION mapping_proj = m_projection;
side0 = 0;
if ( ON_TextureMapping::PROJECTION::ray_projection == mapping_proj )
{
ON_3dVector n(m_Nxyz*N);
t = 0.0;
if ( m_bCapped )
{
// shoot at caps
// The < t check prevents overflow when the
// ray is nearly parallel to the cap.
t = fabs(n.z)*on__overflow_tol;
if ( fabs(1.0+rst.z) < t && fabs(1.0-rst.z) < t )
{
side0 = 2;
side1 = 3;
t0 = (-1.0 - rst.z)/n.z;
Q = rst + t0*n;
if ( fabs(1.0+Q.z) > ON_SQRT_EPSILON
|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
{
// The ray's intersection with the bottom plane missed the
// radius 1 disk that is the bottom of the cylinder.
side0 = 0;
}
t1 = ( 1.0 - rst.z)/n.z;
Q = rst + t1*n;
if ( fabs(1.0-Q.z) > ON_SQRT_EPSILON
|| (Q.x*Q.x + Q.y*Q.y) > 1.0 + 2.0*ON_SQRT_EPSILON + ON_EPSILON )
{
// The ray's intersection with the top plane missed the
// radius 1 disk that is the top of the cylinder.
side1 = 0;
}
if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
{
side0 = side1;
t = t1;
}
else
{
t = t0;
}
}
}
// shoot ray at the cylinder wall
int rc = ON_SolveQuadraticEquation( (n.x*n.x+n.y*n.y),
2.0*(rst.x*n.x+rst.y*n.y),
(rst.x*rst.x+rst.y*rst.y) - 1.0,
&t0, &t1 );
if (rc >= 0 )
{
if ( 2 != rc && 1 == BestHitHelper(t0,t1) )
{
t0 = t1;
}
if ( 0 == side0 )
{
// Either the caps are missing or the ray missed the caps.
// The best hit is the cylinder wall.
side0 = 1;
rst = rst + t0*n;
}
else if ( 1 != BestHitHelper(t0,t) )
{
// The cylinder is capped and the ray hit the cap,
// hit the infinite cylinder wall, and the wall
// hit is "first". If the ray hits the finite
// cylinder wall, the I will use the wall hit.
t1 = rst.z + t0*n.z;
if ( t1 >= -(1.0+ON_SQRT_EPSILON) && t1 <= 1.0+ON_SQRT_EPSILON )
{
// use the hit on the cylinder wall
side0 = 1;
rst.x = rst.x + t0*n.x;
rst.y = rst.y + t0*n.y;
rst.x = t1;
}
}
}
if ( side0 > 1 )
{
// best hit is on a cap
rst = rst + t*n;
}
}
if ( m_bCapped && 0 == side0 )
{
if ( fabs(rst.z) > 1.0+ON_SQRT_EPSILON )
{
if ( fabs(rst.z) > r )
{
side0 = (rst.z < 0.0) ? 2 : 3;
}
}
else if ( r <= 1.001 )
{
// The point is inside the capped cylinder.
// Use normal to dermine which surface to use
// for closest point test.
ON_3dVector n(m_Nxyz*N);
if ( ( fabs(n.z) > fabs(n.x) && fabs(n.z) > fabs(n.y) ) )
{
side0 = (n.z < 0.0) ? 2 : 3;
}
}
}
if ( 2 == side0 || 3 == side0 )
{
// The cylinder is capped and P maps to
// the top (1 == side0) or bottom (2 == side0)
if ( 2 == side0 )
{
// This is the same convention as box mapping.
// Put another way, if you change the mapping
// between box and cylinder, you get the same
// picture on the top and bottom.
rst.x = -rst.x;
}
if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space )
{
if ( r >= 1.0-ON_SQRT_EPSILON )
{
rst.x /= (r+ON_SQRT_EPSILON);
rst.y /= (r+ON_SQRT_EPSILON);
}
}
else if ( r > 1.0 )
{
rst.x /= r;
rst.y /= r;
}
// convert to normalized texture coordinates
rst.x = 0.5*rst.x + 0.5;
if ( rst.x < 0.0) rst.x = 0.0; else if (rst.x > 1.0) rst.x = 1.0;
rst.y = 0.5*rst.y + 0.5;
if ( rst.y < 0.0) rst.y = 0.0; else if (rst.y > 1.0) rst.y = 1.0;
if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space )
{
// bottom uses 4/6 <= x <= 5/6 region of the texture map.
// top uses 5/6 <= x <= 1 region of the texture map.
rst.x = (2.0 + side0 + rst.x)/6.0;
}
}
else
{
// P maps to side of the cylinder.
//
// convert longitude to normalized texture coordinate
t = (0.0 != rst.y || 0.0 != rst.x) ? atan2(rst.y,rst.x) : 0.0;
rst.x = 0.5*t/ON_PI;
if ( rst.x < -ON_EPSILON )
rst.x += 1.0;
else if (rst.x < 0.0 )
rst.x = 0.0;
else if (rst.x > 1.0 )
rst.x = 1.0;
if ( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space )
{
// side uses 0 <= x <= 2/3 region of the texture map
rst.x *= 2.0;
rst.x /= 3.0;
}
// convert height to normalized texture coordinate
rst.y = 0.5*rst.z + 0.5;
if ( m_bCapped )
{
// clamp height
if ( rst.y < 0.0 ) rst.y = 0.0; else if ( rst.y > 1.0 ) rst.y = 1.0;
}
side0 = 1;
}
rst.z = r;
*T = m_uvw*rst;
return side0;
}
int ON_TextureMapping::EvaluateBoxMapping(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
) const
{
// The matrix m_Pxyz transforms the world coordinate
// "mapping cylinder" into the cylinder centered at
// rst = (0,0,0) with radius 1.0. The axis runs
// from rst = (0,0,-1) to rst = (0,0,+1).
ON_3dPoint rst(m_Pxyz*P);
ON_3dVector n(m_Nxyz*N);
n.Unitize();
int side0, side1;
double t0, t1;
side0 = 0;
t0 = 0.0;
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
// 3 = back side (y=-1)
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
if ( m_bCapped )
{
// intersect ray with top and bottom
side0 = IntersectBoxRayHelper(rst,n,2,&t0);
}
// intersect ray with front and back
side1 = IntersectBoxRayHelper(rst,n,0,&t1);
if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
{
side0 = side1;
t0 = t1;
}
// intersect ray with left and right
side1 = IntersectBoxRayHelper(rst,n,1,&t1);
if ( 0 == side0 || 1 == BestHitHelper(t0,t1) )
{
side0 = side1;
t0 = t1;
}
if ( 0 != side0 )
{
// ray hit the box
rst = rst + t0*n;
}
}
if ( 0 == side0 )
{
// set side0 = side closest to the point
side1 = (fabs(rst.x) >= fabs(rst.y)) ? 0 : 1;
if ( m_bCapped && fabs(rst.z) > fabs(((double*)&rst.x)[side1]) )
side1 = 2;
t1 = (&rst.x)[side1];
if ( t1 < 0.0 )
{
side0 = 2*side1 + 1;
}
else
{
side0 = 2*side1 + 2;
}
//if ( fabs(t1) <= 1.0+ON_SQRT_EPSILON )...
//// The point is inside the box. If the normal
//// is not zero, then use it to choose the side
//// used for the closest point projection.
side1 = ( fabs(n.x) >= fabs(n.y) ) ? 0 : 1;
if ( m_bCapped && fabs(n.z) > fabs((&n.x)[side1]))
{
side1 = 2;
}
t1 = n[side1];
if ( 0.0 != t1 )
{
if ( t1 < 0.0 )
side0 = 2*side1 + 1;
else if ( t1 > 0.0 )
side0 = 2*side1 + 2;
}
}
double shift = 0.0;
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
// 3 = back side (y=-1)
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
switch(side0)
{
case 1: // x = -1
rst.x = -rst.y;
rst.y = rst.z;
shift = 3.0;
break;
case 2: // x = +1
rst.x = rst.y;
rst.y = rst.z;
shift = 1.0;
break;
case 3: // y = -1
rst.y = rst.z;
shift = 0.0;
break;
case 4: // y = +1
rst.x = -rst.x;
rst.y = rst.z;
shift = 2.0;
break;
case 5: // z = -1
rst.x = -rst.x;
shift = 4.0;
break;
case 6: // z = +1
shift = 5.0;
break;
}
// normalize texture coordinates
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
rst.z = 0.0;
if( ON_TextureMapping::TEXTURE_SPACE::divided == m_texture_space)
{
rst.x = (shift + rst.x)/(m_bCapped ? 6.0 : 4.0);
}
*T = m_uvw*rst;
return side0;
}
int ON_TextureMapping::Evaluate(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T,
const ON_Xform& P_xform,
const ON_Xform& N_xform
) const
{
int rc;
ON_3dPoint Q = P*P_xform;
if ( ON_TextureMapping::PROJECTION::ray_projection == m_projection )
{
// need a transformed normal
ON_3dVector V = N_xform*N;
V.Unitize();
rc = Evaluate(Q,V,T);
}
else
{
// normal is ignored
rc = Evaluate(Q,N,T);
}
return rc;
}
int ON_TextureMapping::Evaluate(
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
) const
{
int rc;
switch(m_type)
{
case ON_TextureMapping::TYPE::srfp_mapping:
*T = m_uvw * P; // Do NOT apply m_Pxyz here.
rc = 1;
break;
case ON_TextureMapping::TYPE::sphere_mapping:
rc = EvaluateSphereMapping(P,N,T);
break;
case ON_TextureMapping::TYPE::cylinder_mapping:
rc = EvaluateCylinderMapping(P,N,T);
break;
case ON_TextureMapping::TYPE::box_mapping:
rc = EvaluateBoxMapping(P,N,T);
break;
case ON_TextureMapping::TYPE::mesh_mapping_primitive:
rc = 0;
break;
case ON_TextureMapping::TYPE::srf_mapping_primitive:
rc = 0;
break;
case ON_TextureMapping::TYPE::brep_mapping_primitive:
rc = 0;
break;
default:
rc = EvaluatePlaneMapping(P,N,T);
break;
}
return rc;
}
const ON_Object* ON_TextureMapping::CustomMappingPrimitive(void) const
{
return m_mapping_primitive.get();
}
//Returns a valid mesh if the custom mapping primitive is a mesh. Otherwise nullptr.
//Implementation is return ON_Mesh::Cast(CustomMappingPrimitive());
const ON_Mesh* ON_TextureMapping::CustomMappingMeshPrimitive(void) const
{
return ON_Mesh::Cast(CustomMappingPrimitive());
}
//Returns a valid brep if the custom mapping primitive is a brep. Otherwise nullptr.
//Implementation is return ON_Brep::Cast(CustomMappingPrimitive());
const ON_Brep* ON_TextureMapping::CustomMappingBrepPrimitive(void) const
{
return ON_Brep::Cast(CustomMappingPrimitive());
}
//Returns a valid surface if the custom mapping primitive is a surface. Otherwise nullptr.
//Implementation is return ON_Surface::Cast(CustomMappingPrimitive());
const ON_Surface* ON_TextureMapping::CustomMappingSurfacePrimitive(void) const
{
return ON_Surface::Cast(CustomMappingPrimitive());
}
void ON_TextureMapping::SetCustomMappingPrimitive(ON_Object* p)
{
m_mapping_primitive.reset(p);
}
ON__UINT32 ON_TextureMapping::MappingCRC() const
{
// include any member that can change values returned by Evaluate
ON__UINT32 crc32 = 0x12345678;
crc32 = ON_CRC32(crc32,sizeof(m_type),&m_type);
if ( ON_TextureMapping::TYPE::srfp_mapping != m_type )
{
// As of 21 June 2006 m_Pxyz cannot effect ON_TextureMapping::TYPE::srfp_mapping,
// so it shouldn't be included in the CRC for srfp_mappings.
crc32 = ON_CRC32(crc32,sizeof(m_projection), &m_projection);
crc32 = ON_CRC32(crc32,sizeof(m_texture_space), &m_texture_space);
crc32 = ON_CRC32(crc32,sizeof(m_bCapped), &m_bCapped);
crc32 = ON_CRC32(crc32,sizeof(m_Pxyz), &m_Pxyz);
// do not include m_Nxyz here - it won't help and may hurt
if ( m_mapping_primitive )
{
switch( m_type )
{
case ON_TextureMapping::TYPE::mesh_mapping_primitive:
{
const ON_Mesh* mesh = CustomMappingMeshPrimitive();
if ( 0 == mesh )
break;
crc32 = mesh->DataCRC(crc32);
if ( mesh->HasTextureCoordinates() )
{
// 25 August 2010 Dale Lear
// Including m_T[] in crc32 per Jussi and Andy email discussion.
// This is probably correct because users will expect the
// "picture" on the mesh to be applied to the target in
// a visual way.
const ON_2fPoint* tex = mesh->m_T.Array();
crc32 = ON_CRC32(crc32,mesh->m_T.Count()*sizeof(tex[0]),tex);
}
}
break;
case ON_TextureMapping::TYPE::brep_mapping_primitive:
{
const ON_Brep* brep = CustomMappingBrepPrimitive();
if ( 0 == brep )
break;
crc32 = brep->DataCRC(crc32);
// 25 August 2010 Dale Lear
// Should brep's render meshes be included in the crc?
// The texture that is being mapped is actually
// being applied to the brep by the render mesh's
// m_T[] values and some users will want to see
// the "picture" on the brep mapped to the
// "picture" on the
// target.
}
break;
case ON_TextureMapping::TYPE::srf_mapping_primitive:
{
const ON_Surface* surface = CustomMappingSurfacePrimitive();
if ( 0 == surface )
break;
crc32 = surface->DataCRC(crc32);
}
break;
case ON_TextureMapping::TYPE::no_mapping:
case ON_TextureMapping::TYPE::srfp_mapping:
case ON_TextureMapping::TYPE::plane_mapping:
case ON_TextureMapping::TYPE::cylinder_mapping:
case ON_TextureMapping::TYPE::sphere_mapping:
case ON_TextureMapping::TYPE::box_mapping:
default:
break;
}
}
}
crc32 = ON_CRC32(crc32,sizeof(m_uvw), &m_uvw);
return crc32;
}
bool ON_TextureMapping::RequiresVertexNormals() const
{
if ( ON_TextureMapping::TYPE::srfp_mapping == m_type )
return false;
if(m_projection == ON_TextureMapping::PROJECTION::ray_projection)
return true;
if(m_type == ON_TextureMapping::TYPE::box_mapping)
return true;
if(m_type == ON_TextureMapping::TYPE::cylinder_mapping && m_bCapped)
return true;
return false;
}
bool ON_TextureMapping::IsPeriodic(void) const
{
return (m_type == ON_TextureMapping::TYPE::sphere_mapping || m_type == ON_TextureMapping::TYPE::cylinder_mapping);
}
bool ON_TextureMapping::HasMatchingTextureCoordinates(
const ON_Mesh& mesh,
const ON_Xform* mesh_xform
) const
{
bool rc = (mesh.HasTextureCoordinates())
? HasMatchingTextureCoordinates(mesh.m_Ttag,mesh_xform)
: false;
return rc;
}
bool ON_TextureMapping::HasMatchingTextureCoordinates(
const ON_MappingTag& tag,
const ON_Xform* mesh_xform
) const
{
bool rc = false;
// DO NOT COMPARE m_mapping_id's in this function.
// This function returns true if the tc values
// calculated by the mapping will be the same
// as the mapping that was used to set the tag.
if ( tag.m_mapping_crc == MappingCRC() )
{
rc = true;
// zero transformations indicate the mapping
// values are independent of the 3d location
// of the mesh. The ON_TextureMapping::TYPE::srfp_mapping != m_type
// check is used because these mappings are
// alwasy independent of 3d location but
// the transformations are often set.
if ( ON_TextureMapping::TYPE::srfp_mapping != m_type
&& mesh_xform
&& mesh_xform->IsValid()
&& !mesh_xform->IsZero()
&& !tag.m_mesh_xform.IsZero()
)
{
// compare xforms - these can have a bit of slop
const double* a = &mesh_xform->m_xform[0][0];
const double* b = &tag.m_mesh_xform.m_xform[0][0];
for ( int i = 16; i--; /*empty*/ )
{
if ( fabs(*a++ - *b++) > ON_SQRT_EPSILON )
{
rc = false;
break;
}
}
}
}
return rc;
}
static
bool GetSPTCHelper(
const ON_Mesh& mesh,
const ON_TextureMapping& mapping,
float* tc,
int tc_stride
)
{
const int vcnt = mesh.m_V.Count();
if ( vcnt <= 0 )
return false;
if ( !mesh.HasSurfaceParameters() )
return false;
const ON_2dPoint* S = mesh.m_S.Array();
if ( !S )
return false;
int i;
double u, v, a, b;
// srf_udom and srf_vdom record the range
// of parameters saved in the m_S[] array.
ON_Interval srf_udom = mesh.m_srf_domain[0];
ON_Interval srf_vdom = mesh.m_srf_domain[1];
if ( !srf_udom.IsIncreasing() || !srf_vdom.IsIncreasing() )
{
// Attempt to calculate it from m_S[].
srf_udom.m_t[0] = srf_udom.m_t[1] = S[0].x;
srf_vdom.m_t[0] = srf_vdom.m_t[1] = S[0].y;
for ( i = 1; i < vcnt; i++ )
{
u = S[i].x;
if (u < srf_udom.m_t[0]) srf_udom.m_t[0] = u;
else if (u > srf_udom.m_t[1]) srf_udom.m_t[1] = u;
v = S[i].y;
if (v < srf_vdom.m_t[0]) srf_vdom.m_t[0] = v;
else if (v > srf_vdom.m_t[1]) srf_vdom.m_t[1] = v;
}
if ( !srf_udom.IsIncreasing()
|| !srf_vdom.IsIncreasing() )
{
return false;
}
}
bool bHaveUVWXform = mapping.m_uvw.IsValid()
&& !mapping.m_uvw.IsIdentity()
&& !mapping.m_uvw.IsZero();
if ( mesh.HasPackedTextureRegion() )
{
// Packed textures are not compatible with the use
// of m_uvw. m_uvw is ignored in this block
// of code on purpose. //SEE BELOW
const ON_Interval tex_udom = mesh.m_packed_tex_domain[0];
const ON_Interval tex_vdom = mesh.m_packed_tex_domain[1];
for ( i = 0; i < vcnt; i++)
{
//ALB 2011.01.14
//Added support for m_uvw in packed textures. Even though this conceptually makes
//very little sense, it's one of the most requested features for the texture mapping
//system, so I grudgingly add it.
if (bHaveUVWXform)
{
const ON_2dPoint si = mapping.m_uvw*S[i];
u = si.x;
v = si.y;
}
else
{
u = S[i].x;
v = S[i].y;
}
// (u, v) = known surface parameter
if ( mesh.m_packed_tex_rotate )
{
// verify this by checking with mesher
a = 1.0 - srf_vdom.NormalizedParameterAt( v );
b = srf_udom.NormalizedParameterAt( u );
}
else
{
a = srf_udom.NormalizedParameterAt( u );
b = srf_vdom.NormalizedParameterAt( v );
}
// When textures are packed, tex_udom and tex_vdom
// are subintervals of (0,1).
u = tex_udom.ParameterAt(a);
v = tex_vdom.ParameterAt(b);
tc[0] = (float)u;
tc[1] = (float)v;
tc += tc_stride;
}
}
else if ( bHaveUVWXform )
{
const ON_Xform xform(mapping.m_uvw);
ON_3dPoint P;
for ( i = 0; i < vcnt; i++)
{
// normalize surface parameter
P.x = srf_udom.NormalizedParameterAt( S[i].x );
P.y = srf_vdom.NormalizedParameterAt( S[i].y );
P.z = 0.0;
// apply m_uvw transformation
P = xform*P;
tc[0] = (float)P.x;
tc[1] = (float)P.y;
tc += tc_stride;
}
}
else
{
// m_srf_tex_rotate is ignored on purpose.
// It only applies if the texture is packed.
for ( i = 0; i < vcnt; i++)
{
// tc = normalized surface parameter
a = srf_udom.NormalizedParameterAt( S[i].x );
b = srf_vdom.NormalizedParameterAt( S[i].y );
tc[0] = (float)a;
tc[1] = (float)b;
tc += tc_stride;
}
}
return true;
}
bool ON_TextureMapping::GetTextureCoordinates(
const ON_Mesh& mesh,
ON_SimpleArray<ON_3fPoint>& T,
const ON_Xform* mesh_xform,
bool bLazy,
ON_SimpleArray<int>* Tside
) const
{
if ( Tside )
Tside->SetCount(0);
int i;
const int vcnt = mesh.m_V.Count();
if ( vcnt <= 0 )
return false;
if ( bLazy )
{
int tci, tccount = mesh.m_TC.Count();
for ( tci = 0; tci < tccount; tci++ )
{
if ( vcnt == mesh.m_TC[tci].m_T.Count() )
{
if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
{
T = mesh.m_TC[tci].m_T;
return true;
}
}
}
if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
{
T.Reserve(vcnt);
T.SetCount(vcnt);
const ON_2fPoint* f = mesh.m_T.Array();
ON_3fPoint* d = T.Array();
for ( i = vcnt; i--; f++, d++ )
{
d->x = f->x;
d->y = f->y;
d->z = 0.0f;
}
return true;
}
}
bool rc = false;
if ( ON_TextureMapping::TYPE::srfp_mapping == m_type )
{
// uv textures from surface parameterization
T.Reserve(vcnt);
T.SetCount(vcnt);
T.Zero();
rc = GetSPTCHelper(mesh,*this,&T[0].x,3);
}
else
{
ON_3dPoint P, tc;
ON_3dVector N(0.0,0.0,0.0);
const ON_3fPoint* mesh_V = mesh.m_V.Array();
const ON_3fVector* mesh_N = mesh.HasVertexNormals()
? mesh.m_N.Array()
: 0;
T.Reserve(vcnt);
T.SetCount(vcnt);
int* Tsd = 0;
if ( Tside )
{
Tside->Reserve(vcnt);
Tside->SetCount(vcnt);
Tsd = Tside->Array();
memset(Tsd,0,vcnt*sizeof(Tsd[0]));
}
ON_Xform P_xform(ON_Xform::IdentityTransformation), N_xform(ON_Xform::IdentityTransformation);
const double* PT = 0;
const double* NT = 0;
if ( mesh_xform )
{
if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
{
// ignore transformation
mesh_xform = 0;
}
else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
{
PT = &P_xform[0][0];
NT = &N_xform[0][0];
}
else
{
mesh_xform = 0;
}
}
const float* f;
double w;
int sd;
if (ON_TextureMapping::PROJECTION::clspt_projection == m_projection
&& (ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type || ON_TextureMapping::TYPE::brep_mapping_primitive == m_type)
&& nullptr != m_mapping_primitive)
{
rc = false;
}
else if ( mesh_N &&
( ON_TextureMapping::PROJECTION::ray_projection == m_projection
|| ON_TextureMapping::TYPE::box_mapping == m_type
|| ON_TextureMapping::TYPE::cylinder_mapping == m_type
|| ON_TextureMapping::TYPE::mesh_mapping_primitive == m_type
)
)
{
// calculation uses mesh vertex normal
if ( PT && NT )
{
// need to transform vertex and normal
// before calculating texture coordinates
for (i = 0; i < vcnt; i++)
{
f = &mesh_V[i].x;
w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
w = (0.0 != w) ? 1.0/w : 1.0;
P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
f = &mesh_N[i].x;
N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
N.Unitize();
sd = Evaluate(P,N,&tc);
T[i] = tc;
if ( Tsd ) Tsd[i] = sd;
}
}
else
{
// mesh vertex and normal are ok
for (i = 0; i < vcnt; i++)
{
P = mesh_V[i];
N = mesh_N[i];
sd = Evaluate(P,N,&tc);
T[i] = tc;
if ( Tsd ) Tsd[i] = sd;
}
}
}
else if ( PT )
{
// normal is not used
// mesh vertex needs to be transformed
for ( i = 0; i < vcnt; i++ )
{
f = &mesh_V[i].x;
w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
w = (0.0 != w) ? 1.0/w : 1.0;
P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
sd = Evaluate(P,N,&tc);
T[i] = tc;
if ( Tsd )
Tsd[i] = sd;
}
}
else
{
// normal is not used and mesh vertex is ok
for ( i = 0; i < vcnt; i++ )
{
P = mesh_V[i];
sd = Evaluate(P,N,&tc);
T[i] = tc;
if ( Tsd )
Tsd[i] = sd;
}
}
rc = true;
}
return rc;
}
static
void ThreeToTwoHelper(
const ON_SimpleArray<ON_3fPoint>& T3,
ON_SimpleArray<ON_2fPoint>& T2
)
{
int i = T3.Count();
const ON_3fPoint* t3 = T3.Array();
T2.Reserve(i);
T2.SetCount(i);
ON_2fPoint* t2 = T2.Array();
while(i--)
{
t2->x = t3->x;
t2->y = t3->y;
t2++;
t3++;
}
}
bool ON_TextureMapping::GetTextureCoordinates(
const ON_Mesh& mesh,
ON_SimpleArray<ON_2fPoint>& T,
const ON_Xform* mesh_xform,
bool bLazy,
ON_SimpleArray<int>* Tside
) const
{
bool rc = false;
if ( Tside )
Tside->SetCount(0);
if ( bLazy )
{
if ( HasMatchingTextureCoordinates(mesh,mesh_xform ) )
{
if ( T.Array() != mesh.m_T.Array() )
{
// different arrays - copy
T = mesh.m_T;
}
return true;
}
else
{
int vcnt = mesh.m_V.Count();
int tci, tccount = mesh.m_TC.Count();
for ( tci = 0; tci < tccount; tci++ )
{
if ( vcnt == mesh.m_TC[tci].m_T.Count() )
{
if ( HasMatchingTextureCoordinates(mesh.m_TC[tci].m_tag,mesh_xform) )
{
// copy T3d[] results to T[]
ThreeToTwoHelper(mesh.m_TC[tci].m_T,T);
return true;
}
}
}
}
}
if ( ON_TextureMapping::TYPE::srfp_mapping == m_type )
{
if (mesh.HasSurfaceParameters())
{
// uv textures from surface parameterization
T.Reserve(mesh.m_V.Count());
T.SetCount(mesh.m_V.Count());
T.Zero();
rc = GetSPTCHelper(mesh, *this, &T[0].x, 2);
}
else
{
//In this case, we're just going to leave the TC array in place
rc = false;
}
}
else
{
T.SetCount(0);
ON_SimpleArray<ON_3fPoint> T3;
if ( GetTextureCoordinates(mesh, T3, mesh_xform, false, Tside ) )
{
// copy T3d[] results to T[]
ThreeToTwoHelper(T3,T);
rc = true;
}
}
return rc;
}
//bool ON_Mesh::GetSurfaceParameterTextureXform(
// class ON_Xform& StoT
// ) const
//
//{
// bool rc = false;
// StoT.Identity();
//
// // Gets default mesh mapping
// const ON_Interval surface_u_domain(m_srf_domain[0]);
// const ON_Interval surface_v_domain(m_srf_domain[1]);
// const ON_Interval texture_u_domain(m_tex_domain[0]);
// const ON_Interval texture_v_domain(m_tex_domain[1]);
// bool bRotateTexture = m_srf_tex_rotate;
// if ( surface_u_domain.IsInterval()
// && surface_v_domain.IsInterval()
// && texture_u_domain.IsInterval()
// && texture_v_domain.IsInterval()
// )
// {
// double du = 1.0/surface_u_domain.Length();
// double dv = 1.0/surface_v_domain.Length();
// ON_Xform x1(1.0), x2(1.0), x3(1.0);
// x1.m_xform[0][0] = du; x1.m_xform[0][3] = -surface_u_domain[0]*du;
// x1.m_xform[1][1] = dv; x1.m_xform[1][3] = -surface_v_domain[0]*dv;
// if ( bRotateTexture )
// {
// x2.m_xform[0][0] = 0.0; x2.m_xform[0][1] = -1.0; x2.m_xform[0][3] = 1.0;
// x2.m_xform[1][0] = 1.0; x2.m_xform[1][1] = 0.0;
// }
// x3.m_xform[0][0] = texture_u_domain.Length(); x3.m_xform[0][3] = texture_u_domain[0];
// x3.m_xform[1][1] = texture_v_domain.Length(); x3.m_xform[1][3] = texture_v_domain[0];
//
// // transforms surface(u,v) to texture(u,v)
// StoT = x3*x2*x1;
//
// rc = true;
// }
// return rc;
//}
class ON__CMeshFaceTC
{
// DO NOT PUT THIS CLASS IN A HEADER FILE
// IT IS A PRIVATE HELPER CLASS.
public:
int fi;
int quad[4];
float Tx[4];
bool bSetT[4];
};
class ON__CChangeTextureCoordinateHelper
{
// DO NOT PUT THIS CLASS IN A HEADER FILE
// IT IS A PRIVATE HELPER CLASS.
public:
ON__CChangeTextureCoordinateHelper( ON_Mesh& mesh, int newvcnt, float*& mesh_T );
~ON__CChangeTextureCoordinateHelper();
int DupVertex(int vi);
void ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y, float* mesh_T, int mesh_T_stride );
int m_tci;
ON_Mesh& m_mesh;
ON_3dPointArray* m_mesh_dV = nullptr;
bool m_bHasVertexNormals;
bool m_bHasVertexTextures;
bool m_bHasVertexColors;
bool m_bHasSurfaceParameters;
bool m_bHasPrincipalCurvatures;
bool m_bHasHiddenVertices;
bool m_bHasCachedTextures;
ON_SimpleArray< ON_TextureCoordinates* > m_TC;
// m_vuse[] is an array of length = original number of
// vertices in m_mesh and m_vuse[vi] = number of faces
// that reference vertex vi. If this vertex needs to be
// split, vuse[vi] is decremented. The ultimate goal
// is to split a few times as needed so we don't
// bloat the mesh with repeated calls to changing
// texture maps. m_vuse[] is set the first time
// DupVertex() is called.
int m_vuse_count;
ON_SimpleArray< unsigned int > m_vuse;
private:
// no implementation
ON__CChangeTextureCoordinateHelper(const ON__CChangeTextureCoordinateHelper&);
ON__CChangeTextureCoordinateHelper& operator=(const ON__CChangeTextureCoordinateHelper&);
};
void ON__CChangeTextureCoordinateHelper::ChangeTextureCoordinate(int* Fvi, int fvi, float x, float y,
float* mesh_T, int mesh_T_stride )
{
int oldvi = Fvi[fvi];
float* T = mesh_T+(oldvi*mesh_T_stride);
if ( x != T[0] || (y != ON_UNSET_FLOAT && y != T[1]) )
{
int newvi = DupVertex(oldvi);
T = mesh_T + (newvi*mesh_T_stride);
T[0] = x;
if ( y != ON_UNSET_FLOAT )
T[1] = y;
if ( 2 == fvi && oldvi == Fvi[3] )
{
Fvi[2] = newvi;
Fvi[3] = newvi;
}
else
{
Fvi[fvi] = newvi;
}
}
}
ON__CChangeTextureCoordinateHelper::ON__CChangeTextureCoordinateHelper(
ON_Mesh& mesh,
int newvcnt,
float*& mesh_T )
: m_mesh(mesh)
, m_mesh_dV(0)
, m_vuse_count(0)
{
// adding vertices invalidates this cached information.
m_mesh.DestroyTopology();
m_mesh.DestroyPartition();
m_mesh.DestroyTree();
m_tci = -1;
const int vcnt = m_mesh.m_V.Count();
// It is critical to reserve enough room in the arrays
// before duplication starts. Otherwise, during duplication,
// a dyanamic array can be reallocated, which will make
// saved array base pointers will be invalid, and you crash
// the next time they are used.
m_mesh.m_V.Reserve(vcnt+newvcnt);
if ( m_mesh.HasDoublePrecisionVertices() )
{
m_mesh_dV = &m_mesh.m_dV;
m_mesh_dV->Reserve(vcnt+newvcnt);
}
else
{
m_mesh.DestroyDoublePrecisionVertices();
}
m_bHasVertexNormals = m_mesh.HasVertexNormals();
if ( m_bHasVertexNormals )
m_mesh.m_N.Reserve(vcnt+newvcnt);
m_bHasVertexTextures = m_mesh.HasTextureCoordinates();
if ( m_bHasVertexTextures )
{
float* p = (float*)m_mesh.m_T.Array();
m_mesh.m_T.Reserve(vcnt+newvcnt);
if ( p == mesh_T )
mesh_T = (float*)m_mesh.m_T.Array();
}
m_bHasVertexColors = m_mesh.HasVertexColors();
if ( m_bHasVertexColors )
m_mesh.m_C.Reserve(vcnt+newvcnt);
m_bHasSurfaceParameters = m_mesh.HasSurfaceParameters();
if ( m_bHasSurfaceParameters )
m_mesh.m_S.Reserve(vcnt+newvcnt);
m_bHasPrincipalCurvatures = m_mesh.HasPrincipalCurvatures();
if ( m_bHasPrincipalCurvatures )
m_mesh.m_K.Reserve(vcnt+newvcnt);
m_bHasHiddenVertices = (0 != m_mesh.HiddenVertexArray());
if ( m_bHasHiddenVertices )
m_mesh.m_H.Reserve(vcnt+newvcnt);
// Set m_TC[] to be the subset of m_mesh.m_TC[] that is
// valid for duplication.
m_bHasCachedTextures = false;
int tci, tccount = m_mesh.m_TC.Count();
m_TC.Reserve(tccount);
for ( tci = 0; tci < tccount; tci++ )
{
ON_TextureCoordinates& tc = m_mesh.m_TC[tci];
if ( vcnt == tc.m_T.Count() )
{
m_bHasCachedTextures = true;
float* p = (float*)tc.m_T.Array();
tc.m_T.Reserve(vcnt+newvcnt);
if ( p == mesh_T )
mesh_T = (float*)tc.m_T.Array();
m_TC.Append( &tc );
}
}
}
ON__CChangeTextureCoordinateHelper::~ON__CChangeTextureCoordinateHelper()
{
if ( nullptr != m_mesh_dV )
{
m_mesh_dV = 0;
}
}
int ON__CChangeTextureCoordinateHelper::DupVertex(int vi)
{
if ( 0 == m_vuse_count )
{
// m_vuse[] is an array of length = original number of
// vertices in m_mesh and m_vuse[vi] = number of faces
// that reference vertex vi. If this vertex needs to be
// split, vuse[vi] is decremented. The ultimate goal
// is to split a few times as needed so we don't
// bloat the mesh with repeated calls to changing
// texture maps. m_vuse[] is set the first time
// DupVertex() is called.
m_vuse_count = m_mesh.m_V.Count();
m_vuse.Reserve(m_vuse_count);
m_vuse.SetCount(m_vuse_count);
m_vuse.Zero();
for ( int fi = 0; fi < m_mesh.m_F.Count(); fi++ )
{
const int* Fvi = m_mesh.m_F[fi].vi;
int i = Fvi[0];
if ( i >= 0 && i < m_vuse_count )
m_vuse[i]++;
i = Fvi[1];
if ( i >= 0 && i < m_vuse_count )
m_vuse[i]++;
i = Fvi[2];
if ( i >= 0 && i < m_vuse_count )
m_vuse[i]++;
i = Fvi[3];
if ( Fvi[2] != i && i >= 0 && i < m_vuse_count )
m_vuse[i]++;
}
}
if ( vi >= 0 && vi < m_vuse_count )
{
if ( m_vuse[vi] <= 1 )
return vi; // only one face uses this vertex - no need to dup the vertex
// otherwise we will duplicate this vertex, reducing its use count by 1.
m_vuse[vi]--;
}
m_mesh.m_V.AppendNew();
*m_mesh.m_V.Last() = m_mesh.m_V[vi];
if ( 0 != m_mesh_dV )
{
m_mesh_dV->AppendNew();
*(m_mesh_dV->Last()) = m_mesh_dV->operator[](vi);
}
if ( m_bHasVertexTextures )
{
m_mesh.m_T.AppendNew();
*m_mesh.m_T.Last() = m_mesh.m_T[vi];
}
if ( m_bHasVertexNormals )
{
m_mesh.m_N.AppendNew();
*m_mesh.m_N.Last() = m_mesh.m_N[vi];
}
if ( m_bHasVertexColors )
{
m_mesh.m_C.AppendNew();
*m_mesh.m_C.Last() = m_mesh.m_C[vi];
}
if ( m_bHasSurfaceParameters )
{
m_mesh.m_S.AppendNew();
*m_mesh.m_S.Last() = m_mesh.m_S[vi];
}
if ( m_bHasPrincipalCurvatures )
{
m_mesh.m_K.AppendNew();
*m_mesh.m_K.Last() = m_mesh.m_K[vi];
}
if ( m_bHasHiddenVertices )
{
m_mesh.m_H.AppendNew();
if ( 0 != (*m_mesh.m_H.Last() = m_mesh.m_H[vi]) )
m_mesh.m_hidden_count++;
}
if ( m_bHasCachedTextures )
{
// Note: This m_TC[] is the subset of m_mesh.m_TC[]
// that need to be duped. The constructor
// insures that m_TC[i] is not nullptr and
// has the right count and capacity.
//
// DO NOT REFERENCE m_mesh.m_TC[] in this block.
int tccount = m_TC.Count();
for ( int i = 0; i < tccount; i++ )
{
ON_SimpleArray<ON_3fPoint>& T = m_TC[i]->m_T;
T.AppendNew();
*T.Last() = T[vi];
}
}
return m_mesh.m_V.Count()-1;
}
static
float PoleFix( float t0, float t1 )
{
float t = ( ON_UNSET_FLOAT == t0 )
? t1
: ((ON_UNSET_FLOAT == t1 ) ? t0 : (0.5f*(t0+t1)));
return t;
}
static
int IntersectBoxSideRayHelper(int side, const ON_3dPoint& rst, const ON_3dVector& n, double* s)
{
/*
returns:
0 = ray parallel to sides
1 = ray hit left side (x=-1)
2 = ray hit right side (x=+1)
3 = ray hit back side (y=-1)
4 = ray hit front side (y=+1)
5 = ray hit bottom side (z=-1)
6 = ray hit top side (z=+1)
*/
double nx;
ON_3dPoint Q;
double t,t0,t1;
int dir;
switch(side)
{
case 1: // = left side (x=-1)
t1 = -1.0;
dir = 0;
break;
case 2: // right side (x=+1)
t1 = 1.0;
dir = 0;
break;
case 3: // back side (y=-1)
t1 = -1.0;
dir = 1;
break;
case 4: // front side (y=+1)
t1 = 1.0;
dir = 1;
break;
case 5: // bottom side (z=-1)
t1 = -1.0;
dir = 2;
break;
case 6: // top side (z=+1)
t1 = 1.0;
dir = 2;
break;
default:
*s = ON_UNSET_VALUE;
return 0;
break;
}
// protect against overflow
nx = (&n.x)[dir];
t0 = (t1 - (&rst.x)[dir]);
if ( fabs(t0) >= fabs(nx)*on__overflow_tol )
{
*s = ON_UNSET_VALUE;
return 0;
}
t0 /= nx;
Q = rst + t0*n;
if ( dir )
{
t = Q.x;
Q.x = Q[dir];
Q[dir] = t;
}
if ( fabs(Q.x-t1) > ON_SQRT_EPSILON || fabs(Q.y) > 1.0e8 || fabs(Q.z) > 1.0e8 )
{
*s = ON_UNSET_VALUE;
return 0;
}
*s = t0;
return side;
}
static
bool EvBoxSideTextureCoordinateHelper2(
int side,
const ON_TextureMapping& box_mapping,
const ON_3dPoint& P,
const ON_3dVector& N,
ON_3dPoint* T
)
{
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
// 3 = back side (y=-1)
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
// The matrix m_Pxyz transforms the world coordinate
// "mapping cylinder" into the cylinder centered at
// rst = (0,0,0) with radius 1.0. The axis runs
// from rst = (0,0,-1) to rst = (0,0,+1).
ON_3dPoint rst(box_mapping.m_Pxyz*P);
ON_3dVector n(box_mapping.m_Nxyz*N);
n.Unitize();
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
// 3 = back side (y=-1)
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
if ( ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection )
{
double s;
if ( side == IntersectBoxSideRayHelper(side, rst, n, &s) )
{
// ray hit the box side
rst = rst + s*n;
}
}
double shift = 0.0;
// side flag
// 1 = left side (x=-1)
// 2 = right side (x=+1)
// 3 = back side (y=-1)
// 4 = front side (y=+1)
// 5 = bottom side (z=-1)
// 6 = top side (z=+1)
switch(side)
{
case 1: // x = -1
rst.x = -rst.y;
rst.y = rst.z;
shift = 3.0;
break;
case 2: // x = +1
rst.x = rst.y;
rst.y = rst.z;
shift = 1.0;
break;
case 3: // y = -1
rst.y = rst.z;
shift = 0.0;
break;
case 4: // y = +1
rst.x = -rst.x;
rst.y = rst.z;
shift = 2.0;
break;
case 5: // z = -1
rst.x = -rst.x;
shift = 4.0;
break;
case 6: // z = +1
shift = 5.0;
break;
default:
return 0;
break;
}
// normalize texture coordinates
rst.x = 0.5*rst.x + 0.5;
rst.y = 0.5*rst.y + 0.5;
rst.z = 0.0;
if( ON_TextureMapping::TEXTURE_SPACE::divided == box_mapping.m_texture_space)
{
rst.x = (shift + rst.x)/(box_mapping.m_bCapped ? 6.0 : 4.0);
}
*T = box_mapping.m_uvw*rst;
return true;
}
static
bool EvBoxSideTextureCoordinateHelper1(
const ON_Mesh& mesh,
const ON_Xform* mesh_xform,
int vi,
int side,
const ON_TextureMapping& box_mapping,
float* Tx,
float* Ty
)
{
bool rc = false;
ON_3dPoint P, tc;
ON_3dVector N(0.0,0.0,0.0);
const ON_3fPoint* mesh_V = mesh.m_V.Array();
const ON_3fVector* mesh_N = mesh.HasVertexNormals()
? mesh.m_N.Array()
: 0;
ON_Xform P_xform(ON_Xform::IdentityTransformation), N_xform(ON_Xform::IdentityTransformation);
const double* PT = 0;
const double* NT = 0;
if ( mesh_xform )
{
if ( mesh_xform->IsZero() || mesh_xform->IsIdentity() )
{
// ignore transformation
mesh_xform = 0;
}
else if ( 0.0 != mesh_xform->GetMappingXforms(P_xform,N_xform) )
{
PT = &P_xform[0][0];
NT = &N_xform[0][0];
}
else
{
mesh_xform = 0;
}
}
const float* f;
double w;
if ( mesh_N && ON_TextureMapping::PROJECTION::ray_projection == box_mapping.m_projection )
{
// calculation uses mesh vertex normal
if ( PT && NT )
{
// need to transform vertex and normal
// before calculating texture coordinates
f = &mesh_V[vi].x;
w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
w = (0.0 != w) ? 1.0/w : 1.0;
P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
f = &mesh_N[vi].x;
N.x = PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2];
N.y = PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2];
N.z = PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2];
N.Unitize();
}
else
{
// mesh vertex and normal are ok
P = mesh_V[vi];
N = mesh_N[vi];
}
}
else if ( PT )
{
// normal is not used
// mesh vertex needs to be transformed
f = &mesh_V[vi].x;
w = PT[12]*f[0] + PT[13]*f[1] + PT[14]*f[2] + PT[15];
w = (0.0 != w) ? 1.0/w : 1.0;
P.x = w*(PT[0]*f[0] + PT[1]*f[1] + PT[ 2]*f[2] + PT[ 3]);
P.y = w*(PT[4]*f[0] + PT[5]*f[1] + PT[ 6]*f[2] + PT[ 7]);
P.z = w*(PT[8]*f[0] + PT[9]*f[1] + PT[10]*f[2] + PT[11]);
}
else
{
// normal is not used and mesh vertex is ok
P = mesh_V[vi];
}
rc = EvBoxSideTextureCoordinateHelper2(side,box_mapping,P,N,&tc);
if (rc)
{
rc = tc.IsValid();
if (rc)
{
*Tx = (float)tc.x;
*Ty = (float)tc.y;
}
}
return rc;
}
class ON__CNewMeshFace
{
public:
int fi;
int newvcnt;
bool bNewV[4];
ON_2fPoint tc[4];
};
static
float TcDistanceHelper(const ON_2fPoint& tc)
{
float dx = (tc.x > 0.5f) ? (1.0f-tc.x) : tc.x;
if ( dx < 0.0f)
return 0.0f;
float dy = (tc.y > 0.5f) ? (1.0f-tc.y) : tc.y;
if ( dy < 0.0f)
return 0.0f;
return (dx < dy) ? dx : dy;
}
static
void AdjustSingleBoxTextureCoordinatesHelper(
ON_Mesh& mesh,
const ON_Xform* mesh_xform,
float* mesh_T,
int mesh_T_stride,
const int* Tsd,
const ON_TextureMapping& box_mapping
)
{
const int vcnt = mesh.m_V.Count();
const int fcnt = mesh.m_F.Count();
if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() || !Tsd )
return;
const ON_MeshFace* mesh_F = mesh.m_F.Array();
const int* Fvi;
int j, k, fi, sd[4], fvicnt, side, newvcnt=0;
ON__CNewMeshFace mf;
ON_2fPoint tc;
ON_SimpleArray<ON__CNewMeshFace> mflist(512);
float d;
for ( fi = 0; fi < fcnt; fi++ )
{
Fvi = mesh_F[fi].vi;
sd[0] = Tsd[Fvi[0]];
sd[1] = Tsd[Fvi[1]];
sd[2] = Tsd[Fvi[2]];
sd[3] = Tsd[Fvi[3]];
if ( sd[0] == sd[1] && sd[0] == sd[2] && sd[0] == sd[3] )
{
// all texture coords are on same side of box
continue;
}
fvicnt = (Fvi[2] != Fvi[3]) ? 4 : 3;
memset(&mf,0,sizeof(mf));
mf.tc[0] = mesh_T + (Fvi[0]*mesh_T_stride);
mf.tc[1] = mesh_T + (Fvi[1]*mesh_T_stride);
mf.tc[2] = mesh_T + (Fvi[2]*mesh_T_stride);
mf.tc[3] = mesh_T + (Fvi[3]*mesh_T_stride);
// find the side we will use for this face
side = sd[0];
d = TcDistanceHelper(mf.tc[0]);
for ( j = 1; j < fvicnt; j++ )
{
float d1 = TcDistanceHelper(mf.tc[j]);
if (d1 > d)
{
side = sd[j];
d = d1;
}
}
// Jussi, 5th September 2011:
// This 'continue' only works for faces having one or more of its tc's in (0,1)x(0,1).
// I have commented it out as a fix to RR 90329.
//if ( d <= 0.0f )
// continue;
for ( j = 0; j < fvicnt; j++ )
{
if ( sd[j] != side )
{
// calculate new tc for this side
if ( EvBoxSideTextureCoordinateHelper1(
mesh,
mesh_xform,
Fvi[j],
side,
box_mapping,
&tc.x,&tc.y) )
{
if ( tc.x != mf.tc[j].x || tc.y != mf.tc[j].y )
{
mf.tc[j] = tc;
mf.bNewV[j] = true;
mf.newvcnt++;
}
}
else
break;
}
}
if ( j >= fvicnt && mf.newvcnt > 0 )
{
mf.fi = fi;
newvcnt += mf.newvcnt;
mflist.Append(mf);
}
}
if ( newvcnt <= 0 )
return;
ON__CChangeTextureCoordinateHelper helper(mesh,vcnt+newvcnt,mesh_T);
const int mflist_count = mflist.Count();
for ( k = 0; k < mflist_count; k++ )
{
mf = mflist[k];
int* fvi = mesh.m_F[mf.fi].vi;
fvicnt = (fvi[2]!=fvi[3]) ? 4 : 3;
for ( j = 0; j < fvicnt; j++ )
{
if ( mf.bNewV[j] )
{
helper.ChangeTextureCoordinate(fvi,j,mf.tc[j].x,mf.tc[j].y,mesh_T,mesh_T_stride);
}
}
}
}
static
void 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
)
{
// This helper adjusts texture coordinates on faces that
// span the seam on mapping spheres and cylinders and
// resolves the mulitiple valued problem that
// exists at the poles of sphere mappings.
const int vcnt = mesh.m_V.Count();
const int fcnt = mesh.m_F.Count();
if ( vcnt < 3 || fcnt < 1 || vcnt != mesh.m_T.Count() )
return;
// see if any texture coordinate adjustment is necessary
const ON_TextureMapping::TYPE mapping_type = mapping.m_type;
const bool bSphereCheck = ( ON_TextureMapping::TYPE::sphere_mapping == mapping_type );
const bool bCylinderCheck = (Tsd && ON_TextureMapping::TYPE::cylinder_mapping == mapping_type);
const bool bBoxCheck = (Tsd && ON_TextureMapping::TYPE::box_mapping == mapping_type);
if ( bBoxCheck && ON_TextureMapping::TEXTURE_SPACE::single == mapping.m_texture_space )
{
AdjustSingleBoxTextureCoordinatesHelper( mesh, mesh_xform, mesh_T, mesh_T_stride, Tsd, mapping );
return;
}
ON_Workspace ws;
int* quad = ws.GetIntMemory(vcnt); // ~ws will free quad memory
float* Tx = (float*)ws.GetMemory(vcnt*sizeof(Tx[0]));
float t;
int vi, ti, q=0;
int ftc_count = 0;
const float ang0 = (float)(0.25*two_pi_tc);
const float ang1 = (float)(0.75*two_pi_tc);
for ( vi = ti = 0; vi < vcnt; vi++, ti += mesh_T_stride )
{
quad[vi] = 0;
Tx[vi] = mesh_T[ti];
if ( bCylinderCheck )
{
if ( 1 != Tsd[vi] )
continue;
}
else if ( bBoxCheck )
{
if ( 1 != Tsd[vi] && 3 != Tsd[vi] )
continue;
}
else if ( bSphereCheck )
{
t = mesh_T[ti+1]; // t = "v" texture coordinate
if ( t < 0.001f )
{
quad[vi] = 8; q |= 8; // south pole point
ftc_count++;
continue;
}
if ( t > 0.999f )
{
quad[vi] = 8; q |= 8; // north pole point
ftc_count++;
continue;
}
}
t = Tx[vi]; // t = "u" texture coordinate
if ( t < ang0 )
{
quad[vi] = 1; q |= 1; // longitude < pi/2
ftc_count++;
}
else if ( t > ang1 )
{
quad[vi] = 4; q |= 4; // longitude > 3pi/2
ftc_count++;
}
}
if ( 0 == q || 1 == q || 4 == q )
{
// nothing needs to be adjusted
return;
}
// 4*ftc_count = (over) estimate of the number of faces that
// will be changed.
ON_SimpleArray<ON__CMeshFaceTC> ftc_list(ftc_count*4 + 128);
ftc_count = 0;
const ON_MeshFace* F = mesh.m_F.Array();
const int* Fvi;
int fi;
ON__CMeshFaceTC ftc;
memset(&ftc,0,sizeof(ftc));
float t0, t1;
for ( fi = 0; fi < fcnt; fi++ )
{
Fvi = F[fi].vi;
ftc.quad[0] = quad[Fvi[0]];
ftc.quad[1] = quad[Fvi[1]];
ftc.quad[2] = quad[Fvi[2]];
ftc.quad[3] = quad[Fvi[3]];
q = (ftc.quad[0] | ftc.quad[1] | ftc.quad[2] | ftc.quad[3]);
if ( 0 == q || 1 == q || 4 == q )
{
// no adjustments need to be made
continue;
}
// ftc.fi will be set to fi if a texture coordinate needs to be adjusted
ftc.fi = -1;
ftc.Tx[0] = Tx[Fvi[0]];
ftc.Tx[1] = Tx[Fvi[1]];
ftc.Tx[2] = Tx[Fvi[2]];
ftc.Tx[3] = Tx[Fvi[3]];
if ( 0 != (8&q) )
{
// see if check for north/south sphere mapping poles and fix them
if ( 8 == ftc.quad[0] )
{
t0 = (8 == ftc.quad[3]) ? ON_UNSET_FLOAT : ftc.Tx[3];
t1 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
{
ftc.Tx[0] = PoleFix(t0,t1);
ftc.quad[0] = ((ftc.Tx[0] < ang0) ? 1 : ((ftc.Tx[0] > ang1) ? 4 : 0));
q |= ftc.quad[0];
ftc.fi = fi;
}
}
if ( 8 == ftc.quad[1] )
{
t0 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
t1 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
{
ftc.Tx[1] = PoleFix(t0,t1);
ftc.quad[1] = ((ftc.Tx[1] < ang0) ? 1 : ((ftc.Tx[1] > ang1) ? 4 : 0));
q |= ftc.quad[1];
ftc.fi = fi;
}
}
if ( 8 == ftc.quad[2] )
{
int k = (Fvi[2] == Fvi[3]) ? 0 : 3;
t0 = (8 == ftc.quad[1]) ? ON_UNSET_FLOAT : ftc.Tx[1];
t1 = (8 == ftc.quad[k]) ? ON_UNSET_FLOAT : ftc.Tx[k];
if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
{
ftc.Tx[2] = PoleFix(t0,t1);
ftc.quad[2] = ((ftc.Tx[2] < ang0) ? 1 : ((ftc.Tx[2] > ang1) ? 4 : 0));
if ( !k )
{
ftc.Tx[3] = ftc.Tx[2];
ftc.quad[3] = ftc.quad[2];
}
q |= ftc.quad[2];
ftc.fi = fi;
}
}
if ( 8 == ftc.quad[3] && Fvi[2] != Fvi[3] )
{
t0 = (8 == ftc.quad[2]) ? ON_UNSET_FLOAT : ftc.Tx[2];
t1 = (8 == ftc.quad[0]) ? ON_UNSET_FLOAT : ftc.Tx[0];
if ( ON_UNSET_FLOAT != t0 || ON_UNSET_FLOAT != t1 )
{
ftc.Tx[3] = PoleFix(t0,t1);
ftc.quad[3] = ((ftc.Tx[3] < ang0) ? 1 : ((ftc.Tx[3] > ang1) ? 4 : 0));
q |= ftc.quad[3];
ftc.fi = fi;
}
}
}
if ( 5 == (5&q) )
{
// The face has corners on both sides of the seam
if ( two_pi_tc == 1.0 )
{
if ( 1 == ftc.quad[0] ) {ftc.Tx[0] += 1.0f; ftc.fi = fi;}
if ( 1 == ftc.quad[1] ) {ftc.Tx[1] += 1.0f; ftc.fi = fi;}
if ( 1 == ftc.quad[2] ) {ftc.Tx[2] += 1.0f; ftc.fi = fi;}
if ( 1 == ftc.quad[3] ) {ftc.Tx[3] += 1.0f; ftc.fi = fi;}
}
else
{
// With divided textures, wrapping the texture coordinate
// does not work because it wraps into a region of the
// texture not use by this "side". In this case, the
// only thing to do is to pick the best end of the texture
// map and clamp the tcs that hang over. If the mesh
// has edges near the texture seam, the picture will
// still look ok.
float f0=0.0f, f1=0.0f, twopitc = (float)two_pi_tc;;
//int f0cnt=0, f1cnt=0;
if ( 1 == ftc.quad[0] ) f0 += ftc.Tx[0]; else if ( 4 == ftc.quad[0] ) f1 += twopitc-ftc.Tx[0];
if ( 1 == ftc.quad[1] ) f0 += ftc.Tx[1]; else if ( 4 == ftc.quad[1] ) f1 += twopitc-ftc.Tx[1];
if ( 1 == ftc.quad[2] ) f0 += ftc.Tx[2]; else if ( 4 == ftc.quad[2] ) f1 += twopitc-ftc.Tx[2];
if (Fvi[2] != Fvi[3])
{
if ( 1 == ftc.quad[3] ) f0 += ftc.Tx[3]; else if ( 4 == ftc.quad[3] ) f1 += twopitc-ftc.Tx[3];
}
if (f0 >= f1 )
{
// "most" of the face is on the left side of the texture
// If a vertex is on the right side, clamp its tc to 0.
if ( 4 == ftc.quad[0] ) {ftc.Tx[0] = 0.0f; ftc.fi = fi;}
if ( 4 == ftc.quad[1] ) {ftc.Tx[1] = 0.0f; ftc.fi = fi;}
if ( 4 == ftc.quad[2] ) {ftc.Tx[2] = 0.0f; ftc.fi = fi;}
if ( 4 == ftc.quad[3] ) {ftc.Tx[3] = 0.0f; ftc.fi = fi;}
}
else
{
// "most" of the face is on the right side of the texture
// If a vertex is on the left side, clamp its tc to two_pi_tc.
if ( 1 == ftc.quad[0] ) {ftc.Tx[0] = twopitc; ftc.fi = fi;}
if ( 1 == ftc.quad[1] ) {ftc.Tx[1] = twopitc; ftc.fi = fi;}
if ( 1 == ftc.quad[2] ) {ftc.Tx[2] = twopitc; ftc.fi = fi;}
if ( 1 == ftc.quad[3] ) {ftc.Tx[3] = twopitc; ftc.fi = fi;}
}
}
}
if ( ftc.fi >= 0 )
{
// face will require special handling
ftc_list.Append(ftc);
}
}
ftc_count = ftc_list.Count();
if ( ftc_count <= 0 )
return;
// Count the number of new vertices that will be added.
int ftci;
int newvcnt = 0;
for ( ftci = 0; ftci < ftc_count; ftci++ )
{
ON__CMeshFaceTC& ftc_local = ftc_list[ftci];
Fvi = F[ftc_local.fi].vi;
if ( ftc_local.Tx[0] != Tx[Fvi[0]] )
{
ftc_local.bSetT[0] = true;
newvcnt++;
}
if ( ftc_local.Tx[1] != Tx[Fvi[1]] )
{
ftc_local.bSetT[1] = true;
newvcnt++;
}
if ( ftc_local.Tx[2] != Tx[Fvi[2]] )
{
ftc_local.bSetT[2] = true;
newvcnt++;
}
if ( Fvi[2] != Fvi[3] )
{
if ( ftc_local.Tx[3] != Tx[Fvi[3]] )
{
ftc_local.bSetT[3] = true;
newvcnt++;
}
}
}
if ( newvcnt <= 0 )
return;
F = 0; // Setting them to nullptr makes sure anybody who
// tries to use them below will crash.
// reserve room for new vertex information
ON__CChangeTextureCoordinateHelper helper(mesh,newvcnt,mesh_T);
// add vertices and update mesh faces
for ( ftci = 0; ftci < ftc_count; ftci++ )
{
const ON__CMeshFaceTC& ftc_local = ftc_list[ftci];
int* meshFvi = mesh.m_F[ftc_local.fi].vi;
if ( ftc_local.bSetT[0] )
{
helper.ChangeTextureCoordinate(meshFvi,0,ftc_local.Tx[0],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
}
if ( ftc_local.bSetT[1] )
{
helper.ChangeTextureCoordinate(meshFvi,1,ftc_local.Tx[1],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
}
if ( ftc_local.bSetT[2] )
{
helper.ChangeTextureCoordinate(meshFvi,2,ftc_local.Tx[2],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
}
if ( ftc_local.bSetT[3] )
{
helper.ChangeTextureCoordinate(meshFvi,3,ftc_local.Tx[3],ON_UNSET_FLOAT,mesh_T,mesh_T_stride);
}
}
}
static
bool SeamCheckHelper( const ON_TextureMapping& mp,
double& two_pi_tc,
ON_SimpleArray<int>& Tside,
ON_SimpleArray<int>*& Tsd )
{
bool bSeamCheck = false;
switch(mp.m_type)
{
case ON_TextureMapping::TYPE::box_mapping:
if ( ON_TextureMapping::TEXTURE_SPACE::divided == mp.m_texture_space )
{
if ( mp.m_bCapped )
two_pi_tc = 2.0/3.0;
Tsd = &Tside;
bSeamCheck = true;
}
else if ( ON_TextureMapping::TEXTURE_SPACE::single == mp.m_texture_space )
{
Tsd = &Tside;
bSeamCheck = true;
}
break;
case ON_TextureMapping::TYPE::cylinder_mapping:
if ( ON_TextureMapping::TEXTURE_SPACE::divided == mp.m_texture_space )
{
two_pi_tc = 2.0/3.0;
Tsd = &Tside;
}
bSeamCheck = true;
break;
case ON_TextureMapping::TYPE::sphere_mapping:
bSeamCheck = true;
break;
default:
// intentionally skip other enum values
break;
}
return bSeamCheck;
}
//If there are unused vertices, this may not work correctly - but it is very fast.
static inline bool HasSharedVertices(const ON_Mesh& mesh)
{
return mesh.m_V.Count() < ((mesh.TriangleCount() * 3) + (mesh.QuadCount() * 4));
}
const ON_TextureCoordinates* ON_Mesh::SetCachedTextureCoordinates(
const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform,
bool bLazy
)
{
if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
ComputeVertexNormals();
ON_TextureMapping mp = mapping;
double two_pi_tc = 1.0;
ON_SimpleArray<int> Tside;
ON_SimpleArray<int>* Tsd = 0;
bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
if ( bSeamCheck )
mp.m_uvw = ON_Xform::IdentityTransformation;
ON_TextureCoordinates* TC = 0;
{
for ( int i = 0; i < m_TC.Count(); i++ )
{
if ( m_TC[i].m_tag.m_mapping_id == mapping.Id() )
{
TC = &m_TC[i];
break;
}
}
}
if ( bLazy && TC && mapping.HasMatchingTextureCoordinates( TC->m_tag, mesh_xform ) )
return TC;
if ( !TC )
{
m_TC.AppendNew();
TC = m_TC.Last();
}
// Use mp instead of mapping to call GetTextureCoordinates()
// because m_uvw must be the identity if we have seams.
if ( !mp.GetTextureCoordinates( *this,TC->m_T,mesh_xform,false,Tsd) )
{
int tci = (int)(TC - m_TC.Array());
m_TC.Remove(tci);
return 0;
}
TC->m_tag.Set(mapping);
if ( mesh_xform && mesh_xform->IsValid()
&& !mesh_xform->IsIdentity()
&& !mesh_xform->IsZero()
)
{
TC->m_tag.m_mesh_xform = *mesh_xform;
}
TC->m_dim = 2;
if ( bSeamCheck && m_F.Count() > 0 && TC->m_T.Count() == m_V.Count() )
{
float* mesh_T = (float*)TC->m_T.Array();
int mesh_T_stride = sizeof(TC->m_T[0])/sizeof(mesh_T[0]);
if ( Tsd && Tside.Count() != m_V.Count() )
Tsd = 0;
AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
mesh_T = 0; // when the array is grown, the pointer may become invalid
if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
{
// Apply the uvw transformation that is on mapping
// to the texture coordinates.
ON_3dPoint T;
int vi, vcnt = TC->m_T.Count();
ON_3fPoint* meshT = TC->m_T.Array();
for ( vi = 0; vi < vcnt; vi++ )
{
T = meshT[vi];
T = mapping.m_uvw*T;
meshT[vi] = T;
}
}
}
return TC;
}
bool ON_Mesh::SetTextureCoordinates(
const class ON_TextureMapping& mapping,
const class ON_Xform* mesh_xform,
bool bLazy
)
{
if ( mapping.RequiresVertexNormals() && !HasVertexNormals() )
ComputeVertexNormals();
InvalidateTextureCoordinateBoundingBox();
ON_SimpleArray<int> Tside;
ON_SimpleArray<int>* Tsd = 0;
ON_TextureMapping mp = mapping;
double two_pi_tc = 1.0;
bool bSeamCheck = SeamCheckHelper( mp, two_pi_tc, Tside, Tsd ) && HasSharedVertices(*this);
if ( bSeamCheck )
mp.m_uvw = ON_Xform::IdentityTransformation;
// Use mp instead of mapping to call GetTextureCoordinates()
// because m_uvw must be the identity if we have seams.
bool rc = mp.GetTextureCoordinates(*this,m_T,mesh_xform,bLazy,Tsd);
if (Tsd != nullptr && Tsd->Count() == 0)
{
// Tsd array is needed for seam check but it was not filled by GetTextureCoordinates.
// This happened because matching texture coordinates were found. These coordinates
// were already once seam checked. So seam check can be skipped now.
bSeamCheck = false;
}
if (rc)
{
// update the texture coordinate tag
m_Ttag.Set(mapping);
if ( mesh_xform
&& mesh_xform->IsValid()
&& !mesh_xform->IsIdentity()
&& !mesh_xform->IsZero()
)
{
m_Ttag.m_mesh_xform = *mesh_xform;
}
}
if ( rc && bSeamCheck && HasTextureCoordinates() && m_F.Count() > 0 )
{
float* mesh_T = (float*)m_T.Array();
int mesh_T_stride = sizeof(m_T[0])/sizeof(mesh_T[0]);
if ( Tsd && Tside.Count() != m_V.Count() )
Tsd = 0;
AdjustMeshPeriodicTextureCoordinatesHelper( *this, mesh_xform, mesh_T, mesh_T_stride, Tsd ? Tside.Array() : 0, two_pi_tc, mp );
mesh_T = 0; // when the array is grown, the pointer may become invalid
if ( !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero() )
{
// Apply the uvw transformation that is on mapping
// to the texture coordinates.
ON_2fPoint* meshT = m_T.Array();
ON_3dPoint T;
int vi, vcnt = m_T.Count();
for ( vi = 0; vi < vcnt; vi++ )
{
T.x = meshT[vi].x;
T.y = meshT[vi].y;
T.z = 0.0;
T = mapping.m_uvw*T;
meshT[vi].x = (float)T.x;
meshT[vi].y = (float)T.y;
}
}
}
return rc;
}
ON_MappingChannel::ON_MappingChannel()
{
Default();
}
void ON_MappingChannel::Default()
{
memset(this,0,sizeof(*this));
m_mapping_channel_id = 1;
m_mapping_index = -1;
m_object_xform = ON_Xform::IdentityTransformation;
}
int ON_MappingChannel::Compare( const ON_MappingChannel& other ) const
{
int rc = m_mapping_channel_id - other.m_mapping_channel_id;
if (!rc)
rc = ON_UuidCompare(m_mapping_id,other.m_mapping_id);
return rc;
}
bool ON_MappingChannel::Write( ON_BinaryArchive& archive ) const
{
bool rc = archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1);
if (rc)
{
rc = archive.WriteInt(m_mapping_channel_id);
if (rc) rc = archive.WriteUuid(m_mapping_id);
// 1.1 field added 6 June 2006
if (rc) rc = archive.WriteXform(m_object_xform);
if ( !archive.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
bool ON_MappingChannel::Read( ON_BinaryArchive& archive )
{
Default();
int major_version = 0;
int minor_version = 0;
bool rc = archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version);
if (rc)
{
rc = (1 == major_version);
if (rc) rc = archive.ReadInt(&m_mapping_channel_id);
if (rc) rc = archive.ReadUuid(m_mapping_id);
if ( rc && minor_version >= 1 )
{
// 1.1 field added 6 June 2006
if (rc) rc = archive.ReadXform(m_object_xform);
if (rc
&& archive.ArchiveOpenNURBSVersion() < 200610030
&& m_object_xform.IsZero()
)
{
// Between versions 200606060 and 200610030,
// there was a bug that created some mapping
// channels with zero transformations. This
// if clause finds those and sets them to the
// identity.
m_object_xform = ON_Xform::IdentityTransformation;
}
}
if ( !archive.EndRead3dmChunk() )
rc = false;
}
return rc;
}
ON_MaterialRef::ON_MaterialRef()
{
Default();
}
ON_MappingRef::ON_MappingRef()
{
Default();
}
void ON_MaterialRef::Default()
{
memset(this,0,sizeof(*this));
// runtme index value of -1 means not set
m_material_index = -1;
m_material_backface_index = -1;
m_material_source = ON::material_from_layer;
}
void ON_MappingRef::Default()
{
m_plugin_id = ON_nil_uuid;
m_mapping_channels.Destroy();
}
int ON_MaterialRef::Compare( const ON_MaterialRef& other ) const
{
int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
if (rc)
rc = ((int)m_material_source) - ((int)other.m_material_source);
if (!rc)
rc = ON_UuidCompare(m_material_id,other.m_material_id);
if (!rc)
rc = ON_UuidCompare(m_material_backface_id,other.m_material_backface_id);
return rc;
}
int ON_MappingRef::Compare( const ON_MappingRef& other ) const
{
int rc = ON_UuidCompare(m_plugin_id,other.m_plugin_id);
if ( !rc)
{
const int count = m_mapping_channels.Count();
rc = count - other.m_mapping_channels.Count();
if (!rc)
{
for ( int i = 0; i < count && !rc; i++ )
{
rc = m_mapping_channels[i].Compare(other.m_mapping_channels[i]);
}
}
}
return rc;
}
bool ON_MaterialRef::Write( ON_BinaryArchive& archive ) const
{
bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 1 );
if (rc)
{
if (rc) rc = archive.WriteUuid( m_plugin_id );
if (rc) rc = archive.WriteUuid( m_material_id );
// 23 May 2006 Dale lear
// m_mapping_channels[] was removed from ON_MaterialRef.
// To keep from breaking the file format, I need to
// write a zero as the array length.
//
//if (rc) rc = archive.WriteArray( m_mapping_channels );
if (rc) rc = archive.WriteInt(0);
// 23 May 2006 added
if (rc) rc = archive.WriteUuid( m_material_backface_id );
if (rc) rc = archive.WriteInt( m_material_source );
if ( !archive.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
bool ON_MaterialRef::Read( ON_BinaryArchive& archive )
{
Default();
int major_version = 0;
int minor_version = 0;
bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
if (rc)
{
rc = (1 == major_version);
if (rc) rc = archive.ReadUuid( m_plugin_id );
if (rc) rc = archive.ReadUuid( m_material_id );
// 23 May 2006 Dale lear
// m_mapping_channels[] was removed from ON_MaterialRef.
// To keep from breaking the file format, I need to
// write a zero as the array length.
ON_SimpleArray<ON_MappingChannel> obsolete_mapping_channels;
if (rc) rc = archive.ReadArray( obsolete_mapping_channels );
if ( minor_version >= 1 )
{
if (rc) rc = archive.ReadUuid( m_material_backface_id );
int i = m_material_source;
if (rc) rc = archive.ReadInt( &i );
if (rc) m_material_source = (unsigned char)ON::ObjectMaterialSource(i);
}
if ( !archive.EndRead3dmChunk() )
rc = false;
}
return rc;
}
void ON_MaterialRef::SetMaterialSource(
ON::object_material_source material_source
)
{
m_material_source = (unsigned char)material_source;
}
ON::object_material_source ON_MaterialRef::MaterialSource() const
{
return ON::ObjectMaterialSource(m_material_source);
}
bool ON_MappingRef::Write( ON_BinaryArchive& archive ) const
{
bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
if (rc)
{
if (rc) rc = archive.WriteUuid( m_plugin_id );
if (rc) rc = archive.WriteArray( m_mapping_channels );
if ( !archive.EndWrite3dmChunk() )
rc = false;
}
return rc;
}
bool ON_MappingRef::Read( ON_BinaryArchive& archive )
{
Default();
int major_version = 0;
int minor_version = 0;
bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
if (rc)
{
rc = (1 == major_version);
if (rc) rc = archive.ReadUuid( m_plugin_id );
if (rc) rc = archive.ReadArray( m_mapping_channels );
if ( !archive.EndRead3dmChunk() )
rc = false;
}
return rc;
}
bool ON_MappingRef::Transform( const ON_Xform& xform )
{
int count = m_mapping_channels.Count();
if ( count > 0 )
{
for ( ON_MappingChannel* mapping_channel = m_mapping_channels.Array();
count--;
mapping_channel++ )
{
mapping_channel->m_object_xform = xform*mapping_channel->m_object_xform;
}
}
return true;
}
ON_ObjectRenderingAttributes::ON_ObjectRenderingAttributes()
{
Default();
}
ON_RenderingAttributes::ON_RenderingAttributes()
{
Default();
}
void ON_ObjectRenderingAttributes::Default()
{
ON_RenderingAttributes::Default();
m_mappings.Destroy();
m_bCastsShadows = true;
m_bReceivesShadows = true;
m_bits = 0;
m_reserved1 = 0;
}
void ON_RenderingAttributes::Default()
{
m_materials.Destroy();
}
void ON_ObjectRenderingAttributes::EnableAdvancedTexturePreview(bool b)
{
if ( b )
m_bits |= 1; // set bit 1
else
m_bits &= 0xFE; // clear bit 1
}
bool ON_ObjectRenderingAttributes::AdvancedTexturePreview() const
{
return (0 != (1 & m_bits)) ? true : false;
}
bool ON_RenderingAttributes::IsValid( ON_TextLog* text_log ) const
{
// plug-in uuids must be unique
int count;
if( (count = m_materials.Count()) > 1 )
{
const ON_MaterialRef* mr = m_materials.Array();
ON_UUID plugin_id;
int i, j;
for ( i = 0; i < count-1; i++ )
{
plugin_id = mr[i].m_plugin_id;
for ( j = i+1; j < count; j++ )
{
if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
{
if( text_log )
{
text_log->Print("ON_RenderingAttributes error: m_materials[%d] and m_materials[%d] have the same plug-in id.\n",i,j);
}
return false;
}
}
}
}
return true;
}
bool ON_ObjectRenderingAttributes::IsValid( ON_TextLog* text_log ) const
{
if ( !ON_RenderingAttributes::IsValid(text_log) )
return false;
// plug-in uuids must be unique
int count;
if( (count = m_mappings.Count()) > 1 )
{
const ON_MappingRef* mr = m_mappings.Array();
ON_UUID plugin_id;
int i, j;
for ( i = 0; i < count-1; i++ )
{
plugin_id = mr[i].m_plugin_id;
for ( j = i+1; j < count; j++ )
{
if ( !ON_UuidCompare(&plugin_id,&mr[j].m_plugin_id ) )
{
if( text_log )
{
text_log->Print("ON_ObjectRenderingAttributes error: m_mappings[%d] and m_mappings[%d] have the same plug-in id.\n",i,j);
}
return false;
}
}
}
}
return true;
}
int ON_RenderingAttributes::Compare( const ON_RenderingAttributes& other ) const
{
const int count = m_materials.Count();
int rc = count - other.m_materials.Count();
if (!rc)
{
int i;
for ( i = 0; i < count && !rc; i++ )
{
rc = m_materials[i].Compare(other.m_materials[i]);
}
}
return rc;
}
const ON_MaterialRef* ON_RenderingAttributes::MaterialRef( const ON_UUID& plugin_id ) const
{
int count;
if ( (count = m_materials.Count()) > 0 )
{
for ( const ON_MaterialRef* mr = m_materials.Array(); count--; mr++ )
{
if ( plugin_id == mr->m_plugin_id )
return mr;
}
}
return 0;
}
int ON_ObjectRenderingAttributes::Compare( const ON_ObjectRenderingAttributes& other ) const
{
int rc = ON_RenderingAttributes::Compare(other);
if (!rc)
{
int i;
const int count = m_mappings.Count();
rc = other.m_mappings.Count() - count;
for ( i = 0; i < count && !rc; i++ )
{
rc = m_mappings[i].Compare(other.m_mappings[i]);
}
if ( !rc )
{
rc = ((int)(m_bCastsShadows?1:0)) - ((int)(other.m_bCastsShadows?1:0));
if ( !rc )
{
rc = ((int)(m_bReceivesShadows?1:0)) - ((int)(other.m_bReceivesShadows?1:0));
}
if ( !rc )
{
rc = ((int)(AdvancedTexturePreview()?1:0)) - ((int)(other.AdvancedTexturePreview()?1:0));
}
}
}
return rc;
}
bool ON_ObjectRenderingAttributes::Transform( const ON_Xform& xform )
{
int i;
if ( (i = m_mappings.Count()) > 0 )
{
for( ON_MappingRef* mr = m_mappings.Array(); i--; mr++ )
mr->Transform(xform);
}
return true;
}
const ON_MappingRef* ON_ObjectRenderingAttributes::MappingRef(
const ON_UUID& plugin_id ) const
{
int count;
if ( (count = m_mappings.Count()) > 0 )
{
for ( const ON_MappingRef* mr = m_mappings.Array(); count--; mr++ )
{
if ( plugin_id == mr->m_plugin_id )
return mr;
}
}
//ALB 2013.12.03
//Fixes http://mcneel.myjetbrains.com/youtrack/issue/RH-5730
//I'm sick of this bug being considered irrelavent, and since I've decided to go out of my way to
//Sort out as many mapping problems as I can, I'm fixing this one like this.
if (m_mappings.Count() > 0)
{
return &m_mappings[0];
}
return 0;
}
ON_MappingRef* ON_ObjectRenderingAttributes::AddMappingRef(
const ON_UUID& plugin_id
)
{
ON_MappingRef* mr = 0;
int count;
if ( (count = m_mappings.Count()) > 0 )
{
for ( mr = const_cast<ON_MappingRef*>(m_mappings.Array()); count--; mr++ )
{
if ( plugin_id == mr->m_plugin_id )
break;
}
}
if ( !mr )
{
mr = &m_mappings.AppendNew();
mr->m_plugin_id = plugin_id;
}
return mr;
}
bool ON_ObjectRenderingAttributes::DeleteMappingRef(
const ON_UUID& plugin_id
)
{
const ON_MappingRef* mr = MappingRef(plugin_id);
if ( mr )
m_mappings.Remove( (int)(mr - m_mappings.Array()) ); // safe ptr to in conversion
return (0 != mr);
}
const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
const ON_UUID& plugin_id,
const ON_UUID& mapping_id
) const
{
const ON_MappingRef* mr = MappingRef(plugin_id);
if ( mr )
{
int count;
if ( (count = mr->m_mapping_channels.Count()) > 0 )
{
for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
{
if ( mapping_id == mc->m_mapping_id )
return mc;
}
}
}
return 0;
}
const ON_MappingChannel* ON_ObjectRenderingAttributes::MappingChannel(
const ON_UUID& plugin_id,
int mapping_channel_id
) const
{
const ON_MappingRef* mr = MappingRef(plugin_id);
if ( mr )
{
int count;
if ( (count = mr->m_mapping_channels.Count()) > 0 )
{
for ( const ON_MappingChannel* mc = mr->m_mapping_channels.Array(); count--; mc++ )
{
if ( mapping_channel_id == mc->m_mapping_channel_id )
return mc;
}
}
}
return 0;
}
bool ON_ObjectRenderingAttributes::AddMappingChannel(
const ON_UUID& plugin_id,
int mapping_channel_id,
const ON_UUID& mapping_id
)
{
ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
if ( !mr )
{
mr = &m_mappings.AppendNew();
mr->m_plugin_id = plugin_id;
ON_MappingChannel& mc = mr->m_mapping_channels.AppendNew();
mc.m_mapping_channel_id = mapping_channel_id;
mc.m_mapping_id = mapping_id;
mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
mc.m_object_xform = ON_Xform::IdentityTransformation;
return true;
}
return mr->AddMappingChannel(mapping_channel_id,mapping_id);
}
bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
const ON_UUID& plugin_id,
int mapping_channel_id
)
{
ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
return mr ? mr->DeleteMappingChannel(mapping_channel_id) : false;
}
bool ON_ObjectRenderingAttributes::DeleteMappingChannel(
const ON_UUID& plugin_id,
const ON_UUID& mapping_id
)
{
ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
return mr ? mr->DeleteMappingChannel(mapping_id) : false;
}
bool ON_ObjectRenderingAttributes::ChangeMappingChannel(
const ON_UUID& plugin_id,
int old_mapping_channel_id,
int new_mapping_channel_id
)
{
ON_MappingRef* mr = const_cast<ON_MappingRef*>(MappingRef(plugin_id));
return mr ? mr->ChangeMappingChannel(old_mapping_channel_id,new_mapping_channel_id) : false;
}
const ON_MappingChannel* ON_MappingRef::MappingChannel(
const ON_UUID& mapping_id
) const
{
int count;
if ( (count = m_mapping_channels.Count()) > 0 )
{
for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
{
if ( mapping_id == mc->m_mapping_id )
return mc;
}
}
return 0;
}
const ON_MappingChannel* ON_MappingRef::MappingChannel(
int mapping_channel_id
) const
{
int count;
if ( (count = m_mapping_channels.Count()) > 0 )
{
for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); count--; mc++ )
{
if ( mapping_channel_id == mc->m_mapping_channel_id )
return mc;
}
}
return 0;
}
bool ON_MappingRef::AddMappingChannel(
int mapping_channel_id,
const ON_UUID& mapping_id
)
{
int i;
if ( (i = m_mapping_channels.Count()) > 0 )
{
for ( const ON_MappingChannel* mc = m_mapping_channels.Array(); i--; mc++ )
{
if ( mapping_channel_id == mc->m_mapping_channel_id )
{
// a matching mapping channel id exists
// return true if mapping_id matches
return ( 0 == ON_UuidCompare(&mapping_id,&mc->m_mapping_id) );
}
}
}
ON_MappingChannel& mc = m_mapping_channels.AppendNew();
mc.m_mapping_channel_id = mapping_channel_id;
mc.m_mapping_id = mapping_id;
mc.m_mapping_index = -1; // 27th October 2011 John Croudy - constructor is not called by AppendNew().
mc.m_object_xform = ON_Xform::IdentityTransformation;
return true;
}
bool ON_MappingRef::DeleteMappingChannel(int mapping_channel_id)
{
const ON_MappingChannel* mc = MappingChannel(mapping_channel_id);
if ( mc )
{
m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
}
return ( 0 != mc);
}
bool ON_MappingRef::DeleteMappingChannel(const ON_UUID& mapping_id)
{
const ON_MappingChannel* mc = MappingChannel(mapping_id);
if ( mc )
{
m_mapping_channels.Remove((int)(mc - m_mapping_channels.Array()));
}
return ( 0 != mc);
}
bool ON_MappingRef::ChangeMappingChannel(
int old_mapping_channel_id,
int new_mapping_channel_id
)
{
ON_MappingChannel* mc = const_cast<ON_MappingChannel*>(MappingChannel(old_mapping_channel_id));
if ( mc )
{
mc->m_mapping_channel_id = new_mapping_channel_id;
}
return ( 0 != mc );
}
bool ON_RenderingAttributes::Write( ON_BinaryArchive& archive ) const
{
bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 0 );
if ( !rc )
return false;
for(;;)
{
rc = archive.WriteArray(m_materials);
if ( !rc ) break;
break;
}
if ( !archive.EndWrite3dmChunk() )
rc = false;
return rc;
}
bool ON_RenderingAttributes::Read( ON_BinaryArchive& archive )
{
Default();
int major_version = 0;
int minor_version = 0;
bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
if (!rc)
return false;
for(;;)
{
rc = ( 1 == major_version );
if (!rc) break;
rc = archive.ReadArray(m_materials);
if (!rc) break;
break;
}
if ( !archive.EndRead3dmChunk() )
rc = false;
return rc;
}
bool ON_ObjectRenderingAttributes::Write( ON_BinaryArchive& archive ) const
{
bool rc = archive.BeginWrite3dmChunk( TCODE_ANONYMOUS_CHUNK, 1, 3 );
if ( !rc )
return false;
for(;;)
{
// DO NOT CALL ON_RenderingAttributes::Write
rc = archive.WriteArray(m_materials);
if ( !rc ) break;
rc = archive.WriteArray(m_mappings);
if ( !rc ) break;
// version 1.2 fields added 20061129
rc = archive.WriteBool(m_bCastsShadows);
if ( !rc ) break;
rc = archive.WriteBool(m_bReceivesShadows);
if ( !rc ) break;
// version 1.3 fields added 20101019
bool b = AdvancedTexturePreview();
rc = archive.WriteBool(b);
if ( !rc ) break;
break;
}
if ( !archive.EndWrite3dmChunk() )
rc = false;
return rc;
}
bool ON_ObjectRenderingAttributes::Read( ON_BinaryArchive& archive )
{
Default();
int major_version = 0;
int minor_version = 0;
bool rc = archive.BeginRead3dmChunk( TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version );
if (!rc)
return false;
for(;;)
{
rc = ( 1 == major_version && minor_version >= 1 );
if (!rc) break;
// DO NOT CALL ON_RenderingAttributes::Read
if (rc) rc = archive.ReadArray(m_materials);
if (!rc) break;
if (rc) rc = archive.ReadArray(m_mappings);
if (!rc) break;
if ( minor_version <= 1 )
break;
// version 1.2 fields added 20061129
rc = archive.ReadBool(&m_bCastsShadows);
if ( !rc ) break;
rc = archive.ReadBool(&m_bReceivesShadows);
if ( !rc ) break;
if ( minor_version <= 2 )
break;
// version 1.3 fields added 20101019
bool b = AdvancedTexturePreview();
rc = archive.ReadBool(&b);
if ( !rc ) break;
// Jussi 20120430: We don't want to enable advanced texture preview by default. It will be
// turned on when needed (depending on active render plug-in etc).
//EnableAdvancedTexturePreview(b);
break;
}
if ( !archive.EndRead3dmChunk() )
rc = false;
return rc;
}
bool ON_TextureMapping::SetSurfaceParameterMapping(void)
{
// The nullptr check is wierd.
// Speculation: A reference was null and somebody added this
// as a hack to prevent a crash.
if ( false == ON_IsNullPtr(this) )
{
*this = ON_TextureMapping::SurfaceParameterTextureMapping;
SetId(); // new id
}
return true;
}
bool ON_TextureMapping::SetPlaneMapping(
const ON_Plane& plane,
const ON_Interval& dx,
const ON_Interval& dy,
const ON_Interval& dz
)
{
*this = ON_TextureMapping::Unset;
// Don't call plane.IsValid(), because the plane
// equation does not matter and many developers
// forget to set it correctly.
if ( !plane.origin.IsValid() )
return false;
if ( !ON_IsRightHandFrame( plane.xaxis, plane.yaxis, plane.zaxis ) )
return false;
if ( !dx.IsValid() || !dy.IsValid() || !dz.IsValid() )
return false;
ON_3dPoint C = plane.PointAt(dx.Mid(),dy.Mid(),dz.Mid());
C.x = (0.0 == C.x) ? 0.0 : -C.x;
C.y = (0.0 == C.y) ? 0.0 : -C.y;
C.z = (0.0 == C.z) ? 0.0 : -C.z;
ON_3dVector xaxis = plane.xaxis;
ON_3dVector yaxis = plane.yaxis;
ON_3dVector zaxis = plane.zaxis;
// Any "cleanup" needs to be done here
// to xaxis, yaxis, zaxis.
double sx,sy,sz;
if ( 0.0 == (sx = dx.Length())) sx = 2.0;
if ( 0.0 == (sy = dy.Length())) sy = 2.0;
if ( 0.0 == (sz = dz.Length())) sz = 2.0;
// The plane mapping matrix m_Pxyz transforms the
// world coordinate rectangle to a (-1<=r<=1,
// on plane to a
// 1 X 1 square in the xy plane centered at the
// origin.
// m_Pxyz = surface point transformation
ON_3dVector X = (2.0/sx)*xaxis;
ON_3dVector Y = (2.0/sy)*yaxis;
ON_3dVector Z = (2.0/sz)*zaxis;
m_Pxyz.m_xform[0][0] = X.x;
m_Pxyz.m_xform[0][1] = X.y;
m_Pxyz.m_xform[0][2] = X.z;
m_Pxyz.m_xform[0][3] = (X.x*C.x + X.y*C.y + X.z*C.z);
m_Pxyz.m_xform[1][0] = Y.x;
m_Pxyz.m_xform[1][1] = Y.y;
m_Pxyz.m_xform[1][2] = Y.z;
m_Pxyz.m_xform[1][3] = (Y.x*C.x + Y.y*C.y + Y.z*C.z);
m_Pxyz.m_xform[2][0] = Z.x;
m_Pxyz.m_xform[2][1] = Z.y;
m_Pxyz.m_xform[2][2] = Z.z;
m_Pxyz.m_xform[2][3] = (Z.x*C.x + Z.y*C.y + Z.z*C.z);
m_Pxyz.m_xform[3][0] = 0.0;
m_Pxyz.m_xform[3][1] = 0.0;
m_Pxyz.m_xform[3][2] = 0.0;
m_Pxyz.m_xform[3][3] = 1.0;
// m_Nxyz = surface normal transformation
// = inverse transpose of upper 3x3 of m_Pxyz
X = (0.5*sx)*xaxis;
Y = (0.5*sy)*yaxis;
Z = (0.5*sz)*zaxis;
m_Nxyz.m_xform[0][0] = X.x;
m_Nxyz.m_xform[0][1] = X.y;
m_Nxyz.m_xform[0][2] = X.z;
m_Nxyz.m_xform[0][3] = 0.0;
m_Nxyz.m_xform[1][0] = Y.x;
m_Nxyz.m_xform[1][1] = Y.y;
m_Nxyz.m_xform[1][2] = Y.z;
m_Nxyz.m_xform[1][3] = 0.0;
m_Nxyz.m_xform[2][0] = Z.x;
m_Nxyz.m_xform[2][1] = Z.y;
m_Nxyz.m_xform[2][2] = Z.z;
m_Nxyz.m_xform[2][3] = 0.0;
m_Nxyz.m_xform[3][0] = 0.0;
m_Nxyz.m_xform[3][1] = 0.0;
m_Nxyz.m_xform[3][2] = 0.0;
m_Nxyz.m_xform[3][3] = 1.0;
m_type = ON_TextureMapping::TYPE::plane_mapping;
SetId();
#if defined(ON_DEBUG)
{
ON_Plane p;
p.xaxis = (2.0/sx)*plane.xaxis;
p.yaxis = (2.0/sy)*plane.yaxis;
p.zaxis = (2.0/sz)*plane.zaxis;
p.origin.Set(-C.x,-C.y,-C.z);
p.UpdateEquation();
ON_Xform P_dbg, N_dbg;
P_dbg.Rotation(p,ON_xy_plane);
P_dbg.GetSurfaceNormalXform(N_dbg);
for ( int i = 0; i < 4; i++ )
{
for ( int j = 0; j < 4; j++ )
{
if ( fabs(m_Pxyz[i][j] - P_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Pxyz[i][j])+128.0) )
{
ON_ERROR("m_Pxyz is nor right\n");
break;
}
if ( fabs(m_Nxyz[i][j] - N_dbg[i][j]) >= ON_SQRT_EPSILON*(fabs(m_Nxyz[i][j])+128.0) )
{
ON_ERROR("m_Nxyz is nor right\n");
break;
}
}
}
}
#endif
return true;
}
bool ON_TextureMapping::SetBoxMapping(const ON_Plane& plane,
ON_Interval dx,
ON_Interval dy,
ON_Interval dz,
bool bCapped
)
{
bool rc = SetPlaneMapping(plane,dx,dy,dz);
if (rc)
{
m_bCapped = bCapped;
m_type = ON_TextureMapping::TYPE::box_mapping;
}
return rc;
}
bool ON_TextureMapping::SetCylinderMapping(const ON_Cylinder& cylinder, bool bIsCapped)
{
ON_Interval dr, dh;
if ( !ON_IsValid(cylinder.circle.radius ) )
return false;
double r = cylinder.circle.radius;
if ( 0.0 == r )
r = 1.0;
dr.Set(-r,r);
dh.Set(cylinder.height[0],cylinder.height[1]);
if ( dh[0] == dh[1] )
{
if ( ON_UNSET_VALUE == dh[0] )
{
dh.Set(-1.0,1.0);
}
else
{
dh.m_t[0] -= 1.0;
dh.m_t[0] += 1.0;
}
}
if ( !dh.IsValid() )
return false;
bool rc = SetBoxMapping(cylinder.circle.plane,dr,dr,dh,bIsCapped);
if (rc)
{
m_type = ON_TextureMapping::TYPE::cylinder_mapping;
}
return rc;
}
bool ON_TextureMapping::SetSphereMapping(const ON_Sphere& sphere)
{
ON_Interval dr(-sphere.radius,sphere.radius);
bool rc = SetBoxMapping(sphere.plane,dr,dr,dr,false);
if (rc)
{
m_type = ON_TextureMapping::TYPE::sphere_mapping;
}
return rc;
}
bool ON_TextureMapping::GetMappingPlane(ON_Plane& plane,
ON_Interval& dx,
ON_Interval& dy,
ON_Interval& dz
) const
{
ON_Xform xform(m_Pxyz);
ON_3dVector S(((ON_3dVector*)&xform.m_xform[0])->Length(),
((ON_3dVector*)&xform.m_xform[1])->Length(),
((ON_3dVector*)&xform.m_xform[2])->Length());
if ( 0.0 == S.x )
return false;
S.x = 1.0/S.x;
if ( 0.0 == S.y )
return false;
S.y = 1.0/S.y;
if ( 0.0 == S.z )
return false;
S.z = 1.0/S.z;
xform.m_xform[0][0] *= S.x; xform.m_xform[0][1] *= S.x; xform.m_xform[0][2] *= S.x;
xform.m_xform[0][3] *= S.x;
xform.m_xform[1][0] *= S.y; xform.m_xform[1][1] *= S.y; xform.m_xform[1][2] *= S.y;
xform.m_xform[1][3] *= S.y;
xform.m_xform[2][0] *= S.z; xform.m_xform[2][1] *= S.z; xform.m_xform[2][2] *= S.z;
xform.m_xform[2][3] *= S.z;
xform.m_xform[3][0] = 0.0;
xform.m_xform[3][1] = 0.0;
xform.m_xform[3][2] = 0.0;
xform.m_xform[3][3] = 1.0;
ON_Xform inv(xform);
if ( !inv.Invert() )
return false;
plane.origin.Set(inv.m_xform[0][3],inv.m_xform[1][3],inv.m_xform[2][3]);
xform.m_xform[0][3] = 0.0;
xform.m_xform[1][3] = 0.0;
xform.m_xform[2][3] = 0.0;
plane.xaxis = &xform.m_xform[0][0];
plane.yaxis = &xform.m_xform[1][0];
plane.zaxis = &xform.m_xform[2][0];
plane.UpdateEquation();
dx.Set(-S.x,S.x);
dy.Set(-S.y,S.y);
dz.Set(-S.z,S.z);
return plane.IsValid();
}
bool ON_TextureMapping::GetMappingBox(ON_Plane& plane,
ON_Interval& dx,
ON_Interval& dy,
ON_Interval& dz) const
{
return GetMappingPlane(plane, dx, dy, dz);
}
bool ON_TextureMapping::GetMappingCylinder(ON_Cylinder& cylinder) const
{
ON_Interval dx, dy, dz;
ON_Plane plane;
bool rc = GetMappingPlane(cylinder.circle.plane, dx, dy, dz);
if (rc)
{
double r0 = 0.5*dx.Length();
double r1 = 0.5*dy.Length();
cylinder.circle.radius = (r0 == r1) ? r0 : 0.5*(r0+r1);
cylinder.height[0] = dz[0];
cylinder.height[1] = dz[1];
}
return rc && cylinder.IsValid();
}
bool ON_TextureMapping::GetMappingSphere(ON_Sphere& sphere) const
{
ON_Interval dx, dy, dz;
bool rc = GetMappingPlane(sphere.plane, dx, dy, dz);
if (rc)
{
double r0 = 0.5*dx.Length();
double r1 = 0.5*dy.Length();
double r2 = 0.5*dz.Length();
sphere.radius = (r0 == r1 && r0 == r2) ? r0 : (r0+r1+r2)/3.0;
}
return rc && sphere.IsValid();
}
///////////////////////////////////////////////////////////////////////////////
//
// Class ON_PBRMaterial
//
#define SUPPORT_PBR_USERDATA_SERIALIZATION
class ON_PhysicallyBasedMaterialUserData : public ON_UserData
{
private:
ON_OBJECT_DECLARE(ON_PhysicallyBasedMaterialUserData);
bool GetDescription(ON_wString & description)
{
description = L"ON_PhysicallyBasedMaterialUserData";
return true;
}
public:
ON_PhysicallyBasedMaterialUserData()
{
m_userdata_uuid = ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData);
m_application_uuid = ON_opennurbs6_id;
m_userdata_copycount = 1;
}
~ON_PhysicallyBasedMaterialUserData() {}
ON_PhysicallyBasedMaterialUserData(const ON_PhysicallyBasedMaterialUserData& src)
: ON_UserData(src)
{
m_userdata_copycount = src.m_userdata_copycount;
m_parameters = src.m_parameters;
}
ON_PhysicallyBasedMaterialUserData& operator=(const ON_PhysicallyBasedMaterialUserData& src)
{
if (this != &src)
{
ON_UserData::operator = (src);
m_parameters = src.m_parameters;
}
return *this;
}
private:
// ON_Object overrides
#if defined SUPPORT_PBR_USERDATA_SERIALIZATION
bool Write(ON_BinaryArchive& archive) const override
{
ON_ASSERT(IsValid());
const int chunk_version = 1;
if (false == archive.BeginWrite3dmAnonymousChunk(chunk_version))
return false;
bool rc = m_parameters.Write(archive);
if (!archive.EndWrite3dmChunk())
{
rc = false;
}
return rc;
}
bool Read(ON_BinaryArchive& archive) override
{
int chunk_version = 0;
if (false == archive.BeginRead3dmAnonymousChunk(&chunk_version))
return false;
bool rc = false;
if (chunk_version == 1)
{
rc = m_parameters.Read(archive);
}
if (!archive.EndRead3dmChunk())
{
rc = false;
}
ON_ASSERT(IsValid());
return rc;
}
bool Archive() const override
{
return true;
}
#else
bool Archive() const override
{
return false;
}
#endif
bool IsValid(class ON_TextLog* text_log = nullptr) const override
{
if (!m_parameters.IsValid(text_log))
return false;
return ON_UserData::IsValid(text_log);
}
public:
struct Parameters
{
#if defined SUPPORT_PBR_USERDATA_SERIALIZATION
bool Write(ON_BinaryArchive& binary_archive) const
{
if (!binary_archive.WriteColor(base_color)) return false;
if (!binary_archive.WriteInt((int)brdf)) return false;
if (!binary_archive.WriteDouble(subsurface)) return false;
if (!binary_archive.WriteColor(subsurface_scattering_color)) return false;
if (!binary_archive.WriteDouble(subsurface_scattering_radius)) return false;
if (!binary_archive.WriteDouble(metallic)) return false;
if (!binary_archive.WriteDouble(specular)) return false;
if (!binary_archive.WriteDouble(specular_tint)) return false;
if (!binary_archive.WriteDouble(roughness)) return false;
if (!binary_archive.WriteDouble(anisotropic)) return false;
if (!binary_archive.WriteDouble(anisotropic_rotation)) return false;
if (!binary_archive.WriteDouble(sheen)) return false;
if (!binary_archive.WriteDouble(sheen_tint)) return false;
if (!binary_archive.WriteDouble(clearcoat)) return false;
if (!binary_archive.WriteDouble(clearcoat_roughness)) return false;
if (!binary_archive.WriteDouble(opacity_IOR)) return false;
if (!binary_archive.WriteDouble(opacity)) return false;
if (!binary_archive.WriteDouble(opacity_roughness)) return false;
if (!binary_archive.WriteColor(emission)) return false;
return true;
}
bool Read(ON_BinaryArchive& binary_archive)
{
if (!binary_archive.ReadColor(base_color)) return false;
if (!binary_archive.ReadInt((int*)&brdf)) return false;
if (!binary_archive.ReadDouble(&subsurface)) return false;
if (!binary_archive.ReadColor(subsurface_scattering_color)) return false;
if (!binary_archive.ReadDouble(&subsurface_scattering_radius)) return false;
if (!binary_archive.ReadDouble(&metallic)) return false;
if (!binary_archive.ReadDouble(&specular)) return false;
if (!binary_archive.ReadDouble(&specular_tint)) return false;
if (!binary_archive.ReadDouble(&roughness)) return false;
if (!binary_archive.ReadDouble(&anisotropic)) return false;
if (!binary_archive.ReadDouble(&anisotropic_rotation)) return false;
if (!binary_archive.ReadDouble(&sheen)) return false;
if (!binary_archive.ReadDouble(&sheen_tint)) return false;
if (!binary_archive.ReadDouble(&clearcoat)) return false;
if (!binary_archive.ReadDouble(&clearcoat_roughness)) return false;
if (!binary_archive.ReadDouble(&opacity_IOR)) return false;
if (!binary_archive.ReadDouble(&opacity)) return false;
if (!binary_archive.ReadDouble(&opacity_roughness)) return false;
if (!binary_archive.ReadColor(emission)) return false;
return true;
}
#endif
bool IsValid(class ON_TextLog* text_log = nullptr) const
{
//!base_color.IsValid() means that PBR is not supported.
//if (!base_color.IsValid()) return false;
if (ON_UNSET_VALUE == subsurface) return false;
if (!subsurface_scattering_color.IsValid()) return false;
if (ON_IS_UNSET_DOUBLE(subsurface_scattering_radius)) return false;
if (ON_IS_UNSET_DOUBLE(metallic)) return false;
if (ON_IS_UNSET_DOUBLE(specular)) return false;
if (ON_IS_UNSET_DOUBLE(specular_tint)) return false;
if (ON_IS_UNSET_DOUBLE(roughness)) return false;
if (ON_IS_UNSET_DOUBLE(anisotropic)) return false;
if (ON_IS_UNSET_DOUBLE(anisotropic_rotation)) return false;
if (ON_IS_UNSET_DOUBLE(sheen)) return false;
if (ON_IS_UNSET_DOUBLE(sheen_tint)) return false;
if (ON_IS_UNSET_DOUBLE(clearcoat)) return false;
if (ON_IS_UNSET_DOUBLE(clearcoat_roughness)) return false;
if (ON_IS_UNSET_DOUBLE(opacity_IOR)) return false;
if (ON_IS_UNSET_DOUBLE(opacity)) return false;
if (ON_IS_UNSET_DOUBLE(opacity_roughness)) return false;
if (!emission.IsValid()) return false;
return true;
}
ON_4fColor base_color = ON_4fColor::Unset;
double subsurface = 0.0;
ON_4fColor subsurface_scattering_color = ON_Color::White;
double subsurface_scattering_radius = 0.0;
double metallic = 0.0;
double specular = 0.5;
double specular_tint = 0.0;
double roughness = 1.0;
double anisotropic = 0.0;
double anisotropic_rotation = 0.0;
double sheen = 0.0;
double sheen_tint = 0.0;
double clearcoat = 0.0;
double clearcoat_roughness = 0.0;
double opacity_IOR = 1.52;
double opacity = 1.0;
double opacity_roughness = 0.0;
ON_4fColor emission = ON_Color::Black;
ON_PhysicallyBasedMaterial::BRDFs brdf = ON_PhysicallyBasedMaterial::BRDFs::GGX;
} m_parameters;
};
ON_UUID ON_Material::PhysicallyBasedUserdataId(void)
{
// {5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440}
static const ON_UUID id = { 0x5694e1ac, 0x40e6, 0x44f4,{ 0x9c, 0xa9, 0x3b, 0x6d, 0xe, 0x8c, 0x44, 0x40 } };
return id;
}
ON_OBJECT_IMPLEMENT(ON_PhysicallyBasedMaterialUserData, ON_UserData, "5694E1AC-40E6-44F4-9CA9-3B6D0E8C4440");
class ON_PhysicallyBasedMaterial::Impl
{
public:
Impl(ON_Material& mat)
: material(&mat),
m_pDummy(nullptr)
{
}
~Impl()
{
delete m_pDummy;
}
bool UserDataExists(void) const
{
const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
return nullptr != pUD;
}
void RemoveUserData(void) const
{
auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
if (material->DetachUserData(pUD))
{
delete pUD;
}
}
const ON_PhysicallyBasedMaterialUserData& UserData() const
{
const auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
if (pUD)
{
return static_cast<const ON_PhysicallyBasedMaterialUserData&>(*pUD);
}
if (m_pDummy)
{
return *m_pDummy;
}
//This is the const version
m_pDummy = new ON_PhysicallyBasedMaterialUserData();
return *m_pDummy;
}
ON_PhysicallyBasedMaterialUserData& UserData()
{
auto pUD = material->GetUserData(ON_CLASS_ID(ON_PhysicallyBasedMaterialUserData));
if (pUD)
{
return static_cast<ON_PhysicallyBasedMaterialUserData&>(*pUD);
}
ON_PhysicallyBasedMaterialUserData* p = nullptr;
if (nullptr == m_pDummy)
{
p = new ON_PhysicallyBasedMaterialUserData();
}
else
{
p = m_pDummy;
m_pDummy = nullptr;
}
material->AttachUserData(p);
return *p;
}
ON_Material* material;
mutable ON_PhysicallyBasedMaterialUserData* m_pDummy;
};
ON_4fColor ON_PhysicallyBasedMaterial::BaseColor(void) const
{
return Implementation().UserData().m_parameters.base_color;
}
void ON_PhysicallyBasedMaterial::SetBaseColor(const ON_4fColor& c)
{
Implementation().UserData().m_parameters.base_color = c;
}
ON_PhysicallyBasedMaterial::BRDFs ON_PhysicallyBasedMaterial::BRDF(void) const
{
return Implementation().UserData().m_parameters.brdf;
}
void ON_PhysicallyBasedMaterial::SetBRDF(const BRDFs& b)
{
Implementation().UserData().m_parameters.brdf = b;
}
double ON_PhysicallyBasedMaterial::Subsurface(void) const
{
return Implementation().UserData().m_parameters.subsurface;
}
void ON_PhysicallyBasedMaterial::SetSubsurface(double d)
{
Implementation().UserData().m_parameters.subsurface = d;
}
ON_4fColor ON_PhysicallyBasedMaterial::SubsurfaceScatteringColor(void) const
{
return Implementation().UserData().m_parameters.subsurface_scattering_color;
}
void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringColor(const ON_4fColor& c)
{
Implementation().UserData().m_parameters.subsurface_scattering_color = c;
}
double ON_PhysicallyBasedMaterial::SubsurfaceScatteringRadius(void) const
{
return Implementation().UserData().m_parameters.subsurface_scattering_radius;
}
void ON_PhysicallyBasedMaterial::SetSubsurfaceScatteringRadius(double d)
{
Implementation().UserData().m_parameters.subsurface_scattering_radius = d;
}
double ON_PhysicallyBasedMaterial::Metallic(void) const
{
return Implementation().UserData().m_parameters.metallic;
}
void ON_PhysicallyBasedMaterial::SetMetallic(double d)
{
Implementation().UserData().m_parameters.metallic = d;
}
double ON_PhysicallyBasedMaterial::Specular(void) const
{
return Implementation().UserData().m_parameters.specular;
}
double ON_PhysicallyBasedMaterial::ReflectiveIOR(void) const
{
const double d2 = Specular() * 0.08;
return sqrt(d2);
}
void ON_PhysicallyBasedMaterial::SetReflectiveIOR(double ior)
{
const double d = (ior - 1.0) / (ior + 1.0);
SetSpecular(d*d / 0.08);
}
void ON_PhysicallyBasedMaterial::SetSpecular(double d)
{
Implementation().UserData().m_parameters.specular = d;
}
double ON_PhysicallyBasedMaterial::SpecularTint(void) const
{
return Implementation().UserData().m_parameters.specular_tint;
}
void ON_PhysicallyBasedMaterial::SetSpecularTint(double d)
{
Implementation().UserData().m_parameters.specular_tint = d;
}
double ON_PhysicallyBasedMaterial::Roughness(void) const
{
return Implementation().UserData().m_parameters.roughness;
}
void ON_PhysicallyBasedMaterial::SetRoughness(double d)
{
Implementation().UserData().m_parameters.roughness = d;
}
double ON_PhysicallyBasedMaterial::Anisotropic(void) const
{
return Implementation().UserData().m_parameters.anisotropic;
}
void ON_PhysicallyBasedMaterial::SetAnisotropic(double d)
{
Implementation().UserData().m_parameters.anisotropic = d;
}
double ON_PhysicallyBasedMaterial::AnisotropicRotation(void) const
{
return Implementation().UserData().m_parameters.anisotropic_rotation;
}
void ON_PhysicallyBasedMaterial::SetAnisotropicRotation(double d)
{
Implementation().UserData().m_parameters.anisotropic_rotation = d;
}
double ON_PhysicallyBasedMaterial::Sheen(void) const
{
return Implementation().UserData().m_parameters.sheen;
}
void ON_PhysicallyBasedMaterial::SetSheen(double d)
{
Implementation().UserData().m_parameters.sheen = d;
}
double ON_PhysicallyBasedMaterial::SheenTint(void) const
{
return Implementation().UserData().m_parameters.sheen_tint;
}
void ON_PhysicallyBasedMaterial::SetSheenTint(double d)
{
Implementation().UserData().m_parameters.sheen_tint = d;
}
double ON_PhysicallyBasedMaterial::Clearcoat(void) const
{
return Implementation().UserData().m_parameters.clearcoat;
}
void ON_PhysicallyBasedMaterial::SetClearcoat(double d)
{
Implementation().UserData().m_parameters.clearcoat = d;
}
double ON_PhysicallyBasedMaterial::ClearcoatRoughness(void) const
{
return Implementation().UserData().m_parameters.clearcoat_roughness;
}
void ON_PhysicallyBasedMaterial::SetClearcoatRoughness(double d)
{
Implementation().UserData().m_parameters.clearcoat_roughness = d;
}
double ON_PhysicallyBasedMaterial::OpacityIOR(void) const
{
return Implementation().UserData().m_parameters.opacity_IOR;
}
void ON_PhysicallyBasedMaterial::SetOpacityIOR(double d)
{
Implementation().UserData().m_parameters.opacity_IOR = d;
}
double ON_PhysicallyBasedMaterial::Opacity(void) const
{
return Implementation().UserData().m_parameters.opacity;
}
void ON_PhysicallyBasedMaterial::SetOpacity(double d)
{
Implementation().UserData().m_parameters.opacity = d;
}
double ON_PhysicallyBasedMaterial::OpacityRoughness(void) const
{
return Implementation().UserData().m_parameters.opacity_roughness;
}
void ON_PhysicallyBasedMaterial::SetOpacityRoughness(double d)
{
Implementation().UserData().m_parameters.opacity_roughness = d;
}
bool ON_PhysicallyBasedMaterial::IsValid(class ON_TextLog* text_log) const
{
return Implementation().UserData().m_parameters.IsValid(text_log);
}
ON_4fColor ON_PhysicallyBasedMaterial::Emission(void) const
{
return Implementation().UserData().m_parameters.emission;
}
void ON_PhysicallyBasedMaterial::SetEmission(ON_4fColor d)
{
Implementation().UserData().m_parameters.emission = d;
}
ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_Material& src)
{
//Placement new - create the impl in the stack allocated space.
new (&_impl) Impl(const_cast<ON_Material&>(src));
}
ON_PhysicallyBasedMaterial::~ON_PhysicallyBasedMaterial()
{
//delete _pImpl;
//Call the dtor, don't release the memory
Implementation().~Impl();
}
ON_PhysicallyBasedMaterial::ON_PhysicallyBasedMaterial(const ON_PhysicallyBasedMaterial& src)
{
new (&_impl) Impl(*src.Implementation().material);
}
ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void)
{
return ON_PhysicallyBasedMaterial(*this);
}
const ON_PhysicallyBasedMaterial ON_Material::PhysicallyBased(void) const
{
return ON_PhysicallyBasedMaterial(*this);
}
const ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void) const
{
return *reinterpret_cast<const ON_PhysicallyBasedMaterial::Impl*>(_impl);
}
ON_PhysicallyBasedMaterial::Impl& ON_PhysicallyBasedMaterial::Implementation(void)
{
return *reinterpret_cast<ON_PhysicallyBasedMaterial::Impl*>(_impl);
}
bool ON_PhysicallyBasedMaterial::Supported(void) const
{
if (!Implementation().UserDataExists())
return false;
return BaseColor().IsValid();
}
void ON_PhysicallyBasedMaterial::Destroy(void)
{
ON_Material& mat = *Implementation().material;
Implementation().~Impl();
new (&_impl) Impl(const_cast<ON_Material&>(mat));
Implementation().RemoveUserData();
}
void ON_PhysicallyBasedMaterial::SynchronizeLegacyMaterial(void)
{
auto& mat = *Implementation().material;
const bool bIsMetal = Metallic() > 0.5;
mat.SetDiffuse(bIsMetal ? ON_Color::Black : (ON_Color)BaseColor());
const double reflectivity = bIsMetal ? 1.0 : 1.0 - Roughness();
mat.SetFresnelReflections(!bIsMetal);
mat.SetReflectivity(reflectivity);
mat.SetTransparency(1.0 - Opacity());
mat.SetAmbient(ON_Color::Black);
//Gloss
mat.SetShine(ON_Material::MaxShine * reflectivity);
if (bIsMetal)
{
mat.SetSpecular(BaseColor());
mat.m_reflection = BaseColor();
}
else
{
const int bf = (int)(255.0 * reflectivity);
mat.SetSpecular(ON_Color(bf, bf, bf));
mat.m_reflection = ON_Color::White;
}
mat.m_reflection_glossiness = Roughness();
mat.m_refraction_glossiness = OpacityRoughness();
mat.SetEmission(Emission());
mat.m_index_of_refraction = OpacityIOR();
//No need to do the textures, because the ones that are supported are in the right channels already.
}
int ON_PhysicallyBasedMaterial::FindTexture(const wchar_t* filename, ON_Texture::TYPE type, int i0) const
{
return Material().FindTexture(filename, type, i0);
}
int ON_PhysicallyBasedMaterial::AddTexture(const ON_Texture& tx)
{
return Material().AddTexture(tx);
}
int ON_PhysicallyBasedMaterial::AddTexture(const wchar_t* filename, ON_Texture::TYPE type)
{
return Material().AddTexture(filename, type);
}
int ON_PhysicallyBasedMaterial::DeleteTexture(const wchar_t* filename, ON_Texture::TYPE type)
{
return Material().DeleteTexture(filename, type);
}
ON_Material& ON_PhysicallyBasedMaterial::Material(void)
{
return *Implementation().material;
}
const ON_Material& ON_PhysicallyBasedMaterial::Material(void) const
{
return *Implementation().material;
}
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BaseColor(void) { return L"pbr-base-color"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::BRDF(void) { return L"pbr-brdf"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Subsurface(void) { return L"pbr-subsurface"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SubsurfaceScatteringColor(void) { return L"pbr-subsurface-scattering-color"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SubsurfaceScatteringRadius(void) { return L"pbr-subsurface-scattering-radius"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Specular(void) { return L"pbr-specular"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SpecularTint(void) { return L"pbr-specular-tint"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Metallic(void) { return L"pbr-metallic"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Roughness(void) { return L"pbr-roughness"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Anisotropic(void) { return L"pbr-anisotropic"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::AnisotropicRotation(void) { return L"pbr-anisotropic-rotation"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Sheen(void) { return L"pbr-sheen"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::SheenTint(void) { return L"pbr-sheen-tint"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Clearcoat(void) { return L"pbr-clearcoat"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatRoughness(void) { return L"pbr-clearcoat-roughness"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::ClearcoatBump(void) { return L"pbr-clearcoat-bump"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityIor(void) { return L"pbr-opacity-ior"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Opacity(void) { return L"pbr-opacity"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::OpacityRoughness(void) { return L"pbr-opacity-roughness"; }
ON_wString ON_PhysicallyBasedMaterial::ParametersNames::Emission(void) { return L"pbr-emission"; }