mirror of
https://github.com/mcneel/opennurbs.git
synced 2026-03-01 03:26:09 +08:00
2423 lines
66 KiB
C++
2423 lines
66 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"
|
|
|
|
static bool Internal_WriteDouble3(
|
|
const double x[3],
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!archive.WriteDouble(3, x))
|
|
break;
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool Internal_ReadDouble3(
|
|
ON_BinaryArchive& archive,
|
|
double x[3]
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!archive.ReadDouble(3, x))
|
|
break;
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
enum : unsigned char
|
|
{
|
|
ON_SubDComponentArchiveAnonymousChunkMark = 254U,
|
|
ON_SubDComponentArchiveAdditionEndMark = 255U
|
|
};
|
|
|
|
static bool Internal_ReadComponentAdditionSize(ON_BinaryArchive& archive, unsigned char valid_sz, unsigned char* sz)
|
|
{
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
if (0 == valid_sz)
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
if (false == archive.ReadChar(sz))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
if ( 0 != *sz && valid_sz != *sz && ON_SubDComponentArchiveAdditionEndMark != *sz )
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
return true;
|
|
}
|
|
|
|
static bool Internal_WriteComponentAdditionSize(bool bHaveAddition, ON_BinaryArchive& archive, unsigned char sz)
|
|
{
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
if (0 == sz)
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
if (false == bHaveAddition)
|
|
sz = 0;
|
|
if (false == archive.WriteChar(sz))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
return true;
|
|
}
|
|
|
|
static bool Internal_FinishReadingComponentAdditions(ON_BinaryArchive& archive)
|
|
{
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
unsigned char sz = 1;
|
|
if ( false == archive.ReadChar(&sz))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
for (;;)
|
|
{
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true;
|
|
if (ON_SubDComponentArchiveAnonymousChunkMark == sz)
|
|
{
|
|
// skip an addition a future version added as an anonymous chunk
|
|
int v = 0;
|
|
if (false == archive.BeginRead3dmAnonymousChunk(&v))
|
|
break;
|
|
if (false == archive.EndRead3dmChunk())
|
|
break;
|
|
}
|
|
else if ( sz > 0 )
|
|
{
|
|
// skip an addition a future version added as a fixed number of bytes
|
|
// use archive.ReadByte(sz,buffer) instead of archive.SeekForward(sz) so CRC is properly calculated.
|
|
char buffer[256];
|
|
if (false == archive.ReadByte(sz,buffer))
|
|
break;
|
|
}
|
|
sz = 0;
|
|
if (false == archive.ReadChar(&sz))
|
|
break;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool Internal_FinishWritingComponentAdditions(ON_BinaryArchive& archive)
|
|
{
|
|
if (archive.Archive3dmVersion() < 70)
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
const unsigned char sz = ON_SubDComponentArchiveAdditionEndMark;
|
|
return archive.WriteChar(sz);
|
|
}
|
|
|
|
|
|
static bool Internal_WriteArchiveIdAndFlags(
|
|
unsigned int archive_id,
|
|
ON__UINT_PTR ptr_flags,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
if (!archive.WriteInt(archive_id))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
unsigned char flags = (unsigned char)ON_SUBD_COMPONENT_FLAGS(ptr_flags);
|
|
if (!archive.WriteChar(flags))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
return true;
|
|
}
|
|
|
|
static bool Internal_ReadArchiveIdAndFlagsIntoComponentPtr(
|
|
ON_BinaryArchive& archive,
|
|
ON__UINT_PTR& element_ptr
|
|
)
|
|
{
|
|
element_ptr = 0;
|
|
unsigned int archive_id = 0;
|
|
if (!archive.ReadInt(&archive_id))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
unsigned char flags = 0;
|
|
if (!archive.ReadChar(&flags))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
element_ptr = archive_id;
|
|
element_ptr *= (ON_SUBD_COMPONENT_FLAGS_MASK + 1);
|
|
element_ptr += (flags & ON_SUBD_COMPONENT_FLAGS_MASK);
|
|
return true;
|
|
}
|
|
|
|
static bool Internal_WritesSymmetrySetNext(
|
|
const ON_SubDComponentBase& c,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
const ON_SubDComponentPtr symmetry_set_next = ON_SubDArchiveIdMap::SymmetrySetNextForExperts(c);
|
|
const ON_SubDComponentBase* next_c = symmetry_set_next.ComponentBase();
|
|
const unsigned archive_id = (nullptr != next_c) ? next_c->ArchiveId() : 0;
|
|
return Internal_WriteArchiveIdAndFlags(archive_id, symmetry_set_next.m_ptr, archive);
|
|
}
|
|
|
|
|
|
static bool Internal_ReadSymmetrySetNext(
|
|
ON_BinaryArchive& archive,
|
|
const ON_SubDComponentBase& c
|
|
)
|
|
{
|
|
return Internal_ReadArchiveIdAndFlagsIntoComponentPtr(archive, ON_SubDArchiveIdMap::SymmetrySetNextForExperts(c).m_ptr);
|
|
}
|
|
|
|
static bool WriteBase(
|
|
const ON_SubDComponentBase* base,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned int archive_id = base->ArchiveId();
|
|
unsigned int id = base->m_id;
|
|
unsigned short level = (unsigned short)base->SubdivisionLevel();
|
|
if (!archive.WriteInt(archive_id))
|
|
break;
|
|
if (!archive.WriteInt(id))
|
|
break;
|
|
if (!archive.WriteShort(level))
|
|
break;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
// version 6 3dm files
|
|
double P[3];
|
|
const bool bHaveP = base->GetSavedSubdivisionPoint(P);
|
|
|
|
unsigned char cP = bHaveP ? 4U : 0U;
|
|
if (!archive.WriteChar(cP))
|
|
break;
|
|
if (0 != cP)
|
|
{
|
|
if (!Internal_WriteDouble3(P, archive))
|
|
break;
|
|
}
|
|
|
|
unsigned char deprecated_and_never_used_zero = 0U;
|
|
if (!archive.WriteChar(deprecated_and_never_used_zero))
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
// version 7 3dm files and later
|
|
|
|
// never used displacement
|
|
if ( false == Internal_WriteComponentAdditionSize(false,archive,24) )
|
|
break;
|
|
|
|
// 4 byte group id addition
|
|
const bool bWriteGroupId = base->m_group_id > 0;
|
|
if (false == Internal_WriteComponentAdditionSize(bWriteGroupId, archive, 4))
|
|
break;
|
|
if (bWriteGroupId)
|
|
{
|
|
if (!archive.WriteInt(base->m_group_id))
|
|
break;
|
|
}
|
|
|
|
// 5 byte symmetry set next addition Dec 2020 Rhino 7.2 and later
|
|
// 5 bytes = unsigned archive id + char flags
|
|
const bool bWriteSymmetrySetNext = base->InSymmetrySet();
|
|
if (false == Internal_WriteComponentAdditionSize(bWriteSymmetrySetNext, archive, 5))
|
|
break;
|
|
if (bWriteSymmetrySetNext)
|
|
{
|
|
if (!Internal_WritesSymmetrySetNext(*base,archive))
|
|
break;
|
|
}
|
|
|
|
return Internal_FinishWritingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool ReadBase(
|
|
ON_BinaryArchive& archive,
|
|
ON_SubDComponentBase& base
|
|
)
|
|
{
|
|
unsigned int archive_id = 0;
|
|
unsigned int id = 0;
|
|
unsigned short level = 0;
|
|
for (;;)
|
|
{
|
|
if (!archive.ReadInt(&archive_id))
|
|
break;
|
|
if (!archive.ReadInt(&id))
|
|
break;
|
|
if (!archive.ReadShort(&level))
|
|
break;
|
|
|
|
base.m_id = id;
|
|
base.SetArchiveId(archive_id);
|
|
base.SetSubdivisionLevel(level);
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
unsigned char cP = 0U;
|
|
unsigned char deprecated_and_never_used_char = 0U;
|
|
double P[3];
|
|
|
|
if (!archive.ReadChar(&cP))
|
|
break;
|
|
if (0 != cP)
|
|
{
|
|
if (!Internal_ReadDouble3(archive, P))
|
|
break;
|
|
}
|
|
|
|
if (!archive.ReadChar(&deprecated_and_never_used_char))
|
|
break;
|
|
if (0 != deprecated_and_never_used_char)
|
|
{
|
|
double deprecated_and_never_used_V[3];
|
|
if (!Internal_ReadDouble3(archive, deprecated_and_never_used_V))
|
|
break;
|
|
}
|
|
if (4 == cP)
|
|
base.SetSavedSubdivisionPoint(P);
|
|
return true;
|
|
}
|
|
|
|
// read additions
|
|
unsigned char sz;
|
|
|
|
// 24 byte displacement addition
|
|
// This addition was a deprecated prototype that was never used in commercial Rhino.
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 24, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
double deprecated_and_never_used_V[3] = {};
|
|
if (!archive.ReadDouble(3, deprecated_and_never_used_V))
|
|
break;
|
|
}
|
|
|
|
// 4 byte group id addition
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
if (!archive.ReadInt(&base.m_group_id))
|
|
break;
|
|
}
|
|
|
|
// 5 bytes symmetry set next addition Dec 2020 Rhino 7.2 and later
|
|
// 5 bytes = unsigned archive id + char flags
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 5, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
if (!Internal_ReadSymmetrySetNext(archive,base))
|
|
break;
|
|
}
|
|
|
|
|
|
return Internal_FinishReadingComponentAdditions(archive);
|
|
}
|
|
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
// Dale Lear 2024-12-18 Rhino 8.x
|
|
// Due to a bug in v7, the saved limit points were read but nuever used.
|
|
// So I'm simply not writing the limit points anymore.
|
|
//static bool Internal_WriteSavedLimitPointList(
|
|
// unsigned int vertex_face_count,
|
|
// bool bHaveLimitPoint,
|
|
// const ON_SubDSectorSurfacePoint& limit_point,
|
|
// ON_BinaryArchive& archive
|
|
// )
|
|
//{
|
|
// unsigned int limit_point_count = 0;
|
|
// const ON_SubDSectorSurfacePoint* p;
|
|
//
|
|
// if (bHaveLimitPoint)
|
|
// {
|
|
// for (p = &limit_point; nullptr != p && limit_point_count <= vertex_face_count; p = p->m_next_sector_limit_point)
|
|
// {
|
|
// if (!ON_IsValid(p->m_limitP[0]))
|
|
// break;
|
|
// if (limit_point_count > 0 && nullptr == p->m_sector_face)
|
|
// break;
|
|
// limit_point_count++;
|
|
// }
|
|
// if (limit_point_count > vertex_face_count || nullptr != p)
|
|
// limit_point_count = 0;
|
|
//
|
|
// if (limit_point_count > vertex_face_count)
|
|
// limit_point_count = 0;
|
|
// }
|
|
// if (0 == limit_point_count)
|
|
// bHaveLimitPoint = false;
|
|
//
|
|
// for (;;)
|
|
// {
|
|
// unsigned char c = bHaveLimitPoint ? 4 : 0;
|
|
// if (!archive.WriteChar(c))
|
|
// break;
|
|
//
|
|
// if (0 == c)
|
|
// return true;
|
|
//
|
|
// if (!archive.WriteInt(limit_point_count))
|
|
// break;
|
|
//
|
|
// p = &limit_point;
|
|
// for (unsigned int i = 0; i < limit_point_count; i++, p = p->m_next_sector_limit_point )
|
|
// {
|
|
// if (!Internal_WriteDouble3(limit_point.m_limitP, archive))
|
|
// break;
|
|
// if (!Internal_WriteDouble3(limit_point.m_limitT1, archive))
|
|
// break;
|
|
// if (!Internal_WriteDouble3(limit_point.m_limitT2, archive))
|
|
// break;
|
|
// if (!Internal_WriteDouble3(limit_point.m_limitN, archive))
|
|
// break;
|
|
// if (!Internal_WriteArchiveIdAndFlags(limit_point.m_sector_face ? limit_point.m_sector_face->ArchiveId() : 0, 0, archive))
|
|
// break;
|
|
// }
|
|
// return true;
|
|
// }
|
|
// return ON_SUBD_RETURN_ERROR(false);
|
|
//}
|
|
|
|
// Dale Lear 2024-12-18 Rhino 8.x
|
|
// Due to a bug (missing & limit_points so the array was by value rather than by reference)
|
|
// on that's been in this code since v7, the saved
|
|
// limit points were read but never used.
|
|
// This function reads and discards the information that was never used
|
|
// but which exists in millions of older 3dm files.
|
|
// It appears recalculating the limit point information has been fast enough
|
|
// for many years, so I'm making it clear that this is legacy code.
|
|
static bool Internal_IgnoreSavedLimitPointList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned int vertex_face_count
|
|
//ON_SimpleArray< ON_SubDSectorSurfacePoint > limit_points // NOTE MISSING & means this array was by value and not by reference
|
|
)
|
|
{
|
|
//limit_points.SetCount(0);
|
|
|
|
for (;;)
|
|
{
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
|
|
if ( 0 == c)
|
|
return true;
|
|
|
|
unsigned int limit_point_count = 0;
|
|
if (!archive.ReadInt(&limit_point_count))
|
|
break;
|
|
|
|
if ( 0 == limit_point_count )
|
|
break;
|
|
|
|
if (limit_point_count > vertex_face_count)
|
|
break;
|
|
|
|
//limit_points.Reserve(limit_point_count);
|
|
unsigned int i = 0;
|
|
for ( /*empty init*/; i < limit_point_count; i++)
|
|
{
|
|
ON_SubDSectorSurfacePoint limit_point = ON_SubDSectorSurfacePoint::Unset;
|
|
if (!Internal_ReadDouble3(archive,limit_point.m_limitP))
|
|
break;
|
|
if (!Internal_ReadDouble3(archive,limit_point.m_limitT1))
|
|
break;
|
|
if (!Internal_ReadDouble3(archive,limit_point.m_limitT2))
|
|
break;
|
|
if (!Internal_ReadDouble3(archive,limit_point.m_limitN))
|
|
break;
|
|
ON_SubDFacePtr fptr = ON_SubDFacePtr::Null;
|
|
if (!Internal_ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr.m_ptr))
|
|
break;
|
|
//limit_points.Append(limit_point);
|
|
}
|
|
|
|
if (limit_point_count != i )
|
|
break;
|
|
|
|
//if (4 != c)
|
|
// limit_points.SetCount(0);
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
static bool Internal_WriteVertexList(
|
|
unsigned short vertex_count,
|
|
const ON_SubDVertex*const* vertex,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_count,vertex,0,nullptr);
|
|
|
|
if (!archive.WriteShort(vertex_count))
|
|
break;
|
|
|
|
if ( 0 == vertex_count )
|
|
return true;
|
|
|
|
const ON__UINT_PTR ptr_flags = 0; // for future use
|
|
unsigned short i = 0;
|
|
for (i = 0; i < vertex_count; i++)
|
|
{
|
|
const ON_SubDVertex* v = vertex[i];
|
|
if (!Internal_WriteArchiveIdAndFlags((nullptr != v) ? v->ArchiveId() : 0, ptr_flags, archive))
|
|
break;
|
|
}
|
|
if ( i < vertex_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool Internal_ReadVertexList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& vertex_count,
|
|
unsigned short vertex_capacity,
|
|
ON_SubDVertex* vertex[]
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_vertex_count = 0;
|
|
if (!archive.ReadShort(&archive_vertex_count))
|
|
break;
|
|
|
|
if (archive_vertex_count != vertex_count)
|
|
{
|
|
ON_ERROR("Archive vertex count != expected vertex count.");
|
|
if ( archive_vertex_count < vertex_count)
|
|
vertex_count = archive_vertex_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(vertex_count,vertex_capacity,vertex,0,nullptr);
|
|
|
|
unsigned short i = 0;
|
|
for (i = 0; i < vertex_count; i++)
|
|
{
|
|
ON__UINT_PTR vptr = 0;
|
|
if (!Internal_ReadArchiveIdAndFlagsIntoComponentPtr(archive,vptr))
|
|
break;
|
|
vertex[i] = (ON_SubDVertex*)vptr;
|
|
}
|
|
if ( i < vertex_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool Internal_WriteEdgePtrList(
|
|
unsigned short edge_count,
|
|
unsigned short edgeN_capacity,
|
|
const ON_SubDEdgePtr* edgeN,
|
|
unsigned short edgeX_capacity,
|
|
const ON_SubDEdgePtr* edgeX,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX);
|
|
|
|
if (!archive.WriteShort(edge_count))
|
|
break;
|
|
|
|
if ( 0 == edge_count )
|
|
return true;
|
|
|
|
const ON_SubDEdgePtr* eptr = edgeN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < edge_count; i++, eptr++)
|
|
{
|
|
if ( i == edgeN_capacity)
|
|
eptr = edgeX;
|
|
const ON_SubDEdge* edge = ON_SUBD_EDGE_POINTER(eptr->m_ptr);
|
|
if (!Internal_WriteArchiveIdAndFlags((nullptr != edge) ? edge->ArchiveId() : 0,eptr->m_ptr,archive))
|
|
break;
|
|
}
|
|
if ( i < edge_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool Internal_ReadEdgePtrList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& edge_count,
|
|
unsigned short edgeN_capacity,
|
|
ON_SubDEdgePtr* edgeN,
|
|
unsigned short edgeX_capacity,
|
|
ON_SubDEdgePtr* edgeX
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_edge_count = 0;
|
|
if (!archive.ReadShort(&archive_edge_count))
|
|
break;
|
|
|
|
if (archive_edge_count != edge_count)
|
|
{
|
|
ON_ERROR("Archive edge count != expected edge count.");
|
|
if ( archive_edge_count < edge_count)
|
|
edge_count = archive_edge_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(edge_count,edgeN_capacity,edgeN,edgeX_capacity,edgeX);
|
|
|
|
|
|
ON_SubDEdgePtr* eptr = edgeN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < edge_count; i++, eptr++)
|
|
{
|
|
if ( i == edgeN_capacity)
|
|
eptr = edgeX;
|
|
if (!Internal_ReadArchiveIdAndFlagsIntoComponentPtr(archive,eptr->m_ptr))
|
|
break;
|
|
}
|
|
if ( i < edge_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool Internal_WriteFacePtrList(
|
|
unsigned short face_count,
|
|
size_t faceN_capacity,
|
|
const ON_SubDFacePtr* faceN,
|
|
unsigned short faceX_capacity,
|
|
const ON_SubDFacePtr* faceX,
|
|
ON_BinaryArchive& archive
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX);
|
|
|
|
if (!archive.WriteShort(face_count))
|
|
break;
|
|
|
|
if ( 0 == face_count )
|
|
return true;
|
|
|
|
const ON_SubDFacePtr* fptr = faceN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < face_count; i++, fptr++)
|
|
{
|
|
if ( i == faceN_capacity)
|
|
fptr = faceX;
|
|
const ON_SubDFace* face = ON_SUBD_FACE_POINTER(fptr->m_ptr);
|
|
if (!Internal_WriteArchiveIdAndFlags((nullptr != face) ? face->ArchiveId() : 0,fptr->m_ptr,archive))
|
|
break;
|
|
}
|
|
if ( i < face_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
static bool Internal_ReadFacePtrList(
|
|
ON_BinaryArchive& archive,
|
|
unsigned short& face_count,
|
|
unsigned short faceN_capacity,
|
|
ON_SubDFacePtr* faceN,
|
|
unsigned short faceX_capacity,
|
|
ON_SubDFacePtr* faceX
|
|
)
|
|
{
|
|
for (;;)
|
|
{
|
|
unsigned short archive_face_count = 0;
|
|
if (!archive.ReadShort(&archive_face_count))
|
|
break;
|
|
|
|
if (archive_face_count != face_count)
|
|
{
|
|
ON_ERROR("Archive face count != expected face count.");
|
|
if ( archive_face_count < face_count)
|
|
face_count = archive_face_count;
|
|
}
|
|
|
|
ON_SubDArchiveIdMap::ValidateArrayCounts(face_count,faceN_capacity,faceN,faceX_capacity,faceX);
|
|
|
|
ON_SubDFacePtr* fptr = faceN;
|
|
unsigned short i = 0;
|
|
for (i = 0; i < face_count; i++, fptr++)
|
|
{
|
|
if ( i == faceN_capacity)
|
|
fptr = faceX;
|
|
if (!Internal_ReadArchiveIdAndFlagsIntoComponentPtr(archive,fptr->m_ptr))
|
|
break;
|
|
}
|
|
if ( i < face_count )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
ON_SubDComponentPtr& ON_SubDArchiveIdMap::SymmetrySetNextForExperts(const ON_SubDComponentBase& c)
|
|
{
|
|
return const_cast<ON_SubDComponentPtr&>(c.m_symmetry_set_next);
|
|
}
|
|
|
|
bool ON_SubDVertex::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)m_vertex_tag))
|
|
break;
|
|
if (!Internal_WriteDouble3(m_P,archive))
|
|
break;
|
|
if (!archive.WriteShort(m_edge_count))
|
|
break;
|
|
if (!archive.WriteShort(m_face_count))
|
|
break;
|
|
|
|
// Dale Lear 2024-12-18
|
|
// Due to a bug in ON_SubDVertex::Read(), the limit points written
|
|
// by Internal_WriteSavedLimitPointList() were read but never used.
|
|
// It appears that recalculating the limit points every time the
|
|
// file is read is working out just fine. So writing a zero byte
|
|
// here means that old code will be able to read new files and
|
|
// we wont' waste time and disk space saving limit points.
|
|
if (!archive.WriteChar((unsigned char)0))
|
|
break;
|
|
//if (!Internal_WriteSavedLimitPointList(m_face_count, this->SurfacePointIsSet(), m_limit_point, archive))
|
|
// break;
|
|
|
|
if (!Internal_WriteEdgePtrList(m_edge_count,m_edge_capacity,m_edges,0,nullptr, archive))
|
|
break;
|
|
if (!Internal_WriteFacePtrList(m_face_count,m_face_capacity,(const ON_SubDFacePtr*)m_faces,0,nullptr, archive))
|
|
break;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
// mark end with a 0 byte
|
|
if (!archive.WriteChar((unsigned char)0U))
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
|
|
return Internal_FinishWritingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDVertex::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDVertex*& vertex
|
|
)
|
|
{
|
|
vertex = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned char vertex_tag = 0;
|
|
//unsigned char vertex_edge_order = 0;
|
|
//unsigned char vertex_facet_type = 0;
|
|
double P[3];
|
|
unsigned short edge_count = 0;
|
|
unsigned short face_count = 0;
|
|
|
|
//ON_SimpleArray<ON_SubDSectorSurfacePoint> limit_points;
|
|
|
|
if (!ReadBase(archive,base))
|
|
break;
|
|
if (!archive.ReadChar(&vertex_tag))
|
|
break;
|
|
if (!Internal_ReadDouble3(archive,P))
|
|
break;
|
|
if (!archive.ReadShort(&edge_count))
|
|
break;
|
|
if (!archive.ReadShort(&face_count))
|
|
break;
|
|
|
|
if (!Internal_IgnoreSavedLimitPointList(archive, face_count))
|
|
break;
|
|
|
|
ON_SubDVertex* v = subdimple->AllocateVertex(
|
|
base.m_id, // serialization must preserve ON_SubDVertex.m_id
|
|
ON_SubD::VertexTagFromUnsigned(vertex_tag),
|
|
base.SubdivisionLevel(),
|
|
P,
|
|
edge_count,
|
|
face_count
|
|
);
|
|
|
|
if ( nullptr == v )
|
|
break;
|
|
|
|
v->ON_SubDComponentBase::operator=(base);
|
|
|
|
if (!Internal_ReadEdgePtrList(archive,edge_count,v->m_edge_capacity,v->m_edges,0,nullptr))
|
|
break;
|
|
v->m_edge_count = edge_count;
|
|
|
|
if (!Internal_ReadFacePtrList(archive,face_count,v->m_face_capacity,(ON_SubDFacePtr*)v->m_faces,0,nullptr))
|
|
break;
|
|
v->m_face_count = face_count;
|
|
|
|
//for (unsigned int i = 0; i < limit_points.UnsignedCount(); i++)
|
|
//{
|
|
// ON_SubDSectorSurfacePoint limit_point = limit_points[i];
|
|
// limit_point.m_next_sector_limit_point = (const ON_SubDSectorSurfacePoint*)1U; // skips checks
|
|
// if (false == v->SetSavedSurfacePoint( true, limit_point))
|
|
// {
|
|
// v->ClearSavedSurfacePoints();
|
|
// break;
|
|
// }
|
|
//}
|
|
|
|
vertex = v;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
unsigned char sz = 1;
|
|
if (!archive.ReadChar(&sz) || 0 != sz)
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
// read additions
|
|
return Internal_FinishReadingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
|
|
bool ON_SubDEdge::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)m_edge_tag))
|
|
break;
|
|
if (!archive.WriteShort(m_face_count))
|
|
break;
|
|
if (!archive.WriteDouble(2,m_sector_coefficient))
|
|
break;
|
|
if (!archive.WriteDouble(this->m_sharpness[0]))
|
|
break;
|
|
if (!Internal_WriteVertexList(2, m_vertex, archive))
|
|
break;
|
|
if (!Internal_WriteFacePtrList(m_face_count,sizeof(m_face2)/sizeof(m_face2[0]),m_face2,m_facex_capacity,m_facex, archive))
|
|
break;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
// mark end with a 0 byte
|
|
if (!archive.WriteChar((unsigned char)0U))
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
if (archive.Archive3dmVersion() >= 80)
|
|
{
|
|
// 2nd sharpness added Jan 2023 to v8 files.
|
|
if (!archive.WriteChar((unsigned char)8U))
|
|
break;
|
|
if (!archive.WriteDouble(this->m_sharpness[1]))
|
|
break;
|
|
}
|
|
|
|
return Internal_FinishWritingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDEdge::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDEdge*& edge
|
|
)
|
|
{
|
|
edge = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned char edge_tag = 0;
|
|
unsigned short face_count = 0;
|
|
double sector_coefficient[2] = { 0 };
|
|
double sharpness0 = 0.0;
|
|
|
|
if (!ReadBase(archive,base))
|
|
break;
|
|
if (!archive.ReadChar(&edge_tag))
|
|
break;
|
|
if (!archive.ReadShort(&face_count))
|
|
break;
|
|
if (!archive.ReadDouble(2,sector_coefficient))
|
|
break;
|
|
if (!archive.ReadDouble(&sharpness0))
|
|
break;
|
|
|
|
ON_SubDVertex* v[2] = { 0 };
|
|
unsigned short vertex_count = 2;
|
|
if (!Internal_ReadVertexList(archive, vertex_count, 2, v))
|
|
break;
|
|
|
|
ON_SubDEdge* e = subdimple->AllocateEdge(
|
|
base.m_id, // serialization must preserve ON_SubDEdge.m_id
|
|
ON_SubD::EdgeTagFromUnsigned(edge_tag),
|
|
base.SubdivisionLevel(),
|
|
face_count
|
|
);
|
|
|
|
if ( nullptr == e )
|
|
break;
|
|
|
|
e->ON_SubDComponentBase::operator=(base);
|
|
|
|
for ( unsigned short evi = 0; evi < 2 && evi < vertex_count; evi++ )
|
|
e->m_vertex[evi] = v[evi];
|
|
|
|
e->m_sector_coefficient[0] = sector_coefficient[0];
|
|
e->m_sector_coefficient[1] = sector_coefficient[1];
|
|
|
|
if (!Internal_ReadFacePtrList(archive,face_count,sizeof(e->m_face2)/sizeof(e->m_face2[0]),e->m_face2,e->m_facex_capacity,e->m_facex))
|
|
break;
|
|
e->m_face_count = face_count;
|
|
|
|
edge = e;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
unsigned char sz;
|
|
if (false == archive.ReadChar(&sz) || 0 != sz)
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
if (archive.Archive3dmVersion() >= 80)
|
|
{
|
|
unsigned char sz;
|
|
if (false == archive.ReadChar(&sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true;
|
|
if (8 != sz)
|
|
break; // error
|
|
|
|
// 2nd sharpness added Jan 2023 to v8 files.
|
|
double sharpness1 = 0.0;
|
|
if (false == archive.ReadDouble(&sharpness1))
|
|
break;
|
|
if (e->IsSmooth())
|
|
e->SetSharpnessForExperts(ON_SubDEdgeSharpness::FromInterval(sharpness0,sharpness1));
|
|
}
|
|
|
|
return Internal_FinishReadingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDFace::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!WriteBase(this,archive))
|
|
break;
|
|
if (!archive.WriteInt(m_level_zero_face_id))
|
|
break;
|
|
|
|
// OBSOLETE parent face id
|
|
const int obsolete_parent_face_id = 0;
|
|
if (!archive.WriteInt(obsolete_parent_face_id))
|
|
break;
|
|
|
|
if (!archive.WriteShort(m_edge_count))
|
|
break;
|
|
if (!Internal_WriteEdgePtrList(m_edge_count,sizeof(m_edge4)/sizeof(m_edge4[0]),m_edge4,m_edgex_capacity,m_edgex, archive))
|
|
break;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
unsigned char sz = 0;
|
|
if (!archive.WriteChar(sz))
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
// write 34 byte texture domain
|
|
const bool bWritePackRect = PackRectIsSet();
|
|
if (false == Internal_WriteComponentAdditionSize(bWritePackRect, archive, 34))
|
|
break;
|
|
if (bWritePackRect)
|
|
{
|
|
const unsigned char obsolete_per_face_texture_coordinate_type = ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(ON_SubDTextureCoordinateType::Packed);
|
|
if (!archive.WriteChar(obsolete_per_face_texture_coordinate_type))
|
|
break;
|
|
|
|
const unsigned packing_rot = PackRectRotationDegrees();
|
|
const unsigned char packing_rot_dex = (unsigned char)(packing_rot/90U);
|
|
if (!archive.WriteChar(packing_rot_dex))
|
|
break;
|
|
const ON_2dPoint pack_rect_origin = PackRectOrigin();
|
|
if (!archive.WriteDouble(2, &pack_rect_origin.x))
|
|
break;
|
|
const ON_2dVector pack_rect_size = PackRectSize();
|
|
if (!archive.WriteDouble(2, &pack_rect_size.x))
|
|
break;
|
|
}
|
|
|
|
// 4 byte render material channel index
|
|
const int material_channel_index = MaterialChannelIndex();
|
|
const bool bWriteMaterialChannelIndex = (material_channel_index > 0 && material_channel_index <= ON_Material::MaximumMaterialChannelIndex);
|
|
if (false == Internal_WriteComponentAdditionSize(bWriteMaterialChannelIndex, archive, 4))
|
|
break;
|
|
if (bWriteMaterialChannelIndex)
|
|
{
|
|
if (!archive.WriteInt(material_channel_index))
|
|
break;
|
|
}
|
|
|
|
// 4 byte per face color
|
|
const ON_Color per_face_color = PerFaceColor();
|
|
const bool bWritePerFaceColor = (ON_Color::UnsetColor != per_face_color);
|
|
if (false == Internal_WriteComponentAdditionSize(bWritePerFaceColor, archive, 4))
|
|
break;
|
|
if (bWritePerFaceColor)
|
|
{
|
|
if (!archive.WriteColor(per_face_color))
|
|
break;
|
|
}
|
|
|
|
// PackId
|
|
const unsigned pack_id = PackId();
|
|
const bool bPackId = (pack_id > 0U);
|
|
if (false == Internal_WriteComponentAdditionSize(bPackId, archive, 4))
|
|
break;
|
|
if (bPackId)
|
|
{
|
|
if (!archive.WriteInt(pack_id))
|
|
break;
|
|
}
|
|
|
|
// Custom texture coordinates
|
|
const bool bTexturePoints = this->TexturePointsAreSet();
|
|
if (false == Internal_WriteComponentAdditionSize(bTexturePoints, archive, 4))
|
|
break;
|
|
if (bTexturePoints)
|
|
{
|
|
// The number of texture points varies from face to face (EdgeCount()).
|
|
// The maximum size that can be saved in a single component addition is 253 bytes.
|
|
// So, texture points are saved in 10 point chunks ( 10*sizeof(ON_3dPoint) = 240 <= 243.
|
|
const unsigned texture_point_count = this->EdgeCount();
|
|
const unsigned ten_point_chunk_count = texture_point_count / 10;
|
|
const unsigned left_over_points_count = texture_point_count % 10;
|
|
const ON_3dPoint* a = this->m_texture_points;
|
|
const unsigned char sizeof_ten_points = (unsigned char)(10 * sizeof(ON_3dPoint)); // sizeof_ten_points = 240 <= 243
|
|
bool bContinue = archive.WriteInt(ten_point_chunk_count);
|
|
if ( false == bContinue)
|
|
break;
|
|
|
|
// write the 10 point chunks
|
|
for (unsigned i = 0; bContinue && i < ten_point_chunk_count; ++i)
|
|
{
|
|
bContinue = Internal_WriteComponentAdditionSize(true, archive, sizeof_ten_points);
|
|
bContinue = bContinue && archive.WriteDouble(30, (const double*)a);
|
|
a += 10;
|
|
}
|
|
|
|
// write the "left over" points
|
|
if (bContinue && left_over_points_count > 0)
|
|
{
|
|
const unsigned char sizeof_left_over_points = (unsigned char)(left_over_points_count * sizeof(ON_3dPoint)); // sizeof_left_over_points < 240
|
|
bContinue = Internal_WriteComponentAdditionSize(true, archive, sizeof_left_over_points);
|
|
bContinue = bContinue && archive.WriteDouble(3* left_over_points_count, (const double*)a);
|
|
}
|
|
if (false == bContinue)
|
|
break;
|
|
}
|
|
|
|
return Internal_FinishWritingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDFace::Read(
|
|
class ON_BinaryArchive& archive,
|
|
class ON_SubD& subd,
|
|
class ON_SubDFace*& face
|
|
)
|
|
{
|
|
face = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
ON_SubDimple* subdimple = const_cast<ON_SubDimple*>(subd.SubDimple());
|
|
if (nullptr == subdimple)
|
|
break;
|
|
|
|
ON_SubDComponentBase base = ON_SubDComponentBase::Unset;
|
|
unsigned int level_zero_face_id = 0;
|
|
unsigned int obsolete_parent_face_id = 0;
|
|
unsigned short edge_count = 0;
|
|
|
|
if (!ReadBase(archive, base))
|
|
break;
|
|
if (!archive.ReadInt(&level_zero_face_id))
|
|
break;
|
|
if (!archive.ReadInt(&obsolete_parent_face_id))
|
|
break;
|
|
if (!archive.ReadShort(&edge_count))
|
|
break;
|
|
|
|
ON_SubDFace* f = subdimple->AllocateFace(
|
|
base.m_id, // serialization must preserve ON_SubDFace.m_id
|
|
base.SubdivisionLevel(),
|
|
edge_count
|
|
);
|
|
|
|
if (nullptr == f)
|
|
break;
|
|
|
|
f->ON_SubDComponentBase::operator=(base);
|
|
|
|
f->m_level_zero_face_id = level_zero_face_id;
|
|
|
|
if (!Internal_ReadEdgePtrList(archive, edge_count, sizeof(f->m_edge4) / sizeof(f->m_edge4[0]), f->m_edge4, f->m_edgex_capacity, f->m_edgex))
|
|
break;
|
|
f->m_edge_count = edge_count;
|
|
|
|
face = f;
|
|
|
|
if (archive.Archive3dmVersion() < 70)
|
|
{
|
|
unsigned char sz;
|
|
if (false == archive.ReadChar(&sz) || 0 != sz)
|
|
break;
|
|
return true;
|
|
}
|
|
|
|
// read additions
|
|
unsigned char sz;
|
|
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 34, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
// 34 bytes of texture domain information
|
|
unsigned char obsolete_per_face_texture_coordinate_type = 0;
|
|
if (!archive.ReadChar(&obsolete_per_face_texture_coordinate_type))
|
|
break;
|
|
unsigned char packing_rot_dex = 0;
|
|
if (!archive.ReadChar(&packing_rot_dex))
|
|
break;
|
|
const unsigned packing_rot = ((unsigned int)packing_rot_dex) * 90U;
|
|
ON_2dPoint pack_rect_origin(ON_2dPoint::Origin);
|
|
if (!archive.ReadDouble(2, &pack_rect_origin.x))
|
|
break;
|
|
ON_2dVector pack_rect_delta(ON_2dVector::ZeroVector);
|
|
if (!archive.ReadDouble(2, &pack_rect_delta.x))
|
|
break;
|
|
|
|
if (ON_SubDFace::IsValidPackRect(pack_rect_origin, pack_rect_delta, packing_rot) )
|
|
f->SetPackRectForExperts(pack_rect_origin, pack_rect_delta, packing_rot);
|
|
}
|
|
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
// 4 bytes of material channel index
|
|
int material_channel_index = 0;
|
|
if (false == archive.ReadInt(&material_channel_index))
|
|
break;
|
|
f->SetMaterialChannelIndex(material_channel_index);
|
|
}
|
|
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
// 4 bytes of per face color
|
|
ON_Color per_face_color = ON_Color::UnsetColor;
|
|
if (false == archive.ReadColor(per_face_color))
|
|
break;
|
|
f->SetPerFaceColor(per_face_color);
|
|
}
|
|
|
|
|
|
// PackId
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
// 4 bytes of pack id
|
|
unsigned pack_id = 0U;
|
|
if (false == archive.ReadInt(&pack_id))
|
|
break;
|
|
f->m_pack_id = pack_id;
|
|
}
|
|
|
|
|
|
// Custom texture coordinates
|
|
sz = 0;
|
|
if (false == Internal_ReadComponentAdditionSize(archive, 4, &sz))
|
|
break;
|
|
if (ON_SubDComponentArchiveAdditionEndMark == sz)
|
|
return true; // end of additions
|
|
if (0 != sz)
|
|
{
|
|
// The number of texture points varies from face to face (EdgeCount()).
|
|
// The maximum size that can be saved in a single component addition is 253 bytes.
|
|
// So, texture points are saved in 10 point chunks ( 10*sizeof(ON_3dPoint) = 240 <= 243.
|
|
const unsigned texture_point_count = f->EdgeCount();
|
|
unsigned ten_point_chunk_count = 0xFFFFFFFFU;
|
|
if (false == archive.ReadInt(&ten_point_chunk_count))
|
|
break;
|
|
if (ten_point_chunk_count != texture_point_count / 10)
|
|
break;
|
|
const unsigned left_over_points_count = texture_point_count % 10;
|
|
const unsigned char sizeof_ten_points = (unsigned char)(10 * sizeof(ON_3dPoint)); // sizeof_ten_points = 240 <= 243
|
|
ON_3dPoint a[10];
|
|
bool bContinue = true;
|
|
|
|
// Even if allocation fails, we need to read the points so we can get get
|
|
// future information that is after the points out of the archive.
|
|
subdimple->AllocateFaceTexturePoints(f);
|
|
ON_3dPoint* tp = f->m_texture_points;
|
|
|
|
// read the 10 point chunks
|
|
for (unsigned i = 0; bContinue && i < ten_point_chunk_count; ++i)
|
|
{
|
|
sz = 0;
|
|
bContinue = Internal_ReadComponentAdditionSize(archive, sizeof_ten_points, &sz);
|
|
bContinue = bContinue && (sizeof_ten_points == sz);
|
|
bContinue = bContinue && archive.ReadDouble(30, (double*)a);
|
|
if (bContinue && nullptr != tp)
|
|
{
|
|
for (unsigned j = 0; j < 10; ++j)
|
|
*tp++ = a[j];
|
|
}
|
|
}
|
|
|
|
// read the "left over" points.
|
|
if (bContinue && left_over_points_count > 0)
|
|
{
|
|
const unsigned char sizeof_left_over_points = (unsigned char)(left_over_points_count * sizeof(ON_3dPoint)); // sizeof_left_over_points < 240
|
|
sz = 0;
|
|
bContinue = Internal_ReadComponentAdditionSize(archive, sizeof_left_over_points, &sz);
|
|
bContinue = bContinue && (sizeof_left_over_points == sz);
|
|
bContinue = bContinue && archive.ReadDouble(3 * left_over_points_count, (double*)a);
|
|
if (bContinue && nullptr != tp)
|
|
{
|
|
for (unsigned j = 0; j < left_over_points_count; ++j)
|
|
*tp++ = a[j];
|
|
}
|
|
}
|
|
if (false == bContinue)
|
|
break;
|
|
if ( nullptr != tp)
|
|
f->m_texture_status_bits |= ON_SubDFace::TextureStatusBits::TexturePointsSet;
|
|
}
|
|
|
|
|
|
return Internal_FinishReadingComponentAdditions(archive);
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
unsigned int ON_SubDLevel::SetArchiveId(
|
|
const ON_SubDimple& subdimple,
|
|
unsigned int archive_id_partition[4],
|
|
bool bLevelLinkedListIncreasingId[3]
|
|
) const
|
|
{
|
|
unsigned int archive_id = 1;
|
|
//archive_id_partition[0] = 0;
|
|
//archive_id_partition[1] = 0;
|
|
//archive_id_partition[2] = 0;
|
|
//archive_id_partition[3] = 0;
|
|
|
|
const ON_SubDComponentPtr::Type component_type[3] = {
|
|
ON_SubDComponentPtr::Type::Vertex,
|
|
ON_SubDComponentPtr::Type::Edge,
|
|
ON_SubDComponentPtr::Type::Face
|
|
};
|
|
|
|
const ON_SubDComponentBaseLink* first_link[3] = {
|
|
(const ON_SubDComponentBaseLink*)m_vertex[0],
|
|
(const ON_SubDComponentBaseLink*)m_edge[0],
|
|
(const ON_SubDComponentBaseLink*)m_face[0]
|
|
};
|
|
|
|
|
|
for (unsigned int listdex = 0; listdex < 3; listdex++)
|
|
{
|
|
bLevelLinkedListIncreasingId[listdex]
|
|
= nullptr != first_link[listdex]
|
|
&& first_link[listdex]->m_id > 0U;
|
|
unsigned int prev_id = 0;
|
|
archive_id_partition[listdex] = archive_id;
|
|
unsigned int linked_list_count = 0;
|
|
for (const ON_SubDComponentBaseLink* clink = first_link[listdex]; nullptr != clink; clink = clink->m_next)
|
|
{
|
|
++linked_list_count;
|
|
if (prev_id < clink->m_id)
|
|
{
|
|
prev_id = clink->m_id;
|
|
clink->SetArchiveId(archive_id++);
|
|
continue;
|
|
}
|
|
|
|
// the for(..) scope we are currently in is exited below.
|
|
bLevelLinkedListIncreasingId[listdex] = false;
|
|
|
|
// m_id values are not increasing in the linked list.
|
|
// This happens when the subd is edited and components are deleted
|
|
// and then added back later.
|
|
// Finish counting components in the linked list.
|
|
for (clink = clink->m_next; nullptr != clink; clink = clink->m_next)
|
|
++linked_list_count;
|
|
|
|
// Now iterate the fixed size pool (which always iterates in increasing id order),
|
|
// skip components not on this level, and set archive id of the ones on this level.
|
|
unsigned int cidit_level_count = 0;
|
|
archive_id = archive_id_partition[listdex];
|
|
ON_SubDComponentIdIterator cidit;
|
|
subdimple.InitializeComponentIdIterator(component_type[listdex],cidit);
|
|
const unsigned level_index = this->m_level_index;
|
|
prev_id = 0;
|
|
for (const ON_SubDComponentBase* c = cidit.FirstComponent(); nullptr != c; c = cidit.NextComponent())
|
|
{
|
|
if (prev_id >= c->m_id)
|
|
{
|
|
// This is a serious error!
|
|
// Continue because this allows us to save something do the disk in these bad cases.
|
|
ON_SUBD_ERROR("The m_id values of the active components in the fixed size pool are corrupt.");
|
|
}
|
|
else
|
|
{
|
|
prev_id = c->m_id;
|
|
}
|
|
if (level_index != c->SubdivisionLevel())
|
|
continue;
|
|
++cidit_level_count;
|
|
c->SetArchiveId(archive_id++);
|
|
}
|
|
if (cidit_level_count != linked_list_count)
|
|
{
|
|
// This is a serious error!
|
|
// Continue because this allows us to save something do the disk in these bad cases.
|
|
ON_SUBD_ERROR("The m_level values of the active components in the fixed size pool are corrupt.");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
archive_id_partition[3] = archive_id;
|
|
|
|
return archive_id-1;
|
|
}
|
|
|
|
|
|
void ON_SubDLevel::ClearArchiveId() const
|
|
{
|
|
// archive ids can be cleared in any order.
|
|
for (const ON_SubDVertex* v = m_vertex[0]; nullptr != v; v = v->m_next_vertex)
|
|
v->SetArchiveId(0);
|
|
for (const ON_SubDEdge* e = m_edge[0]; nullptr != e; e = e->m_next_edge)
|
|
e->SetArchiveId(0);
|
|
for (const ON_SubDFace* f = m_face[0]; nullptr != f; f = f->m_next_face)
|
|
f->SetArchiveId(0);
|
|
}
|
|
|
|
bool ON_SubDLevel::Write(
|
|
const ON_SubDimple& subdimple,
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
if (!archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK,1,1))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if (!archive.WriteShort((unsigned short)m_level_index))
|
|
break;
|
|
|
|
// from early days when there was a possibility of different types of subdivision algorithm
|
|
// 4,4,4 means catmull clark quad
|
|
if (!archive.WriteChar((unsigned char)4))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)4))
|
|
break;
|
|
if (!archive.WriteChar((unsigned char)4))
|
|
break;
|
|
|
|
ON_BoundingBox bbox = m_aggregates.m_bDirtyBoundingBox ? ON_BoundingBox::EmptyBoundingBox : m_aggregates.m_controlnet_bbox;
|
|
if (!archive.WriteDouble(3,bbox[0]))
|
|
break;
|
|
if (!archive.WriteDouble(3,bbox[1]))
|
|
break;
|
|
|
|
|
|
unsigned int archive_id_partition[4] = {};
|
|
bool bLevelLinkedListIncreasingId[3] = {};
|
|
SetArchiveId(subdimple, archive_id_partition, bLevelLinkedListIncreasingId);
|
|
|
|
if (!archive.WriteInt(4,archive_id_partition))
|
|
break;
|
|
|
|
const ON_SubDVertex* v = nullptr;
|
|
const ON_SubDEdge* e = nullptr;
|
|
const ON_SubDFace* f = nullptr;
|
|
|
|
// Have to use idit because subd editing (deleting and then adding) can leave the level's linked lists
|
|
// with components in an order that is not increasing in id and it is critical that the next three for
|
|
// loops iterate the level's components in order of increasing id.
|
|
ON_SubDLevelComponentIdIterator idit;
|
|
|
|
// must iterate vertices in order of increasing id
|
|
idit.Initialize(bLevelLinkedListIncreasingId[0], ON_SubDComponentPtr::Type::Vertex, subdimple, *this);
|
|
for (v = idit.FirstVertex(); nullptr != v; v = idit.NextVertex())
|
|
{
|
|
if( !v->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != v )
|
|
break;
|
|
|
|
// must iterate edges in order of increasing id
|
|
idit.Initialize(bLevelLinkedListIncreasingId[1], ON_SubDComponentPtr::Type::Edge, subdimple, *this);
|
|
for (e = idit.FirstEdge(); nullptr != e; e = idit.NextEdge())
|
|
{
|
|
if( !e->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != e )
|
|
break;
|
|
|
|
// must iterate faces in order of increasing id
|
|
idit.Initialize(bLevelLinkedListIncreasingId[2], ON_SubDComponentPtr::Type::Face, subdimple, *this);
|
|
for (f = idit.FirstFace(); nullptr != f; f = idit.NextFace())
|
|
{
|
|
if( !f->Write(archive) )
|
|
break;
|
|
}
|
|
if ( nullptr != f )
|
|
break;
|
|
|
|
// chunk 1.1 has meshes
|
|
unsigned char c = 0;
|
|
if (archive.Save3dmRenderMesh(ON::object_type::subd_object) || archive.Save3dmAnalysisMesh(ON::object_type::subd_object))
|
|
{
|
|
// no reason to save the m_control_net_mesh
|
|
if (false == m_surface_mesh.IsEmpty())
|
|
{
|
|
c = 0;
|
|
// c = 1; TODO change to c = 1 when ON_SubDMesh::Write()/Read() actually work
|
|
}
|
|
}
|
|
|
|
if (!archive.WriteChar(c))
|
|
break;
|
|
|
|
if (1 == c)
|
|
{
|
|
//if (!m_limit_mesh.Write(archive))
|
|
// break;
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
ClearArchiveId();
|
|
|
|
if (rc)
|
|
return rc;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDLevel::Read(
|
|
ON_BinaryArchive& archive,
|
|
class ON_SubDArchiveIdMap& element_list,
|
|
ON_SubD& subd
|
|
)
|
|
{
|
|
if ( false == element_list.Reset())
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
int major_version = 1;
|
|
int minor_version = 0;
|
|
if (!archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK,&major_version,&minor_version))
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
if ( 1 != major_version)
|
|
break;
|
|
|
|
unsigned short level_index = 0;
|
|
if (!archive.ReadShort(&level_index))
|
|
break;
|
|
m_level_index = level_index;
|
|
|
|
// from early days when there was a possibility of different types of subdivision algorithm
|
|
unsigned char ignored_c[3] = {};
|
|
if (!archive.ReadChar(&ignored_c[0]))
|
|
break;
|
|
if (!archive.ReadChar(&ignored_c[1]))
|
|
break;
|
|
if (!archive.ReadChar(&ignored_c[2]))
|
|
break;
|
|
|
|
ON_BoundingBox controlnet_bbox;
|
|
if (!archive.ReadDouble(3, controlnet_bbox[0]))
|
|
break;
|
|
if (!archive.ReadDouble(3, controlnet_bbox[1]))
|
|
break;
|
|
if (controlnet_bbox.IsValid())
|
|
{
|
|
m_aggregates.m_bDirtyBoundingBox = false;
|
|
m_aggregates.m_controlnet_bbox = controlnet_bbox;
|
|
}
|
|
else
|
|
{
|
|
m_aggregates.m_bDirtyBoundingBox = true;
|
|
}
|
|
|
|
if (!archive.ReadInt(4,element_list.m_archive_id_partition))
|
|
break;
|
|
|
|
unsigned int archive_id = 0;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[0]; archive_id < element_list.m_archive_id_partition[1]; archive_id++ )
|
|
{
|
|
ON_SubDVertex* v = nullptr;
|
|
if ( false == ON_SubDVertex::Read(archive, subd, v) )
|
|
break;
|
|
if ( nullptr == v )
|
|
break;
|
|
if (archive_id != v->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(v) )
|
|
break;
|
|
AddVertex(v);
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[1] )
|
|
break;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[1]; archive_id < element_list.m_archive_id_partition[2]; archive_id++ )
|
|
{
|
|
ON_SubDEdge* e = nullptr;
|
|
if ( false == ON_SubDEdge::Read(archive, subd, e) )
|
|
break;
|
|
if ( nullptr == e )
|
|
break;
|
|
if (archive_id != e->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(e) )
|
|
break;
|
|
AddEdge(e);
|
|
|
|
#if !defined(OPENNURBS_IN_RHINO) && !defined(OPENNURBS_7_17_SUBD_SKIP_CHECK_CORNER_SECTOR_COEFFICIENTS)
|
|
// Versions <= 7.16 saved incorrect sector coefficient values, for smooth edges to corner vertices.
|
|
// Rhino updates all sector coefficient values when adding the SubD to the document so has no need for this check.
|
|
if (
|
|
archive.ArchiveOpenNURBSVersion() <= ON_VersionNumberConstruct(7, 16, 2099, 12, 31, 6)
|
|
&& (e->m_edge_tag == ON_SubDEdgeTag::Smooth || e->m_edge_tag == ON_SubDEdgeTag::SmoothX)
|
|
)
|
|
{
|
|
static const ON_String format{
|
|
L"The value of m_sector_coefficient[% i] for edge with m_id %u in SubD %u "
|
|
"is incorrect. Recompute it before using it.\n"
|
|
"Recompile OpenNURBS with the OPENNURBS_7_17_SUBD_SKIP_CHECK_CORNER_SECTOR_COEFFICIENTS "
|
|
"flag to skip this check and silence this warning, or update and save your file in "
|
|
"OpenNURBS >= 7.17.\n"
|
|
};
|
|
for (unsigned short evi = 0; evi < 2; evi++)
|
|
{
|
|
ON_SubDVertex* vp{ const_cast<ON_SubDVertex*>(e->m_vertex[evi]) };
|
|
if (!element_list.ConvertArchiveIdToRuntimeVertexPtr(1, 1, &vp)) continue;
|
|
if (vp->m_vertex_tag == ON_SubDVertexTag::Corner)
|
|
{
|
|
ON_String msg{};
|
|
msg.Format(format, evi, e->m_id, subd.ModelObjectId());
|
|
ON_WARNING(msg);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[2] )
|
|
break;
|
|
|
|
for (archive_id = element_list.m_archive_id_partition[2]; archive_id < element_list.m_archive_id_partition[3]; archive_id++ )
|
|
{
|
|
ON_SubDFace* f = nullptr;
|
|
if ( false == ON_SubDFace::Read(archive, subd, f) )
|
|
break;
|
|
if ( nullptr == f )
|
|
break;
|
|
if (archive_id != f->ArchiveId())
|
|
break;
|
|
if ( !element_list.Add(f) )
|
|
break;
|
|
AddFace(f);
|
|
}
|
|
if ( archive_id != element_list.m_archive_id_partition[3] )
|
|
break;
|
|
|
|
if (archive_id != element_list.Count())
|
|
break;
|
|
|
|
// Convert archive_id references to runtime pointers.
|
|
archive_id = element_list.ConvertArchiveIdsToRuntimePointers();
|
|
if ( archive_id <= 0 )
|
|
break;
|
|
|
|
if (0 == minor_version )
|
|
break;
|
|
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
|
|
if (1 == c)
|
|
{
|
|
//if (!m_limit_mesh.Read(archive))
|
|
// break;
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
ClearArchiveId();
|
|
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
if (rc)
|
|
return rc;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDimple::Write(
|
|
ON_BinaryArchive& archive
|
|
) const
|
|
{
|
|
const_cast< ON_SubDHeap* >(&m_heap)->ClearArchiveId();
|
|
|
|
const int minor_version = (archive.Archive3dmVersion() < 70) ? 0 : 4;
|
|
if ( !archive.BeginWrite3dmChunk(TCODE_ANONYMOUS_CHUNK, 1, minor_version) )
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
bool rc = false;
|
|
for (;;)
|
|
{
|
|
unsigned int level_count = m_levels.UnsignedCount();
|
|
unsigned int level_index;
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
if (nullptr == m_levels[level_index])
|
|
{
|
|
level_count = level_index;
|
|
break;
|
|
}
|
|
}
|
|
if (!archive.WriteInt(level_count))
|
|
break;
|
|
|
|
if (!archive.WriteInt(MaximumVertexId()))
|
|
break;
|
|
if (!archive.WriteInt(MaximumEdgeId()))
|
|
break;
|
|
if (!archive.WriteInt(MaximumFaceId()))
|
|
break;
|
|
|
|
// a global bounding box was saved before May 2015.
|
|
// Something has to be here so file IO is not broken.
|
|
if (!archive.WriteBoundingBox(ON_BoundingBox::EmptyBoundingBox))
|
|
break;
|
|
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
if ( !m_levels[level_index]->Write(*this,archive) )
|
|
break;
|
|
}
|
|
if (level_index < level_count)
|
|
break;
|
|
|
|
|
|
if (minor_version <= 0)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
// minor version = 1 additions
|
|
const unsigned char obsolete_texture_domain_type = ON_SubD::ObsoleteTextureDomainTypeFromTextureCoordinateType(TextureCoordinateType());
|
|
if (false == archive.WriteChar(obsolete_texture_domain_type))
|
|
break;
|
|
|
|
if (false == m_texture_mapping_tag.Write(archive))
|
|
break;
|
|
|
|
// minor version = 2 additions
|
|
if (false == m_symmetry.Write(archive))
|
|
break;
|
|
|
|
// minor version = 3 additions
|
|
// runtime content number used to compare with the one from the saved on m_symmetry
|
|
// Dale Lear Sep 2020 - Turns out saving the runtime GeometryContentSerialNumber()
|
|
// was a bad idea. Doing it the way I came up with today while adding
|
|
// m_face_packing_topology_hash is much better because all decisions get
|
|
// made at save time when we have the most reliable information.
|
|
// I've added bSyncSymmetricContentSerialNumber below, but saving gsn has to stay
|
|
// so pre-today Rhino can read files saved from post today Rhino.
|
|
const ON__UINT64 gsn = GeometryContentSerialNumber();
|
|
if (false == archive.WriteBigInt(gsn))
|
|
break;
|
|
|
|
// minor version = 4 additions
|
|
// bSubDIsSymmetric = true if this subd currently has the symmetry specified by m_symmetry.
|
|
const bool bSubDIsSymmetric = m_symmetry.SameSymmetricObjectGeometry(this);
|
|
|
|
if (false == archive.WriteBool(bSubDIsSymmetric))
|
|
break;
|
|
|
|
if (false == archive.WriteUuid(m_face_packing_id))
|
|
break;
|
|
|
|
const bool bSyncFacePackingHashSerialNumbers
|
|
= m_face_packing_topology_hash.IsNotEmpty()
|
|
&& this->RuntimeSerialNumber == m_face_packing_topology_hash.SubDRuntimeSerialNumber()
|
|
&& gsn > 0 && gsn == m_face_packing_topology_hash.SubDGeometryContentSerialNumber()
|
|
;
|
|
if (false == archive.WriteBool(bSyncFacePackingHashSerialNumbers))
|
|
break;
|
|
|
|
if (false == m_face_packing_topology_hash.Write(archive))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
if (rc)
|
|
return true;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
bool ON_SubDimple::Read(
|
|
ON_BinaryArchive& archive,
|
|
class ON_SubD& subd
|
|
)
|
|
{
|
|
m_heap.Clear();
|
|
int major_version = 0;
|
|
int minor_version = 0;
|
|
if ( !archive.BeginRead3dmChunk(TCODE_ANONYMOUS_CHUNK, &major_version, &minor_version) )
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
bool rc = false;
|
|
|
|
|
|
// Code before Feb 10, 2020 cared about these values
|
|
unsigned int obsolete_archive_max_vertex_id = 0;
|
|
unsigned int obsolete_archive_max_edge_id = 0;
|
|
unsigned int obsolete_archive_max_face_id = 0;
|
|
|
|
bool bSubDIsSymmetric = false;
|
|
bool bSyncFacePackingHashSerialNumbers = false;
|
|
|
|
for (;;)
|
|
{
|
|
if (1 != major_version)
|
|
break;
|
|
|
|
unsigned int i;
|
|
if (!archive.ReadInt(&i))
|
|
break;
|
|
const unsigned int level_count = i;
|
|
|
|
if (!archive.ReadInt(&obsolete_archive_max_vertex_id))
|
|
break;
|
|
if (!archive.ReadInt(&obsolete_archive_max_edge_id))
|
|
break;
|
|
if (!archive.ReadInt(&obsolete_archive_max_face_id))
|
|
break;
|
|
|
|
|
|
ON_BoundingBox bbox_unsued_after_May_2015;
|
|
if (!archive.ReadBoundingBox(bbox_unsued_after_May_2015))
|
|
break;
|
|
|
|
ON_SubDArchiveIdMap element_list;
|
|
|
|
unsigned int level_index;
|
|
for (level_index = 0; level_index < level_count; level_index++)
|
|
{
|
|
ON_SubDLevel* level = SubDLevel(level_index,true);
|
|
if ( nullptr == level )
|
|
break;
|
|
if (false == level->Read(archive, element_list, subd ) )
|
|
break;
|
|
m_active_level = level;
|
|
}
|
|
|
|
if ( level_index != level_count)
|
|
break;
|
|
|
|
if (minor_version >= 1)
|
|
{
|
|
unsigned char obsolete_texture_domain_type = 0;
|
|
if (false == archive.ReadChar(&obsolete_texture_domain_type))
|
|
break;
|
|
m_texture_coordinate_type = ON_SubD::TextureCoordinateTypeFromObsoleteTextureDomainType(obsolete_texture_domain_type);
|
|
|
|
if (false == m_texture_mapping_tag.Read(archive))
|
|
break;
|
|
|
|
if (minor_version >= 2)
|
|
{
|
|
if (false == m_symmetry.Read(archive))
|
|
break;
|
|
|
|
if (minor_version >= 3)
|
|
{
|
|
//
|
|
ON__UINT64 legacy_gsn_at_save_time = 0;
|
|
if (false == archive.ReadBigInt(&legacy_gsn_at_save_time))
|
|
break;
|
|
|
|
if (minor_version >= 4)
|
|
{
|
|
// minor version = 4 additions
|
|
if (false == archive.ReadBool(&bSubDIsSymmetric))
|
|
break;
|
|
if (false == archive.ReadUuid(m_face_packing_id))
|
|
break;
|
|
if (false == archive.ReadBool(&bSyncFacePackingHashSerialNumbers))
|
|
break;
|
|
if (false == m_face_packing_topology_hash.Read(archive))
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
// Heap id validation. Always an error if max_heap_..._id > m_max_..._id.
|
|
if (false == m_heap.IsValid(false,nullptr))
|
|
{
|
|
ON_SUBD_ERROR("m_heap.IsValid() is false.");
|
|
m_heap.ResetIds(); // breaks component id references, but this is a serious error.
|
|
}
|
|
|
|
if (archive.ArchiveOpenNURBSVersion() < 2382394661)
|
|
{
|
|
// try to get texture information set correctly when it comes from old files
|
|
const ON_MappingTag file_tag = this->TextureMappingTag(true);
|
|
ON_MappingTag new_tag = file_tag;
|
|
|
|
const ON_SubDTextureCoordinateType file_type = this->TextureCoordinateType();
|
|
ON_SubDTextureCoordinateType new_type = file_type;
|
|
|
|
if (ON_TextureMapping::TYPE::srfp_mapping == file_tag.m_mapping_type)
|
|
{
|
|
new_tag = ON_MappingTag::SurfaceParameterMapping;
|
|
if (ON_SubDTextureCoordinateType::FromMapping == file_type || ON_SubDTextureCoordinateType::Unset == file_type)
|
|
new_type = ON_SubDTextureCoordinateType::Packed;
|
|
}
|
|
else
|
|
{
|
|
const bool bTagIsSet
|
|
= ON_TextureMapping::TYPE::srfp_mapping != file_tag.m_mapping_type
|
|
&& ON_TextureMapping::TYPE::no_mapping != file_tag.m_mapping_type
|
|
&& file_tag.IsSet()
|
|
;
|
|
if (ON_SubDTextureCoordinateType::Unset == file_type)
|
|
{
|
|
if (bTagIsSet)
|
|
new_type = ON_SubDTextureCoordinateType::FromMapping;
|
|
else
|
|
new_tag = ON_MappingTag::Unset;
|
|
}
|
|
else if (ON_SubDTextureCoordinateType::FromMapping == file_type)
|
|
{
|
|
if (false == bTagIsSet)
|
|
{
|
|
new_tag = ON_MappingTag::Unset;
|
|
new_type = ON_SubDTextureCoordinateType::Packed;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (0 != ON_MappingTag::CompareAll(file_tag, new_tag))
|
|
this->SetTextureMappingTag(new_tag);
|
|
if (file_type != new_type)
|
|
this->SetTextureCoordinateType(new_type);
|
|
}
|
|
|
|
ChangeGeometryContentSerialNumber(false);
|
|
|
|
|
|
///////////////////////////////////////////
|
|
//
|
|
// No changes to "this SubD" below here.
|
|
//
|
|
// The rest is updating information that is used to determine if this SubD
|
|
// is the same SubD that existed when symmetry and texture information
|
|
// was saved. It's ok if this is not the same subd. If and when appropriate
|
|
// something downstream will update either the SubD or the symmetry/texture
|
|
// information.
|
|
// It most certainly is NOT appropriate to update any of that here.
|
|
//
|
|
|
|
if (bSubDIsSymmetric)
|
|
m_symmetry.SetSymmetricObject(this);
|
|
else
|
|
m_symmetry.ClearSymmetricObject();
|
|
|
|
if (bSyncFacePackingHashSerialNumbers)
|
|
{
|
|
// When the file was saved, the values of subd.GeometryContentSerialNumber() and
|
|
// and m_face_packing_topology_hash.SubDGeometryContentSerialNumber() were the same.
|
|
m_face_packing_topology_hash.m_subd_runtime_serial_number = this->RuntimeSerialNumber;
|
|
m_face_packing_topology_hash.m_subd_geometry_content_serial_number = this->GeometryContentSerialNumber();
|
|
}
|
|
else
|
|
{
|
|
m_face_packing_topology_hash.m_subd_runtime_serial_number = 0;
|
|
m_face_packing_topology_hash.m_subd_geometry_content_serial_number = 0;
|
|
}
|
|
|
|
if (rc)
|
|
return true;
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
//virtual
|
|
bool ON_SubD::Write(
|
|
ON_BinaryArchive& archive
|
|
) const // override
|
|
{
|
|
for (;;)
|
|
{
|
|
const ON_SubDimple* subdimple = SubDimple();
|
|
unsigned char c = (nullptr == subdimple) ? 0 : 1;
|
|
if (!archive.WriteChar(c))
|
|
break;
|
|
if (nullptr != subdimple)
|
|
{
|
|
if (!subdimple->Write(archive))
|
|
break;
|
|
}
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
//virtual
|
|
bool ON_SubD::Read(
|
|
ON_BinaryArchive& archive
|
|
) // override
|
|
{
|
|
Destroy();
|
|
for (;;)
|
|
{
|
|
unsigned char c = 0;
|
|
if (!archive.ReadChar(&c))
|
|
break;
|
|
if (1 == c)
|
|
{
|
|
ON_SubDimple* subdimple = SubDimple(true);
|
|
if ( nullptr == subdimple)
|
|
break;
|
|
if (false == subdimple->Read(archive,*this))
|
|
{
|
|
Destroy();
|
|
break;
|
|
}
|
|
}
|
|
else if ( 0 != c )
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
return ON_SUBD_RETURN_ERROR(false);
|
|
}
|
|
|
|
/////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ON_SubDMeshProxyUserData
|
|
//
|
|
|
|
ON_OBJECT_IMPLEMENT(ON_SubDMeshProxyUserData,ON_UserData,"2868B9CD-28AE-4EA7-8073-BD390B3E97C8");
|
|
|
|
const bool ON_SubDMeshProxyUserData::Internal_MeshHasFaces(const ON_Mesh* mesh)
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == mesh)
|
|
break;
|
|
if (mesh->m_F.UnsignedCount() <= 0)
|
|
break;
|
|
if (mesh->m_V.UnsignedCount() <= 2)
|
|
break;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_FaceSHA1(const ON_Mesh* mesh)
|
|
{
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
return ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
ON_SHA1 sha1;
|
|
const ON_MeshFace* f = mesh->m_F.Array();
|
|
sha1.AccumulateBytes(f, mesh->m_F.UnsignedCount() * sizeof(*f));
|
|
return sha1.Hash();
|
|
}
|
|
|
|
const ON_SHA1_Hash ON_SubDMeshProxyUserData::Internal_VertexSHA1(const ON_Mesh* mesh)
|
|
{
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
return ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
ON_SHA1 sha1;
|
|
const ON_3fPoint* v = mesh->m_V.Array();
|
|
sha1.AccumulateBytes(v, mesh->m_V.UnsignedCount() * sizeof(*v));
|
|
return sha1.Hash();
|
|
}
|
|
|
|
|
|
void ON_SubDMeshProxyUserData::Internal_CopyFrom(const ON_SubDMeshProxyUserData& src)
|
|
{
|
|
if ( src.IsValid() )
|
|
{
|
|
m_subd = new ON_SubD(*src.m_subd);
|
|
m_mesh_face_count = src.m_mesh_face_count;
|
|
m_mesh_vertex_count = src.m_mesh_vertex_count;
|
|
m_mesh_face_array_sha1 = src.m_mesh_face_array_sha1;
|
|
m_mesh_vertex_array_sha1 = src.m_mesh_vertex_array_sha1;
|
|
}
|
|
}
|
|
|
|
void ON_SubDMeshProxyUserData::Internal_Destroy()
|
|
{
|
|
if (nullptr != m_subd)
|
|
{
|
|
delete m_subd;
|
|
m_subd = nullptr;
|
|
}
|
|
m_mesh_face_count = 0;
|
|
m_mesh_vertex_count = 0;
|
|
m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
}
|
|
|
|
|
|
const ON_SubDDisplayParameters ON_SubDMeshProxyUserData::MeshProxyDisplayParameters()
|
|
{
|
|
return ON_SubDDisplayParameters::CreateFromDisplayDensity(4);
|
|
|
|
}
|
|
|
|
ON_Mesh* ON_SubDMeshProxyUserData::MeshProxyFromSubD(
|
|
const ON_SubD* subd
|
|
)
|
|
{
|
|
ON_Mesh* mesh = nullptr;
|
|
ON_SubD* subd_copy = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == subd)
|
|
break;
|
|
subd_copy = new ON_SubD(*subd);
|
|
if (nullptr == subd_copy)
|
|
break;
|
|
mesh = subd_copy->GetControlNetMesh(nullptr, ON_SubDGetControlNetMeshPriority::Geometry);
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
break;
|
|
ON_SubDMeshProxyUserData* ud = new ON_SubDMeshProxyUserData();
|
|
ud->m_subd = subd_copy;
|
|
ud->m_mesh_face_count = mesh->FaceUnsignedCount();
|
|
ud->m_mesh_vertex_count = mesh->VertexUnsignedCount();
|
|
ud->m_mesh_face_array_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh);
|
|
ud->m_mesh_vertex_array_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh);
|
|
if (false == mesh->AttachUserData(ud))
|
|
{
|
|
ud->m_subd = nullptr;
|
|
delete ud;
|
|
break;
|
|
}
|
|
return mesh;
|
|
}
|
|
|
|
if (nullptr != mesh)
|
|
delete mesh;
|
|
|
|
if (nullptr != subd_copy)
|
|
delete subd_copy;
|
|
|
|
return nullptr;
|
|
}
|
|
|
|
|
|
ON_SubD* ON_SubDMeshProxyUserData::SubDFromMeshProxy(
|
|
const ON_Mesh* mesh
|
|
)
|
|
{
|
|
ON_SubD* subd = nullptr;
|
|
ON_SubDMeshProxyUserData* ud = nullptr;
|
|
|
|
for (;;)
|
|
{
|
|
if (nullptr == mesh)
|
|
break;
|
|
const ON_UUID udid = ON_CLASS_ID(ON_SubDMeshProxyUserData);
|
|
ud = ON_SubDMeshProxyUserData::Cast(mesh->GetUserData(udid));
|
|
if (nullptr == ud)
|
|
break;
|
|
if (false == ud->IsValid())
|
|
break;
|
|
if (false == ud->ParentMeshValid())
|
|
break;
|
|
subd = ud->m_subd;
|
|
ud->m_subd = nullptr;
|
|
}
|
|
|
|
if (nullptr != ud)
|
|
delete ud;
|
|
|
|
return subd;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::IsSubDMeshProxy(
|
|
const ON_Mesh* mesh
|
|
)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData()
|
|
{
|
|
m_userdata_uuid = ON_CLASS_ID(ON_SubDMeshProxyUserData);
|
|
m_application_uuid = ON_opennurbs6_id; // opennurbs.dll reads/writes this userdata
|
|
// The id must be the version 4 id because
|
|
// V5 SaveAs V4 needs to work.
|
|
m_userdata_copycount = 1;
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::~ON_SubDMeshProxyUserData()
|
|
{
|
|
Internal_Destroy();
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData::ON_SubDMeshProxyUserData(const ON_SubDMeshProxyUserData& src)
|
|
: ON_UserData(src)
|
|
{
|
|
Internal_CopyFrom(src);
|
|
}
|
|
|
|
|
|
ON_SubDMeshProxyUserData& ON_SubDMeshProxyUserData::operator=(const ON_SubDMeshProxyUserData& src)
|
|
{
|
|
if (this != &src)
|
|
{
|
|
Internal_Destroy();
|
|
Internal_CopyFrom(src);
|
|
}
|
|
return *this;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::Write(ON_BinaryArchive& archive) const
|
|
{
|
|
const int chunk_version = 1;
|
|
if ( false == archive.BeginWrite3dmAnonymousChunk(chunk_version) )
|
|
return false;
|
|
|
|
bool rc = false;
|
|
|
|
for (;;)
|
|
{
|
|
const bool bIsValid = IsValid();
|
|
if (!archive.WriteBool(bIsValid))
|
|
break;
|
|
|
|
if (false == bIsValid)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!m_subd->Write(archive))
|
|
break;
|
|
if (!archive.WriteInt(m_mesh_face_count))
|
|
break;
|
|
if (!archive.WriteInt(m_mesh_vertex_count))
|
|
break;
|
|
if (!m_mesh_face_array_sha1.Write(archive))
|
|
break;
|
|
if (!m_mesh_vertex_array_sha1.Write(archive))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndWrite3dmChunk())
|
|
rc = false;
|
|
|
|
return rc;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::Read(ON_BinaryArchive& archive)
|
|
{
|
|
Internal_Destroy();
|
|
|
|
int chunk_version = 0;
|
|
if ( false == archive.BeginRead3dmAnonymousChunk(&chunk_version) )
|
|
return false;
|
|
|
|
bool rc = false;
|
|
|
|
for (;;)
|
|
{
|
|
if (chunk_version <= 0)
|
|
break;
|
|
bool bIsValid = false;
|
|
|
|
if (!archive.ReadBool(&bIsValid))
|
|
break;
|
|
|
|
if (false == bIsValid)
|
|
{
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
m_subd = new ON_SubD();
|
|
if (!m_subd->Read(archive))
|
|
break;
|
|
if (!archive.ReadInt(&m_mesh_face_count))
|
|
break;
|
|
if (!archive.ReadInt(&m_mesh_vertex_count))
|
|
break;
|
|
if (!m_mesh_face_array_sha1.Read(archive))
|
|
break;
|
|
if (!m_mesh_vertex_array_sha1.Read(archive))
|
|
break;
|
|
|
|
rc = true;
|
|
break;
|
|
}
|
|
|
|
if (!archive.EndRead3dmChunk())
|
|
rc = false;
|
|
|
|
if (!rc || !IsValid())
|
|
Internal_Destroy();
|
|
|
|
return rc;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::ParentMeshValid() const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (!IsValid())
|
|
break;
|
|
|
|
const ON_Mesh* mesh = ON_Mesh::Cast(Owner());
|
|
if (false == ON_SubDMeshProxyUserData::Internal_MeshHasFaces(mesh))
|
|
break;
|
|
|
|
if (m_mesh_face_count != mesh->m_F.UnsignedCount())
|
|
break;
|
|
|
|
if (m_mesh_vertex_count != mesh->m_V.UnsignedCount())
|
|
break;
|
|
|
|
const ON_SHA1_Hash f_sha1 = ON_SubDMeshProxyUserData::Internal_FaceSHA1(mesh);
|
|
if (f_sha1 != m_mesh_face_array_sha1)
|
|
break;
|
|
|
|
const ON_SHA1_Hash v_sha1 = ON_SubDMeshProxyUserData::Internal_VertexSHA1(mesh);
|
|
if (v_sha1 != m_mesh_vertex_array_sha1)
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
m_mesh_face_count = 0;
|
|
m_mesh_vertex_count = 0;
|
|
m_mesh_face_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
m_mesh_vertex_array_sha1 = ON_SHA1_Hash::EmptyContentHash;
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ON_SubDMeshProxyUserData::IsValid(
|
|
class ON_TextLog* text_log
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (nullptr == m_subd)
|
|
break;
|
|
if (m_mesh_face_count <= 0 )
|
|
break;
|
|
if (m_mesh_vertex_count <= 2 )
|
|
break;
|
|
if (ON_SHA1_Hash::EmptyContentHash == m_mesh_face_array_sha1)
|
|
break;
|
|
if (ON_SHA1_Hash::EmptyContentHash == m_mesh_vertex_array_sha1)
|
|
break;
|
|
if (false == m_userdata_xform.IsIdentity())
|
|
break;
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::GetDescription(ON_wString& description)
|
|
{
|
|
if (IsValid())
|
|
description = L"SubD attached to a valid proxy mesh.";
|
|
else
|
|
description = L"SubD attached to an invalid proxy mesh.";
|
|
return true;
|
|
}
|
|
|
|
|
|
bool ON_SubDMeshProxyUserData::WriteToArchive(
|
|
const class ON_BinaryArchive& archive,
|
|
const class ON_Object* parent_object
|
|
) const
|
|
{
|
|
for (;;)
|
|
{
|
|
if (archive.Archive3dmVersion() >= 60)
|
|
break;
|
|
if (false == IsValid())
|
|
return false;
|
|
if (false == ParentMeshValid())
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|