mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-02 03:57:00 +08:00
1904 lines
65 KiB
C++
1904 lines
65 KiB
C++
//
|
|
// Copyright (c) 1993-2022 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
|
|
|
|
#include "opennurbs_subd_data.h"
|
|
|
|
unsigned char ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(ON_SubDTextureCoordinateType texture_coordinate_type)
|
|
{
|
|
/*
|
|
OBSOLETE enum class ON_SubDTextureDomainType : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Texture domains are not set.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// Each face texture domain is a unique rectangle of normalized texture space.
|
|
///</summary>
|
|
PerFace = 1,
|
|
|
|
///<summary>
|
|
/// Each face texture domain is a unique rectangle of normalized texture space.
|
|
/// When possible, faces are partitioned into quad groups. Adjacent members
|
|
/// of the group are assigned adjacent rectangles in texture space.
|
|
///</summary>
|
|
Packed = 2,
|
|
|
|
///<summary>
|
|
/// All face texture domain values are zero.
|
|
///</summary>
|
|
Zero = 3,
|
|
|
|
///<summary>
|
|
/// All face texture domain values are nan.
|
|
///</summary>
|
|
Nan = 4,
|
|
|
|
///<summary>
|
|
/// All face texture domain is (0,1)x(0,1).
|
|
///</summary>
|
|
UnitSquare = 5,
|
|
|
|
///<summary>
|
|
/// Code outside of opennurbs set the values. No other information is available.
|
|
///</summary>
|
|
Custom = 7,
|
|
};
|
|
*/
|
|
unsigned char obsolete_texture_domain_type = 0;
|
|
switch (texture_coordinate_type)
|
|
{
|
|
case ON_SubDTextureCoordinateType::Unset:
|
|
obsolete_texture_domain_type = 0; // OBSOLETE ON_SubDTextureDomainType::Unset = 0
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Unpacked:
|
|
obsolete_texture_domain_type = 1; // OBSOLETE ON_SubDTextureDomainType::PerFace = 1
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Packed:
|
|
obsolete_texture_domain_type = 2; // OBSOLETE ON_SubDTextureDomainType::Packed = 2
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Zero:
|
|
obsolete_texture_domain_type = 3; // OBSOLETE ON_SubDTextureDomainType::Zero = 3
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Nan:
|
|
obsolete_texture_domain_type = 4; // OBSOLETE ON_SubDTextureDomainType::Nan = 4
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromFaceTexturePoints:
|
|
obsolete_texture_domain_type = 6; // no corresponding OBSOLETE ON_SubDTextureDomainType
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromMapping:
|
|
obsolete_texture_domain_type = 7; // OBSOLETE ON_SubDTextureDomainType::Custom = 7
|
|
break;
|
|
default:
|
|
obsolete_texture_domain_type = 0;
|
|
break;
|
|
}
|
|
return obsolete_texture_domain_type;
|
|
}
|
|
|
|
|
|
ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateTypeFromObsoleteTextureDomainType(
|
|
unsigned int obsolete_texture_domain_type_as_unsigned
|
|
)
|
|
{
|
|
/*
|
|
OBSOLETE enum class ON_SubDTextureDomainType : unsigned char
|
|
{
|
|
///<summary>
|
|
/// Texture domains are not set.
|
|
///</summary>
|
|
Unset = 0,
|
|
|
|
///<summary>
|
|
/// Each face texture domain is a unique rectangle of normalized texture space.
|
|
///</summary>
|
|
PerFace = 1,
|
|
|
|
///<summary>
|
|
/// Each face texture domain is a unique rectangle of normalized texture space.
|
|
/// When possible, faces are partitioned into quad groups. Adjacent members
|
|
/// of the group are assigned adjacent rectangles in texture space.
|
|
///</summary>
|
|
Packed = 2,
|
|
|
|
///<summary>
|
|
/// All face texture domain values are zero.
|
|
///</summary>
|
|
Zero = 3,
|
|
|
|
///<summary>
|
|
/// All face texture domain values are nan.
|
|
///</summary>
|
|
Nan = 4,
|
|
|
|
///<summary>
|
|
/// All face texture domain is (0,1)x(0,1).
|
|
///</summary>
|
|
UnitSquare = 5,
|
|
|
|
///<summary>
|
|
/// Code outside of opennurbs set the values. No other information is available.
|
|
///</summary>
|
|
Custom = 7,
|
|
};
|
|
*/
|
|
|
|
switch (obsolete_texture_domain_type_as_unsigned)
|
|
{
|
|
case 0: // OBSOLETE ON_SubDTextureDomainType::Unset = 0
|
|
return ON_SubDTextureCoordinateType::Unset;
|
|
break;
|
|
case 1: // OBSOLETE ON_SubDTextureDomainType::PerFace = 1
|
|
return ON_SubDTextureCoordinateType::Unpacked;
|
|
break;
|
|
case 2: // OBSOLETE ON_SubDTextureDomainType::Packed = 2
|
|
return ON_SubDTextureCoordinateType::Packed;
|
|
break;
|
|
case 3: // OBSOLETE ON_SubDTextureDomainType:: Zero = 3
|
|
return ON_SubDTextureCoordinateType::Zero;
|
|
break;
|
|
case 4: // OBSOLETE ON_SubDTextureDomainType::Nan = 4
|
|
return ON_SubDTextureCoordinateType::Nan;
|
|
break;
|
|
case 5: // OBSOLETE ON_SubDTextureDomainType::UnitSquare = 5
|
|
return ON_SubDTextureCoordinateType::Unset;
|
|
break;
|
|
case 6: // no corresponding OBSOLETE ON_SubDTextureDomainType
|
|
return ON_SubDTextureCoordinateType::FromFaceTexturePoints;
|
|
break;
|
|
case 7: // OBSOLETE ON_SubDTextureDomainType::Custom = 7
|
|
return ON_SubDTextureCoordinateType::FromMapping;
|
|
break;
|
|
}
|
|
return ON_SubDTextureCoordinateType::Unset;
|
|
}
|
|
|
|
ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateTypeFromUnsigned
|
|
(
|
|
unsigned int texture_coordinate_type_as_unsigned
|
|
)
|
|
{
|
|
switch (texture_coordinate_type_as_unsigned)
|
|
{
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Unset);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Unpacked);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Packed);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Zero);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::Nan);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::FromFaceTexturePoints);
|
|
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureCoordinateType::FromMapping);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(ON_SubDTextureCoordinateType::Unset);
|
|
}
|
|
|
|
const ON_wString ON_SubD::TextureCoordinateTypeToString(
|
|
ON_SubDTextureCoordinateType texture_coordinate_type
|
|
)
|
|
{
|
|
const wchar_t* s;
|
|
switch (texture_coordinate_type)
|
|
{
|
|
case ON_SubDTextureCoordinateType::Unset:
|
|
s = L"Unset";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Unpacked:
|
|
s = L"Unpacked";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Packed:
|
|
s = L"Packed";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Zero:
|
|
s = L"Zero";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Nan:
|
|
s = L"Nan";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromFaceTexturePoints:
|
|
s = L"FromFaceTexturePoints";
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromMapping:
|
|
s = L"FromMapping";
|
|
break;
|
|
default:
|
|
s = nullptr;
|
|
break;
|
|
}
|
|
return (nullptr != s && 0 != s[0])
|
|
? ON_wString(s)
|
|
: ON_wString::FormatToString(L"ON_SubDTextureCoordinateType(%u)", ((unsigned)static_cast<unsigned char>(texture_coordinate_type)));
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDimple - texture coordinates
|
|
//
|
|
#if 1
|
|
#pragma region ON_SubD - texture coordinates
|
|
|
|
void ON_SubDimple::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const
|
|
{
|
|
if (0 != ON_MappingTag::CompareAll(this->m_texture_mapping_tag, mapping_tag))
|
|
{
|
|
this->m_texture_mapping_tag = mapping_tag;
|
|
ChangeRenderContentSerialNumber();
|
|
}
|
|
}
|
|
|
|
const ON_MappingTag ON_SubDimple::TextureMappingTag(bool bIgnoreTextureCoordinateType) const
|
|
{
|
|
return
|
|
(bIgnoreTextureCoordinateType || ON_SubDTextureCoordinateType::FromMapping == TextureCoordinateType())
|
|
? m_texture_mapping_tag
|
|
: ON_MappingTag::Unset;
|
|
}
|
|
|
|
#pragma endregion
|
|
#endif
|
|
|
|
const ON_MappingTag ON_SubDimple::ColorsMappingTag() const
|
|
{
|
|
return m_colors_mapping_tag;
|
|
}
|
|
|
|
void ON_SubDimple::SetColorsMappingTag(const ON_MappingTag& mapping_tag) const
|
|
{
|
|
if (0 != ON_MappingTag::CompareAll(this->m_colors_mapping_tag, mapping_tag))
|
|
{
|
|
this->m_colors_mapping_tag = mapping_tag;
|
|
ChangeRenderContentSerialNumber();
|
|
}
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubD - texture coordinates
|
|
//
|
|
#if 1
|
|
#pragma region ON_SubD - texture coordinates
|
|
|
|
ON_SubDTextureCoordinateType ON_SubD::TextureCoordinateType() const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
return (nullptr != subdimple) ? subdimple->TextureCoordinateType() : ON_SubDTextureCoordinateType::Unset;
|
|
}
|
|
|
|
ON_SubDTextureCoordinateType ON_SubDimple::TextureCoordinateType() const
|
|
{
|
|
return m_texture_coordinate_type;
|
|
}
|
|
|
|
|
|
unsigned int ON_SubD::TexturePointsAreSet() const
|
|
{
|
|
unsigned int count = 0;
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (f->TexturePointsAreSet())
|
|
++count;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
unsigned int ON_SubD::ClearTexturePoints() const
|
|
{
|
|
const bool bUpdateFragments = ON_SubDTextureCoordinateType::FromFaceTexturePoints == this->TextureCoordinateType();
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
const unsigned count = (nullptr != subdimple) ? subdimple->ClearTexturePoints() : 0;
|
|
if (bUpdateFragments)
|
|
this->Internal_SetFragmentTextureCoordinatesWithoutMapping();
|
|
return count;
|
|
}
|
|
|
|
unsigned int ON_SubDimple::ClearTexturePoints() const
|
|
{
|
|
bool bSetPacked = ON_SubDTextureCoordinateType::FromFaceTexturePoints == this->TextureCoordinateType();
|
|
if ( bSetPacked)
|
|
this->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Unset);
|
|
const ON_SubDLevel& level = this->ActiveLevel();
|
|
unsigned int count = 0;
|
|
for (const ON_SubDFace* f = level.m_face[0]; nullptr != f; f = f->m_next_face)
|
|
{
|
|
if (f->TexturePointsAreSet())
|
|
++count;
|
|
this->ReturnFaceTexturePoints(f);
|
|
if (bSetPacked && false == f->PackRectIsSet())
|
|
bSetPacked = false;
|
|
}
|
|
if (bSetPacked)
|
|
this->SetTextureCoordinateType(ON_SubDTextureCoordinateType::Packed);
|
|
return count;
|
|
}
|
|
|
|
void ON_SubDimple::SetTextureCoordinateType(
|
|
ON_SubDTextureCoordinateType texture_coordinate_type
|
|
) const
|
|
{
|
|
if (m_texture_coordinate_type != texture_coordinate_type)
|
|
{
|
|
ChangeRenderContentSerialNumber();
|
|
m_texture_coordinate_type = texture_coordinate_type;
|
|
}
|
|
}
|
|
|
|
const ON_2udex ON_SubD::TextureDomainGridSize(
|
|
unsigned minimum_rectangle_count,
|
|
double image_width,
|
|
double image_height
|
|
)
|
|
{
|
|
if (0 == minimum_rectangle_count)
|
|
return ON_2udex(1, 1);
|
|
|
|
unsigned i = (unsigned)floor(sqrt((double)(minimum_rectangle_count)));
|
|
while (i < minimum_rectangle_count && i*i < minimum_rectangle_count)
|
|
++i;
|
|
unsigned int j = i;
|
|
if (j > 1 && (j - 1)*i >= minimum_rectangle_count)
|
|
--j;
|
|
return (image_height > image_width) ? ON_2udex(j, i) : ON_2udex(i, j);
|
|
}
|
|
|
|
double ON_SubDFace::PackRectGapInPixels(
|
|
double pack_rect_distance_in_pixels
|
|
)
|
|
{
|
|
// In order to produce repeatable rendering results,
|
|
// it is critical that TextureImageSuggestedMinimumSize be a
|
|
// constant and context independent value.
|
|
|
|
// As is clearly stated in the definition of ON_SubD::TextureImageSuggestedMinimumSize,
|
|
// this code assumes ON_SubD::TextureImageSuggestedMinimumSize is a power of 2 and
|
|
// ON_SubD::TextureImageSuggestedMinimumSize >= 512.
|
|
// As a result the following code assumes min_distance_in_pixels >= 8.0 and an integer value;
|
|
const double min_distance_in_pixels = ((double)(ON_SubD::TextureImageSuggestedMinimumSize))/64.0;
|
|
|
|
if (pack_rect_distance_in_pixels >= min_distance_in_pixels)
|
|
{
|
|
if (pack_rect_distance_in_pixels <= 2.0 * min_distance_in_pixels)
|
|
return 1.0;
|
|
if (pack_rect_distance_in_pixels <= 4.0 * min_distance_in_pixels)
|
|
return 2.0;
|
|
return 3.0;
|
|
}
|
|
return 0.0;
|
|
}
|
|
|
|
const ON_2udex ON_SubDFace::GetNgonSubPackRectSizeAndDelta(
|
|
unsigned int ngon_edge_count,
|
|
ON_2dVector ngon_face_pack_rect_size,
|
|
ON_2dVector& ngon_sub_pack_rect_size,
|
|
ON_2dVector& ngon_sub_pack_rect_delta
|
|
)
|
|
{
|
|
ON_2udex grid_size(0, 0);
|
|
ngon_sub_pack_rect_size = ON_2dVector::ZeroVector;
|
|
ngon_sub_pack_rect_delta = ON_2dVector::ZeroVector;
|
|
|
|
// This function determines how to partition ngon_face_pack_rect_size into
|
|
// ngon_edge_count sub quads that the ngon will use when it is rendered.
|
|
|
|
// Validate input parameters
|
|
if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount)
|
|
{
|
|
ON_SUBD_ERROR("Invalid ngon_edge_count parameter. Value must be >= 5 and <= ON_SubDFace::MaximumEdgeCount.");
|
|
return grid_size;
|
|
}
|
|
|
|
if (false == (ngon_face_pack_rect_size.x > 0.0 && ngon_face_pack_rect_size.x < ON_UNSET_POSITIVE_VALUE))
|
|
{
|
|
ON_SUBD_ERROR("Invalid ngon_face_pack_rect_size.x parameter. Value must be > 0.");
|
|
return grid_size;
|
|
}
|
|
|
|
if (false == (ngon_face_pack_rect_size.y > 0.0 && ngon_face_pack_rect_size.y < ON_UNSET_POSITIVE_VALUE))
|
|
{
|
|
ON_SUBD_ERROR("Invalid ngon_face_pack_rect_size.y parameter. Value must be > 0.");
|
|
return grid_size;
|
|
}
|
|
|
|
// The ngon's face pack rect will be partitioned into grid_size.i X grid_size.j sub pack rects.
|
|
const int pack_rect_aspectdex
|
|
= (ngon_face_pack_rect_size.x > ngon_face_pack_rect_size.y)
|
|
? 1
|
|
: ((ngon_face_pack_rect_size.x < ngon_face_pack_rect_size.y) ? -1 : 0)
|
|
;
|
|
grid_size = ON_SubD::TextureDomainGridSize(ngon_edge_count, 0.0, 0.0);
|
|
if (grid_size.i * grid_size.j < ngon_edge_count)
|
|
{
|
|
ON_SUBD_ERROR("Failed to get a valid grid_size.");
|
|
return ON_2udex(0, 0);
|
|
}
|
|
else
|
|
{
|
|
const int grid_aspectdex
|
|
= (grid_size.i > grid_size.j)
|
|
? 1
|
|
: ((grid_size.i < grid_size.j) ? -1 : 0)
|
|
;
|
|
if (grid_aspectdex * pack_rect_aspectdex < 0)
|
|
grid_size = ON_2udex(grid_size.j, grid_size.i);
|
|
}
|
|
|
|
// Estimate image pixels assigned to this ngon's pack rect.
|
|
// In order to produce repeatable rendering results,
|
|
// it is critical that TextureImageSuggestedMinimumSize be a
|
|
// constant and context independent value.
|
|
//
|
|
// The portion of the image assigned to the ngon face pack rect
|
|
// is rect_image_width X rect_image_height pixels.
|
|
// All variables ending in "size" have pixels as units.
|
|
const double subd_image_size = ON_SubD::TextureImageSuggestedMinimumSize;
|
|
const double rect_image_size[2] = {
|
|
subd_image_size * ngon_face_pack_rect_size.x,
|
|
subd_image_size * ngon_face_pack_rect_size.y
|
|
};
|
|
double subrect_image_size[2] = {
|
|
rect_image_size[0] / ((double)(grid_size.i)),
|
|
rect_image_size[1] / ((double)(grid_size.j))
|
|
};
|
|
double subrect_gap_size[2] = {
|
|
ON_SubDFace::PackRectGapInPixels(subrect_image_size[0]),
|
|
ON_SubDFace::PackRectGapInPixels(subrect_image_size[1])
|
|
};
|
|
|
|
// As is clearly stated in the definition of ON_SubD::TextureImageSuggestedMinimumSize,
|
|
// this code assumes ON_SubD::TextureImageSuggestedMinimumSize is a power of 2 and
|
|
// ON_SubD::TextureImageSuggestedMinimumSize >= 512.
|
|
// As a result the following code assumes min_distance_in_pixels >= 8.0 and an integer value;
|
|
//const double min_distance_in_pixels = ((double)(ON_SubD::TextureImageSuggestedMinimumSize)) / 64.0;
|
|
|
|
bool bApplyGap[2] = { false,false };
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
bApplyGap[i] = (subrect_gap_size[i] >= 1.0 && 8.0 * subrect_gap_size[i] <= subrect_image_size[i]);
|
|
if (bApplyGap[i])
|
|
subrect_image_size[i] = floor(subrect_image_size[i]) - subrect_gap_size[i];
|
|
}
|
|
|
|
// Dividing by subd_image_size converts pixel values to normalized dimensionless pack subrect values.
|
|
ngon_sub_pack_rect_size = ON_2dVector(
|
|
bApplyGap[0] ? (subrect_image_size[0] / subd_image_size) : (1.0 - ON_EPSILON) * (ngon_face_pack_rect_size.x / ((double)(grid_size.i))),
|
|
bApplyGap[1] ? (subrect_image_size[1] / subd_image_size) : (1.0 - ON_EPSILON) * (ngon_face_pack_rect_size.y / ((double)(grid_size.j)))
|
|
);
|
|
ngon_sub_pack_rect_delta = ON_2dVector(
|
|
ngon_sub_pack_rect_size.x + (bApplyGap[0] ? (subrect_gap_size[0] / subd_image_size) : 0.0),
|
|
ngon_sub_pack_rect_size.y + (bApplyGap[1] ? (subrect_gap_size[1] / subd_image_size) : 0.0)
|
|
);
|
|
|
|
return grid_size;
|
|
}
|
|
|
|
void ON_SubD::SetTextureCoordinateType(
|
|
ON_SubDTextureCoordinateType texture_coordinate_type
|
|
) const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
if (nullptr != subdimple)
|
|
subdimple->SetTextureCoordinateType(texture_coordinate_type);
|
|
}
|
|
|
|
bool ON_SubD::TextureMappingRequired() const
|
|
{
|
|
if (ON_SubDTextureCoordinateType::FromMapping == TextureCoordinateType())
|
|
{
|
|
const ON_MappingTag texture_mapping_tag = TextureMappingTag(false);
|
|
if (
|
|
ON_TextureMapping::TYPE::no_mapping != texture_mapping_tag.m_mapping_type
|
|
&& ON_TextureMapping::TYPE::srfp_mapping != texture_mapping_tag.m_mapping_type
|
|
&& texture_mapping_tag.IsSet()
|
|
)
|
|
return true; // need an explicit ON_TextureMapping class to evaluate texture coordinates.
|
|
}
|
|
return false;
|
|
}
|
|
|
|
ON_SubDTextureCoordinateType ON_SubD::Internal_BestChoiceTextureCoordinateType(
|
|
const class ON_TextureMapping& available_mapping
|
|
) const
|
|
{
|
|
ON_SubDTextureCoordinateType subd_texture_coordinate_type = TextureCoordinateType();
|
|
|
|
if (ON_SubDTextureCoordinateType::FromMapping == subd_texture_coordinate_type)
|
|
{
|
|
const ON_MappingTag texture_mapping_tag = TextureMappingTag(false);
|
|
switch (texture_mapping_tag.m_mapping_type)
|
|
{
|
|
case ON_TextureMapping::TYPE::no_mapping:
|
|
subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset;
|
|
break;
|
|
case ON_TextureMapping::TYPE::srfp_mapping:
|
|
subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Packed; // <- breakpoint here when debugging texture issues
|
|
break;
|
|
default:
|
|
{
|
|
const bool bMappingAvailable
|
|
= ON_TextureMapping::TYPE::srfp_mapping != texture_mapping_tag.m_mapping_type
|
|
&& texture_mapping_tag.IsSet()
|
|
&& texture_mapping_tag.m_mapping_type == available_mapping.m_type
|
|
&& texture_mapping_tag.m_mapping_crc == available_mapping.MappingCRC()
|
|
;
|
|
if (false == bMappingAvailable)
|
|
subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Unset; // <- breakpoint here when debugging texture issues
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (ON_SubDTextureCoordinateType::Unset == subd_texture_coordinate_type)
|
|
subd_texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType;
|
|
|
|
return subd_texture_coordinate_type;
|
|
}
|
|
|
|
bool ON_SubD::Internal_SetFragmentTextureCoordinatesWithoutMapping() const
|
|
{
|
|
|
|
// face_corners[] initialized to unpacked.
|
|
ON_SubDTextureCoordinateType subd_texture_coordinate_type = Internal_BestChoiceTextureCoordinateType(ON_TextureMapping::Unset);
|
|
|
|
bool bPacked = false;
|
|
bool bUnpacked = false;
|
|
bool bConstant = false;
|
|
bool bFromFaceTexturePoints = false;
|
|
ON_3dPoint face_corners[4];
|
|
|
|
switch (subd_texture_coordinate_type)
|
|
{
|
|
default:
|
|
// no break here
|
|
case ON_SubDTextureCoordinateType::FromMapping:
|
|
ON_SUBD_ERROR("Bug - these cases should have been eliminated in code above.");
|
|
subd_texture_coordinate_type = ON_SubDTextureCoordinateType::Packed;
|
|
// no break here
|
|
|
|
case ON_SubDTextureCoordinateType::Packed:
|
|
bPacked = true;
|
|
// If packing information is present, these values get updated for each face.
|
|
face_corners[0] = ON_3dPoint::NanPoint;
|
|
face_corners[1] = ON_3dPoint::NanPoint;
|
|
face_corners[2] = ON_3dPoint::NanPoint;
|
|
face_corners[3] = ON_3dPoint::NanPoint;
|
|
break;
|
|
|
|
case ON_SubDTextureCoordinateType::Unpacked:
|
|
bUnpacked = true;
|
|
// fragment grid order for unit texture corners
|
|
face_corners[0] = ON_3dPoint(0, 0, 0);
|
|
face_corners[1] = ON_3dPoint(1, 0, 0);
|
|
face_corners[2] = ON_3dPoint(0, 1, 0);
|
|
face_corners[3] = ON_3dPoint(1, 1, 0);
|
|
break;
|
|
|
|
case ON_SubDTextureCoordinateType::Zero:
|
|
bConstant = true;
|
|
face_corners[0] = ON_3dPoint::Origin;
|
|
face_corners[1] = ON_3dPoint::Origin;
|
|
face_corners[2] = ON_3dPoint::Origin;
|
|
face_corners[3] = ON_3dPoint::Origin;
|
|
break;
|
|
|
|
case ON_SubDTextureCoordinateType::Nan:
|
|
bConstant = true;
|
|
face_corners[0] = ON_3dPoint::NanPoint;
|
|
face_corners[1] = ON_3dPoint::NanPoint;
|
|
face_corners[2] = ON_3dPoint::NanPoint;
|
|
face_corners[3] = ON_3dPoint::NanPoint;
|
|
break;
|
|
|
|
case ON_SubDTextureCoordinateType::FromFaceTexturePoints:
|
|
bFromFaceTexturePoints = true;
|
|
face_corners[0] = ON_3dPoint::NanPoint;
|
|
face_corners[1] = ON_3dPoint::NanPoint;
|
|
face_corners[2] = ON_3dPoint::NanPoint;
|
|
face_corners[3] = ON_3dPoint::NanPoint;
|
|
break;
|
|
}
|
|
|
|
ON_SHA1_Hash hash = ON_SHA1_Hash::EmptyContentHash;
|
|
if (bPacked || bUnpacked || bConstant || bFromFaceTexturePoints)
|
|
{
|
|
ON_SubDFaceIterator fit(*this);
|
|
|
|
// DELETE // // estimate face image size - used to add empty space around ngon-subdfragments
|
|
// DELETE // const unsigned int face_image_size = ON_SubD::TextureImageSuggestedMinimumSize(ON_SubD::TextureDomainGridSize(fit.FaceCount(), 0, 0));
|
|
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (f->m_edge_count < 3)
|
|
continue;
|
|
const ON_SubDMeshFragment* fragment = f->MeshFragments();
|
|
if (nullptr == fragment)
|
|
continue;
|
|
|
|
const unsigned short frag_count = (4 == f->m_edge_count) ? 1 : f->m_edge_count;
|
|
if (bUnpacked || bConstant)
|
|
{
|
|
for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment)
|
|
fragment->SetTextureCoordinateCornersForExperts(true, face_corners, true);
|
|
continue;
|
|
}
|
|
|
|
if (bFromFaceTexturePoints && f->TexturePointsAreSet())
|
|
{
|
|
// All the code in this if clause handles setting custom texture coordinates.
|
|
ON_3dPoint frag_texture_corners[4];
|
|
if (1 == frag_count)
|
|
{
|
|
// f is a quad face and there is one fragment
|
|
frag_texture_corners[0] = f->TexturePoint(0);
|
|
frag_texture_corners[1] = f->TexturePoint(1);
|
|
frag_texture_corners[2] = f->TexturePoint(2);
|
|
frag_texture_corners[3] = f->TexturePoint(3);
|
|
fragment->SetTextureCoordinateCornersForExperts(false, frag_texture_corners, true);
|
|
}
|
|
else if ( frag_count >= 3)
|
|
{
|
|
// f is a n-gon with n subd fragments
|
|
const int k = 2;
|
|
frag_texture_corners[(2 + k) % 4] = f->TextureCenterPoint();
|
|
ON_Line L(f->TexturePoint(f->m_edge_count - 1), f->TexturePoint(0));
|
|
frag_texture_corners[(1 + k) % 4] = L.PointAt(0.5);
|
|
for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment)
|
|
{
|
|
L.from = L.to;
|
|
L.to = f->TexturePoint((frag_dex + 1) % frag_count);
|
|
frag_texture_corners[(3 + k) % 4] = frag_texture_corners[(1 + k) % 4];
|
|
frag_texture_corners[(0 + k) % 4] = L.from;
|
|
frag_texture_corners[(1 + k) % 4] = L.PointAt(0.5);
|
|
fragment->SetTextureCoordinateCornersForExperts(false, frag_texture_corners, true);
|
|
}
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// The remaining code in this scope handles setting packed texture coordinates on face fragments.
|
|
// We end up here when bPacked or a face is missing texture points.
|
|
if (f->PackRectIsSet())
|
|
{
|
|
// even when bPacked is false, use packed coordinates as a fallback
|
|
face_corners[0] = f->PackRectCorner(true, 0);
|
|
face_corners[1] = f->PackRectCorner(true, 1);
|
|
face_corners[2] = f->PackRectCorner(true, 2);
|
|
face_corners[3] = f->PackRectCorner(true, 3);
|
|
}
|
|
else
|
|
{
|
|
face_corners[0] = ON_2dPoint::NanPoint;
|
|
face_corners[1] = ON_2dPoint::NanPoint;
|
|
face_corners[2] = ON_2dPoint::NanPoint;
|
|
face_corners[3] = ON_2dPoint::NanPoint;
|
|
}
|
|
|
|
if (1 == frag_count || 3 == frag_count)
|
|
{
|
|
// A quad ON_SubDFace has a single fragment and that fragment gets the entire face_texture_domain
|
|
// A 3-gon ON_SubDFace has 3 fragments that get a portion of the entire face_texture_domain
|
|
for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment)
|
|
fragment->SetQuadOr3gonFaceFragmentTextureCoordinateCorners(true, face_corners, true);
|
|
}
|
|
else if (frag_count >= 5)
|
|
{
|
|
// A n-gon ON_SubDFace (n has n fragments that get a portion of the entire face_texture_domain
|
|
|
|
// General case n-gon with n >= 5.
|
|
// This face in an n-gon with n fragments that each get a portion of the
|
|
// texture domain assigned to the face.
|
|
const ON_2dVector face_pack_rect_size = f->PackRectIsSet() ? f->PackRectSize() : ON_2dVector(1.0, 1.0);
|
|
ON_2dVector ngon_sub_pack_rect_size = ON_2dVector::NanVector;
|
|
ON_2dVector ngon_sub_pack_rect_delta = ON_2dVector::NanVector;
|
|
const ON_2udex ngon_grid_size = ON_SubDFace::GetNgonSubPackRectSizeAndDelta(
|
|
frag_count,
|
|
face_pack_rect_size,
|
|
ngon_sub_pack_rect_size,
|
|
ngon_sub_pack_rect_delta
|
|
);
|
|
for (unsigned short frag_dex = 0; frag_dex < frag_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment)
|
|
fragment->SetNgonFaceFragmentTextureCoordinateCorners(true, face_corners, face_pack_rect_size, ngon_grid_size, ngon_sub_pack_rect_size, ngon_sub_pack_rect_delta, true);
|
|
}
|
|
}
|
|
hash = ON_SubD::TextureSettingsHash(subd_texture_coordinate_type, ON_MappingTag::Unset);
|
|
ChangeRenderContentSerialNumber();
|
|
}
|
|
|
|
Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ON_SubD::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
if (nullptr != subdimple)
|
|
subdimple->SetTextureMappingTag(mapping_tag);
|
|
}
|
|
|
|
void ON_SubD::ClearFragmentTextureCoordinatesTextureSettingsHash() const
|
|
{
|
|
Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash::EmptyContentHash);
|
|
}
|
|
|
|
static bool SetGridMeshMappingTCs(const unsigned int pointCount, const double* P, const size_t P_stride, const double* N, const size_t N_stride, double* T, const size_t T_stride, const ON_TextureMapping& mapping, const ON_Xform* P_xform, const ON_Xform* N_xform)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
bool ON_SubD::SetFragmentTextureCoordinates(
|
|
const class ON_TextureMapping& mapping,
|
|
bool bLazy
|
|
) const
|
|
{
|
|
const ON_SubDTextureCoordinateType subd_texture_coordinate_type = Internal_BestChoiceTextureCoordinateType(mapping);
|
|
const ON_MappingTag mapping_tag = this->TextureMappingTag(false);
|
|
const ON_SHA1_Hash hash = ON_SubD::TextureSettingsHash(subd_texture_coordinate_type, mapping_tag);
|
|
|
|
if (bLazy)
|
|
{
|
|
// fragment texture coordinates match what's be asked for.
|
|
const ON_SHA1_Hash current_hash = FragmentTextureCoordinatesTextureSettingsHash();
|
|
if (hash == current_hash)
|
|
return true;
|
|
}
|
|
|
|
|
|
if (ON_SubDTextureCoordinateType::FromMapping != subd_texture_coordinate_type || false == this->TextureMappingRequired())
|
|
return Internal_SetFragmentTextureCoordinatesWithoutMapping();
|
|
|
|
// we have a valid and nontrivial mapping.
|
|
const ON_Xform subd_xform(mapping_tag.Transform());
|
|
const bool bApplySubDXform = ON_MappingTag::TransformTreatedIsIdentity(&subd_xform) ? false : true;
|
|
ON_Xform P_xform, N_xform;
|
|
if (bApplySubDXform)
|
|
subd_xform.GetMappingXforms(P_xform, N_xform);
|
|
|
|
ON_3dPoint tc;
|
|
ON_SubDMeshFragmentIterator frit(*this);
|
|
for (const ON_SubDMeshFragment* fragment = frit.FirstFragment(); nullptr != fragment; fragment = frit.NextFragment())
|
|
{
|
|
const unsigned P_count = fragment->PointCount();
|
|
if (P_count < 4)
|
|
continue;
|
|
const double* P = fragment->m_P;
|
|
const size_t P_stride = fragment->m_P_stride;
|
|
unsigned T_count = fragment->TextureCoordinateCount();
|
|
if (P_count != T_count)
|
|
continue;
|
|
double* T = fragment->m_T;
|
|
if (nullptr == T)
|
|
continue;
|
|
size_t T_stride = fragment->m_T_stride;
|
|
if (0 == T_stride)
|
|
{
|
|
T_stride = 3;
|
|
T_count = 1;
|
|
}
|
|
const unsigned N_count = fragment->NormalCount();
|
|
const double* N = (N_count == P_count) ? fragment->m_N : &ON_3dVector::ZeroVector.x;
|
|
const size_t N_stride = (N_count == P_count) ? fragment->m_N_stride : 0;
|
|
if (T_count != P_count || N_count != P_count || !SetGridMeshMappingTCs(P_count, P, P_stride, N, N_stride, T, T_stride, mapping, bApplySubDXform ? &P_xform : nullptr, bApplySubDXform ? &N_xform : nullptr))
|
|
{
|
|
for (double* T1 = T + T_stride * T_count; T < T1; T += T_stride, P += P_stride, N += N_stride)
|
|
{
|
|
const bool ok = bApplySubDXform ?
|
|
mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc, P_xform, N_xform) :
|
|
mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc);
|
|
if (!ok)
|
|
tc = ON_3dPoint::NanPoint;
|
|
T[0] = tc.x;
|
|
T[1] = tc.y;
|
|
T[2] = tc.z;
|
|
}
|
|
}
|
|
}
|
|
|
|
Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash);
|
|
|
|
return true;
|
|
}
|
|
|
|
void ON_SubD::Internal_SetFragmentTextureCoordinatesTextureSettingsHash(ON_SHA1_Hash hash) const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
if (nullptr != subdimple)
|
|
subdimple->Internal_SetFragmentTextureCoordinatesTextureSettingsHash(hash);
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubD::FragmentTextureCoordinatesTextureSettingsHash() const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
return (nullptr != subdimple) ? subdimple->FragmentTextureCoordinatesTextureSettingsHash() : ON_SHA1_Hash::EmptyContentHash;
|
|
}
|
|
|
|
const ON_MappingTag ON_SubD::TextureMappingTag(bool bIgnoreTextureCoordinateType) const
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
return (nullptr != subdimple) ? subdimple->TextureMappingTag(bIgnoreTextureCoordinateType) : ON_MappingTag();
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubD::TextureSettingsHash(ON_SubDTextureCoordinateType texture_coordinate_type, const ON_MappingTag& texture_mapping_tag)
|
|
{
|
|
bool bHashMapping = false;
|
|
switch (texture_coordinate_type)
|
|
{
|
|
case ON_SubDTextureCoordinateType::Unset:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Unpacked:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Packed:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Zero:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::Nan:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromFaceTexturePoints:
|
|
break;
|
|
case ON_SubDTextureCoordinateType::FromMapping:
|
|
switch (texture_mapping_tag.m_mapping_type)
|
|
{
|
|
case ON_TextureMapping::TYPE::no_mapping:
|
|
texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType;
|
|
break;
|
|
case ON_TextureMapping::TYPE::srfp_mapping:
|
|
texture_coordinate_type = ON_SubDTextureCoordinateType::Packed;
|
|
break;
|
|
default:
|
|
if (texture_mapping_tag.IsSet())
|
|
bHashMapping = true;
|
|
else
|
|
texture_coordinate_type = ON_SubD::DefaultTextureCoordinateType;
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
ON_SUBD_ERROR("Invalid texture_coordinate_type parameter");
|
|
texture_coordinate_type = ON_SubDTextureCoordinateType::Unset;
|
|
break;
|
|
}
|
|
|
|
ON_SHA1 sha1;
|
|
|
|
sha1.AccumulateBytes(&texture_coordinate_type, sizeof(texture_coordinate_type)); // 1 byte
|
|
if (bHashMapping)
|
|
sha1.AccumulateSubHash(texture_mapping_tag.Hash());
|
|
|
|
return sha1.Hash();
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubD::TextureSettingsHash() const
|
|
{
|
|
return ON_SubD::TextureSettingsHash(this->TextureCoordinateType(), this->TextureMappingTag(false));
|
|
}
|
|
|
|
|
|
#pragma endregion
|
|
#endif
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDFace - texture coordinates
|
|
//
|
|
void ON_SubDFace::SetMaterialChannelIndex(int material_channel_index) const
|
|
{
|
|
if ( material_channel_index > 0 && material_channel_index <= ON_Material::MaximumMaterialChannelIndex )
|
|
{
|
|
m_material_channel_index = (unsigned short)material_channel_index;
|
|
}
|
|
else
|
|
{
|
|
if (0 != material_channel_index)
|
|
{
|
|
ON_ERROR("Invalid material_channel_index value.");
|
|
}
|
|
ClearMaterialChannelIndex(); // makes it easier to detect when per face setting is cleared.
|
|
}
|
|
}
|
|
|
|
void ON_SubDFace::ClearMaterialChannelIndex() const
|
|
{
|
|
m_material_channel_index = 0;
|
|
}
|
|
|
|
int ON_SubDFace::MaterialChannelIndex() const
|
|
{
|
|
return (int)m_material_channel_index;
|
|
}
|
|
|
|
void ON_SubDFace::SetPerFaceColor(
|
|
ON_Color color
|
|
) const
|
|
{
|
|
if (ON_Color::UnsetColor == color)
|
|
ClearPerFaceColor(); // makes it easier to detect when the per face setting is cleared.
|
|
else
|
|
m_per_face_color = color;
|
|
}
|
|
|
|
void ON_SubDFace::ClearPerFaceColor() const
|
|
{
|
|
m_per_face_color = ON_Color::UnsetColor;
|
|
}
|
|
|
|
|
|
const ON_Color ON_SubDFace::PerFaceColor() const
|
|
{
|
|
return m_per_face_color;
|
|
}
|
|
|
|
|
|
unsigned int ON_SubD::ClearPerFaceMaterialChannelIndices()
|
|
{
|
|
unsigned change_count = 0;
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (0 != f->MaterialChannelIndex())
|
|
{
|
|
f->ClearMaterialChannelIndex();
|
|
++change_count;
|
|
}
|
|
}
|
|
|
|
if (change_count>0)
|
|
ChangeRenderContentSerialNumber();
|
|
|
|
return change_count;
|
|
}
|
|
|
|
bool ON_SubD::HasPerFaceMaterialChannelIndices() const
|
|
{
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (0 != f->MaterialChannelIndex())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
unsigned int ON_SubD::ClearPerFaceColors() const
|
|
{
|
|
unsigned change_count = 0;
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (ON_Color::UnsetColor != f->PerFaceColor())
|
|
{
|
|
f->ClearPerFaceColor();
|
|
++change_count;
|
|
}
|
|
}
|
|
if (change_count > 0)
|
|
ChangeRenderContentSerialNumber();
|
|
return change_count;
|
|
}
|
|
|
|
bool ON_SubD::HasPerFaceColors() const
|
|
{
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
if (ON_Color::UnsetColor != f->PerFaceColor())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void ON_SubD::SetPerFaceColorsFromPackId() const
|
|
{
|
|
if (FaceCount() <= 0)
|
|
return;
|
|
|
|
ON_SubDFaceIterator fit(*this);
|
|
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
|
|
{
|
|
const unsigned pack_id = f->PackId();
|
|
if (0 == pack_id)
|
|
f->ClearPerFaceColor();
|
|
else
|
|
f->SetPerFaceColor(ON_Color::RandomColor(pack_id));
|
|
}
|
|
ChangeRenderContentSerialNumber(); // face color changes.
|
|
}
|
|
|
|
bool ON_SubD::HasPerFaceColorsFromPackId() const
|
|
{
|
|
ON_SubDFaceIterator fit(*this);
|
|
const ON_SubDFace* f = fit.FirstFace();
|
|
if (nullptr == f)
|
|
return false;
|
|
bool bHasPerFaceColorsFromPackId = false;
|
|
for (/*empty init*/; nullptr != f; f = fit.NextFace())
|
|
{
|
|
const ON_Color f_color = f->PerFaceColor();
|
|
if (((unsigned int)f_color) == ((unsigned int)ON_Color::UnsetColor))
|
|
continue;
|
|
const unsigned pack_id = f->PackId();
|
|
const ON_Color pack_id_color
|
|
= (0 == pack_id)
|
|
? ON_Color::UnsetColor
|
|
: ON_Color::RandomColor(pack_id);
|
|
if (((unsigned int)pack_id_color) != ((unsigned int)f_color))
|
|
return false;
|
|
bHasPerFaceColorsFromPackId = true;
|
|
}
|
|
return bHasPerFaceColorsFromPackId;
|
|
}
|
|
|
|
|
|
|
|
|
|
void ON_SubD::SetComponentMarkBitsFromSymmetryMotif() const
|
|
{
|
|
this->ClearComponentMarkBits(true, true, true);
|
|
}
|
|
|
|
void ON_SubD::SetPerFaceColorsFromSymmetryMotif() const
|
|
{
|
|
if (FaceCount() < 1)
|
|
return;
|
|
|
|
this->ClearPerFaceColors();
|
|
ChangeRenderContentSerialNumber(); // face color changes.
|
|
}
|
|
|
|
bool ON_SubD::HasPerFaceColorsFromSymmetryMotif() const
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDMeshFragment - texture coordinates
|
|
//
|
|
#if 1
|
|
#pragma region ON_SubDMeshFragment - texture coordinates
|
|
|
|
|
|
void ON_SubDMeshFragment::ClearTextureCoordinates() const
|
|
{
|
|
m_vertex_count_etc &= ~ON_SubDMeshFragment::EtcTextureCoordinatesExistBit;
|
|
const size_t count = sizeof(this->m_ctrlnetT) / sizeof(this->m_ctrlnetT[0][0]);
|
|
double* p = &this->m_ctrlnetT[0][0];
|
|
double* p1 = p + count;
|
|
while (p < p1)
|
|
*p++ = ON_DBL_QNAN;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshFragment::TextureCoordinatesExistForExperts() const
|
|
{
|
|
return (0 != (m_vertex_count_etc & ON_SubDMeshFragment::EtcTextureCoordinatesExistBit));
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetTextureCoordinatesExistForExperts(bool bTextureCoordinatesExist) const
|
|
{
|
|
if (bTextureCoordinatesExist)
|
|
m_vertex_count_etc |= ON_SubDMeshFragment::EtcTextureCoordinatesExistBit;
|
|
else
|
|
ClearTextureCoordinates();
|
|
}
|
|
|
|
|
|
unsigned int ON_SubDMeshFragment::TextureCoordinateCount() const
|
|
{
|
|
return (TextureCoordinatesExistForExperts() && nullptr != m_T && (0 == m_T_stride || m_T_stride >= 3)) ? VertexCount() : 0U;
|
|
}
|
|
|
|
unsigned int ON_SubDMeshFragment::TextureCoordinateCapacity() const
|
|
{
|
|
return (nullptr != m_T && m_T_stride >= 3) ? VertexCapacity() : 0U;
|
|
}
|
|
|
|
const double* ON_SubDMeshFragment::TextureCoordinateArray(ON_SubDComponentLocation subd_appearance)const
|
|
{
|
|
return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? &m_ctrlnetT[0][0] : m_T;
|
|
}
|
|
|
|
size_t ON_SubDMeshFragment::TextureCoordinateArrayStride(ON_SubDComponentLocation subd_appearance)const
|
|
{
|
|
return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 3 : m_T_stride;
|
|
}
|
|
|
|
unsigned ON_SubDMeshFragment::TextureCoordinateArrayCount(ON_SubDComponentLocation subd_appearance) const
|
|
{
|
|
if (false == TextureCoordinatesExistForExperts())
|
|
return 0U;
|
|
return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? 4U : TextureCoordinateCount();
|
|
}
|
|
|
|
bool ON_SubDMeshFragment::GetTextureCoordinteCorners(
|
|
bool bGridOrder,
|
|
ON_3dPoint texture_coordinate_corners[4]
|
|
) const
|
|
{
|
|
// mutable double m_T_default_bbox[2][2];
|
|
if (nullptr != texture_coordinate_corners)
|
|
{
|
|
int i;
|
|
texture_coordinate_corners[0].x = m_ctrlnetT[0][0];
|
|
texture_coordinate_corners[0].y = m_ctrlnetT[0][1];
|
|
texture_coordinate_corners[0].z = m_ctrlnetT[0][2];
|
|
|
|
texture_coordinate_corners[1].x = m_ctrlnetT[1][0];
|
|
texture_coordinate_corners[1].y = m_ctrlnetT[1][1];
|
|
texture_coordinate_corners[1].z = m_ctrlnetT[1][2];
|
|
|
|
i = bGridOrder ? 2 : 3;
|
|
texture_coordinate_corners[i].x = m_ctrlnetT[2][0];
|
|
texture_coordinate_corners[i].y = m_ctrlnetT[2][1];
|
|
texture_coordinate_corners[i].z = m_ctrlnetT[2][2];
|
|
|
|
i = bGridOrder ? 3 : 2;
|
|
texture_coordinate_corners[i].x = m_ctrlnetT[3][0];
|
|
texture_coordinate_corners[i].y = m_ctrlnetT[3][1];
|
|
texture_coordinate_corners[i].z = m_ctrlnetT[3][2];
|
|
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
static const ON_2dPoint Internal_NgonFragmentPackRectCorner(
|
|
bool bGridOrder,
|
|
const ON_2dPoint face_pack_rect_corners[4],
|
|
double s,
|
|
double t
|
|
)
|
|
{
|
|
// used for 3-gons and for n-gons with n >= 5
|
|
return (1.0 - s) * (1.0 - t) * face_pack_rect_corners[0] + s * (1.0 - t) * face_pack_rect_corners[1] + (1.0 - s) * t * face_pack_rect_corners[bGridOrder ? 2 : 3] + s * t * face_pack_rect_corners[bGridOrder ? 3 : 2];
|
|
}
|
|
|
|
static const ON_3dPoint Internal_NgonFragmentTextureCoordinateCorner(
|
|
bool bGridOrder,
|
|
const ON_3dPoint face_texture_coordinate_corners[4],
|
|
double s,
|
|
double t
|
|
)
|
|
{
|
|
// used for 3-gons and for n-gons with n >= 5
|
|
return (1.0 - s) * (1.0 - t) * face_texture_coordinate_corners[0] + s * (1.0 - t) * face_texture_coordinate_corners[1] + (1.0 - s) * t * face_texture_coordinate_corners[bGridOrder ? 2 : 3] + s * t * face_texture_coordinate_corners[bGridOrder ? 3 : 2];
|
|
}
|
|
|
|
|
|
void ON_SubDMeshFragment::SetPackRectCornersForExperts(
|
|
bool bGridOrder,
|
|
const ON_2dPoint pack_rect_corners[4]
|
|
)
|
|
{
|
|
if (nullptr != pack_rect_corners)
|
|
{
|
|
// m_ctrlnetT[] is in grid order.
|
|
|
|
// lower left
|
|
m_pack_rect[0][0] = pack_rect_corners[0].x;
|
|
m_pack_rect[0][1] = pack_rect_corners[0].y;
|
|
|
|
// lower right
|
|
m_pack_rect[1][0] = pack_rect_corners[1].x;
|
|
m_pack_rect[1][1] = pack_rect_corners[1].y;
|
|
|
|
// upper left
|
|
int i = bGridOrder ? 2 : 3;
|
|
m_pack_rect[2][0] = pack_rect_corners[i].x;
|
|
m_pack_rect[2][1] = pack_rect_corners[i].y;
|
|
|
|
// upper right
|
|
i = bGridOrder ? 3 : 2;
|
|
m_pack_rect[3][0] = pack_rect_corners[i].x;
|
|
m_pack_rect[3][1] = pack_rect_corners[i].y;
|
|
}
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetQuadOr3gonFaceFragmentTextureCoordinateCorners(
|
|
bool bGridOrder,
|
|
const ON_3dPoint face_texture_coordinate_corners[4],
|
|
bool bSetTextureCoordinates
|
|
) const
|
|
{
|
|
if (1 == m_face_fragment_count && 0 == m_face_fragment_index)
|
|
{
|
|
// A quad ON_SubDFace is rendered with one ON_SubDMeshFragments.
|
|
SetTextureCoordinateCornersForExperts(bGridOrder, face_texture_coordinate_corners, bSetTextureCoordinates);
|
|
}
|
|
else
|
|
{
|
|
ON_3dPoint fragment_corners[4];
|
|
|
|
if (3 == m_face_fragment_count)
|
|
{
|
|
// A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments.
|
|
// The pack rect assigned to the face is divided into quarters and
|
|
// 3 of those quarters are assigned to the 3-gon fragments
|
|
// in a continuous manner.
|
|
|
|
const int i2 = bGridOrder ? 2 : 3;
|
|
const int i3 = bGridOrder ? 3 : 2;
|
|
|
|
fragment_corners[0] = ON_3dPoint::Midpoint(
|
|
ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]),
|
|
ON_3dPoint::Midpoint(face_texture_coordinate_corners[i2], face_texture_coordinate_corners[i3])
|
|
);
|
|
|
|
switch (m_face_fragment_index)
|
|
{
|
|
case 0:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 1st fragment
|
|
fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[i2]);
|
|
fragment_corners[2] = face_texture_coordinate_corners[0];
|
|
fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]);
|
|
break;
|
|
|
|
case 1:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 2nd fragment in
|
|
fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[0], face_texture_coordinate_corners[1]);
|
|
fragment_corners[2] = face_texture_coordinate_corners[1];
|
|
fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[1], face_texture_coordinate_corners[i3]);
|
|
break;
|
|
|
|
case 2:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 3rd fragment
|
|
fragment_corners[1] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[1], face_texture_coordinate_corners[i3]);
|
|
fragment_corners[2] = face_texture_coordinate_corners[i3];
|
|
fragment_corners[3] = ON_3dPoint::Midpoint(face_texture_coordinate_corners[i2], face_texture_coordinate_corners[i3]);
|
|
break;
|
|
|
|
default:
|
|
ON_SUBD_ERROR("Invalid m_face_fragment_index value");
|
|
fragment_corners[0] = ON_3dPoint::NanPoint;
|
|
fragment_corners[1] = ON_3dPoint::NanPoint;
|
|
fragment_corners[2] = ON_3dPoint::NanPoint;
|
|
fragment_corners[3] = ON_3dPoint::NanPoint;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// n-gon with n >= 5 or initialized m_face_fragment_count.
|
|
// If m_face_fragment_count >= 5, then you should be calling SetNgonFaceFragmentTextureCoordinateCorners().
|
|
ON_SUBD_ERROR("Invalid m_face_fragment_count value");
|
|
fragment_corners[0] = ON_3dPoint::NanPoint;
|
|
fragment_corners[1] = ON_3dPoint::NanPoint;
|
|
fragment_corners[2] = ON_3dPoint::NanPoint;
|
|
fragment_corners[3] = ON_3dPoint::NanPoint;
|
|
}
|
|
|
|
SetTextureCoordinateCornersForExperts(false, fragment_corners, true);
|
|
}
|
|
}
|
|
|
|
bool ON_SubDFace::GetMeshFragmentPackRectCorners(
|
|
bool bGridOrder,
|
|
unsigned int fragment_index,
|
|
ON_2dPoint mesh_fragment_pack_rect_corners[4]
|
|
) const
|
|
{
|
|
if (nullptr == mesh_fragment_pack_rect_corners)
|
|
return true; // confused caller
|
|
|
|
if (fragment_index >= ( (4 == m_edge_count) ? 1U : ((unsigned)m_edge_count) ) )
|
|
{
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
if (PackRectIsSet())
|
|
{
|
|
switch (m_edge_count)
|
|
{
|
|
case 0:
|
|
case 1:
|
|
case 2:
|
|
mesh_fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint;
|
|
return false;
|
|
break;
|
|
|
|
case 3: // triangle
|
|
{
|
|
// A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments.
|
|
// The pack rect assigned to the face is divided into quarters and
|
|
// 3 of those quarters are assigned to the 3-gon fragments
|
|
// in a continuous manner.
|
|
|
|
const ON_2dPoint face_pack_rect_corners[4] =
|
|
{
|
|
PackRectCorner(bGridOrder, 0),
|
|
PackRectCorner(bGridOrder, 1),
|
|
PackRectCorner(bGridOrder, 2),
|
|
PackRectCorner(bGridOrder, 3)
|
|
};
|
|
const int i2 = bGridOrder ? 2 : 3;
|
|
const int i3 = bGridOrder ? 3 : 2;
|
|
|
|
mesh_fragment_pack_rect_corners[0] = ON_2dPoint::Midpoint(
|
|
ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]),
|
|
ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3])
|
|
);
|
|
|
|
switch (fragment_index)
|
|
{
|
|
case 0:
|
|
// mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 1st fragment
|
|
mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]);
|
|
mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[0];
|
|
mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
break;
|
|
|
|
case 1:
|
|
// mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 2nd fragment in
|
|
mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[1];
|
|
mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
break;
|
|
|
|
case 2:
|
|
// mesh_fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 3rd fragment
|
|
mesh_fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
mesh_fragment_pack_rect_corners[i2] = face_pack_rect_corners[i3];
|
|
mesh_fragment_pack_rect_corners[i3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]);
|
|
break;
|
|
}
|
|
}
|
|
return true;
|
|
break;
|
|
|
|
case 4: // quad
|
|
return this->GetFacePackRectCorners(bGridOrder,mesh_fragment_pack_rect_corners);
|
|
break;
|
|
|
|
default: // n-gon with n >= 5
|
|
{
|
|
// An n-gon ON_SubDFace is rendered with n ON_SubDMeshFragments.
|
|
ON_2dPoint face_pack_rect_corners[4];
|
|
if (this->GetFacePackRectCorners(bGridOrder, face_pack_rect_corners))
|
|
{
|
|
const ON_2dVector face_pack_rect_size = this->PackRectSize();
|
|
ON_2dVector ngon_sub_pack_rect_size;
|
|
ON_2dVector ngon_sub_pack_rect_delta;
|
|
const ON_2udex ngon_grid_size = ON_SubDFace::GetNgonSubPackRectSizeAndDelta(
|
|
m_edge_count,
|
|
face_pack_rect_size,
|
|
ngon_sub_pack_rect_size,
|
|
ngon_sub_pack_rect_delta
|
|
);
|
|
if (ngon_grid_size.i > 0 && ngon_grid_size.j > 0 && ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners(
|
|
m_edge_count,
|
|
fragment_index,
|
|
bGridOrder,
|
|
face_pack_rect_corners,
|
|
face_pack_rect_size,
|
|
ngon_grid_size,
|
|
ngon_sub_pack_rect_size,
|
|
ngon_sub_pack_rect_delta,
|
|
mesh_fragment_pack_rect_corners
|
|
))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
mesh_fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint;
|
|
mesh_fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint;
|
|
return false;
|
|
}
|
|
|
|
|
|
static const ON_2dPoint Internal_PackRectCenterPoint(
|
|
const ON_2dPoint face_pack_rect_corners[4]
|
|
)
|
|
{
|
|
// This code works for face_pack_rect_corners[] in either grid order or counter clockwise order
|
|
// because swapping 2 and 3 does not change the result.
|
|
return ON_2dPoint::Midpoint(
|
|
ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]),
|
|
ON_2dPoint::Midpoint(face_pack_rect_corners[2], face_pack_rect_corners[3])
|
|
);
|
|
}
|
|
|
|
bool ON_SubDMeshFragment::Get3gonFaceFragmentPackRectCorners(
|
|
bool bFaceGridOrder,
|
|
const ON_2dPoint face_pack_rect_corners[4],
|
|
unsigned int fragment_index,
|
|
bool bFragmentGridOrder,
|
|
ON_2dPoint fragment_pack_rect_corners[4])
|
|
{
|
|
fragment_pack_rect_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners);
|
|
// A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments.
|
|
// The pack rect assigned to the face is divided into quarters and
|
|
// 3 of those quarters are assigned to the 3-gon fragments
|
|
// in a continuous manner.
|
|
|
|
const int i2 = bFaceGridOrder ? 2 : 3;
|
|
const int i3 = bFaceGridOrder ? 3 : 2;
|
|
|
|
const int j2 = bFragmentGridOrder ? 3 : 2;
|
|
const int j3 = bFragmentGridOrder ? 2 : 3;
|
|
|
|
fragment_pack_rect_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners);
|
|
|
|
bool rc = true;
|
|
switch (fragment_index)
|
|
{
|
|
case 0:
|
|
// fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 1st fragment
|
|
fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]);
|
|
fragment_pack_rect_corners[j2] = face_pack_rect_corners[0];
|
|
fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
break;
|
|
|
|
case 1:
|
|
// fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 2nd fragment in
|
|
fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
fragment_pack_rect_corners[j2] = face_pack_rect_corners[1];
|
|
fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
break;
|
|
|
|
case 2:
|
|
// fragment_pack_rect_corners[] in counter-clockwise order for 3-gon's 3rd fragment
|
|
fragment_pack_rect_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
fragment_pack_rect_corners[j2] = face_pack_rect_corners[i3];
|
|
fragment_pack_rect_corners[j3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]);
|
|
break;
|
|
|
|
default:
|
|
ON_SUBD_ERROR("Invalid m_face_fragment_index value");
|
|
fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint;
|
|
rc = false;
|
|
break;
|
|
}
|
|
return rc;
|
|
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetQuadOr3gonFaceFragmentPackRectCorners(
|
|
bool bGridOrder,
|
|
const ON_2dPoint face_pack_rect_corners[4]
|
|
)
|
|
{
|
|
if (1 == m_face_fragment_count && 0 == m_face_fragment_index)
|
|
{
|
|
// A quad ON_SubDFace is rendered with one ON_SubDMeshFragments.
|
|
SetPackRectCornersForExperts(bGridOrder, face_pack_rect_corners);
|
|
}
|
|
else
|
|
{
|
|
ON_2dPoint fragment_corners[4];
|
|
|
|
if (3 == m_face_fragment_count)
|
|
{
|
|
// A 3-gon ON_SubDFace is rendered with three ON_SubDMeshFragments.
|
|
// The pack rect assigned to the face is divided into quarters and
|
|
// 3 of those quarters are assigned to the 3-gon fragments
|
|
// in a continuous manner.
|
|
|
|
const int i2 = bGridOrder ? 2 : 3;
|
|
const int i3 = bGridOrder ? 3 : 2;
|
|
|
|
fragment_corners[0] = Internal_PackRectCenterPoint(face_pack_rect_corners);
|
|
|
|
switch (m_face_fragment_index)
|
|
{
|
|
case 0:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 1st fragment
|
|
fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[i2]);
|
|
fragment_corners[2] = face_pack_rect_corners[0];
|
|
fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
break;
|
|
|
|
case 1:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 2nd fragment in
|
|
fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[0], face_pack_rect_corners[1]);
|
|
fragment_corners[2] = face_pack_rect_corners[1];
|
|
fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
break;
|
|
|
|
case 2:
|
|
// fragment_corners[] in counter-clockwise order for 3-gon's 3rd fragment
|
|
fragment_corners[1] = ON_2dPoint::Midpoint(face_pack_rect_corners[1], face_pack_rect_corners[i3]);
|
|
fragment_corners[2] = face_pack_rect_corners[i3];
|
|
fragment_corners[3] = ON_2dPoint::Midpoint(face_pack_rect_corners[i2], face_pack_rect_corners[i3]);
|
|
break;
|
|
|
|
default:
|
|
ON_SUBD_ERROR("Invalid m_face_fragment_index value");
|
|
fragment_corners[0] = ON_2dPoint::NanPoint;
|
|
fragment_corners[1] = ON_2dPoint::NanPoint;
|
|
fragment_corners[2] = ON_2dPoint::NanPoint;
|
|
fragment_corners[3] = ON_2dPoint::NanPoint;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// n-gon with n >= 5 or initialized m_face_fragment_count.
|
|
// If m_face_fragment_count >= 5, then you should be calling SetNgonFaceFragmentPackRectCorners().
|
|
ON_SUBD_ERROR("Invalid m_face_fragment_count value");
|
|
fragment_corners[0] = ON_2dPoint::NanPoint;
|
|
fragment_corners[1] = ON_2dPoint::NanPoint;
|
|
fragment_corners[2] = ON_2dPoint::NanPoint;
|
|
fragment_corners[3] = ON_2dPoint::NanPoint;
|
|
}
|
|
|
|
SetPackRectCornersForExperts(false, fragment_corners);
|
|
}
|
|
}
|
|
|
|
static double Internal_ClampBetweenZeroAndOne(double x)
|
|
{
|
|
if (x < 0.0)
|
|
return 0.0;
|
|
if (x > 1.0)
|
|
return 1.0;
|
|
return x;
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetNgonFaceFragmentTextureCoordinateCorners(
|
|
bool bGridOrder,
|
|
const ON_3dPoint face_texture_coordinate_corners[4],
|
|
ON_2dVector face_pack_rect_size,
|
|
ON_2udex ngon_grid_size,
|
|
ON_2dVector ngon_sub_pack_rect_size,
|
|
ON_2dVector ngon_sub_pack_rect_delta,
|
|
bool bSetTextureCoordinates
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (
|
|
face_texture_coordinate_corners[0] == face_texture_coordinate_corners[1]
|
|
&& face_texture_coordinate_corners[0] == face_texture_coordinate_corners[2]
|
|
&& face_texture_coordinate_corners[0] == face_texture_coordinate_corners[3]
|
|
)
|
|
{
|
|
// constant texture coordinates
|
|
SetTextureCoordinateCornersForExperts(true, face_texture_coordinate_corners, bSetTextureCoordinates);
|
|
return;
|
|
}
|
|
|
|
const unsigned ngon_edge_count = m_face_fragment_count;
|
|
const unsigned ngon_fragment_index = m_face_fragment_index;
|
|
if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount)
|
|
break;
|
|
if (ngon_fragment_index >= ngon_edge_count)
|
|
break;
|
|
if (ngon_grid_size.i * ngon_grid_size.j < ngon_edge_count)
|
|
break;
|
|
const ON_2udex frag_k(ngon_fragment_index % ngon_grid_size.i, ngon_fragment_index / ngon_grid_size.i);
|
|
const double s0 = Internal_ClampBetweenZeroAndOne(frag_k.i * (ngon_sub_pack_rect_delta.x/face_pack_rect_size.x));
|
|
const double s1 = Internal_ClampBetweenZeroAndOne(s0 + (ngon_sub_pack_rect_size.x/ face_pack_rect_size.x));
|
|
const double t0 = Internal_ClampBetweenZeroAndOne(frag_k.j * (ngon_sub_pack_rect_delta.y/ face_pack_rect_size.y));
|
|
const double t1 = Internal_ClampBetweenZeroAndOne(t0 + (ngon_sub_pack_rect_size.y/ face_pack_rect_size.y));
|
|
const ON_3dPoint fragment_texture_coordinate_corners[4] = {
|
|
Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s0,t0),
|
|
Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s1,t0),
|
|
Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s0,t1),
|
|
Internal_NgonFragmentTextureCoordinateCorner(bGridOrder,face_texture_coordinate_corners,s1,t1)
|
|
};
|
|
// fragment_texture_coordinate_corners[] is in grid order
|
|
SetTextureCoordinateCornersForExperts(true, fragment_texture_coordinate_corners, bSetTextureCoordinates);
|
|
return;
|
|
}
|
|
ON_SUBD_ERROR("Invalid input.");
|
|
const ON_3dPoint nan_corners[4] = { ON_3dPoint ::NanPoint,ON_3dPoint::NanPoint ,ON_3dPoint::NanPoint ,ON_3dPoint::NanPoint };
|
|
SetTextureCoordinateCornersForExperts(bGridOrder, nan_corners, bSetTextureCoordinates);
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners(
|
|
unsigned int ngon_edge_count,
|
|
unsigned int ngon_fragment_index,
|
|
bool bGridOrder,
|
|
const ON_2dPoint face_pack_rect_corners[4],
|
|
ON_2dVector face_pack_rect_size,
|
|
ON_2udex ngon_grid_size,
|
|
ON_2dVector ngon_sub_pack_rect_size,
|
|
ON_2dVector ngon_sub_pack_rect_delta,
|
|
ON_2dPoint fragment_pack_rect_corners[4]
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (ngon_edge_count < 5 || ngon_edge_count > ON_SubDFace::MaximumEdgeCount)
|
|
break;
|
|
if (ngon_fragment_index >= ngon_edge_count)
|
|
break;
|
|
if (ngon_grid_size.i * ngon_grid_size.j < ngon_edge_count)
|
|
break;
|
|
if (nullptr == fragment_pack_rect_corners)
|
|
break;
|
|
const ON_2udex frag_k(ngon_fragment_index % ngon_grid_size.i, ngon_fragment_index / ngon_grid_size.i);
|
|
const double s0 = Internal_ClampBetweenZeroAndOne(frag_k.i * (ngon_sub_pack_rect_delta.x / face_pack_rect_size.x));
|
|
const double s1 = Internal_ClampBetweenZeroAndOne(s0 + (ngon_sub_pack_rect_size.x / face_pack_rect_size.x));
|
|
const double t0 = Internal_ClampBetweenZeroAndOne(frag_k.j * (ngon_sub_pack_rect_delta.y / face_pack_rect_size.y));
|
|
const double t1 = Internal_ClampBetweenZeroAndOne(t0 + (ngon_sub_pack_rect_size.y / face_pack_rect_size.y));
|
|
const int i2 = bGridOrder ? 2 : 3;
|
|
const int i3 = bGridOrder ? 3 : 2;
|
|
fragment_pack_rect_corners[0] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s0,t0);
|
|
fragment_pack_rect_corners[1] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s1,t0);
|
|
fragment_pack_rect_corners[i2] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s0,t1);
|
|
fragment_pack_rect_corners[i3] = Internal_NgonFragmentPackRectCorner(bGridOrder,face_pack_rect_corners,s1,t1);
|
|
return true;
|
|
}
|
|
|
|
ON_SUBD_ERROR("Invalid input.");
|
|
if (nullptr != fragment_pack_rect_corners)
|
|
{
|
|
fragment_pack_rect_corners[0] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[1] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[2] = ON_2dPoint::NanPoint;
|
|
fragment_pack_rect_corners[3] = ON_2dPoint::NanPoint;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetNgonFaceFragmentPackRectCorners(
|
|
bool bGridOrder,
|
|
const ON_2dPoint face_pack_rect_corners[4],
|
|
ON_2dVector face_pack_rect_size,
|
|
ON_2udex ngon_grid_size,
|
|
ON_2dVector ngon_sub_pack_rect_size,
|
|
ON_2dVector ngon_sub_pack_rect_delta
|
|
)
|
|
{
|
|
ON_2dPoint fragment_pack_rect_corners[4];
|
|
ON_SubDMeshFragment::GetNgonFaceFragmentPackRectCorners(
|
|
m_face_fragment_count,
|
|
m_face_fragment_index,
|
|
bGridOrder,
|
|
face_pack_rect_corners,
|
|
face_pack_rect_size,
|
|
ngon_grid_size,
|
|
ngon_sub_pack_rect_size,
|
|
ngon_sub_pack_rect_delta,
|
|
fragment_pack_rect_corners
|
|
);
|
|
SetPackRectCornersForExperts(bGridOrder, fragment_pack_rect_corners);
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetTextureCoordinateCornersForExperts(
|
|
bool bGridOrder,
|
|
const ON_3dPoint fragment_texture_coordinate_corners[4],
|
|
bool bSetTextureCoordinates
|
|
) const
|
|
{
|
|
if (nullptr != fragment_texture_coordinate_corners)
|
|
{
|
|
// m_ctrlnetT[] is in grid order.
|
|
|
|
// lower left
|
|
m_ctrlnetT[0][0] = fragment_texture_coordinate_corners[0].x;
|
|
m_ctrlnetT[0][1] = fragment_texture_coordinate_corners[0].y;
|
|
m_ctrlnetT[0][2] = fragment_texture_coordinate_corners[0].z;
|
|
|
|
// lower right
|
|
m_ctrlnetT[1][0] = fragment_texture_coordinate_corners[1].x;
|
|
m_ctrlnetT[1][1] = fragment_texture_coordinate_corners[1].y;
|
|
m_ctrlnetT[1][2] = fragment_texture_coordinate_corners[1].z;
|
|
|
|
// upper left
|
|
int i = bGridOrder ? 2 : 3;
|
|
m_ctrlnetT[2][0] = fragment_texture_coordinate_corners[i].x;
|
|
m_ctrlnetT[2][1] = fragment_texture_coordinate_corners[i].y;
|
|
m_ctrlnetT[2][2] = fragment_texture_coordinate_corners[i].z;
|
|
|
|
// upper right
|
|
i = bGridOrder ? 3 : 2;
|
|
m_ctrlnetT[3][0] = fragment_texture_coordinate_corners[i].x;
|
|
m_ctrlnetT[3][1] = fragment_texture_coordinate_corners[i].y;
|
|
m_ctrlnetT[3][2] = fragment_texture_coordinate_corners[i].z;
|
|
|
|
if (bSetTextureCoordinates)
|
|
SetTextureCoordinatesFromCorners();
|
|
}
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate(
|
|
ON_2udex grid2dex
|
|
) const
|
|
{
|
|
return VertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j));
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate(
|
|
unsigned grid2dex_i,
|
|
unsigned grid2dex_j
|
|
) const
|
|
{
|
|
return VertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j));
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinate(
|
|
unsigned grid_point_index
|
|
) const
|
|
{
|
|
return
|
|
(grid_point_index >= TextureCoordinateCount())
|
|
? ON_3dPoint::NanPoint
|
|
: ON_3dPoint(m_T + grid_point_index * m_T_stride);
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(unsigned grid2dex_i, unsigned grid2dex_j) const
|
|
{
|
|
const unsigned n = m_grid.SideSegmentCount();
|
|
if (n <= 0U || grid2dex_i > n || grid2dex_j > n)
|
|
return ON_3dPoint::NanPoint;
|
|
const double s = ((double)grid2dex_i) / ((double)n);
|
|
const double t = ((double)grid2dex_j) / ((double)n);
|
|
const double c[4] = { (1.0 - s)*(1.0 - t), s*(1.0 - t), (1.0 - s)*t, s*t };
|
|
return ON_3dPoint(
|
|
c[0] * m_ctrlnetT[0][0] + c[1] * m_ctrlnetT[1][0] + c[2] * m_ctrlnetT[2][0] + c[3] * m_ctrlnetT[3][0],
|
|
c[0] * m_ctrlnetT[0][1] + c[1] * m_ctrlnetT[1][1] + c[2] * m_ctrlnetT[2][1] + c[3] * m_ctrlnetT[3][1],
|
|
c[0] * m_ctrlnetT[0][2] + c[1] * m_ctrlnetT[1][2] + c[2] * m_ctrlnetT[2][2] + c[3] * m_ctrlnetT[3][2]
|
|
);
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(ON_2udex grid2dex) const
|
|
{
|
|
return VertexTextureCoordinateFromCorners(grid2dex.i, grid2dex.j);
|
|
}
|
|
|
|
const ON_3dPoint ON_SubDMeshFragment::VertexTextureCoordinateFromCorners(unsigned grid_point_index) const
|
|
{
|
|
return VertexTextureCoordinateFromCorners(m_grid.Grid2dexFromPointIndex(grid_point_index));
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetTextureCoordinatesFromCorners() const
|
|
{
|
|
Internal_SetTextureCoordinatesFromCorners(m_ctrlnetT[0], m_ctrlnetT[1], m_ctrlnetT[2], m_ctrlnetT[3], ON_DBL_QNAN, 3);
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetPackedTextureCoordinates() const
|
|
{
|
|
Internal_SetTextureCoordinatesFromCorners(m_pack_rect[0], m_pack_rect[1], m_pack_rect[2], m_pack_rect[3], 0.0, 2);
|
|
}
|
|
|
|
void ON_SubDMeshFragment::SetUnpackedTextureCoordinates() const
|
|
{
|
|
const double unpacked_corners[4][2] =
|
|
{
|
|
{0.0,0.0},
|
|
{1.0,0.0},
|
|
{0.0,1.0},
|
|
{1.0,1.0},
|
|
};
|
|
Internal_SetTextureCoordinatesFromCorners(unpacked_corners[0], unpacked_corners[1], unpacked_corners[2], unpacked_corners[3], 0.0, 2);
|
|
}
|
|
|
|
void ON_SubDMeshFragment::Internal_SetTextureCoordinatesFromCorners(
|
|
const double* corner0,
|
|
const double* corner1,
|
|
const double* corner2,
|
|
const double* corner3,
|
|
double default_coordinate_value,
|
|
int corner_dim
|
|
) const
|
|
{
|
|
const unsigned n = m_grid.SideSegmentCount();
|
|
if (n <= 0U || n > ON_SubDMeshFragment::MaximumSideSegmentCount)
|
|
return;
|
|
// TextureCoordinateCapacity() check insures m_T_stride >= 3
|
|
if (TextureCoordinateCapacity() < n * n)
|
|
return;
|
|
if (corner_dim <= 0)
|
|
return;
|
|
if (corner_dim > 3)
|
|
corner_dim = 3;
|
|
if (nullptr == corner0 || nullptr == corner1 || nullptr == corner2 || nullptr == corner3)
|
|
return;
|
|
|
|
SetTextureCoordinatesExistForExperts(true);
|
|
double* T = m_T;
|
|
const double d = (double)n;
|
|
double s, t;
|
|
ON_3dPoint tc(default_coordinate_value, default_coordinate_value, default_coordinate_value);
|
|
bool bConstant[3] = { corner_dim < 1, corner_dim < 2, corner_dim < 3 };
|
|
for (int i = 0; i < corner_dim; ++i)
|
|
{
|
|
double c = corner0[i];
|
|
if (c == corner1[i] && c == corner2[i] && c == corner3[i])
|
|
{
|
|
tc[i] = c;
|
|
bConstant[i] = true;
|
|
}
|
|
else if (false == (c == c) || false == (corner1[i] == corner1[i]) || false == (corner2[i] == corner2[i]) || false == (corner3[i] == corner3[i]))
|
|
{
|
|
tc[i] = ON_DBL_QNAN;
|
|
bConstant[i] = true;
|
|
}
|
|
}
|
|
|
|
for (unsigned j = 0U; j <= n; ++j)
|
|
{
|
|
t = ((double)j) / d;
|
|
for (unsigned i = 0U; i <= n; ++i)
|
|
{
|
|
s = ((double)i) / d;
|
|
const double c[4] = { (1.0 - s) * (1.0 - t), s * (1.0 - t), (1.0 - s) * t, s * t };
|
|
if (false == bConstant[0])
|
|
tc.x = c[0] * corner0[0] + c[1] * corner1[0] + c[2] * corner2[0] + c[3] * corner3[0];
|
|
if (false == bConstant[1])
|
|
tc.y = c[0] * corner0[1] + c[1] * corner1[1] + c[2] * corner2[1] + c[3] * corner3[1];
|
|
if (false == bConstant[2])
|
|
tc.z = c[0] * corner0[2] + c[1] * corner1[2] + c[2] * corner2[2] + c[3] * corner3[2];
|
|
T[0] = tc.x;
|
|
T[1] = tc.y;
|
|
T[2] = tc.z;
|
|
T += m_T_stride;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshFragment::SetVertexTextureCoordinate(
|
|
ON_2udex grid2dex,
|
|
ON_3dPoint texture_coordinate
|
|
) const
|
|
{
|
|
return SetVertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex.i, grid2dex.j), texture_coordinate);
|
|
}
|
|
|
|
bool ON_SubDMeshFragment::SetVertexTextureCoordinate(
|
|
unsigned grid2dex_i,
|
|
unsigned grid2dex_j,
|
|
ON_3dPoint texture_coordinate
|
|
) const
|
|
{
|
|
return SetVertexTextureCoordinate(m_grid.PointIndexFromGrid2dex(grid2dex_i, grid2dex_j), texture_coordinate);
|
|
}
|
|
|
|
bool ON_SubDMeshFragment::SetVertexTextureCoordinate(
|
|
unsigned grid_point_index,
|
|
ON_3dPoint texture_coordinate
|
|
) const
|
|
{
|
|
if (grid_point_index >= TextureCoordinateCapacity())
|
|
return false;
|
|
double* T = (m_T + grid_point_index * m_T_stride);
|
|
T[0] = texture_coordinate.x;
|
|
T[1] = texture_coordinate.y;
|
|
T[2] = texture_coordinate.z;
|
|
SetTextureCoordinatesExistForExperts(true);
|
|
return true;
|
|
}
|
|
|
|
#pragma endregion
|
|
#endif
|