Changeset View
Changeset View
Standalone View
Standalone View
extern/opennurbs/opennurbs_subd_texture.cpp
- This file was added.
| #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_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 if (false == mapping.Evaluate(ON_3dPoint(P), ON_3dVector(N), &tc)) | |||||
| 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; | |||||
| } | |||||
| } | |||||
| 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 | |||||