Files
opennurbs/opennurbs_subd_texture.cpp
Bozo The Builder 01cdb463e6 Sync changes from upstream repository
Co-authored-by: Andrew Le Bihan <andy@mcneel.com>
Co-authored-by: chuck <chuck@mcneel.com>
Co-authored-by: Dale Fugier <dale@mcneel.com>
Co-authored-by: Dale Lear <dalelear@mcneel.com>
Co-authored-by: David Eränen <david.eranen@mcneel.com>
Co-authored-by: Greg Arden <greg@mcneel.com>
Co-authored-by: John Croudy <john.croudy@mcneel.com>
Co-authored-by: Lowell Walmsley <lowell@mcneel.com>
Co-authored-by: Nathan Letwory <nathan@mcneel.com>
Co-authored-by: piac <giulio@mcneel.com>
Co-authored-by: Steve Baer <steve@mcneel.com>
Co-authored-by: Tim Hemmelman <tim@mcneel.com>
2020-03-12 09:00:26 -07:00

1051 lines
31 KiB
C++

#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"
/* $NoKeywords: $ */
/*
//
// Copyright (c) 1993-2019 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>.
//
////////////////////////////////////////////////////////////////
*/
ON_SubDTextureDomainType ON_SubD::TextureDomainTypeFromUnsigned
(
unsigned int texture_domain_type_as_unsigned
)
{
switch (texture_domain_type_as_unsigned)
{
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Unset);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::PerFace);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Packed);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Zero);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Nan);
ON_ENUM_FROM_UNSIGNED_CASE(ON_SubDTextureDomainType::Custom);
}
return ON_SUBD_RETURN_ERROR(ON_SubDTextureDomainType::Unset);
}
const ON_wString ON_SubD::TextureDomainTypeToString(
ON_SubDTextureDomainType texture_domain_type
)
{
const wchar_t* s;
switch (texture_domain_type)
{
case ON_SubDTextureDomainType::Unset:
s = L"Unset";
break;
case ON_SubDTextureDomainType::PerFace:
s = L"PerFace";
break;
case ON_SubDTextureDomainType::Packed:
s = L"Packed";
break;
case ON_SubDTextureDomainType::Zero:
s = L"Zero";
break;
case ON_SubDTextureDomainType::Nan:
s = L"Nan";
break;
case ON_SubDTextureDomainType::Custom:
s = L"Custom";
break;
default:
s = nullptr;
break;
}
return (nullptr != s && 0 != s[0])
? ON_wString(s)
: ON_wString::FormatToString(L"ON_SubDTextureDomainType(%u)", ((unsigned)static_cast<unsigned char>(texture_domain_type)));
}
//////////////////////////////////////////////////////////////////////////////
//
// ON_SubDimple - texture coordinates
//
#if 1
#pragma region ON_SubD - texture coordinates
void ON_SubDimple::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const
{
this->m_texture_mapping_tag = mapping_tag;
}
const ON_MappingTag ON_SubDimple::TextureMappingTag() const
{
return m_texture_mapping_tag;
}
#pragma endregion
#endif
//////////////////////////////////////////////////////////////////////////////
//
// ON_SubD - texture coordinates
//
#if 1
#pragma region ON_SubD - texture coordinates
ON_SubDTextureDomainType ON_SubD::TextureDomainType() const
{
const ON_SubDimple* subdimple = SubDimple();
return (nullptr != subdimple) ? subdimple->TextureDomainType() : ON_SubDTextureDomainType::Unset;
}
ON_SubDTextureDomainType ON_SubDimple::TextureDomainType() const
{
return m_texture_domain_type;
}
void ON_SubDimple::SetTextureDomainType(
ON_SubDTextureDomainType texture_domain_type
) const
{
m_texture_domain_type = texture_domain_type;
}
unsigned int ON_SubD::TextureImageSuggestedMinimumSize() const
{
const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(FaceCount(), 0.0, 0.0);
return ON_SubD::TextureImageSuggestedMinimumSize(grid_size);
}
unsigned int ON_SubD::TextureImageSuggestedMinimumSize(
ON_2udex grid_size
)
{
const unsigned min_pixels = 16;
const unsigned max_pixels = 4098;
unsigned pixels = grid_size.i >= grid_size.j ? grid_size.i : grid_size.j;
if (0 == pixels)
return ON_SUBD_RETURN_ERROR(1);
if (pixels < max_pixels / min_pixels)
pixels *= min_pixels;
else
pixels = max_pixels;
if (pixels < min_pixels)
{
pixels = min_pixels;
const unsigned smallgrid_min_pixels = 128;
if (grid_size.i > 0 && grid_size.j > 0)
{
while (grid_size.i * grid_size.j * pixels < smallgrid_min_pixels)
pixels *= 2;
}
}
if (pixels > max_pixels)
pixels = max_pixels;
return pixels;
}
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);
}
const ON_2udex ON_SubD::GetTextureDomainAndDelta(
unsigned quad_count,
double image_width,
double image_height,
ON_2dVector& quad_texture_domain,
ON_2dVector& quad_texture_delta
)
{
ON_2udex grid_size(0, 0);
quad_texture_domain = ON_2dVector::ZeroVector;
quad_texture_delta = ON_2dVector::ZeroVector;
const bool bImageSizeKnown
= image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE
&& image_width > 0.0 && image_width < ON_UNSET_POSITIVE_VALUE;
if (false == bImageSizeKnown)
{
// unknown image size - assume it's square.
grid_size = ON_SubD::TextureDomainGridSize(quad_count, 0.0, 0.0);
// use grid size to set minimum sugggested square image size
image_width = image_height = (double)ON_SubD::TextureImageSuggestedMinimumSize(grid_size);
}
else
{
// use image size to help set grid size
grid_size = ON_SubD::TextureDomainGridSize(quad_count, image_width, image_height);
}
if (0 == grid_size.i || 0 == grid_size.j)
return ON_SUBD_RETURN_ERROR(grid_size);
const double image_size[2] = { image_width ,image_height };
// quad_image_size = size of image for each quad
const ON_2dVector quad_image_size(image_size[0] / ((double)grid_size.i), image_size[1] / ((double)grid_size.j));
if (1U == quad_count)
{
// quad uses entire image_delta x image_delta region
quad_texture_delta.Set(1.0, 1.0);
quad_texture_domain = quad_texture_delta;
}
else
{
for (int k = 0; k < 2; ++k)
{
if (quad_image_size[k] >= 16.0)
{
// An image with image_delta.x X image_delta.y pixels
// doesn't have to share a pixel with its neighbors.
quad_texture_delta[k] = floor(quad_image_size[k]) / image_size[k];
quad_texture_domain[k] = (floor(quad_image_size[k]) - 1.0) / image_size[k];
}
else
{
// Not as good, but a quad is getting at most 16x16 pixels in from a texture image
// with image_delta x image_delta pixels, so the texture will look bad anyway.
quad_texture_delta[k] = (quad_image_size[k] / image_size[k]);
quad_texture_domain[k] = (15.0 / 16.0)*quad_texture_delta[k];
}
}
}
return grid_size;
}
bool ON_SubD::SetTextureDomains(
ON_SubDTextureDomainType texture_domain_type,
bool bLazy,
bool bSetFragmentTextureCoordinates
) const
{
const ON_SubDimple* subdimple = SubDimple();
if (nullptr == subdimple)
return (ON_SubDTextureDomainType::Unset == texture_domain_type);
if (ON_SubDTextureDomainType::Unset == texture_domain_type || ON_SubDTextureDomainType::Custom == texture_domain_type)
bLazy = true;
if (bLazy && texture_domain_type == subdimple->TextureDomainType())
return true;
if (0 == FaceCount())
{
subdimple->SetTextureDomainType(ON_SubDTextureDomainType::Unset);
return (ON_SubDTextureDomainType::Unset == texture_domain_type);
}
bool rc = false;
switch (texture_domain_type)
{
case ON_SubDTextureDomainType::Unset:
rc = true;
break;
case ON_SubDTextureDomainType::PerFace:
case ON_SubDTextureDomainType::Packed:
case ON_SubDTextureDomainType::Zero:
case ON_SubDTextureDomainType::Nan:
{
ON_SubDFaceIterator fit(*this);
rc = ON_SubD::SetTextureDomains(fit, texture_domain_type, bSetFragmentTextureCoordinates);
}
break;
case ON_SubDTextureDomainType::Custom:
rc = false;
break;
default:
rc = false;
break;
}
if (rc && nullptr != subdimple)
subdimple->SetTextureDomainType(texture_domain_type);
return rc;
}
bool ON_SubD::SetTextureDomains(
ON_SubDFaceIterator& fit,
ON_SubDTextureDomainType texture_domain_type,
bool bSetFragmentTextureCoordinates
)
{
if (ON_SubDTextureDomainType::Custom == texture_domain_type)
return ON_SUBD_RETURN_ERROR(false);
bool bFragmentsExist = false;
const double default_tc = (ON_SubDTextureDomainType::Zero == texture_domain_type) ? 0.0 : ON_DBL_QNAN;
const ON_2dPoint default_origin(default_tc, default_tc);
const ON_2dVector default_delta(default_tc, default_tc);
unsigned face_count = 0;
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace())
{
f->SetTextureDomain(texture_domain_type, default_origin, default_delta, 0);
if (f->m_edge_count >= 3)
++face_count;
if (f->MeshFragments())
bFragmentsExist = true;
}
bool rc
= ON_SubDTextureDomainType::Unset == texture_domain_type
|| ON_SubDTextureDomainType::Zero == texture_domain_type
|| ON_SubDTextureDomainType::Nan == texture_domain_type;
if (ON_SubDTextureDomainType::Packed == texture_domain_type)
{
texture_domain_type = ON_SubDTextureDomainType::PerFace;
}
if (ON_SubDTextureDomainType::PerFace == texture_domain_type)
{
const ON_2udex grid_size = ON_SubD::TextureDomainGridSize(face_count, 0.0, 0.0);
const double image_width = ON_SubD::TextureImageSuggestedMinimumSize(grid_size);
const double image_height = image_width;
ON_2dVector face_texture_domain = ON_2dVector::NanVector;
ON_2dVector face_texture_delta = ON_2dVector::NanVector;
ON_SubD::GetTextureDomainAndDelta(
face_count,
image_height,
image_width,
face_texture_domain,
face_texture_delta
);
ON_2udex k(0, 0);
unsigned int face_dex = 0;
for (const ON_SubDFace* f = fit.FirstFace(); nullptr != f; f = fit.NextFace(), ++face_dex)
{
if (0 != face_dex)
{
k.i = (++k.i % grid_size.i);
if (0 == k.i)
++k.j;
}
const ON_2dPoint face_texture_origin(k.i*face_texture_delta.x, k.j*face_texture_delta.y);
f->SetTextureDomain(texture_domain_type, face_texture_origin, face_texture_domain, 0);
if (false == bFragmentsExist && nullptr != f->MeshFragments())
bFragmentsExist = true;
}
rc = true;
}
// Use the domains to set fragment texture coordinates
if (bFragmentsExist && bSetFragmentTextureCoordinates)
ON_SubD::SetTextureCoordinatesFromFaceDomains(fit);
return rc;
}
bool ON_SubD::SetTextureCoordinatesFromFaceDomains() const
{
if (ON_SubDTextureDomainType::Unset == this->TextureDomainType())
{
// uset default to packed
if (false == SetTextureDomains(ON_SubDTextureDomainType::Packed, false, false))
return false;
}
ON_SubDFaceIterator fit(*this);
const bool rc = ON_SubD::SetTextureCoordinatesFromFaceDomains(fit);
const ON_MappingTag& mapping_tag
= (rc)
? ON_MappingTag::SurfaceParameterMapping
: ON_MappingTag::Unset;
SetTextureMappingTag(mapping_tag);
return rc;
}
bool ON_SubD::SetTextureCoordinatesFromFaceDomains(ON_SubDFaceIterator& fit)
{
// estimate face image size - used to add empty space around ngon-subdfragments
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 ON_2dPoint face_corners[4] = {
f->TextureDomainCorner(true, false, 0),
f->TextureDomainCorner(true, false, 1),
f->TextureDomainCorner(true, false, 2),
f->TextureDomainCorner(true, false, 3)
};
if (4 == f->m_edge_count)
{
// This face in a quad with a single fragment and that fragment gets the entire face_texture_domain
fragment->SetTextureCoordinateCorners(true, face_corners, true);
}
else
{
// This face in an n-gon with n fragments that each get a portion of the face_texture_domain
ON_2dVector frag_texture_domain = ON_2dVector::NanVector;
ON_2dVector frag_texture_delta = ON_2dVector::NanVector;
const ON_2udex fragment_grid_size = ON_SubD::GetTextureDomainAndDelta(
f->m_edge_count,
face_image_size,
face_image_size,
frag_texture_domain,
frag_texture_delta
);
ON_2udex frag_k(0, 0);
for (unsigned short frag_dex = 0; frag_dex < f->m_edge_count && nullptr != fragment; ++frag_dex, fragment = fragment->m_next_fragment)
{
if (0 != frag_dex)
{
frag_k.i = (++frag_k.i % fragment_grid_size.i);
if (0 == frag_k.i)
++frag_k.j;
}
const double s0 = frag_k.i*frag_texture_delta.x;
const double t0 = frag_k.j*frag_texture_delta.y;
const double s2 = s0 + frag_texture_domain.x;
const double t2 = t0 + frag_texture_domain.y;
fragment->SetTextureCoordinateCorners(true, face_corners, s0, s2, t0, t2, true);
}
}
}
return true;
}
void ON_SubD::SetTextureMappingTag(const ON_MappingTag &mapping_tag) const
{
const ON_SubDimple* subdimple = SubDimple();
if (nullptr != subdimple)
subdimple->SetTextureMappingTag(mapping_tag);
}
bool ON_SubD::SetTextureCoordinates(
const class ON_TextureMapping& mapping,
const class ON_Xform* subd_xform,
bool bLazy
) const
{
const ON_TextureMapping::TYPE mt = mapping.m_type;
if (nullptr != subd_xform)
{
if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt)
subd_xform = nullptr;
else if (false == subd_xform->IsValid() || subd_xform->IsIdentity() || subd_xform->IsZero())
subd_xform = nullptr;
}
const ON_MappingTag tag(mapping, subd_xform);
if (0 == TextureMappingTag().Compare(tag))
return true;
if (ON_TextureMapping::TYPE::srfp_mapping == mt || ON_TextureMapping::TYPE::no_mapping == mt)
SetTextureCoordinatesFromFaceDomains();
const bool bApplyUVW = (mapping.m_uvw.IsValid() && !mapping.m_uvw.IsIdentity() && !mapping.m_uvw.IsZero());
if (ON_TextureMapping::TYPE::srfp_mapping != mt || bApplyUVW)
{
//
// NOTE WELL:
// ON_SubDMeshFragment store the mesh used to render ON_SubD objects
// The mesh topology, 3d vertex locations, 3d vertex normals
// CANNOT be modified to insert "texture seams."
//
ON_Xform P_xform, N_xform;
if (subd_xform && ON_TextureMapping::TYPE::srfp_mapping != mt)
{
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;
for (double* T1 = T + T_stride * T_count; T < T1; T += T_stride, P += P_stride, N += N_stride)
{
if (ON_TextureMapping::TYPE::srfp_mapping == mt)
tc = ON_3dPoint(T[0], T[1], 0.0);
else
{
bool ok = subd_xform ?
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;
}
if (bApplyUVW)
tc = mapping.m_uvw * tc;
T[0] = tc.x;
T[1] = tc.y;
T[2] = tc.z;
}
}
}
SetTextureMappingTag(tag);
return true;
}
const ON_MappingTag ON_SubD::TextureMappingTag() const
{
const ON_SubDimple* subdimple = SubDimple();
return (nullptr != subdimple) ? subdimple->TextureMappingTag() : ON_MappingTag();
}
#pragma endregion
#endif
//////////////////////////////////////////////////////////////////////////////
//
// ON_SubDFace - texture coordinates
//
#if 1
#pragma region ON_SubDFace - texture coordinates
void ON_SubDFace::SetTextureDomain(
ON_SubDTextureDomainType texture_domain_type,
ON_2dPoint origin,
ON_2dVector delta,
int packing_rotation_degrees
) const
{
m_texture_coordinate_bits = 0;
unsigned char domain_type_bits = static_cast<unsigned char>(texture_domain_type);
if (domain_type_bits < 8)
{
domain_type_bits <<= 4;
if (domain_type_bits != (domain_type_bits & TextureCoordinateBits::DomainTypeMask))
domain_type_bits = 0;
}
if (
0 != domain_type_bits
&& origin.IsValid()
&& delta.x >= 0.0 && delta.x < ON_UNSET_POSITIVE_VALUE
&& delta.y >= 0.0 && delta.y < ON_UNSET_POSITIVE_VALUE
&& 0 == packing_rotation_degrees % 90
)
{
m_texture_coordinate_origin[0] = origin.x;
m_texture_coordinate_origin[1] = origin.y;
m_texture_coordinate_delta[0] = delta.x;
m_texture_coordinate_delta[1] = delta.y;
ON_SubDFace::TextureCoordinateBits packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate0;
switch (((packing_rotation_degrees % 360) + 360) % 360)
{
case 90:
packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate90;
break;
case 180:
packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate180;
break;
case 270:
packing_rotation = ON_SubDFace::TextureCoordinateBits::PackingRotate270;
break;
}
m_texture_coordinate_bits |= packing_rotation;
m_texture_coordinate_bits |= domain_type_bits;
}
else
{
m_texture_coordinate_origin[0] = ON_DBL_QNAN;
m_texture_coordinate_origin[1] = ON_DBL_QNAN;
m_texture_coordinate_delta[0] = ON_DBL_QNAN;
m_texture_coordinate_delta[1] = ON_DBL_QNAN;
}
}
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
{
ON_ERROR("Invalid material_channel_index value.");
m_material_channel_index = 0;
}
}
int ON_SubDFace::MaterialChannelIndex() const
{
return (int)m_material_channel_index;
}
const bool ON_SubDFace::TextureDomainIsSet() const
{
return (ON_SubDTextureDomainType::Unset != TextureDomainType());
}
const ON_2dPoint ON_SubDFace::TextureDomainOrigin() const
{
return ON_2dPoint(m_texture_coordinate_origin);
}
const ON_2dVector ON_SubDFace::TextureDomainDelta() const
{
return ON_2dVector(m_texture_coordinate_delta);
}
ON_SubDTextureDomainType ON_SubDFace::TextureDomainType() const
{
return ON_SubD::TextureDomainTypeFromUnsigned((m_texture_coordinate_bits & TextureCoordinateBits::DomainTypeMask) >> 4);
}
unsigned int ON_SubDFace::TextureDomainRotationDegrees() const
{
unsigned int packing_rotation_degrees = 0;
switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask)
{
case ON_SubDFace::TextureCoordinateBits::PackingRotate90:
packing_rotation_degrees = 90;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate180:
packing_rotation_degrees = 180;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate270:
packing_rotation_degrees = 270;
break;
}
return packing_rotation_degrees;
}
double ON_SubDFace::TextureDomainRotationRadians() const
{
double x = 0.0;
switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask)
{
case ON_SubDFace::TextureCoordinateBits::PackingRotate90:
x = 1.0;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate180:
x = 2.0;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate270:
x = 3.0;
break;
}
return x * 0.5*ON_PI;
}
const ON_2dPoint ON_SubDFace::TextureDomainCorner(bool bGridOrder, bool bNormalized, int corner_index) const
{
if (false == TextureDomainIsSet())
return ON_2dPoint::Origin;
corner_index = ((corner_index % 4) + 4) % 4;
// now corner_index = 0,1,2 or 3.
if (bGridOrder)
{
if (2 == corner_index)
corner_index = 3;
else if (3 == corner_index)
corner_index = 2;
}
// now corner index is a counter-clockwise corner index
int packrot_dex = 0;
switch (m_texture_coordinate_bits & ON_SubDFace::TextureCoordinateBits::PackingRotateMask)
{
case ON_SubDFace::TextureCoordinateBits::PackingRotate90:
packrot_dex = 3;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate180:
packrot_dex = 2;
break;
case ON_SubDFace::TextureCoordinateBits::PackingRotate270:
packrot_dex = 1;
break;
}
corner_index = (corner_index + packrot_dex) % 4;
// now the packing rotation is taken into account.
ON_2dPoint corner = bNormalized ? ON_2dPoint::Origin : TextureDomainOrigin();
const ON_2dVector delta = bNormalized ? ON_2dVector(1.0, 1.0) : TextureDomainDelta();
switch (corner_index)
{
case 1:
corner.x += delta.x;
break;
case 2:
corner.x += delta.x;
corner.y += delta.y;
break;
case 3:
corner.y += delta.y;
break;
}
return corner;
}
#pragma endregion
#endif
//////////////////////////////////////////////////////////////////////////////
//
// ON_SubDMeshFragment - texture coordinates
//
#if 1
#pragma region ON_SubDMeshFragment - texture coordinates
bool ON_SubDMeshFragment::TextureCoordinatesExist() const
{
return (0 != (m_vertex_count_etc & ON_SubDMeshFragment::EtcTextureCoordinatesExistBit));
}
void ON_SubDMeshFragment::SetTextureCoordinatesExist(bool TextureCoordinatesExist) const
{
if (TextureCoordinatesExist)
m_vertex_count_etc |= ON_SubDMeshFragment::EtcTextureCoordinatesExistBit;
else
m_vertex_count_etc &= ~ON_SubDMeshFragment::EtcTextureCoordinatesExistBit;
}
unsigned int ON_SubDMeshFragment::TextureCoordinateCount() const
{
return (TextureCoordinatesExist() && 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
{
return (ON_SubDComponentLocation::ControlNet == subd_appearance) ? (TextureCoordinatesExist()?4U:0U) : TextureCoordinateCount();
}
bool ON_SubDMeshFragment::GetTextureCoordinteCorners(
bool bGridOrder,
ON_3dPoint texture_coordinate_coeners[4]
) const
{
// mutable double m_T_default_bbox[2][2];
if (nullptr != texture_coordinate_coeners)
{
int i;
texture_coordinate_coeners[0].x = m_ctrlnetT[0][0];
texture_coordinate_coeners[0].y = m_ctrlnetT[0][1];
texture_coordinate_coeners[0].z = m_ctrlnetT[0][2];
texture_coordinate_coeners[1].x = m_ctrlnetT[1][0];
texture_coordinate_coeners[1].y = m_ctrlnetT[1][1];
texture_coordinate_coeners[1].z = m_ctrlnetT[1][2];
i = bGridOrder ? 2 : 3;
texture_coordinate_coeners[i].x = m_ctrlnetT[2][0];
texture_coordinate_coeners[i].y = m_ctrlnetT[2][1];
texture_coordinate_coeners[i].z = m_ctrlnetT[2][2];
i = bGridOrder ? 3 : 2;
texture_coordinate_coeners[i].x = m_ctrlnetT[3][0];
texture_coordinate_coeners[i].y = m_ctrlnetT[3][1];
texture_coordinate_coeners[i].z = m_ctrlnetT[3][2];
return true;
}
return false;
}
static const ON_2dPoint InternalFragTextureCorner(
bool bGridOrder,
const ON_2dPoint Tcorners[4],
double s,
double t
)
{
return (1 - s)*(1 - t)*Tcorners[0] + s * (1 - t)*Tcorners[1] + (1 - s)*t*Tcorners[bGridOrder ? 2 : 3] + s * t*Tcorners[bGridOrder ? 3 : 2];
}
void ON_SubDMeshFragment::SetTextureCoordinateCorners(
bool bGridOrder,
const ON_2dPoint texture_coordinate_corners[4],
double s0,
double s1,
double t0,
double t1,
bool bSetTextureCoordinates
) const
{
const ON_2dPoint c[4] = {
InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t0),
InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s1,t0),
InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1),
InternalFragTextureCorner(bGridOrder,texture_coordinate_corners,s0,t1)
};
bGridOrder = true; // c[] is in grid order
SetTextureCoordinateCorners(bGridOrder, c, bSetTextureCoordinates);
}
void ON_SubDMeshFragment::SetTextureCoordinateCorners(
bool bGridOrder,
const ON_2dPoint texture_coordinate_corners[4],
bool bSetTextureCoordinates
) const
{
if (nullptr != texture_coordinate_corners)
{
// m_ctrlnetT[] is in grid order.
// lower left
m_ctrlnetT[0][0] = texture_coordinate_corners[0].x;
m_ctrlnetT[0][1] = texture_coordinate_corners[0].y;
m_ctrlnetT[0][2] = 0.0;
// lower right
m_ctrlnetT[1][0] = texture_coordinate_corners[1].x;
m_ctrlnetT[1][1] = texture_coordinate_corners[1].y;
m_ctrlnetT[1][2] = 0.0;
// upper left
int i = bGridOrder ? 2 : 3;
m_ctrlnetT[2][0] = texture_coordinate_corners[i].x;
m_ctrlnetT[2][1] = texture_coordinate_corners[i].y;
m_ctrlnetT[2][2] = 0.0;
// upper right
i = bGridOrder ? 3 : 2;
m_ctrlnetT[3][0] = texture_coordinate_corners[i].x;
m_ctrlnetT[3][1] = texture_coordinate_corners[i].y;
m_ctrlnetT[3][2] = 0.0;
if (bSetTextureCoordinates)
SetTextureCoordinatesFromCorners();
}
}
void ON_SubDMeshFragment::SetTextureCoordinateCorners(
bool bGridOrder,
const ON_3dPoint texture_coordinate_corners[4],
bool bSetTextureCoordinates
) const
{
if (nullptr != texture_coordinate_corners)
{
// m_ctrlnetT[] is in grid order.
// lower left
m_ctrlnetT[0][0] = texture_coordinate_corners[0].x;
m_ctrlnetT[0][1] = texture_coordinate_corners[0].y;
m_ctrlnetT[0][2] = texture_coordinate_corners[0].z;
// lower right
m_ctrlnetT[1][0] = texture_coordinate_corners[1].x;
m_ctrlnetT[1][1] = texture_coordinate_corners[1].y;
m_ctrlnetT[1][2] = texture_coordinate_corners[1].z;
// upper left
int i = bGridOrder ? 2 : 3;
m_ctrlnetT[2][0] = texture_coordinate_corners[i].x;
m_ctrlnetT[2][1] = texture_coordinate_corners[i].y;
m_ctrlnetT[2][2] = texture_coordinate_corners[i].z;
// upper right
i = bGridOrder ? 3 : 2;
m_ctrlnetT[3][0] = texture_coordinate_corners[i].x;
m_ctrlnetT[3][1] = texture_coordinate_corners[i].y;
m_ctrlnetT[3][2] = 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
{
const unsigned n = m_grid.SideSegmentCount();
if (n <= 0U || n > ON_SubDMeshFragment::MaximumSideSegmentCount)
return;
if (TextureCoordinateCapacity() < n*n)
return;
SetTextureCoordinatesExist(true);
double * T = m_T;
const double d = (double)n;
double s, t;
ON_3dPoint tc;
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 };
tc = 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]
);
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;
SetTextureCoordinatesExist(true);
return true;
}
#pragma endregion
#endif