diff --git a/Cargo.toml b/Cargo.toml index 8802dbc6e5..16924db390 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,14 @@ [package] -name = "mikktspace" +name = "mikktspace-sys" version = "0.1.0" -authors = ["Benjamin Wasty "] +authors = ["alteous "] +build = "build.rs" + +[build-dependencies] +cmake = "0.1" [dependencies] -mikktspace-sys = { path = "./mikktspace-sys" } +gltf = "0.7" + +[[example]] +name = "generate" \ No newline at end of file diff --git a/build.rs b/build.rs new file mode 100644 index 0000000000..155a9ed067 --- /dev/null +++ b/build.rs @@ -0,0 +1,9 @@ + +extern crate cmake; + +fn main() { + let dst = cmake::build("libmikktspace"); + println!("cargo:rustc-link-search=native={}", dst.display()); + println!("cargo:rustc-link-lib=static=mikktspace"); +} + diff --git a/examples/generate.rs b/examples/generate.rs new file mode 100644 index 0000000000..ed4444a406 --- /dev/null +++ b/examples/generate.rs @@ -0,0 +1,45 @@ + +extern crate gltf; + +use std::io::Write; + +fn main() { + let path = "test-data/Avocado.gltf"; + let gltf = gltf::Import::from_path(path).sync().unwrap(); + let mesh = gltf.meshes().nth(0).unwrap(); + let primitive = mesh.primitives().nth(0).unwrap(); + let positions: Vec<[f32; 3]> = primitive.positions().unwrap().collect(); + let normals: Vec<[f32; 3]> = primitive.normals().unwrap().collect(); + let mut tex_coords: Vec<[f32; 2]> = vec![]; + let mut indices: Vec = vec![]; + match primitive.tex_coords(0).unwrap() { + gltf::mesh::TexCoords::F32(iter) => tex_coords.extend(iter), + _ => unreachable!(), + } + match primitive.indices().unwrap() { + gltf::mesh::Indices::U16(iter) => indices.extend(iter), + _ => unreachable!(), + } + + let file = std::fs::File::create("Avocado.obj").unwrap(); + let mut writer = std::io::BufWriter::new(file); + for position in &positions { + writeln!(writer, "v {} {} {}", position[0], position[1], position[2]); + } + for normal in &normals { + writeln!(writer, "vn {} {} {}", normal[0], normal[1], normal[2]); + } + for tex_coord in &tex_coords { + writeln!(writer, "vt {} {}", tex_coord[0], tex_coord[1]); + } + let mut i = indices.iter(); + while let (Some(v0), Some(v1), Some(v2)) = (i.next(), i.next(), i.next()) { + writeln!( + writer, + "f {}/{}/{} {}/{}/{} {}/{}/{}", + 1 + v0, 1 + v0, 1 + v0, + 1 + v1, 1 + v1, 1 + v1, + 1 + v2, 1 + v2, 1 + v2, + ); + } +} diff --git a/libmikktspace/CMakeLists.txt b/libmikktspace/CMakeLists.txt new file mode 100644 index 0000000000..376409d8c8 --- /dev/null +++ b/libmikktspace/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 2.8) +project(mikktspace) +set(PROJECT_VERSION_MAJOR "1") +set(PROJECT_VERSION_MINOR "0") +set(PROJECT_VERSION_PATCH "0") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wall -std=c11") +set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_DEBUG} -ggdb -DDEBUG") +set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS} ${CMAKE_C_FLAGS_RELEASE} -O2") +set(SOURCES mikktspace.h mikktspace.c) +add_library(mikktspace STATIC ${SOURCES}) +install(TARGETS mikktspace ARCHIVE DESTINATION ".") diff --git a/libmikktspace/mikktspace.c b/libmikktspace/mikktspace.c new file mode 100644 index 0000000000..62aa2da251 --- /dev/null +++ b/libmikktspace/mikktspace.c @@ -0,0 +1,1890 @@ +/** \file mikktspace/mikktspace.c + * \ingroup mikktspace + */ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#include +#include +#include +#include +#include +#include + +#include "mikktspace.h" + +#define TFALSE 0 +#define TTRUE 1 + +#ifndef M_PI +#define M_PI 3.1415926535897932384626433832795 +#endif + +#define INTERNAL_RND_SORT_SEED 39871946 + +// internal structure +typedef struct { + float x, y, z; +} SVec3; + +static tbool veq( const SVec3 v1, const SVec3 v2 ) +{ + return (v1.x == v2.x) && (v1.y == v2.y) && (v1.z == v2.z); +} + +static SVec3 vadd( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x + v2.x; + vRes.y = v1.y + v2.y; + vRes.z = v1.z + v2.z; + + return vRes; +} + + +static SVec3 vsub( const SVec3 v1, const SVec3 v2 ) +{ + SVec3 vRes; + + vRes.x = v1.x - v2.x; + vRes.y = v1.y - v2.y; + vRes.z = v1.z - v2.z; + + return vRes; +} + +static SVec3 vscale(const float fS, const SVec3 v) +{ + SVec3 vRes; + + vRes.x = fS * v.x; + vRes.y = fS * v.y; + vRes.z = fS * v.z; + + return vRes; +} + +static float LengthSquared( const SVec3 v ) +{ + return v.x*v.x + v.y*v.y + v.z*v.z; +} + +static float Length( const SVec3 v ) +{ + return sqrtf(LengthSquared(v)); +} + +static SVec3 Normalize( const SVec3 v ) +{ + return vscale(1 / Length(v), v); +} + +static float vdot( const SVec3 v1, const SVec3 v2) +{ + return v1.x*v2.x + v1.y*v2.y + v1.z*v2.z; +} + + +static tbool NotZero(const float fX) +{ + // could possibly use FLT_EPSILON instead + return fabsf(fX) > FLT_MIN; +} + +static tbool VNotZero(const SVec3 v) +{ + // might change this to an epsilon based test + return NotZero(v.x) || NotZero(v.y) || NotZero(v.z); +} + + + +typedef struct { + int iNrFaces; + int * pTriMembers; +} SSubGroup; + +typedef struct { + int iNrFaces; + int * pFaceIndices; + int iVertexRepresentitive; + tbool bOrientPreservering; +} SGroup; + +// +#define MARK_DEGENERATE 1 +#define QUAD_ONE_DEGEN_TRI 2 +#define GROUP_WITH_ANY 4 +#define ORIENT_PRESERVING 8 + + + +typedef struct { + int FaceNeighbors[3]; + SGroup * AssignedGroup[3]; + + // normalized first order face derivatives + SVec3 vOs, vOt; + float fMagS, fMagT; // original magnitudes + + // determines if the current and the next triangle are a quad. + int iOrgFaceNumber; + int iFlag, iTSpacesOffs; + unsigned char vert_num[4]; +} STriInfo; + +typedef struct { + SVec3 vOs; + float fMagS; + SVec3 vOt; + float fMagT; + int iCounter; // this is to average back into quads. + tbool bOrient; +} STSpace; + +static int GenerateInitialVerticesIndexList(STriInfo pTriInfos[], int piTriList_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn); +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext); + +static int MakeIndex(const int iFace, const int iVert) +{ + assert(iVert>=0 && iVert<4 && iFace>=0); + return (iFace<<2) | (iVert&0x3); +} + +static void IndexToData(int * piFace, int * piVert, const int iIndexIn) +{ + piVert[0] = iIndexIn&0x3; + piFace[0] = iIndexIn>>2; +} + +static STSpace AvgTSpace(const STSpace * pTS0, const STSpace * pTS1) +{ + STSpace ts_res; + + // this if is important. Due to floating point precision + // averaging when ts0==ts1 will cause a slight difference + // which results in tangent space splits later on + if (pTS0->fMagS==pTS1->fMagS && pTS0->fMagT==pTS1->fMagT && + veq(pTS0->vOs,pTS1->vOs) && veq(pTS0->vOt, pTS1->vOt)) + { + ts_res.fMagS = pTS0->fMagS; + ts_res.fMagT = pTS0->fMagT; + ts_res.vOs = pTS0->vOs; + ts_res.vOt = pTS0->vOt; + } + else + { + ts_res.fMagS = 0.5f*(pTS0->fMagS+pTS1->fMagS); + ts_res.fMagT = 0.5f*(pTS0->fMagT+pTS1->fMagT); + ts_res.vOs = vadd(pTS0->vOs,pTS1->vOs); + ts_res.vOt = vadd(pTS0->vOt,pTS1->vOt); + if ( VNotZero(ts_res.vOs) ) ts_res.vOs = Normalize(ts_res.vOs); + if ( VNotZero(ts_res.vOt) ) ts_res.vOt = Normalize(ts_res.vOt); + } + + return ts_res; +} + + + +static SVec3 GetPosition(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index); +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index); + + +// degen triangles +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris); +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris); + + +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext) +{ + return genTangSpace(pContext, 180.0f); +} + +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold) +{ + // count nr_triangles + int * piTriListIn = NULL, * piGroupTrianglesBuffer = NULL; + STriInfo * pTriInfos = NULL; + SGroup * pGroups = NULL; + STSpace * psTspace = NULL; + int iNrTrianglesIn = 0, f=0, t=0, i=0; + int iNrTSPaces = 0, iTotTris = 0, iDegenTriangles = 0, iNrMaxGroups = 0; + int iNrActiveGroups = 0, index = 0; + const int iNrFaces = pContext->m_pInterface->m_getNumFaces(pContext); + tbool bRes = TFALSE; + const float fThresCos = (float) cos((fAngularThreshold*(float)M_PI)/180.0f); + + // verify all call-backs have been set + if ( pContext->m_pInterface->m_getNumFaces==NULL || + pContext->m_pInterface->m_getNumVerticesOfFace==NULL || + pContext->m_pInterface->m_getPosition==NULL || + pContext->m_pInterface->m_getNormal==NULL || + pContext->m_pInterface->m_getTexCoord==NULL ) + return TFALSE; + + // count triangles on supported faces + for (f=0; fm_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts==3) ++iNrTrianglesIn; + else if (verts==4) iNrTrianglesIn += 2; + } + if (iNrTrianglesIn<=0) return TFALSE; + + // allocate memory for an index list + piTriListIn = (int *) malloc(sizeof(int)*3*iNrTrianglesIn); + pTriInfos = (STriInfo *) malloc(sizeof(STriInfo)*iNrTrianglesIn); + if (piTriListIn==NULL || pTriInfos==NULL) + { + if (piTriListIn!=NULL) free(piTriListIn); + if (pTriInfos!=NULL) free(pTriInfos); + return TFALSE; + } + + // make an initial triangle --> face index list + iNrTSPaces = GenerateInitialVerticesIndexList(pTriInfos, piTriListIn, pContext, iNrTrianglesIn); + + // make a welded index list of identical positions and attributes (pos, norm, texc) + //printf("gen welded index list begin\n"); + GenerateSharedVerticesIndexList(piTriListIn, pContext, iNrTrianglesIn); + //printf("gen welded index list end\n"); + + // Mark all degenerate triangles + iTotTris = iNrTrianglesIn; + iDegenTriangles = 0; + for (t=0; tm_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + + // I've decided to let degenerate triangles and group-with-anythings + // vary between left/right hand coordinate systems at the vertices. + // All healthy triangles on the other hand are built to always be either or. + + /*// force the coordinate system orientation to be uniform for every face. + // (this is already the case for good triangles but not for + // degenerate ones and those with bGroupWithAnything==true) + bool bOrient = psTspace[index].bOrient; + if (psTspace[index].iCounter == 0) // tspace was not derived from a group + { + // look for a space created in GenerateTSpaces() by iCounter>0 + bool bNotFound = true; + int i=1; + while (i 0) bNotFound=false; + else ++i; + } + if (!bNotFound) bOrient = psTspace[index+i].bOrient; + }*/ + + // set data + for (i=0; ivOs.x, pTSpace->vOs.y, pTSpace->vOs.z}; + float bitang[] = {pTSpace->vOt.x, pTSpace->vOt.y, pTSpace->vOt.z}; + if (pContext->m_pInterface->m_setTSpace!=NULL) + pContext->m_pInterface->m_setTSpace(pContext, tang, bitang, pTSpace->fMagS, pTSpace->fMagT, pTSpace->bOrient, f, i); + if (pContext->m_pInterface->m_setTSpaceBasic!=NULL) + pContext->m_pInterface->m_setTSpaceBasic(pContext, tang, pTSpace->bOrient==TTRUE ? 1.0f : (-1.0f), f, i); + + ++index; + } + } + + free(psTspace); + + + return TTRUE; +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef struct { + float vert[3]; + int index; +} STmpVert; + +static const int g_iCells = 2048; + +#ifdef _MSC_VER + #define NOINLINE __declspec(noinline) +#else + #define NOINLINE __attribute__ ((noinline)) +#endif + +// it is IMPORTANT that this function is called to evaluate the hash since +// inlining could potentially reorder instructions and generate different +// results for the same effective input value fVal. +static NOINLINE int FindGridCell(const float fMin, const float fMax, const float fVal) +{ + const float fIndex = g_iCells * ((fVal-fMin)/(fMax-fMin)); + const int iIndex = (int)fIndex; + return iIndex < g_iCells ? (iIndex >= 0 ? iIndex : 0) : (g_iCells - 1); +} + +static void MergeVertsFast(int piTriList_in_and_out[], STmpVert pTmpVert[], const SMikkTSpaceContext * pContext, const int iL_in, const int iR_in); +static void MergeVertsSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int pTable[], const int iEntries); +static void GenerateSharedVerticesIndexListSlow(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn); + +static void GenerateSharedVerticesIndexList(int piTriList_in_and_out[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + + // Generate bounding box + int * piHashTable=NULL, * piHashCount=NULL, * piHashOffsets=NULL, * piHashCount2=NULL; + STmpVert * pTmpVert = NULL; + int i=0, iChannel=0, k=0, e=0; + int iMaxCount=0; + SVec3 vMin = GetPosition(pContext, 0), vMax = vMin, vDim; + float fMin, fMax; + for (i=1; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + + const SVec3 vP = GetPosition(pContext, index); + if (vMin.x > vP.x) vMin.x = vP.x; + else if (vMax.x < vP.x) vMax.x = vP.x; + if (vMin.y > vP.y) vMin.y = vP.y; + else if (vMax.y < vP.y) vMax.y = vP.y; + if (vMin.z > vP.z) vMin.z = vP.z; + else if (vMax.z < vP.z) vMax.z = vP.z; + } + + vDim = vsub(vMax,vMin); + iChannel = 0; + fMin = vMin.x; fMax=vMax.x; + if (vDim.y>vDim.x && vDim.y>vDim.z) + { + iChannel=1; + fMin = vMin.y, fMax=vMax.y; + } + else if (vDim.z>vDim.x) + { + iChannel=2; + fMin = vMin.z, fMax=vMax.z; + } + + // make allocations + piHashTable = (int *) malloc(sizeof(int)*iNrTrianglesIn*3); + piHashCount = (int *) malloc(sizeof(int)*g_iCells); + piHashOffsets = (int *) malloc(sizeof(int)*g_iCells); + piHashCount2 = (int *) malloc(sizeof(int)*g_iCells); + + if (piHashTable==NULL || piHashCount==NULL || piHashOffsets==NULL || piHashCount2==NULL) + { + if (piHashTable!=NULL) free(piHashTable); + if (piHashCount!=NULL) free(piHashCount); + if (piHashOffsets!=NULL) free(piHashOffsets); + if (piHashCount2!=NULL) free(piHashCount2); + GenerateSharedVerticesIndexListSlow(piTriList_in_and_out, pContext, iNrTrianglesIn); + return; + } + memset(piHashCount, 0, sizeof(int)*g_iCells); + memset(piHashCount2, 0, sizeof(int)*g_iCells); + + // count amount of elements in each cell unit + for (i=0; i<(iNrTrianglesIn*3); i++) + { + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const float fVal = iChannel==0 ? vP.x : (iChannel==1 ? vP.y : vP.z); + const int iCell = FindGridCell(fMin, fMax, fVal); + ++piHashCount[iCell]; + } + + // evaluate start index of each cell. + piHashOffsets[0]=0; + for (k=1; kpTmpVert[l].vert[c]) fvMin[c]=pTmpVert[l].vert[c]; + else if (fvMax[c]dx && dy>dz) channel=1; + else if (dz>dx) channel=2; + + fSep = 0.5f*(fvMax[channel]+fvMin[channel]); + + // terminate recursion when the separation/average value + // is no longer strictly between fMin and fMax values. + if (fSep>=fvMax[channel] || fSep<=fvMin[channel]) + { + // complete the weld + for (l=iL_in; l<=iR_in; l++) + { + int i = pTmpVert[l].index; + const int index = piTriList_in_and_out[i]; + const SVec3 vP = GetPosition(pContext, index); + const SVec3 vN = GetNormal(pContext, index); + const SVec3 vT = GetTexCoord(pContext, index); + + tbool bNotFound = TTRUE; + int l2=iL_in, i2rec=-1; + while (l20); // at least 2 entries + + // separate (by fSep) all points between iL_in and iR_in in pTmpVert[] + while (iL < iR) + { + tbool bReadyLeftSwap = TFALSE, bReadyRightSwap = TFALSE; + while ((!bReadyLeftSwap) && iL=iL_in && iL<=iR_in); + bReadyLeftSwap = !(pTmpVert[iL].vert[channel]=iL_in && iR<=iR_in); + bReadyRightSwap = pTmpVert[iR].vert[channel]m_pInterface->m_getNumFaces(pContext); f++) + { + const int verts = pContext->m_pInterface->m_getNumVerticesOfFace(pContext, f); + if (verts!=3 && verts!=4) continue; + + pTriInfos[iDstTriIndex].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex].iTSpacesOffs = iTSpacesOffs; + + if (verts==3) + { + unsigned char * pVerts = pTriInfos[iDstTriIndex].vert_num; + pVerts[0]=0; pVerts[1]=1; pVerts[2]=2; + piTriList_out[iDstTriIndex*3+0] = MakeIndex(f, 0); + piTriList_out[iDstTriIndex*3+1] = MakeIndex(f, 1); + piTriList_out[iDstTriIndex*3+2] = MakeIndex(f, 2); + ++iDstTriIndex; // next + } + else + { + { + pTriInfos[iDstTriIndex+1].iOrgFaceNumber = f; + pTriInfos[iDstTriIndex+1].iTSpacesOffs = iTSpacesOffs; + } + + { + // need an order independent way to evaluate + // tspace on quads. This is done by splitting + // along the shortest diagonal. + const int i0 = MakeIndex(f, 0); + const int i1 = MakeIndex(f, 1); + const int i2 = MakeIndex(f, 2); + const int i3 = MakeIndex(f, 3); + const SVec3 T0 = GetTexCoord(pContext, i0); + const SVec3 T1 = GetTexCoord(pContext, i1); + const SVec3 T2 = GetTexCoord(pContext, i2); + const SVec3 T3 = GetTexCoord(pContext, i3); + const float distSQ_02 = LengthSquared(vsub(T2,T0)); + const float distSQ_13 = LengthSquared(vsub(T3,T1)); + tbool bQuadDiagIs_02; + if (distSQ_02m_pInterface->m_getPosition(pContext, pos, iF, iI); + res.x=pos[0]; res.y=pos[1]; res.z=pos[2]; + return res; +} + +static SVec3 GetNormal(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float norm[3]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getNormal(pContext, norm, iF, iI); + res.x=norm[0]; res.y=norm[1]; res.z=norm[2]; + return res; +} + +static SVec3 GetTexCoord(const SMikkTSpaceContext * pContext, const int index) +{ + int iF, iI; + SVec3 res; float texc[2]; + IndexToData(&iF, &iI, index); + pContext->m_pInterface->m_getTexCoord(pContext, texc, iF, iI); + res.x=texc[0]; res.y=texc[1]; res.z=1.0f; + return res; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +typedef union { + struct + { + int i0, i1, f; + }; + int array[3]; +} SEdge; + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn); +static void BuildNeighborsSlow(STriInfo pTriInfos[], const int piTriListIn[], const int iNrTrianglesIn); + +// returns the texture area times 2 +static float CalcTexArea(const SMikkTSpaceContext * pContext, const int indices[]) +{ + const SVec3 t1 = GetTexCoord(pContext, indices[0]); + const SVec3 t2 = GetTexCoord(pContext, indices[1]); + const SVec3 t3 = GetTexCoord(pContext, indices[2]); + + const float t21x = t2.x-t1.x; + const float t21y = t2.y-t1.y; + const float t31x = t3.x-t1.x; + const float t31y = t3.y-t1.y; + + const float fSignedAreaSTx2 = t21x*t31y - t21y*t31x; + + return fSignedAreaSTx2<0 ? (-fSignedAreaSTx2) : fSignedAreaSTx2; +} + +static void InitTriInfo(STriInfo pTriInfos[], const int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn) +{ + int f=0, i=0, t=0; + // pTriInfos[f].iFlag is cleared in GenerateInitialVerticesIndexList() which is called before this function. + + // generate neighbor info list + for (f=0; f0 ? ORIENT_PRESERVING : 0); + + if ( NotZero(fSignedAreaSTx2) ) + { + const float fAbsArea = fabsf(fSignedAreaSTx2); + const float fLenOs = Length(vOs); + const float fLenOt = Length(vOt); + const float fS = (pTriInfos[f].iFlag&ORIENT_PRESERVING)==0 ? (-1.0f) : 1.0f; + if ( NotZero(fLenOs) ) pTriInfos[f].vOs = vscale(fS/fLenOs, vOs); + if ( NotZero(fLenOt) ) pTriInfos[f].vOt = vscale(fS/fLenOt, vOt); + + // evaluate magnitudes prior to normalization of vOs and vOt + pTriInfos[f].fMagS = fLenOs / fAbsArea; + pTriInfos[f].fMagT = fLenOt / fAbsArea; + + // if this is a good triangle + if ( NotZero(pTriInfos[f].fMagS) && NotZero(pTriInfos[f].fMagT)) + pTriInfos[f].iFlag &= (~GROUP_WITH_ANY); + } + } + + // force otherwise healthy quads to a fixed orientation + while (t<(iNrTrianglesIn-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + + // bad triangles should already have been removed by + // DegenPrologue(), but just in case check bIsDeg_a and bIsDeg_a are false + if ((bIsDeg_a||bIsDeg_b)==TFALSE) + { + const tbool bOrientA = (pTriInfos[t].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bOrientB = (pTriInfos[t+1].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + // if this happens the quad has extremely bad mapping!! + if (bOrientA!=bOrientB) + { + //printf("found quad with bad mapping\n"); + tbool bChooseOrientFirstTri = TFALSE; + if ((pTriInfos[t+1].iFlag&GROUP_WITH_ANY)!=0) bChooseOrientFirstTri = TTRUE; + else if ( CalcTexArea(pContext, &piTriListIn[t*3+0]) >= CalcTexArea(pContext, &piTriListIn[(t+1)*3+0]) ) + bChooseOrientFirstTri = TTRUE; + + // force match + { + const int t0 = bChooseOrientFirstTri ? t : (t+1); + const int t1 = bChooseOrientFirstTri ? (t+1) : t; + pTriInfos[t1].iFlag &= (~ORIENT_PRESERVING); // clear first + pTriInfos[t1].iFlag |= (pTriInfos[t0].iFlag&ORIENT_PRESERVING); // copy bit + } + } + } + t += 2; + } + else + ++t; + } + + // match up edge pairs + { + SEdge * pEdges = (SEdge *) malloc(sizeof(SEdge)*iNrTrianglesIn*3); + if (pEdges==NULL) + BuildNeighborsSlow(pTriInfos, piTriListIn, iNrTrianglesIn); + else + { + BuildNeighborsFast(pTriInfos, pEdges, piTriListIn, iNrTrianglesIn); + + free(pEdges); + } + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], const int iMyTriIndex, SGroup * pGroup); +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex); + +static int Build4RuleGroups(STriInfo pTriInfos[], SGroup pGroups[], int piGroupTrianglesBuffer[], const int piTriListIn[], const int iNrTrianglesIn) +{ + const int iNrMaxGroups = iNrTrianglesIn*3; + int iNrActiveGroups = 0; + int iOffset = 0, f=0, i=0; + (void)iNrMaxGroups; /* quiet warnings in non debug mode */ + for (f=0; fiVertexRepresentitive = vert_index; + pTriInfos[f].AssignedGroup[i]->bOrientPreservering = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0; + pTriInfos[f].AssignedGroup[i]->iNrFaces = 0; + pTriInfos[f].AssignedGroup[i]->pFaceIndices = &piGroupTrianglesBuffer[iOffset]; + ++iNrActiveGroups; + + AddTriToGroup(pTriInfos[f].AssignedGroup[i], f); + bOrPre = (pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + neigh_indexL = pTriInfos[f].FaceNeighbors[i]; + neigh_indexR = pTriInfos[f].FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexL, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexL].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + if (neigh_indexR>=0) // neighbor + { + const tbool bAnswer = + AssignRecur(piTriListIn, pTriInfos, neigh_indexR, + pTriInfos[f].AssignedGroup[i] ); + + const tbool bOrPre2 = (pTriInfos[neigh_indexR].iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + const tbool bDiff = bOrPre!=bOrPre2 ? TTRUE : TFALSE; + assert(bAnswer || bDiff); + (void)bAnswer, (void)bDiff; /* quiet warnings in non debug mode */ + } + + // update offset + iOffset += pTriInfos[f].AssignedGroup[i]->iNrFaces; + // since the groups are disjoint a triangle can never + // belong to more than 3 groups. Subsequently something + // is completely screwed if this assertion ever hits. + assert(iOffset <= iNrMaxGroups); + } + } + } + + return iNrActiveGroups; +} + +static void AddTriToGroup(SGroup * pGroup, const int iTriIndex) +{ + pGroup->pFaceIndices[pGroup->iNrFaces] = iTriIndex; + ++pGroup->iNrFaces; +} + +static tbool AssignRecur(const int piTriListIn[], STriInfo psTriInfos[], + const int iMyTriIndex, SGroup * pGroup) +{ + STriInfo * pMyTriInfo = &psTriInfos[iMyTriIndex]; + + // track down vertex + const int iVertRep = pGroup->iVertexRepresentitive; + const int * pVerts = &piTriListIn[3*iMyTriIndex+0]; + int i=-1; + if (pVerts[0]==iVertRep) i=0; + else if (pVerts[1]==iVertRep) i=1; + else if (pVerts[2]==iVertRep) i=2; + assert(i>=0 && i<3); + + // early out + if (pMyTriInfo->AssignedGroup[i] == pGroup) return TTRUE; + else if (pMyTriInfo->AssignedGroup[i]!=NULL) return TFALSE; + if ((pMyTriInfo->iFlag&GROUP_WITH_ANY)!=0) + { + // first to group with a group-with-anything triangle + // determines it's orientation. + // This is the only existing order dependency in the code!! + if ( pMyTriInfo->AssignedGroup[0] == NULL && + pMyTriInfo->AssignedGroup[1] == NULL && + pMyTriInfo->AssignedGroup[2] == NULL ) + { + pMyTriInfo->iFlag &= (~ORIENT_PRESERVING); + pMyTriInfo->iFlag |= (pGroup->bOrientPreservering ? ORIENT_PRESERVING : 0); + } + } + { + const tbool bOrient = (pMyTriInfo->iFlag&ORIENT_PRESERVING)!=0 ? TTRUE : TFALSE; + if (bOrient != pGroup->bOrientPreservering) return TFALSE; + } + + AddTriToGroup(pGroup, iMyTriIndex); + pMyTriInfo->AssignedGroup[i] = pGroup; + + { + const int neigh_indexL = pMyTriInfo->FaceNeighbors[i]; + const int neigh_indexR = pMyTriInfo->FaceNeighbors[i>0?(i-1):2]; + if (neigh_indexL>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexL, pGroup); + if (neigh_indexR>=0) + AssignRecur(piTriListIn, psTriInfos, neigh_indexR, pGroup); + } + + + + return TTRUE; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2); +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed); +static STSpace EvalTspace(int face_indices[], const int iFaces, const int piTriListIn[], const STriInfo pTriInfos[], const SMikkTSpaceContext * pContext, const int iVertexRepresentitive); + +static tbool GenerateTSpaces(STSpace psTspace[], const STriInfo pTriInfos[], const SGroup pGroups[], + const int iNrActiveGroups, const int piTriListIn[], const float fThresCos, + const SMikkTSpaceContext * pContext) +{ + STSpace * pSubGroupTspace = NULL; + SSubGroup * pUniSubGroups = NULL; + int * pTmpMembers = NULL; + int iMaxNrFaces=0, iUniqueTspaces=0, g=0, i=0; + for (g=0; giNrFaces; i++) // triangles + { + const int f = pGroup->pFaceIndices[i]; // triangle number + int index=-1, iVertIndex=-1, iOF_1=-1, iMembers=0, j=0, l=0; + SSubGroup tmp_group; + tbool bFound; + SVec3 n, vOs, vOt; + if (pTriInfos[f].AssignedGroup[0]==pGroup) index=0; + else if (pTriInfos[f].AssignedGroup[1]==pGroup) index=1; + else if (pTriInfos[f].AssignedGroup[2]==pGroup) index=2; + assert(index>=0 && index<3); + + iVertIndex = piTriListIn[f*3+index]; + assert(iVertIndex==pGroup->iVertexRepresentitive); + + // is normalized already + n = GetNormal(pContext, iVertIndex); + + // project + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + // original face number + iOF_1 = pTriInfos[f].iOrgFaceNumber; + + iMembers = 0; + for (j=0; jiNrFaces; j++) + { + const int t = pGroup->pFaceIndices[j]; // triangle number + const int iOF_2 = pTriInfos[t].iOrgFaceNumber; + + // project + SVec3 vOs2 = vsub(pTriInfos[t].vOs, vscale(vdot(n,pTriInfos[t].vOs), n)); + SVec3 vOt2 = vsub(pTriInfos[t].vOt, vscale(vdot(n,pTriInfos[t].vOt), n)); + if ( VNotZero(vOs2) ) vOs2 = Normalize(vOs2); + if ( VNotZero(vOt2) ) vOt2 = Normalize(vOt2); + + { + const tbool bAny = ( (pTriInfos[f].iFlag | pTriInfos[t].iFlag) & GROUP_WITH_ANY )!=0 ? TTRUE : TFALSE; + // make sure triangles which belong to the same quad are joined. + const tbool bSameOrgFace = iOF_1==iOF_2 ? TTRUE : TFALSE; + + const float fCosS = vdot(vOs,vOs2); + const float fCosT = vdot(vOt,vOt2); + + assert(f!=t || bSameOrgFace); // sanity check + if (bAny || bSameOrgFace || (fCosS>fThresCos && fCosT>fThresCos)) + pTmpMembers[iMembers++] = t; + } + } + + // sort pTmpMembers + tmp_group.iNrFaces = iMembers; + tmp_group.pTriMembers = pTmpMembers; + if (iMembers>1) + { + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + QuickSort(pTmpMembers, 0, iMembers-1, uSeed); + } + + // look for an existing match + bFound = TFALSE; + l=0; + while (liVertexRepresentitive); + ++iUniqueSubGroups; + } + + // output tspace + { + const int iOffs = pTriInfos[f].iTSpacesOffs; + const int iVert = pTriInfos[f].vert_num[index]; + STSpace * pTS_out = &psTspace[iOffs+iVert]; + assert(pTS_out->iCounter<2); + assert(((pTriInfos[f].iFlag&ORIENT_PRESERVING)!=0) == pGroup->bOrientPreservering); + if (pTS_out->iCounter==1) + { + *pTS_out = AvgTSpace(pTS_out, &pSubGroupTspace[l]); + pTS_out->iCounter = 2; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + else + { + assert(pTS_out->iCounter==0); + *pTS_out = pSubGroupTspace[l]; + pTS_out->iCounter = 1; // update counter + pTS_out->bOrient = pGroup->bOrientPreservering; + } + } + } + + // clean up and offset iUniqueTspaces + for (s=0; s=0 && i<3); + + // project + index = piTriListIn[3*f+i]; + n = GetNormal(pContext, index); + vOs = vsub(pTriInfos[f].vOs, vscale(vdot(n,pTriInfos[f].vOs), n)); + vOt = vsub(pTriInfos[f].vOt, vscale(vdot(n,pTriInfos[f].vOt), n)); + if ( VNotZero(vOs) ) vOs = Normalize(vOs); + if ( VNotZero(vOt) ) vOt = Normalize(vOt); + + i2 = piTriListIn[3*f + (i<2?(i+1):0)]; + i1 = piTriListIn[3*f + i]; + i0 = piTriListIn[3*f + (i>0?(i-1):2)]; + + p0 = GetPosition(pContext, i0); + p1 = GetPosition(pContext, i1); + p2 = GetPosition(pContext, i2); + v1 = vsub(p0,p1); + v2 = vsub(p2,p1); + + // project + v1 = vsub(v1, vscale(vdot(n,v1),n)); if ( VNotZero(v1) ) v1 = Normalize(v1); + v2 = vsub(v2, vscale(vdot(n,v2),n)); if ( VNotZero(v2) ) v2 = Normalize(v2); + + // weight contribution by the angle + // between the two edge vectors + fCos = vdot(v1,v2); fCos=fCos>1?1:(fCos<(-1) ? (-1) : fCos); + fAngle = (float) acos(fCos); + fMagS = pTriInfos[f].fMagS; + fMagT = pTriInfos[f].fMagT; + + res.vOs=vadd(res.vOs, vscale(fAngle,vOs)); + res.vOt=vadd(res.vOt,vscale(fAngle,vOt)); + res.fMagS+=(fAngle*fMagS); + res.fMagT+=(fAngle*fMagT); + fAngleSum += fAngle; + } + } + + // normalize + if ( VNotZero(res.vOs) ) res.vOs = Normalize(res.vOs); + if ( VNotZero(res.vOt) ) res.vOt = Normalize(res.vOt); + if (fAngleSum>0) + { + res.fMagS /= fAngleSum; + res.fMagT /= fAngleSum; + } + + return res; +} + +static tbool CompareSubGroups(const SSubGroup * pg1, const SSubGroup * pg2) +{ + tbool bStillSame=TTRUE; + int i=0; + if (pg1->iNrFaces!=pg2->iNrFaces) return TFALSE; + while (iiNrFaces && bStillSame) + { + bStillSame = pg1->pTriMembers[i]==pg2->pTriMembers[i] ? TTRUE : TFALSE; + if (bStillSame) ++i; + } + return bStillSame; +} + +static void QuickSort(int* pSortBuffer, int iLeft, int iRight, unsigned int uSeed) +{ + int iL, iR, n, index, iMid, iTmp; + + // Random + unsigned int t=uSeed&31; + t=(uSeed<>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft; iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL]; + + + do + { + while (pSortBuffer[iL] < iMid) + ++iL; + while (pSortBuffer[iR] > iMid) + --iR; + + if (iL <= iR) + { + iTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = iTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSort(pSortBuffer, iLeft, iR, uSeed); + if (iL < iRight) + QuickSort(pSortBuffer, iL, iRight, uSeed); +} + +///////////////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////// + +static void QuickSortEdges(SEdge * pSortBuffer, int iLeft, int iRight, const int channel, unsigned int uSeed); +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in); + +static void BuildNeighborsFast(STriInfo pTriInfos[], SEdge * pEdges, const int piTriListIn[], const int iNrTrianglesIn) +{ + // build array of edges + unsigned int uSeed = INTERNAL_RND_SORT_SEED; // could replace with a random seed? + int iEntries=0, iCurStartIndex=-1, f=0, i=0; + for (f=0; f pSortBuffer[iRight].array[channel]) + { + sTmp = pSortBuffer[iLeft]; + pSortBuffer[iLeft] = pSortBuffer[iRight]; + pSortBuffer[iRight] = sTmp; + } + return; + } + + // Random + t=uSeed&31; + t=(uSeed<>(32-t)); + uSeed=uSeed+t+3; + // Random end + + iL=iLeft, iR=iRight; + n = (iR-iL)+1; + assert(n>=0); + index = (int) (uSeed%n); + + iMid=pSortBuffer[index + iL].array[channel]; + + do + { + while (pSortBuffer[iL].array[channel] < iMid) + ++iL; + while (pSortBuffer[iR].array[channel] > iMid) + --iR; + + if (iL <= iR) + { + sTmp = pSortBuffer[iL]; + pSortBuffer[iL] = pSortBuffer[iR]; + pSortBuffer[iR] = sTmp; + ++iL; --iR; + } + } + while (iL <= iR); + + if (iLeft < iR) + QuickSortEdges(pSortBuffer, iLeft, iR, channel, uSeed); + if (iL < iRight) + QuickSortEdges(pSortBuffer, iL, iRight, channel, uSeed); +} + +// resolve ordering and edge number +static void GetEdge(int * i0_out, int * i1_out, int * edgenum_out, const int indices[], const int i0_in, const int i1_in) +{ + *edgenum_out = -1; + + // test if first index is on the edge + if (indices[0]==i0_in || indices[0]==i1_in) + { + // test if second index is on the edge + if (indices[1]==i0_in || indices[1]==i1_in) + { + edgenum_out[0]=0; // first edge + i0_out[0]=indices[0]; + i1_out[0]=indices[1]; + } + else + { + edgenum_out[0]=2; // third edge + i0_out[0]=indices[2]; + i1_out[0]=indices[0]; + } + } + else + { + // only second and third index is on the edge + edgenum_out[0]=1; // second edge + i0_out[0]=indices[1]; + i1_out[0]=indices[2]; + } +} + + +///////////////////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////// Degenerate triangles //////////////////////////////////// + +static void DegenPrologue(STriInfo pTriInfos[], int piTriList_out[], const int iNrTrianglesIn, const int iTotTris) +{ + int iNextGoodTriangleSearchIndex=-1; + tbool bStillFindingGoodOnes; + + // locate quads with only one good triangle + int t=0; + while (t<(iTotTris-1)) + { + const int iFO_a = pTriInfos[t].iOrgFaceNumber; + const int iFO_b = pTriInfos[t+1].iOrgFaceNumber; + if (iFO_a==iFO_b) // this is a quad + { + const tbool bIsDeg_a = (pTriInfos[t].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + const tbool bIsDeg_b = (pTriInfos[t+1].iFlag&MARK_DEGENERATE)!=0 ? TTRUE : TFALSE; + if ((bIsDeg_a^bIsDeg_b)!=0) + { + pTriInfos[t].iFlag |= QUAD_ONE_DEGEN_TRI; + pTriInfos[t+1].iFlag |= QUAD_ONE_DEGEN_TRI; + } + t += 2; + } + else + ++t; + } + + // reorder list so all degen triangles are moved to the back + // without reordering the good triangles + iNextGoodTriangleSearchIndex = 1; + t=0; + bStillFindingGoodOnes = TTRUE; + while (t (t+1)); + + // swap triangle t0 and t1 + if (!bJustADegenerate) + { + int i=0; + for (i=0; i<3; i++) + { + const int index = piTriList_out[t0*3+i]; + piTriList_out[t0*3+i] = piTriList_out[t1*3+i]; + piTriList_out[t1*3+i] = index; + } + { + const STriInfo tri_info = pTriInfos[t0]; + pTriInfos[t0] = pTriInfos[t1]; + pTriInfos[t1] = tri_info; + } + } + else + bStillFindingGoodOnes = TFALSE; // this is not supposed to happen + } + + if (bStillFindingGoodOnes) ++t; + } + + assert(bStillFindingGoodOnes); // code will still work. + assert(iNrTrianglesIn == t); +} + +static void DegenEpilogue(STSpace psTspace[], STriInfo pTriInfos[], int piTriListIn[], const SMikkTSpaceContext * pContext, const int iNrTrianglesIn, const int iTotTris) +{ + int t=0, i=0; + // deal with degenerate triangles + // punishment for degenerate triangles is O(N^2) + for (t=iNrTrianglesIn; t http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf + * Note that though the tangent spaces at the vertices are generated in an order-independent way, + * by this implementation, the interpolated tangent space is still affected by which diagonal is + * chosen to split each quad. A sensible solution is to have your tools pipeline always + * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. + * If these have the same length then compare the diagonals defined by the texture coordinates. + * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin + * and also quad triangulator plugin. + */ + + +typedef int tbool; +typedef struct SMikkTSpaceContext SMikkTSpaceContext; + +typedef struct { + // Returns the number of faces (triangles/quads) on the mesh to be processed. + int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); + + // Returns the number of vertices on face number iFace + // iFace is a number in the range {0, 1, ..., getNumFaces()-1} + int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); + + // returns the position/normal/texcoord of the referenced face of vertex number iVert. + // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. + void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); + void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); + void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); + + // either (or both) of the two setTSpace callbacks can be set. + // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. + + // This function is used to return the tangent and fSign to the application. + // fvTangent is a unit length vector. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); + + // This function is used to return tangent space results to the application. + // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their + // true magnitudes which can be used for relief mapping effects. + // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. + // However, both are perpendicular to the vertex normal. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert); +} SMikkTSpaceInterface; + +struct SMikkTSpaceContext +{ + SMikkTSpaceInterface * m_pInterface; // initialized with callback functions + void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call) +}; + +// these are both thread safe! +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); + + +// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the +// normal map sampler must use the exact inverse of the pixel shader transformation. +// The most efficient transformation we can possibly do in the pixel shader is +// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. +// pixel shader (fast transform out) +// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); +// where vNt is the tangent space normal. The normal map sampler must likewise use the +// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. +// sampler does (exact inverse of pixel shader): +// float3 row0 = cross(vB, vN); +// float3 row1 = cross(vN, vT); +// float3 row2 = cross(vT, vB); +// float fSign = dot(vT, row0)<0 ? -1 : 1; +// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); +// where vNout is the sampled normal in some chosen 3D space. +// +// Should you choose to reconstruct the bitangent in the pixel shader instead +// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. +// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of +// quads as your renderer then problems will occur since the interpolated tangent spaces will differ +// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before +// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. +// However, this must be used both by the sampler and your tools/rendering pipeline. + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/mikktspace-sys/src/lib.rs b/mikktspace-sys/src/lib.rs index 84f7a6876b..f0644a98e5 100644 --- a/mikktspace-sys/src/lib.rs +++ b/mikktspace-sys/src/lib.rs @@ -4,7 +4,6 @@ mod ffi; use std::os::raw::*; use std::mem; -/// Rust FFI for the MikkTSpace implementation. const INTERFACE: ffi::SMikkTSpaceInterface = ffi::SMikkTSpaceInterface { m_getNumFaces: faces, m_getNumVerticesOfFace: vertices, @@ -16,26 +15,7 @@ const INTERFACE: ffi::SMikkTSpaceInterface = ffi::SMikkTSpaceInterface { }; pub struct Context { - faces: Box>, - positions: Box>, - tex_coords: Box>, - normals: Box>, -} - -/// Specifies whether a face is a triangle or a quad. -pub enum Face { - Triangle, - Quad, -} - -impl Face { - /// Returns the number of vertices bound by this face. - pub fn vertices(&self) -> i32 { - match self { - &Face::Triangle { .. } => 3, - &Face::Quad { .. } => 4, - } - } + faces: i32, } /// Returns the number of faces (triangles/quads) on the mesh to be processed. @@ -43,7 +23,7 @@ impl Face { extern "C" fn faces(pContext: *const ffi::SMikkTSpaceContext) -> c_int { unsafe { let m: *const Context = mem::transmute(pContext); - (*m).faces.len() as c_int + (*m).faces as c_int } } @@ -56,7 +36,7 @@ extern "C" fn vertices( ) -> c_int { unsafe { let _: *const Context = mem::transmute(pContext); - 3 + unimplemented!() } } @@ -134,19 +114,16 @@ extern "C" fn set_tspace( } impl Context { - /// Constructor for a MikkTSpace `Context`. - pub fn new(faces: F, positions: P, tex_coords: T, normals: N) -> Self - where - F: 'static + ExactSizeIterator, - P: 'static + ExactSizeIterator, - T: 'static + ExactSizeIterator, - N: 'static + ExactSizeIterator, - { + pub fn new() -> Self { Context { - faces: Box::new(faces), - positions: Box::new(positions), - tex_coords: Box::new(tex_coords), - normals: Box::new(normals), + faces: 3, } } } + +#[cfg(test)] +mod tests { + #[test] + fn it_works() { + } +} diff --git a/src/ffi.rs b/src/ffi.rs new file mode 100644 index 0000000000..05e700bf30 --- /dev/null +++ b/src/ffi.rs @@ -0,0 +1,129 @@ + +#![allow(bad_style)] +#![allow(dead_code)] + +use std::os::raw::*; + +pub type tbool = c_int; +pub const TFALSE: tbool = 0; +pub const TTRUE: tbool = 1; + +#[repr(C)] +pub struct SMikkTSpaceInterface { + /// Returns the number of faces (triangles/quads) on the mesh to be processed. + pub m_getNumFaces: extern "C" fn(pContext: *const SMikkTSpaceContext) -> c_int, + + /// Returns the number of vertices on face number iFace + /// iFace is a number in the range {0, 1, ..., getNumFaces()-1} + pub m_getNumVerticesOfFace: extern "C" fn( + pContext: *const SMikkTSpaceContext, + iFace: c_int, + ) -> c_int, + + /// Returns the position of the referenced face of vertex number + /// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. + pub m_getPosition: extern "C" fn( + pContext: *const SMikkTSpaceContext, + fvPosOut: *mut c_float, + iFace: c_int, + iVert: c_int, + ), + + /// Returns the normal of the referenced face of vertex number + /// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. + pub m_getNormal: extern "C" fn( + pContext: *const SMikkTSpaceContext, + fvNormOut: *mut c_float, + iFace: c_int, + iVert: c_int, + ), + + /// Returns the texcoord of the referenced face of vertex number + /// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. + pub m_getTexCoord: extern "C" fn( + pContext: *const SMikkTSpaceContext, + fvTexcOut: *mut c_float, + iFace: c_int, + iVert: c_int, + ), + + /// either (or both) of the two setTSpace callbacks can be set. + /// The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. + + /// This function is used to return the tangent and fSign to the application. + /// fvTangent is a unit length vector. + /// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + /// bitangent = fSign * cross(vN, tangent); + /// Note that the results are returned unindexed. It is possible to generate a new index list + /// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + /// DO NOT! use an already existing index list. + pub m_setTSpaceBasic: extern "C" fn( + pContext: *mut SMikkTSpaceContext, + fvTangent: *const c_float, + fSign: *const c_float, + iFace: c_int, + iVert: c_int, + ), + + /// This function is used to return tangent space results to the application. + /// fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their + /// true magnitudes which can be used for relief mapping effects. + /// fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. + /// However, both are perpendicular to the vertex normal. + /// For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + /// fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); + /// bitangent = fSign * cross(vN, tangent); + /// Note that the results are returned unindexed. It is possible to generate a new index list + /// But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + /// DO NOT! use an already existing index list. + pub m_setTSpace: extern "C" fn( + pContext: *mut SMikkTSpaceContext, + fvTangent: *const c_float, + fvBiTangent: *const c_float, + fMagS: *const c_float, + fMagT: *const c_float, + bIsOrientationPreserving: tbool, + iFace: c_int, + iVert: c_int, + ), +} + +/// these are both thread safe! +/// Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) +extern "system" { + pub fn genTangSpaceDefault(pContext: *const SMikkTSpaceContext) -> tbool; + #[allow(dead_code)] + pub fn genTangSpace(pContext: *const SMikkTSpaceContext, fAngularThreshold: c_float) -> tbool; +} + +/// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the +/// normal map sampler must use the exact inverse of the pixel shader transformation. +/// The most efficient transformation we can possibly do in the pixel shader is +/// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. +/// pixel shader (fast transform out) +/// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); +/// where vNt is the tangent space normal. The normal map sampler must likewise use the +/// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. +/// sampler does (exact inverse of pixel shader): +/// float3 row0 = cross(vB, vN); +/// float3 row1 = cross(vN, vT); +/// float3 row2 = cross(vT, vB); +/// float fSign = dot(vT, row0)<0 ? -1 : 1; +/// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); +/// where vNout is the sampled normal in some chosen 3D space. +/// +/// Should you choose to reconstruct the bitangent in the pixel shader instead +/// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. +/// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of +/// quads as your renderer then problems will occur since the interpolated tangent spaces will differ +/// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before +/// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. +/// However, this must be used both by the sampler and your tools/rendering pipeline. + +#[repr(C)] +pub struct SMikkTSpaceContext { + /// initialized with callback functions + pub m_pInterface: *const SMikkTSpaceInterface, + /// pointer to client side mesh data etc. (passed as the first parameter with every interface call) + pub m_pUserData: *mut c_void, +} diff --git a/src/lib.rs b/src/lib.rs index cdfbe1aa56..5f91648b90 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,6 +1,168 @@ -#[cfg(test)] -mod tests { - #[test] - fn it_works() { +#![allow(bad_style)] + +mod ffi; + +use std::os::raw::*; +use std::mem; +use std::ptr; + +/// Rust FFI for the MikkTSpace implementation. +const INTERFACE: ffi::SMikkTSpaceInterface = ffi::SMikkTSpaceInterface { + m_getNumFaces: faces, + m_getNumVerticesOfFace: vertices, + m_getPosition: position, + m_getNormal: normal, + m_getTexCoord: tex_coord, + m_setTSpaceBasic: set_tspace_basic, + m_setTSpace: set_tspace, +}; + +struct Closures<'a> { + pub vertices_per_face: &'a Fn() -> usize, + + /// Returns the number of faces. + pub face_count: &'a Fn() -> usize, + + /// Returns the positions of the indexed face. + pub position: &'a Fn(usize, usize) -> &'a [f32; 3], + + /// Returns the normals of the indexed face. + pub normal: &'a Fn(usize, usize) -> &'a [f32; 3], + + /// Returns the texture co-ordinates of the indexed face. + pub tex_coord: &'a Fn(usize, usize) -> &'a [f32; 2], + + /// Sets the generated tangent for the indexed face. + pub set_tangent: &'a mut FnMut(usize, usize, [f32; 4]), +} + +/// Returns the number of faces (triangles/quads) on the mesh to be processed. +extern "C" fn faces(pContext: *const ffi::SMikkTSpaceContext) -> c_int { + unsafe { + let x = (*pContext).m_pUserData as *const Closures; + ((*x).face_count)() as c_int } } + +/// Returns the number of vertices on face number iFace +/// iFace is a number in the range {0, 1, ..., getNumFaces()-1} +extern "C" fn vertices( + pContext: *const ffi::SMikkTSpaceContext, + _iFace: c_int, +) -> c_int { + unsafe { + let x = (*pContext).m_pUserData as *const Closures; + ((*x).vertices_per_face)() as c_int + } +} + +/// Returns the position of the referenced face of vertex number +/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. +extern "C" fn position( + pContext: *const ffi::SMikkTSpaceContext, + fvPosOut: *mut c_float, + iFace: c_int, + iVert: c_int, +) { + unsafe { + let x = (*pContext).m_pUserData as *const Closures; + let slice = ((*x).position)(iFace as usize, iVert as usize); + let src = slice.as_ptr() as *const c_float; + ptr::copy_nonoverlapping::(src, fvPosOut, 3); + } +} + +/// Returns the normal of the referenced face of vertex number +/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. +extern "C" fn normal( + pContext: *const ffi::SMikkTSpaceContext, + fvNormOut: *mut c_float, + iFace: c_int, + iVert: c_int, +) { + unsafe { + let x = (*pContext).m_pUserData as *const Closures; + let slice = ((*x).normal)(iFace as usize, iVert as usize); + let src = slice.as_ptr() as *const c_float; + ptr::copy_nonoverlapping::(src, fvNormOut, 3); + } +} + +/// Returns the texcoord of the referenced face of vertex number +/// iVert, in the range {0,1,2} for triangles, and {0,1,2,3} for quads. +extern "C" fn tex_coord( + pContext: *const ffi::SMikkTSpaceContext, + fvTexcOut: *mut c_float, + iFace: c_int, + iVert: c_int, +) { + unsafe { + let x = (*pContext).m_pUserData as *const Closures; + let slice = ((*x).tex_coord)(iFace as usize, iVert as usize); + let src = slice.as_ptr() as *const c_float; + ptr::copy_nonoverlapping::(src, fvTexcOut, 2); + } +} + +/// Returns the tangent and its sign to the application. +extern "C" fn set_tspace_basic( + pContext: *mut ffi::SMikkTSpaceContext, + fvTangent: *const c_float, + fSign: *const c_float, + iFace: c_int, + iVert: c_int, +) { + unsafe { + let x = (*pContext).m_pUserData as *mut Closures; + let mut tangent: [f32; 4] = mem::uninitialized(); + let dst: *mut c_float = tangent.as_mut_ptr(); + ptr::copy_nonoverlapping::(fvTangent, dst, 3); + tangent[3] = *fSign; + ((*x).set_tangent)(iFace as usize, iVert as usize, tangent); + } +} + +/// Returns tangent space results to the application. +extern "C" fn set_tspace( + _pContext: *mut ffi::SMikkTSpaceContext, + _fvTangent: *const c_float, + _fvBiTangent: *const c_float, + _fMagS: *const c_float, + _fMagT: *const c_float, + _bIsOrientationPreserving: ffi::tbool, + _iFace: c_int, + _iVert: c_int, +) { + unimplemented!() +} + +impl<'a> Closures<'a> { + pub fn generate(mut self) -> bool { + let ctx = ffi::SMikkTSpaceContext { + m_pInterface: &INTERFACE, + m_pUserData: &mut self as *mut Closures as *mut c_void, + }; + unsafe { + ffi::genTangSpaceDefault(&ctx) == ffi::TTRUE + } + } +} + +pub fn generate<'a>( + vertices_per_face: &'a Fn() -> usize, + face_count: &'a Fn() -> usize, + position: &'a Fn(usize, usize) -> &'a [f32; 3], + normal: &'a Fn(usize, usize) -> &'a [f32; 3], + tex_coord: &'a Fn(usize, usize) -> &'a [f32; 2], + set_tangent: &'a mut FnMut(usize, usize, [f32; 4]), +) -> bool { + let closures = Closures { + vertices_per_face, + face_count, + position, + normal, + tex_coord, + set_tangent, + }; + closures.generate() +} diff --git a/test-data/Avocado.bin b/test-data/Avocado.bin new file mode 100644 index 0000000000..79c28dec5f Binary files /dev/null and b/test-data/Avocado.bin differ diff --git a/test-data/Avocado.gltf b/test-data/Avocado.gltf new file mode 100644 index 0000000000..238e75b5c2 --- /dev/null +++ b/test-data/Avocado.gltf @@ -0,0 +1,185 @@ +{ + "accessors": [ + { + "bufferView": 0, + "componentType": 5126, + "count": 406, + "type": "VEC2", + "max": [ + 0.9824978, + -0.00462793233 + ], + "min": [ + 0.00678020436, + -0.997018039 + ] + }, + { + "bufferView": 1, + "componentType": 5126, + "count": 406, + "type": "VEC3", + "max": [ + 0.9959669, + 0.974968433, + 0.9986338 + ], + "min": [ + -0.995177031, + -0.9995665, + -0.9988549 + ] + }, + { + "bufferView": 2, + "componentType": 5126, + "count": 406, + "type": "VEC4", + "max": [ + 0.929839253, + 0.998867154, + 0.9930595, + 1.0 + ], + "min": [ + -0.999970555, + -0.999693751, + -0.995075762, + 1.0 + ] + }, + { + "bufferView": 3, + "componentType": 5126, + "count": 406, + "type": "VEC3", + "max": [ + 0.02128091, + 0.06284806, + 0.0138090011 + ], + "min": [ + -0.02128091, + -4.773855E-05, + -0.013809 + ] + }, + { + "bufferView": 4, + "componentType": 5123, + "count": 2046, + "type": "SCALAR", + "max": [ + 405 + ], + "min": [ + 0 + ] + } + ], + "asset": { + "generator": "glTF Tools for Unity", + "version": "2.0" + }, + "bufferViews": [ + { + "buffer": 0, + "byteLength": 3248 + }, + { + "buffer": 0, + "byteOffset": 3248, + "byteLength": 4872 + }, + { + "buffer": 0, + "byteOffset": 8120, + "byteLength": 6496 + }, + { + "buffer": 0, + "byteOffset": 14616, + "byteLength": 4872 + }, + { + "buffer": 0, + "byteOffset": 19488, + "byteLength": 4092 + } + ], + "buffers": [ + { + "uri": "Avocado.bin", + "byteLength": 23580 + } + ], + "images": [ + { + "uri": "Avocado_baseColor.png" + }, + { + "uri": "Avocado_roughnessMetallic.png" + }, + { + "uri": "Avocado_normal.png" + } + ], + "meshes": [ + { + "primitives": [ + { + "attributes": { + "TEXCOORD_0": 0, + "NORMAL": 1, + "TANGENT": 2, + "POSITION": 3 + }, + "indices": 4, + "material": 0 + } + ], + "name": "Avocado" + } + ], + "materials": [ + { + "pbrMetallicRoughness": { + "baseColorTexture": { + "index": 0 + }, + "metallicRoughnessTexture": { + "index": 1 + } + }, + "normalTexture": { + "index": 2 + }, + "name": "2256_Avocado_d" + } + ], + "nodes": [ + { + "mesh": 0, + "name": "Avocado" + } + ], + "scene": 0, + "scenes": [ + { + "nodes": [ + 0 + ] + } + ], + "textures": [ + { + "source": 0 + }, + { + "source": 1 + }, + { + "source": 2 + } + ] +} \ No newline at end of file diff --git a/test-data/Avocado.json.REMOVED.git-id b/test-data/Avocado.json.REMOVED.git-id new file mode 100644 index 0000000000..cf2f3389f2 --- /dev/null +++ b/test-data/Avocado.json.REMOVED.git-id @@ -0,0 +1 @@ +d785dd82e0c6344c7b8ea21b3bf5836fdaf05762 \ No newline at end of file diff --git a/test-data/Avocado.obj b/test-data/Avocado.obj new file mode 100644 index 0000000000..cfa8696c61 --- /dev/null +++ b/test-data/Avocado.obj @@ -0,0 +1,1900 @@ +v -0.0027212794 0.016771588 -0.009253962 +v -0.0036110256 0.015894055 -0.0081206625 +v -0.0013003338 0.014820849 -0.0075045973 +v -0.0010924106 0.016502861 -0.009438972 +v -0.0017466295 0.018695682 -0.011097683 +v -0.0038340627 0.018517554 -0.010284259 +v -0.005676434 0.018124256 -0.008878768 +v 0.00087683956 0.016428258 -0.009840459 +v 0.0008544619 0.01461544 -0.0075505916 +v 0.003093893 0.014918234 -0.0075980895 +v 0.0030003842 0.016363623 -0.009703802 +v 0.005180463 0.016774934 -0.009274959 +v 0.0055352035 0.019003915 -0.01073667 +v 0.006682096 0.017397586 -0.008470591 +v 0.0051500145 0.015853178 -0.0076412517 +v 0.0068167034 0.01728162 -0.007675768 +v 0.008332013 0.019456685 -0.0077065765 +v 0.0074443766 0.01935774 -0.00927683 +v 0.003081462 0.018637698 -0.011395 +v 0.003108063 0.021738732 -0.012493531 +v 0.005974777 0.02210493 -0.011744164 +v 0.008157968 0.022603476 -0.009836518 +v 0.006261516 0.025579216 -0.012090017 +v 0.008449819 0.02604046 -0.009994383 +v 0.009500919 0.024408579 -0.0077282004 +v 0.009310668 0.022740204 -0.0077252444 +v 0.008850863 0.02091272 -0.0077566234 +v 0.0001612103 0.021718249 -0.0128639955 +v 0.000567469 0.018720757 -0.011511858 +v -0.0027097943 0.021786835 -0.012576052 +v -0.0032275426 0.0252708 -0.013329696 +v -0.00006436318 0.025203649 -0.013413021 +v 0.0032955324 0.02516356 -0.013285204 +v -0.00013787538 0.02881489 -0.013769598 +v 0.003149328 0.028709603 -0.013259409 +v 0.006052418 0.02909521 -0.011925771 +v 0.00293603 0.032207314 -0.012751718 +v 0.0054854136 0.032516345 -0.011372526 +v 0.0072477437 0.03292663 -0.009388695 +v 0.008093519 0.029526874 -0.0097672595 +v 0.008557424 0.031615254 -0.007695801 +v 0.009048419 0.029689688 -0.0076218406 +v -0.005319561 0.02162351 -0.011713118 +v -0.006061148 0.025171233 -0.012392483 +v -0.0074155144 0.021248719 -0.01002924 +v -0.00822025 0.025009232 -0.010212998 +v -0.00862462 0.028825492 -0.010528574 +v -0.006391821 0.028922379 -0.012822353 +v -0.0034052092 0.028929299 -0.013809 +v -0.006025294 0.032809768 -0.012818484 +v -0.0030834393 0.032604005 -0.013367862 +v -0.00011192277 0.032380283 -0.013229622 +v -0.0023424532 0.03610682 -0.012342898 +v 0.00017764806 0.035710037 -0.012170106 +v 0.002588355 0.035336018 -0.011607924 +v 0.0023995154 0.037232935 -0.010914092 +v 0.0008302614 0.037958972 -0.011084382 +v 0.0016773045 0.039539106 -0.009857423 +v 0.003547685 0.03804001 -0.009995009 +v 0.004506785 0.03563744 -0.010266761 +v -0.004882295 0.036434513 -0.011848111 +v -0.0033365928 0.0391884 -0.01032846 +v -0.0013014957 0.038760886 -0.010952718 +v -0.0019193911 0.040994942 -0.008821992 +v -0.0000884369 0.040509667 -0.009433859 +v 0.0027855693 0.040495347 -0.0081774155 +v 0.0009921913 0.041677568 -0.008008503 +v -0.0013326323 0.041837886 -0.007486483 +v 0.0008382356 0.041772537 -0.00753259 +v 0.0045964876 0.0388286 -0.008395742 +v 0.0020006204 0.041237507 -0.0075577395 +v 0.002901316 0.040822923 -0.007577227 +v -0.006871439 0.036430374 -0.010054075 +v -0.008182734 0.032734543 -0.01056713 +v -0.0070818886 0.035869524 -0.0073679877 +v -0.0077571217 0.034222055 -0.007354703 +v -0.009265907 0.028631845 -0.0073459647 +v -0.009248717 0.026509669 -0.007387604 +v -0.009153851 0.024831133 -0.0073944363 +v -0.008893395 0.022424597 -0.0074434546 +v -0.008617877 0.020962581 -0.007426132 +v -0.0077994196 0.01922752 -0.0075755888 +v -0.008930057 0.030251333 -0.0073379185 +v -0.008381523 0.032299314 -0.007329005 +v -0.006542866 0.017607352 -0.007495452 +v -0.0037329113 0.015806966 -0.0074523403 +v 0.009632101 0.026198892 -0.007690335 +v 0.009308757 0.028222285 -0.007657608 +v 0.0047344468 0.03890669 -0.007617337 +v 0.0036087325 0.040083434 -0.0075927065 +v 0.0056195944 0.037566356 -0.00763704 +v 0.0064396984 0.036324516 -0.0076552955 +v 0.006053721 0.036207777 -0.008885651 +v 0.00714677 0.034925364 -0.00767125 +v 0.008051095 0.033135887 -0.007691655 +v -0.0048898254 0.039188106 -0.008915948 +v -0.004707316 0.03944801 -0.007416155 +v -0.0062325723 0.037340343 -0.007383081 +v -0.0028603654 0.041043647 -0.0075273598 +v -0.007683933 0.06284806 -0.0072202925 +v -0.004925772 0.062315594 -0.0071993326 +v -0.0049568885 0.062316544 -0.006069333 +v -0.0074337325 0.062813275 -0.00621352 +v -0.0043799644 0.06207914 -0.0048451843 +v -0.0060828016 0.06225827 -0.0045835287 +v -0.0038507518 0.061532296 -0.0039007678 +v -0.00486081 0.061611332 -0.0036051797 +v -0.0037630596 0.061033543 -0.0032862064 +v -0.002343379 0.060466416 -0.003246569 +v -0.0024447169 0.06115232 -0.004136074 +v -0.0027521597 0.061605822 -0.0050208364 +v -0.003068576 0.06179452 -0.0060623647 +v -0.0031096563 0.06172523 -0.007104195 +v -0.00092638086 0.06008591 -0.003608958 +v -0.0010691088 0.06058447 -0.004246438 +v -0.001165444 0.0609529 -0.0051238537 +v -0.0012285495 0.06112401 -0.0061387955 +v -0.001316189 0.061050426 -0.0070412224 +v 0.00022108301 0.05995584 -0.0043021524 +v 0.00031023278 0.060301345 -0.005171638 +v 0.0005040294 0.060336225 -0.006262276 +v 0.0005793215 0.060419474 -0.0071223015 +v 0.0015413349 0.05968219 -0.0051263534 +v 0.0030108895 0.059510306 -0.006438311 +v 0.0030128856 0.059571486 -0.0070895026 +v 0.00033937482 0.06011754 -0.0077761165 +v 0.003198924 0.05926948 -0.00777081 +v -0.0016268472 0.060598213 -0.007766567 +v -0.0034430842 0.061112363 -0.0077674976 +v -0.0052737696 0.061546165 -0.0077715395 +v -0.0077264337 0.061831668 -0.0077715395 +v 0.0036480674 0.058707044 -0.0077283625 +v 0.0039742733 0.058906816 -0.0071539935 +v 0.0046437085 0.05772278 -0.0077719716 +v 0.0048625595 0.057808727 -0.007064709 +v 0.004271808 0.05772623 -0.0058453814 +v 0.005375416 0.055201966 -0.0053275255 +v 0.0041601537 0.0552102 -0.003401877 +v 0.003261235 0.057478588 -0.0044304365 +v 0.0025114464 0.055110745 -0.0018684071 +v 0.001816247 0.057290606 -0.0027772028 +v 0.00029257327 0.055539 -0.0009366333 +v -0.00013253382 0.057592314 -0.0018753093 +v -0.0021404966 0.056221403 -0.000632872 +v -0.0022727922 0.058260374 -0.0015107298 +v -0.0046065054 0.056828406 -0.0009460479 +v -0.0043978156 0.059087437 -0.0017388266 +v -0.006745545 0.057032492 -0.0016584694 +v -0.0063547245 0.059844155 -0.0024799465 +v -0.008545278 0.05752274 -0.0032400086 +v -0.007955934 0.060127348 -0.003772346 +v -0.009626473 0.05764825 -0.0049975216 +v -0.008966422 0.06057162 -0.005388255 +v -0.009978807 0.05857521 -0.0067508053 +v -0.009562624 0.06099041 -0.0068864003 +v 0.0010603573 0.058755297 -0.0035297042 +v 0.0024382414 0.058724448 -0.0047749043 +v -0.0005497693 0.059071954 -0.0027393394 +v -0.0023090856 0.059586048 -0.0024220864 +v -0.004066479 0.060236998 -0.0025651013 +v -0.0057711713 0.060981367 -0.0030976185 +v -0.0071210144 0.061576054 -0.0040902556 +v -0.00829224 0.06190379 -0.0056668944 +v -0.00873637 0.062122315 -0.0069852867 +v -0.01012948 0.052749984 -0.0048000384 +v -0.009151844 0.05267105 -0.0024192024 +v -0.010978117 0.049494553 -0.0044405237 +v -0.011153772 0.04938598 -0.0065409862 +v -0.010224977 0.052879 -0.0065748063 +v -0.009800586 0.049535688 -0.0015501904 +v -0.007105539 0.052678134 -0.00053511676 +v -0.007485344 0.049612865 0.00080332457 +v -0.004499979 0.052639194 0.00035025834 +v -0.0043311142 0.04946382 0.0018798256 +v -0.0016232484 0.0524314 0.00078695535 +v -0.0011046871 0.04936849 0.0023295789 +v 0.0011725932 0.05220279 0.0004058081 +v 0.0019957453 0.049248915 0.0018111974 +v 0.0035138715 0.052107953 -0.0009459156 +v 0.0046154633 0.04903926 0.00007516503 +v 0.005490742 0.0521272 -0.0026814872 +v 0.0070581976 0.049126975 -0.0020381992 +v 0.007050727 0.052185908 -0.0048990822 +v 0.008834841 0.049209107 -0.0046446314 +v 0.007593969 0.05213833 -0.0066322093 +v 0.00931384 0.04910791 -0.0064962306 +v 0.009411219 0.044462807 -0.0003326559 +v 0.011274436 0.044517066 -0.003813432 +v 0.011472759 0.039757665 0.0014797163 +v 0.013770351 0.03981402 -0.0028531214 +v 0.011986854 0.0444413 -0.0061470317 +v 0.014678177 0.03974108 -0.0057424842 +v 0.007851944 0.039674357 0.0049224133 +v 0.006470691 0.044380654 0.0024387925 +v 0.0035576762 0.03974878 0.0072299354 +v 0.0030341414 0.04445835 0.0043231146 +v -0.0013511208 0.03980966 0.008100354 +v -0.0009392716 0.04451449 0.005063936 +v -0.006295917 0.03987172 0.007409035 +v -0.0049697035 0.044601582 0.00457616 +v -0.010831358 0.039951947 0.005235123 +v -0.008829559 0.044777896 0.002910456 +v -0.014298527 0.03991771 0.0016666728 +v -0.011668863 0.044784818 -0.000018419623 +v -0.016398242 0.039803624 -0.00286848 +v -0.013258321 0.044606566 -0.0037994024 +v -0.017287377 0.03971563 -0.005759444 +v -0.01405766 0.044623226 -0.0061718994 +v 0.004432255 0.03508442 0.009648683 +v 0.009532364 0.03505547 0.007009267 +v 0.0053005666 0.030266767 0.011612496 +v 0.011011231 0.030266767 0.008704073 +v 0.01360682 0.035053067 0.0029316156 +v 0.015606944 0.030296527 0.004180428 +v 0.016174786 0.035025187 -0.0022030103 +v 0.018448336 0.030266767 -0.0015415382 +v 0.017254107 0.034970216 -0.0054743886 +v 0.019546652 0.030181734 -0.0051948256 +v -0.0010289158 0.030266767 0.012617871 +v -0.0012328114 0.035075437 0.010522098 +v -0.007358398 0.030266767 0.011612496 +v -0.0069076177 0.035063215 0.009590341 +v -0.013069064 0.030266767 0.008704073 +v -0.0120561095 0.035082854 0.0069958535 +v -0.017226135 0.030401928 0.0038795548 +v -0.016001575 0.035042223 0.0028218955 +v -0.019620063 0.03029667 -0.0014926165 +v -0.018595513 0.035139304 -0.0021603352 +v -0.020799346 0.030268984 -0.005196333 +v -0.019355586 0.035098925 -0.005454328 +v -0.007655645 0.025712145 0.012544935 +v -0.0009929023 0.0257167 0.013601002 +v -0.0076317834 0.02101988 0.0127252955 +v -0.0008022494 0.021022595 0.013809001 +v 0.005669264 0.025721194 0.012540898 +v 0.0060269665 0.02102527 0.012723055 +v 0.011679034 0.025725216 0.009478735 +v 0.012187559 0.021027654 0.009584178 +v 0.0165024 0.025738524 0.004716503 +v 0.017164284 0.021019416 0.0047054593 +v 0.019506019 0.025730504 -0.0013045048 +v 0.020209314 0.021030838 -0.0014700443 +v 0.020693058 0.025702145 -0.0050950786 +v 0.02128091 0.021059927 -0.0051647616 +v -0.013794222 0.021017374 0.009587756 +v -0.0136688445 0.025707953 0.009485151 +v -0.01826456 0.021051552 0.004459277 +v -0.018151173 0.025653975 0.004543981 +v -0.020550124 0.021005508 -0.0013879066 +v -0.020461036 0.02572643 -0.0012093263 +v -0.02128091 0.021029161 -0.005211966 +v -0.021154692 0.02570207 -0.0050947326 +v -0.017943067 0.01672916 0.0041212505 +v -0.013463942 0.016658355 0.009026948 +v -0.017184174 0.012747428 0.0036315673 +v -0.012746503 0.012731835 0.008074617 +v -0.0074729365 0.016655995 0.012079186 +v -0.007144932 0.012722051 0.010906751 +v -0.0008416055 0.01665561 0.013123225 +v -0.0009431099 0.012705631 0.011877963 +v 0.0057854764 0.016659042 0.012052429 +v 0.0052621076 0.012667177 0.010896888 +v 0.011746743 0.016667709 0.008985116 +v 0.010887418 0.012593514 0.008078215 +v 0.016599169 0.016622173 0.0042549795 +v 0.015340888 0.01256986 0.0036319036 +v 0.019570746 0.016626863 -0.0017265707 +v 0.018051662 0.012654687 -0.0019988024 +v 0.020590313 0.016699139 -0.0052720914 +v 0.019041207 0.012772935 -0.0053900224 +v 0.015782403 0.008820505 -0.0024466775 +v 0.016686084 0.008989498 -0.0055812695 +v 0.0128640365 0.00553174 -0.003186428 +v 0.013644967 0.0057515977 -0.0058939126 +v 0.010713966 0.0054509686 0.0011821347 +v 0.013360953 0.008711488 0.0026069193 +v 0.0073339497 0.005505211 0.00453098 +v 0.009303419 0.008751545 0.0066663604 +v 0.0031212862 0.005550115 0.006610414 +v 0.0043222313 0.008824498 0.009118574 +v -0.002050025 0.005715675 0.0073370067 +v -0.0013612823 0.008927628 0.009971744 +v -0.006598995 0.0058435653 0.00655541 +v -0.006962272 0.009036298 0.009074017 +v -0.010889443 0.005882876 0.0044182795 +v -0.011939633 0.009079609 0.00656842 +v -0.014378354 0.005892198 0.0009706539 +v -0.01602567 0.009095513 0.0025866097 +v -0.015784556 0.0059526754 -0.0032288008 +v -0.017496228 0.009157941 -0.0023945658 +v -0.016737813 0.0059807133 -0.0059079025 +v -0.01854465 0.009198126 -0.0055626514 +v -0.018854924 0.012634552 -0.0018671392 +v -0.019849649 0.012711572 -0.005360334 +v -0.02024872 0.016532103 -0.0015353482 +v -0.021010349 0.016640488 -0.0052631907 +v -0.021224797 0.02101936 -0.0077715395 +v -0.021094868 0.025700452 -0.0077715395 +v -0.02048767 0.030265711 -0.0077715395 +v -0.019175863 0.035057046 -0.0077715395 +v -0.020708041 0.016644297 -0.0077715395 +v -0.019754881 0.012764952 -0.0077715395 +v -0.018374396 0.009335129 -0.0077993656 +v -0.016438715 0.0061997697 -0.007840798 +v -0.014407494 0.003433187 -0.0063731647 +v -0.013670494 0.0033989118 -0.004326524 +v -0.010847223 0.001544199 -0.005532553 +v -0.011366785 0.0015937566 -0.0068765315 +v -0.01228816 0.0033815622 -0.0010953236 +v -0.009661939 0.0015238714 -0.0032674829 +v -0.009506809 0.0033838535 0.0016727644 +v -0.007842823 0.0015218686 -0.0014860689 +v -0.005761482 0.0033658384 0.0034471755 +v -0.004039947 0.001436851 -0.00013470292 +v -0.002938472 0.0032905936 0.0039445143 +v 0.0018306178 0.0030686783 0.0033193843 +v 0.00024351239 0.0012813568 -0.00029644906 +v 0.005381505 0.003085308 0.0015768533 +v 0.0036228027 0.0013597345 -0.0023339444 +v 0.007572917 0.003031349 -0.000568006 +v 0.009507544 0.0030084203 -0.0042774198 +v 0.0057413196 0.0012756013 -0.0055276933 +v 0.010137486 0.0032433937 -0.0063541797 +v 0.0062199724 0.0014797997 -0.006875975 +v -0.0061466764 0.0002486372 -0.0049380935 +v -0.0072636483 0.00034294603 -0.0066278563 +v -0.007308647 0.00034636972 -0.0073377583 +v 0.000105468775 0.00010766029 -0.004926141 +v -0.002671833 0.00017653465 -0.003840059 +v -0.00278162 0.00006137371 -0.005412998 +v 0.0012192081 0.00034145353 -0.0066158003 +v -0.0027620625 -0.00004773855 -0.0066222753 +v -0.0027522212 0.0003670168 -0.0073359017 +v 0.0012972687 0.00039099215 -0.007333745 +v -0.011280782 0.001639502 -0.0077715395 +v -0.014203098 0.0037224363 -0.007876075 +v -0.0074965917 0.0003989911 -0.0077715395 +v -0.0027405666 0.0004991579 -0.0077715395 +v 0.0014314817 0.0006128645 -0.0077715395 +v 0.0060903095 0.0016620182 -0.0077715395 +v 0.00987801 0.0037095665 -0.007865126 +v -0.011280782 0.001639502 -0.0077715395 +v -0.0074965917 0.0003989911 -0.0077715395 +v -0.0027405666 0.0004991579 -0.0077715395 +v 0.0060903095 0.0016620182 -0.0077715395 +v -0.014203098 0.0037224363 -0.007876075 +v 0.00987801 0.0037095665 -0.007865126 +v -0.016438715 0.0061997697 -0.007840798 +v 0.013228936 0.0061435867 -0.007834004 +v -0.018374396 0.009335129 -0.0077993656 +v 0.016359858 0.009232743 -0.0077970526 +v -0.019754881 0.012764952 -0.0077715395 +v 0.018744549 0.012870114 -0.0077715395 +v -0.020708041 0.016644297 -0.0077715395 +v 0.020055413 0.016766503 -0.0077715395 +v -0.021224797 0.02101936 -0.0077715395 +v 0.020735867 0.02108426 -0.0077715395 +v -0.021094868 0.025700452 -0.0077715395 +v 0.020212766 0.025674798 -0.0077715395 +v -0.02048767 0.030265711 -0.0077715395 +v 0.019041594 0.030105207 -0.0077715395 +v -0.019175863 0.035057046 -0.0077715395 +v 0.016895363 0.03492588 -0.0077715395 +v 0.014298181 0.03966023 -0.0077715395 +v -0.016920492 0.03965302 -0.0077715395 +v 0.01165466 0.044355948 -0.0077715395 +v -0.013374941 0.044365276 -0.0077715395 +v 0.0089680925 0.049000796 -0.0077715395 +v -0.010592856 0.0491692 -0.0077715395 +v 0.0073660547 0.051968746 -0.0077715395 +v -0.009930811 0.05285528 -0.0077715395 +v 0.005755598 0.055162165 -0.0077715395 +v -0.0098881 0.05865022 -0.0077715395 +v 0.0046437085 0.05772278 -0.0077719716 +v -0.009312317 0.060707908 -0.0077715395 +v -0.0034430842 0.061112363 -0.0077674976 +v -0.0052737696 0.061546165 -0.0077715395 +v -0.008652135 0.06137016 -0.0077715395 +v -0.0077264337 0.061831668 -0.0077715395 +v -0.0016268472 0.060598213 -0.007766567 +v 0.00033937482 0.06011754 -0.0077761165 +v 0.003198924 0.05926948 -0.00777081 +v 0.0036480674 0.058707044 -0.0077283625 +v 0.020212766 0.025674798 -0.0077715395 +v 0.020735867 0.02108426 -0.0077715395 +v 0.020055413 0.016766503 -0.0077715395 +v 0.018744549 0.012870114 -0.0077715395 +v 0.016359858 0.009232743 -0.0077970526 +v 0.013228936 0.0061435867 -0.007834004 +v 0.019041594 0.030105207 -0.0077715395 +v 0.016895363 0.03492588 -0.0077715395 +v 0.014298181 0.03966023 -0.0077715395 +v 0.01165466 0.044355948 -0.0077715395 +v 0.0089680925 0.049000796 -0.0077715395 +v 0.0073660547 0.051968746 -0.0077715395 +v 0.0058874898 0.055173997 -0.0068075294 +v 0.005755598 0.055162165 -0.0077715395 +v -0.016920492 0.03965302 -0.0077715395 +v -0.013374941 0.044365276 -0.0077715395 +v -0.010592856 0.0491692 -0.0077715395 +v -0.009930811 0.05285528 -0.0077715395 +v -0.0098881 0.05865022 -0.0077715395 +v -0.009312317 0.060707908 -0.0077715395 +v -0.008652135 0.06137016 -0.0077715395 +v 0.0037049775 0.058744457 -0.0061950707 +v 0.0014314817 0.0006128645 -0.0077715395 +vn -0.2598943 -0.6500324 -0.7140817 +vn -0.36579886 -0.71107394 -0.6004707 +vn -0.14474204 -0.75279516 -0.64214426 +vn -0.160781 -0.6828584 -0.7126387 +vn -0.18765841 -0.52047306 -0.83300185 +vn -0.3424457 -0.5480389 -0.7631411 +vn -0.5032597 -0.5817188 -0.6390092 +vn -0.044392496 -0.690854 -0.72163004 +vn -0.032494754 -0.78505653 -0.6185711 +vn 0.15717842 -0.80886734 -0.56659377 +vn 0.15812388 -0.69786793 -0.69855356 +vn 0.39866537 -0.6537743 -0.6431524 +vn 0.44372502 -0.40642583 -0.79870284 +vn 0.6478502 -0.5769894 -0.4973665 +vn 0.37433597 -0.7967568 -0.47439548 +vn 0.6988567 -0.6240747 -0.349471 +vn 0.81265646 -0.43399724 -0.38889062 +vn 0.6987311 -0.3655979 -0.61490893 +vn 0.17032525 -0.45494398 -0.87407976 +vn 0.19169876 -0.26195568 -0.9458493 +vn 0.49270773 -0.22505778 -0.840588 +vn 0.75213724 -0.17972419 -0.6340258 +vn 0.5374527 -0.035296656 -0.8425549 +vn 0.7634181 0.008489956 -0.6458489 +vn 0.8775554 -0.091124654 -0.4707365 +vn 0.8636268 -0.1610017 -0.47773138 +vn 0.8421918 -0.2496965 -0.47787514 +vn -0.00786015 -0.28941867 -0.9571703 +vn -0.03618096 -0.4859219 -0.873253 +vn -0.18728486 -0.3298957 -0.9252531 +vn -0.17124175 -0.1692409 -0.9705842 +vn 0.024802262 -0.13193086 -0.9909485 +vn 0.22527723 -0.09325601 -0.9698213 +vn 0.0691711 0.028512433 -0.9971973 +vn 0.26302764 0.07715146 -0.96169853 +vn 0.5555312 0.13828063 -0.8199168 +vn 0.30332044 0.24545974 -0.9207313 +vn 0.54678583 0.28893968 -0.7858366 +vn 0.71649474 0.3083834 -0.62572753 +vn 0.77490115 0.1770773 -0.6067716 +vn 0.8645292 0.24601969 -0.43825048 +vn 0.8805767 0.18739264 -0.43527994 +vn -0.43727046 -0.36250663 -0.82303315 +vn -0.5080891 -0.18473892 -0.8412591 +vn -0.67826396 -0.3516422 -0.6452177 +vn -0.79754305 -0.15956269 -0.5817773 +vn -0.8646401 -0.0056391302 -0.5023601 +vn -0.52680933 -0.02904738 -0.84948695 +vn -0.13911858 -0.009582124 -0.9902293 +vn -0.48040077 0.2069647 -0.8522798 +vn -0.07876644 0.19536647 -0.9775622 +vn 0.11790507 0.21274103 -0.96996886 +vn -0.029936384 0.37054703 -0.92833126 +vn 0.1537216 0.3346147 -0.92973256 +vn 0.33828542 0.34969437 -0.87365717 +vn 0.3609008 0.3782714 -0.85244435 +vn 0.19111262 0.4577359 -0.8683051 +vn 0.30477273 0.60094535 -0.7389034 +vn 0.4970793 0.462351 -0.7342641 +vn 0.537292 0.37641904 -0.75473577 +vn -0.3974738 0.4485658 -0.80050194 +vn -0.29653472 0.63087714 -0.71698064 +vn 0.0025697337 0.5448302 -0.8385424 +vn -0.23870023 0.78718305 -0.568652 +vn 0.09932843 0.6698692 -0.73580503 +vn 0.42878893 0.6674719 -0.6087868 +vn 0.18435952 0.78397465 -0.5927861 +vn -0.118827336 0.8922913 -0.43554154 +vn -0.0078447545 0.90350264 -0.4285107 +vn 0.5892477 0.5314467 -0.60856515 +vn 0.44807982 0.80977756 -0.37879387 +vn 0.5377095 0.7461241 -0.39264154 +vn -0.76864225 0.4734537 -0.43015203 +vn -0.83721745 0.22921318 -0.49651626 +vn -0.92634785 0.3765065 -0.011065358 +vn -0.93926346 0.34260616 -0.02012834 +vn -0.97807956 0.022095656 -0.20705594 +vn -0.96283406 -0.07662837 -0.25899538 +vn -0.94489247 -0.07814471 -0.3179175 +vn -0.89707196 -0.18381222 -0.40183932 +vn -0.8760137 -0.23850246 -0.41918582 +vn -0.67151755 -0.5086273 -0.5388529 +vn -0.9785382 0.15321587 -0.13779616 +vn -0.95954126 0.28077117 -0.02116826 +vn -0.5934014 -0.60469955 -0.5312375 +vn -0.4984621 -0.7592663 -0.4183901 +vn 0.88665694 0.057047907 -0.45889553 +vn 0.8898816 0.13597928 -0.43545422 +vn 0.76491123 0.61324257 -0.19708963 +vn 0.6262879 0.68506086 -0.37209558 +vn 0.81133205 0.5159255 -0.27488387 +vn 0.83757627 0.4365736 -0.32843477 +vn 0.69096166 0.4105911 -0.594968 +vn 0.84388274 0.38912106 -0.36938697 +vn 0.8279057 0.35343233 -0.43549708 +vn -0.6556667 0.67672956 -0.3348704 +vn -0.7059026 0.6982448 -0.11897817 +vn -0.8219187 0.56959504 0.0033339271 +vn -0.5443885 0.7997212 -0.25315467 +vn -0.0972819 0.9063393 -0.41119975 +vn 0.20795451 0.86883104 -0.44931906 +vn 0.21211214 0.97496843 0.06667005 +vn -0.13004433 0.9628114 0.23681788 +vn 0.21979263 0.9496367 0.22334124 +vn -0.054568224 0.90851647 0.41427043 +vn 0.25834197 0.86002386 0.44002086 +vn 0.038120322 0.7796052 0.62511003 +vn 0.1829142 0.6860004 0.7042342 +vn 0.28972867 0.7016131 0.65099645 +vn 0.3373531 0.83453596 0.43559435 +vn 0.32852215 0.92371726 0.19702685 +vn 0.3127828 0.94975 0.011910289 +vn 0.2901814 0.8727268 -0.39261028 +vn 0.375967 0.7001017 0.60704726 +vn 0.3819251 0.82394916 0.41861808 +vn 0.39202636 0.8971336 0.20363376 +vn 0.37209603 0.92766786 0.0312564 +vn 0.3106994 0.8944059 -0.32172027 +vn 0.44450143 0.7323719 0.5158002 +vn 0.39346814 0.8922505 0.22152203 +vn 0.3636377 0.9243481 0.11553441 +vn 0.3104791 0.9270211 -0.21032035 +vn 0.45614338 0.7948452 0.400193 +vn 0.4770452 0.8241769 0.3052216 +vn 0.39271826 0.90966344 -0.13522132 +vn 0.16722122 0.5639592 -0.8086946 +vn 0.24359307 0.49071544 -0.83657676 +vn 0.030996742 0.09995485 -0.99450904 +vn 0.031194193 0.09517517 -0.99497163 +vn 0.07662464 0.34484166 -0.93552816 +vn -0.056470983 0.34089905 -0.9384023 +vn 0.2848843 0.28361902 -0.91564244 +vn 0.7286196 0.6847282 0.01614949 +vn 0.05804086 0.03032872 -0.99785334 +vn 0.8775079 0.46343762 0.12331052 +vn 0.74862504 0.47899413 0.4583942 +vn 0.778579 0.46099803 0.42578804 +vn 0.6672922 0.45508486 0.58959216 +vn 0.64600444 0.507536 0.5701626 +vn 0.51928645 0.43790755 0.7338791 +vn 0.5159462 0.506378 0.69092757 +vn 0.30854586 0.41531974 0.8557505 +vn 0.3484369 0.48071665 0.8046758 +vn 0.056904048 0.3628852 0.9300947 +vn 0.14966422 0.44882464 0.8809978 +vn -0.17717497 0.30921954 0.9343406 +vn -0.08749711 0.4168094 0.904773 +vn -0.47413766 0.26776972 0.8387447 +vn -0.38837284 0.39009458 0.8348609 +vn -0.7506112 0.22186083 0.622383 +vn -0.6621079 0.3543062 0.6603638 +vn -0.9255576 0.15126923 0.3470746 +vn -0.8150025 0.33455333 0.4731225 +vn -0.9907879 0.11413031 0.07289441 +vn -0.920605 0.35646895 0.15942523 +vn 0.48691598 0.6102272 0.62492853 +vn 0.562739 0.61338615 0.55415004 +vn 0.3724818 0.5992329 0.70864457 +vn 0.23596136 0.5943142 0.7688386 +vn 0.0678589 0.58771455 0.8062176 +vn -0.19559701 0.60368997 0.77285206 +vn -0.44737628 0.62945604 0.6353263 +vn -0.60400283 0.6369683 0.4790115 +vn -0.71751285 0.6963405 -0.016885225 +vn -0.9401367 0.18832217 0.2840383 +vn -0.7797923 0.2522942 0.5729498 +vn -0.8787031 0.39692876 0.26519516 +vn -0.9088607 0.40696177 -0.0914019 +vn -0.99088705 0.13098992 -0.031378247 +vn -0.7608753 0.41088864 0.50223434 +vn -0.48454714 0.31671858 0.81541604 +vn -0.473846 0.44581187 0.759422 +vn -0.19387291 0.3567117 0.9138764 +vn -0.1985808 0.46345282 0.8635838 +vn 0.032090325 0.4042569 0.91408235 +vn 0.030434104 0.47965333 0.8769301 +vn 0.3192083 0.4397228 0.8394939 +vn 0.3196341 0.4944287 0.8083157 +vn 0.5318177 0.4359326 0.72603905 +vn 0.52013856 0.5008289 0.6918282 +vn 0.66132176 0.45256984 0.5981924 +vn 0.65411884 0.50298613 0.56491905 +vn 0.77446985 0.48476788 0.40644372 +vn 0.7812275 0.49847174 0.37577847 +vn 0.8664357 0.49599203 0.05727985 +vn 0.86637354 0.49825025 0.033817086 +vn 0.67114663 0.5105819 0.5374647 +vn 0.7903904 0.4975706 0.35736048 +vn 0.69287264 0.49110365 0.52796274 +vn 0.8032745 0.47983044 0.35286364 +vn 0.86477286 0.49894515 0.056759395 +vn 0.86926556 0.48691717 0.08537603 +vn 0.5080235 0.49984264 0.70147675 +vn 0.5099764 0.5166361 0.68775815 +vn 0.28059086 0.5010987 0.8186384 +vn 0.3037556 0.5182111 0.7994935 +vn 0.011903828 0.4927485 0.87009037 +vn 0.03386566 0.5191336 0.8540219 +vn -0.25246903 0.47943297 0.8404781 +vn -0.210459 0.51737535 0.82947564 +vn -0.51584667 0.4688905 0.71696854 +vn -0.47518572 0.5133338 0.71462363 +vn -0.7286024 0.47211772 0.49622905 +vn -0.71908665 0.50862724 0.47351107 +vn -0.8152472 0.48360616 0.31858617 +vn -0.8100078 0.5122361 0.2854847 +vn -0.8662228 0.4956388 0.0632479 +vn -0.8418531 0.539653 -0.007624645 +vn 0.2809014 0.44146723 0.8521744 +vn 0.5236284 0.45408353 0.7208477 +vn 0.2952495 0.3123257 0.90292877 +vn 0.55555034 0.32226983 0.7664893 +vn 0.71680176 0.45858875 0.52525365 +vn 0.7637149 0.3334586 0.5527612 +vn 0.819985 0.45292386 0.34997782 +vn 0.8672662 0.33809772 0.36543038 +vn 0.8862243 0.45426455 0.09082967 +vn 0.9350064 0.34169367 0.09491354 +vn 0.00027406338 0.30595055 0.95204735 +vn 0.00050774886 0.42498577 0.9051999 +vn -0.29325098 0.30142704 0.90727377 +vn -0.27890176 0.408029 0.8693251 +vn -0.580173 0.2929708 0.75997853 +vn -0.55299157 0.38709104 0.73780817 +vn -0.8040751 0.2682769 0.53055686 +vn -0.7695546 0.35949832 0.5277752 +vn -0.9115467 0.22818385 0.34207422 +vn -0.87261283 0.3449231 0.3457959 +vn -0.97375363 0.19614416 0.11546112 +vn -0.93321353 0.34470868 0.1014312 +vn -0.30754095 0.1198955 0.9439511 +vn -0.0015940816 0.12505662 0.9921483 +vn -0.30704638 -0.05471042 0.9501207 +vn 0.0032217149 -0.05215361 0.9986338 +vn 0.3040885 0.13458449 0.94308925 +vn 0.3129051 -0.04664297 0.9486385 +vn 0.5762841 0.14765418 0.80380034 +vn 0.58753157 -0.036767483 0.8083655 +vn 0.796251 0.16380441 0.58236796 +vn 0.8112409 -0.021979533 0.58429885 +vn 0.9066063 0.17433645 0.38428086 +vn 0.9227809 -0.012903768 0.38510904 +vn 0.97787344 0.18447073 0.09866104 +vn 0.9959669 -0.009557866 0.089210816 +vn -0.6080783 -0.052920267 0.7921113 +vn -0.6060352 0.12032871 0.78628397 +vn -0.8468336 -0.043733954 0.53005683 +vn -0.8385746 0.11495874 0.5325195 +vn -0.95349145 -0.03209283 0.2997068 +vn -0.9444594 0.09678821 0.3140517 +vn -0.99517703 -0.025447266 0.094736926 +vn -0.9913999 0.07200363 0.10927882 +vn -0.8326247 -0.18225591 0.5229902 +vn -0.5924585 -0.20133252 0.7800373 +vn -0.79728186 -0.32845566 0.5064174 +vn -0.55460536 -0.34037235 0.7593152 +vn -0.30098346 -0.21747535 0.9285006 +vn -0.28417328 -0.36057702 0.888386 +vn 0.0018541547 -0.22218145 0.9750036 +vn 0.0015259683 -0.3702382 0.9289356 +vn 0.30325973 -0.22528453 0.92589444 +vn 0.28291816 -0.37461805 0.88296014 +vn 0.5701924 -0.22866727 0.789045 +vn 0.54015446 -0.3845634 0.7485614 +vn 0.787943 -0.23896493 0.56748706 +vn 0.743043 -0.40780145 0.5306458 +vn 0.8959173 -0.24771135 0.36874297 +vn 0.839467 -0.42676273 0.33640587 +vn 0.96453375 -0.2510921 0.08140899 +vn 0.893483 -0.44239345 0.07730523 +vn 0.75485206 -0.5881906 0.29022443 +vn 0.7956506 -0.60437244 0.040915035 +vn 0.6270642 -0.74301773 0.23391263 +vn 0.67171603 -0.74037975 -0.025205959 +vn 0.54806364 -0.7395393 0.39077863 +vn 0.6658191 -0.5787686 0.47086287 +vn 0.38596183 -0.73160005 0.5619562 +vn 0.47790137 -0.56001 0.67675626 +vn 0.18129937 -0.728371 0.66076183 +vn 0.24140453 -0.5455556 0.8025541 +vn -0.022025174 -0.7113617 0.70248085 +vn -0.009093012 -0.53285325 0.84615886 +vn -0.23989671 -0.6891581 0.6837475 +vn -0.26933846 -0.515222 0.81363565 +vn -0.43074283 -0.67665726 0.59715617 +vn -0.50694674 -0.49800295 0.703561 +vn -0.648182 -0.642738 0.40834785 +vn -0.7464657 -0.47362196 0.46740916 +vn -0.76027447 -0.598239 0.25316554 +vn -0.858462 -0.43963894 0.26412266 +vn -0.8078365 -0.5820162 0.093044855 +vn -0.89259756 -0.43294 0.12582682 +vn -0.9020594 -0.33008683 0.27808568 +vn -0.93715525 -0.32863697 0.117208295 +vn -0.93752605 -0.18231039 0.2963239 +vn -0.97942734 -0.17794636 0.095169716 +vn -0.065324545 -0.0018420148 -0.9978624 +vn -0.0664808 0.004371959 -0.9977781 +vn -0.06808811 0.013248381 -0.9975914 +vn -0.066742174 0.024516705 -0.99746907 +vn -0.063780054 -0.011107639 -0.99790215 +vn -0.06400378 -0.01793928 -0.9977884 +vn -0.06497192 -0.021789104 -0.99764913 +vn -0.06396083 -0.032391526 -0.99742657 +vn -0.65473574 -0.7543242 0.048124593 +vn -0.5938799 -0.7733419 0.22192131 +vn -0.39724597 -0.9040424 0.15780649 +vn -0.45032337 -0.89262027 0.02092764 +vn -0.5030426 -0.7984666 0.33075568 +vn -0.35429284 -0.9036689 0.24053943 +vn -0.32042855 -0.82769936 0.46069434 +vn -0.21262097 -0.9142786 0.34479994 +vn -0.19273074 -0.83346236 0.51787585 +vn -0.08966713 -0.9228636 0.3745431 +vn -0.028359225 -0.84835994 0.52865976 +vn 0.12275748 -0.8674374 0.4821648 +vn 0.0884864 -0.9333378 0.3479232 +vn 0.2809211 -0.86401093 0.41781384 +vn 0.22626548 -0.945281 0.23504846 +vn 0.40436473 -0.8659296 0.29437247 +vn 0.460609 -0.8696496 0.17762041 +vn 0.30228525 -0.9458871 0.11798839 +vn 0.5285447 -0.8446519 -0.084874146 +vn 0.3605081 -0.92424893 -0.12568936 +vn -0.15083945 -0.96967906 0.19227605 +vn -0.2156863 -0.975713 0.03825507 +vn -0.16694625 -0.97980875 -0.11001705 +vn 0.14201164 -0.977693 0.15475444 +vn -0.013178711 -0.9669358 0.25467902 +vn -0.012364896 -0.9995665 0.026718983 +vn 0.18054774 -0.983453 0.014921277 +vn -0.0037832842 -0.9948055 -0.101723455 +vn 0.0074272463 -0.956612 -0.29127005 +vn 0.15035732 -0.96468705 -0.21626742 +vn -0.04494138 -0.1334155 -0.9900407 +vn -0.05847324 -0.05977893 -0.9964976 +vn -0.13309295 -0.61534745 -0.7769388 +vn 0.0027958972 -0.18816574 -0.9821333 +vn 0.12453992 -0.702485 -0.7007172 +vn 0.041420013 -0.14902434 -0.98796564 +vn 0.055301838 -0.07870305 -0.995363 +vn -0.04494138 -0.1334155 -0.9900407 +vn -0.13309295 -0.61534745 -0.7769388 +vn 0.0027958972 -0.18816574 -0.9821333 +vn 0.041420013 -0.14902434 -0.98796564 +vn -0.05847324 -0.05977893 -0.9964976 +vn 0.055301838 -0.07870305 -0.995363 +vn -0.06396083 -0.032391526 -0.99742657 +vn 0.061002646 -0.055290177 -0.99660504 +vn -0.06497192 -0.021789104 -0.99764913 +vn 0.06643435 -0.04306075 -0.9968612 +vn -0.06400378 -0.01793928 -0.9977884 +vn 0.06741223 -0.03092649 -0.9972458 +vn -0.063780054 -0.011107639 -0.99790215 +vn 0.064158775 -0.016229788 -0.9978077 +vn -0.065324545 -0.0018420148 -0.9978624 +vn 0.06834196 -0.000665209 -0.9976617 +vn -0.0664808 0.004371959 -0.9977781 +vn 0.066111945 0.012525018 -0.9977336 +vn -0.06808811 0.013248381 -0.9975914 +vn 0.06751911 0.024274819 -0.9974227 +vn -0.066742174 0.024516705 -0.99746907 +vn 0.06447045 0.032001827 -0.9974063 +vn 0.06263975 0.03433365 -0.99744546 +vn -0.065879256 0.037827183 -0.99711037 +vn 0.06125393 0.03439735 -0.9975293 +vn -0.0574933 0.03651568 -0.99767786 +vn 0.055202015 0.030840127 -0.9979988 +vn -0.05326304 0.023248304 -0.99830985 +vn 0.05202122 0.02795954 -0.9982545 +vn -0.07385792 0.007570241 -0.99724007 +vn 0.04323651 0.020484699 -0.9988549 +vn -0.08630983 0.0064403424 -0.9962476 +vn 0.05804086 0.03032872 -0.99785334 +vn -0.08223625 0.029102797 -0.99618787 +vn 0.031194193 0.09517517 -0.99497163 +vn 0.07662464 0.34484166 -0.93552816 +vn -0.28461114 0.26959842 -0.9199529 +vn -0.056470983 0.34089905 -0.9384023 +vn 0.030996742 0.09995485 -0.99450904 +vn 0.16722122 0.5639592 -0.8086946 +vn 0.24359307 0.49071544 -0.83657676 +vn 0.2848843 0.28361902 -0.91564244 +vn 0.066111945 0.012525018 -0.9977336 +vn 0.06834196 -0.000665209 -0.9976617 +vn 0.064158775 -0.016229788 -0.9978077 +vn 0.06741223 -0.03092649 -0.9972458 +vn 0.06643435 -0.04306075 -0.9968612 +vn 0.061002646 -0.055290177 -0.99660504 +vn 0.06751911 0.024274819 -0.9974227 +vn 0.06447045 0.032001827 -0.9974063 +vn 0.06263975 0.03433365 -0.99744546 +vn 0.06125393 0.03439735 -0.9975293 +vn 0.055202015 0.030840127 -0.9979988 +vn 0.05202122 0.02795954 -0.9982545 +vn 0.8813179 0.4584067 0.11463828 +vn 0.04323651 0.020484699 -0.9988549 +vn -0.065879256 0.037827183 -0.99711037 +vn -0.0574933 0.03651568 -0.99767786 +vn -0.05326304 0.023248304 -0.99830985 +vn -0.07385792 0.007570241 -0.99724007 +vn -0.08630983 0.0064403424 -0.9962476 +vn -0.08223625 0.029102797 -0.99618787 +vn -0.28461114 0.26959842 -0.9199529 +vn 0.63805366 0.62041515 0.45604008 +vn 0.12453992 -0.702485 -0.7007172 +vt 0.87410265 -0.7009108 +vt 0.88697016 -0.687139 +vt 0.86037403 -0.66977674 +vt 0.8549956 -0.69746304 +vt 0.8596887 -0.7283755 +vt 0.8847403 -0.72484446 +vt 0.9097713 -0.7164922 +vt 0.8314321 -0.6982262 +vt 0.83310485 -0.66625553 +vt 0.8042989 -0.6689277 +vt 0.80663186 -0.6967806 +vt 0.78050333 -0.6990846 +vt 0.7773746 -0.72846836 +vt 0.7598034 -0.70109004 +vt 0.776987 -0.6787789 +vt 0.75379884 -0.6943997 +vt 0.7317077 -0.7199709 +vt 0.7504002 -0.7257925 +vt 0.80579853 -0.7274015 +vt 0.8039231 -0.76233566 +vt 0.7714569 -0.76362956 +vt 0.7391141 -0.763554 +vt 0.7655471 -0.80093616 +vt 0.73214555 -0.8033695 +vt 0.70778817 -0.7800976 +vt 0.71337783 -0.75946254 +vt 0.7225805 -0.7371215 +vt 0.83588153 -0.76423657 +vt 0.8335378 -0.72917664 +vt 0.8669029 -0.7659282 +vt 0.86965007 -0.80335265 +vt 0.83586854 -0.80091745 +vt 0.80012035 -0.79890907 +vt 0.8341225 -0.8381373 +vt 0.79888636 -0.8356755 +vt 0.76434165 -0.8390689 +vt 0.7984901 -0.87267673 +vt 0.76716876 -0.87740064 +vt 0.7387058 -0.88583267 +vt 0.7319062 -0.8446393 +vt 0.7127994 -0.8728976 +vt 0.70671415 -0.8481062 +vt 0.89657676 -0.7643122 +vt 0.9018903 -0.80336154 +vt 0.92508453 -0.75737643 +vt 0.93542385 -0.80066 +vt 0.9352636 -0.8443241 +vt 0.9012287 -0.842909 +vt 0.86829793 -0.84090614 +vt 0.89377576 -0.8819817 +vt 0.86286867 -0.8780975 +vt 0.83146644 -0.87507457 +vt 0.8542324 -0.9152799 +vt 0.8268958 -0.9112735 +vt 0.7993664 -0.90825516 +vt 0.80086064 -0.9304264 +vt 0.8191273 -0.9376656 +vt 0.8084862 -0.9585638 +vt 0.7865593 -0.94201577 +vt 0.7739893 -0.9152369 +vt 0.88160664 -0.9201899 +vt 0.866217 -0.9541426 +vt 0.84300363 -0.94668627 +vt 0.84995514 -0.97999436 +vt 0.82854646 -0.97109157 +vt 0.7923221 -0.9767689 +vt 0.81415427 -0.98958427 +vt 0.8426641 -0.99701804 +vt 0.81538033 -0.9950067 +vt 0.77010864 -0.9573378 +vt 0.8005023 -0.9887159 +vt 0.7896262 -0.9837509 +vt 0.90918195 -0.9275978 +vt 0.92595434 -0.887148 +vt 0.93617654 -0.93860126 +vt 0.949073 -0.91888916 +vt 0.9701607 -0.8452163 +vt 0.9700747 -0.81726164 +vt 0.96761006 -0.7957755 +vt 0.9605967 -0.76483595 +vt 0.9543616 -0.7470298 +vt 0.9410668 -0.72607523 +vt 0.9675133 -0.8666992 +vt 0.96012527 -0.89395356 +vt 0.92521745 -0.7057165 +vt 0.89145595 -0.6811384 +vt 0.70400614 -0.8028821 +vt 0.70448756 -0.8293559 +vt 0.763888 -0.9631759 +vt 0.7797861 -0.9759054 +vt 0.75147796 -0.9473652 +vt 0.7404678 -0.93236583 +vt 0.75248665 -0.9255871 +vt 0.7306502 -0.9152506 +vt 0.7191779 -0.89213586 +vt 0.8884868 -0.962642 +vt 0.89406294 -0.9779866 +vt 0.9214354 -0.95590156 +vt 0.86452615 -0.9910474 +vt 0.41080356 -0.26150134 +vt 0.38204223 -0.25408554 +vt 0.37992197 -0.26562482 +vt 0.40511817 -0.26941258 +vt 0.3722542 -0.27731445 +vt 0.38971713 -0.28098723 +vt 0.36564672 -0.2876374 +vt 0.37657827 -0.29080492 +vt 0.36405227 -0.29554895 +vt 0.34788546 -0.2969987 +vt 0.34980148 -0.2857594 +vt 0.3540274 -0.27552423 +vt 0.3583449 -0.26428807 +vt 0.35987964 -0.2529845 +vt 0.3316774 -0.2942653 +vt 0.33376163 -0.28579763 +vt 0.33536634 -0.27540216 +vt 0.33648366 -0.2638727 +vt 0.33728868 -0.253599 +vt 0.31870556 -0.28715685 +vt 0.31800982 -0.27663738 +vt 0.31529763 -0.2644736 +vt 0.3140349 -0.25481752 +vt 0.3038209 -0.27958947 +vt 0.28640044 -0.26786867 +vt 0.28461546 -0.2609352 +vt 0.31462684 -0.24624553 +vt 0.27864996 -0.2546753 +vt 0.33917874 -0.24355172 +vt 0.36265787 -0.24259946 +vt 0.38686934 -0.24396916 +vt 0.41833717 -0.25291285 +vt 0.27042735 -0.25805253 +vt 0.27218685 -0.265196 +vt 0.25496402 -0.26728266 +vt 0.25887105 -0.27446526 +vt 0.26844028 -0.28555748 +vt 0.24797009 -0.3101144 +vt 0.26854512 -0.324791 +vt 0.2811486 -0.29968983 +vt 0.29036826 -0.3373009 +vt 0.29953676 -0.31529352 +vt 0.31719342 -0.34217793 +vt 0.3222311 -0.32221448 +vt 0.345352 -0.3412869 +vt 0.34661067 -0.3227508 +vt 0.3731602 -0.33670303 +vt 0.370202 -0.31808713 +vt 0.3974292 -0.3309487 +vt 0.39130118 -0.30956253 +vt 0.4195995 -0.31824178 +vt 0.4096763 -0.30002734 +vt 0.43718624 -0.30650064 +vt 0.42328313 -0.28766328 +vt 0.4465626 -0.29025257 +vt 0.4325389 -0.27416256 +vt 0.3086502 -0.30030707 +vt 0.2925295 -0.28841808 +vt 0.32712975 -0.30694705 +vt 0.34702238 -0.30857104 +vt 0.36682177 -0.30595493 +vt 0.3855058 -0.29878104 +vt 0.39953607 -0.28929976 +vt 0.41374937 -0.27849337 +vt 0.4216731 -0.26710668 +vt 0.45987076 -0.3370643 +vt 0.4360737 -0.35357466 +vt 0.48053986 -0.36456987 +vt 0.50033855 -0.34926447 +vt 0.4733157 -0.32340217 +vt 0.44875982 -0.38362408 +vt 0.40621927 -0.3659782 +vt 0.41202125 -0.39735 +vt 0.3744772 -0.372426 +vt 0.3733361 -0.4052077 +vt 0.3402823 -0.37605166 +vt 0.33498794 -0.40848532 +vt 0.3067979 -0.37494427 +vt 0.29794878 -0.40717164 +vt 0.27615064 -0.3672775 +vt 0.26192036 -0.40008742 +vt 0.24827823 -0.3559739 +vt 0.22670269 -0.38789693 +vt 0.22154821 -0.34073827 +vt 0.19415855 -0.3716492 +vt 0.2049188 -0.32852262 +vt 0.17555729 -0.3595659 +vt 0.20032297 -0.44372517 +vt 0.15862791 -0.42569426 +vt 0.18212494 -0.5002313 +vt 0.1294461 -0.48341352 +vt 0.13287455 -0.4126053 +vt 0.09603778 -0.47076914 +vt 0.23544088 -0.5106939 +vt 0.24448653 -0.45581827 +vt 0.28717256 -0.515571 +vt 0.2884194 -0.4618729 +vt 0.33969128 -0.5161748 +vt 0.33424044 -0.4632349 +vt 0.39203647 -0.51277184 +vt 0.38031912 -0.45964372 +vt 0.44482777 -0.50515527 +vt 0.4276304 -0.45035383 +vt 0.4973263 -0.49326566 +vt 0.4725614 -0.43592155 +vt 0.55062705 -0.47539783 +vt 0.51633644 -0.41595337 +vt 0.5835363 -0.4622532 +vt 0.54237175 -0.4016285 +vt 0.28285962 -0.56395197 +vt 0.22560297 -0.5612738 +vt 0.27930173 -0.6092223 +vt 0.21939664 -0.6092108 +vt 0.16653946 -0.5545354 +vt 0.15669324 -0.60715073 +vt 0.10505756 -0.5422266 +vt 0.09006201 -0.6018155 +vt 0.06582583 -0.5328767 +vt 0.046823055 -0.59759545 +vt 0.33816555 -0.6079447 +vt 0.3393254 -0.56363845 +vt 0.39722478 -0.60601723 +vt 0.3960126 -0.5609401 +vt 0.45808885 -0.60302824 +vt 0.4540037 -0.5554207 +vt 0.5220091 -0.59602195 +vt 0.5138059 -0.546347 +vt 0.5862993 -0.5887998 +vt 0.5743478 -0.53164 +vt 0.6313995 -0.5818866 +vt 0.6120333 -0.5198937 +vt 0.39870524 -0.64470476 +vt 0.3386665 -0.64584583 +vt 0.39828646 -0.6829825 +vt 0.33787468 -0.68343383 +vt 0.27894083 -0.6477716 +vt 0.27795795 -0.6860185 +vt 0.21797058 -0.6501959 +vt 0.21697016 -0.6911073 +vt 0.15375248 -0.6527444 +vt 0.152758 -0.69871366 +vt 0.08457784 -0.65454316 +vt 0.083862856 -0.7084591 +vt 0.039671876 -0.6554829 +vt 0.04164571 -0.7144832 +vt 0.46091142 -0.68515885 +vt 0.46072975 -0.64429045 +vt 0.5272326 -0.6896874 +vt 0.5263617 -0.6444337 +vt 0.5950462 -0.6965635 +vt 0.5934604 -0.6421646 +vt 0.6405161 -0.7011503 +vt 0.63990414 -0.6403841 +vt 0.52292407 -0.73238295 +vt 0.45867354 -0.72358704 +vt 0.51323366 -0.7719665 +vt 0.45325214 -0.75968546 +vt 0.39777496 -0.71904695 +vt 0.3961556 -0.75329703 +vt 0.33903196 -0.71893346 +vt 0.34069127 -0.7527651 +vt 0.28076154 -0.7218987 +vt 0.28574255 -0.7562443 +vt 0.22158773 -0.72920567 +vt 0.2301106 -0.7657979 +vt 0.15959284 -0.74123687 +vt 0.17317383 -0.7810714 +vt 0.09403709 -0.75807244 +vt 0.11332988 -0.8033297 +vt 0.05413946 -0.76876724 +vt 0.076227404 -0.818442 +vt 0.1417643 -0.8477887 +vt 0.11003719 -0.86686 +vt 0.17571892 -0.8878661 +vt 0.15083908 -0.90774 +vt 0.21767387 -0.8592317 +vt 0.1929328 -0.82117057 +vt 0.26103625 -0.83973664 +vt 0.24444985 -0.80272424 +vt 0.30449086 -0.82812226 +vt 0.29423505 -0.7920002 +vt 0.35199058 -0.8230008 +vt 0.34512714 -0.78773755 +vt 0.39635465 -0.8244042 +vt 0.39651123 -0.7883905 +vt 0.44193968 -0.83259916 +vt 0.44787613 -0.7959088 +vt 0.48807782 -0.8482962 +vt 0.5016766 -0.81033796 +vt 0.5279709 -0.8742795 +vt 0.5510263 -0.8354655 +vt 0.55511856 -0.89209956 +vt 0.5853702 -0.8532047 +vt 0.57068974 -0.7941366 +vt 0.6107448 -0.8083432 +vt 0.5869183 -0.747846 +vt 0.6300737 -0.7572925 +vt 0.67196167 -0.70485187 +vt 0.6731878 -0.6381898 +vt 0.6620835 -0.57348585 +vt 0.63775986 -0.50808495 +vt 0.6594595 -0.7666756 +vt 0.6373285 -0.82126516 +vt 0.60690236 -0.86945873 +vt 0.57057106 -0.90923065 +vt 0.52095 -0.9251498 +vt 0.50176305 -0.9095297 +vt 0.4685337 -0.94218683 +vt 0.48035678 -0.9540157 +vt 0.47125182 -0.8862127 +vt 0.4466384 -0.9236284 +vt 0.43384576 -0.8705796 +vt 0.42145762 -0.9111317 +vt 0.3920489 -0.8623125 +vt 0.377124 -0.9044464 +vt 0.3621735 -0.86094886 +vt 0.3155018 -0.86688256 +vt 0.3304366 -0.9079675 +vt 0.27676734 -0.8789288 +vt 0.2903174 -0.9252146 +vt 0.24798857 -0.894526 +vt 0.21403447 -0.9239229 +vt 0.25827402 -0.9547746 +vt 0.19694866 -0.9416407 +vt 0.24784239 -0.96807617 +vt 0.4071198 -0.953236 +vt 0.42487115 -0.96897113 +vt 0.42735064 -0.9766296 +vt 0.33120522 -0.9586698 +vt 0.36422485 -0.94663453 +vt 0.36647856 -0.9637594 +vt 0.31561837 -0.97673035 +vt 0.36694252 -0.9773659 +vt 0.36734042 -0.9866475 +vt 0.31372392 -0.9846812 +vt 0.48351327 -0.96364725 +vt 0.53079194 -0.9398395 +vt 0.43088257 -0.9805561 +vt 0.367496 -0.991673 +vt 0.31100777 -0.98974174 +vt 0.24580921 -0.97843575 +vt 0.18891776 -0.9584442 +vt 0.8762099 -0.017411212 +vt 0.8362333 -0.0046279323 +vt 0.7861149 -0.0060479427 +vt 0.69313437 -0.018979967 +vt 0.90716887 -0.039166614 +vt 0.65337324 -0.040871408 +vt 0.93092036 -0.06510684 +vt 0.6182423 -0.06678116 +vt 0.9515601 -0.09800587 +vt 0.5854799 -0.099580206 +vt 0.9663705 -0.13405058 +vt 0.56062484 -0.13810071 +vt 0.97671366 -0.17486592 +vt 0.54710656 -0.1792686 +vt 0.9824978 -0.22093979 +vt 0.54026324 -0.22482868 +vt 0.9814881 -0.27028713 +vt 0.5461271 -0.27317047 +vt 0.97543573 -0.31844807 +vt 0.55881023 -0.31977385 +vt 0.9619753 -0.36904553 +vt 0.58179796 -0.37041634 +vt 0.6095306 -0.42011642 +vt 0.93855655 -0.41765815 +vt 0.6377509 -0.46940768 +vt 0.90154517 -0.46759596 +vt 0.6664202 -0.5181625 +vt 0.8725903 -0.5184464 +vt 0.6835323 -0.5493222 +vt 0.8658907 -0.55735016 +vt 0.7007474 -0.5828558 +vt 0.8658774 -0.6184287 +vt 0.712659 -0.6097567 +vt 0.8599652 -0.64015764 +vt 0.7981406 -0.6448644 +vt 0.81746686 -0.6492974 +vt 0.8530578 -0.6471869 +vt 0.8433366 -0.65212107 +vt 0.7789601 -0.6395828 +vt 0.75820094 -0.6346673 +vt 0.7279861 -0.62598467 +vt 0.7232089 -0.62007666 +vt 0.0067802044 -0.6532506 +vt 0.009763658 -0.71917236 +vt 0.025631167 -0.7801991 +vt 0.051752422 -0.83412236 +vt 0.09066121 -0.8858602 +vt 0.13756375 -0.92779803 +vt 0.016245646 -0.58913267 +vt 0.04051848 -0.5203011 +vt 0.075652525 -0.4565051 +vt 0.11813221 -0.3994619 +vt 0.16517168 -0.348235 +vt 0.19549315 -0.31912693 +vt 0.23475935 -0.29843137 +vt 0.22821015 -0.28961208 +vt 0.6030971 -0.44760397 +vt 0.5572109 -0.38702372 +vt 0.5112022 -0.3377211 +vt 0.48196736 -0.31431577 +vt 0.45384753 -0.28301895 +vt 0.4395599 -0.26691946 +vt 0.430349 -0.25996894 +vt 0.2772415 -0.27508792 +vt 0.74215305 -0.007565812 +f 3/3/3 2/2/2 1/1/1 +f 4/4/4 3/3/3 1/1/1 +f 5/5/5 4/4/4 1/1/1 +f 6/6/6 5/5/5 1/1/1 +f 7/7/7 6/6/6 1/1/1 +f 2/2/2 7/7/7 1/1/1 +f 3/3/3 4/4/4 8/8/8 +f 4/4/4 5/5/5 8/8/8 +f 9/9/9 3/3/3 8/8/8 +f 9/9/9 8/8/8 10/10/10 +f 8/8/8 11/11/11 10/10/10 +f 11/11/11 12/12/12 10/10/10 +f 12/12/12 11/11/11 13/13/13 +f 12/12/12 13/13/13 14/14/14 +f 15/15/15 12/12/12 14/14/14 +f 12/12/12 15/15/15 10/10/10 +f 16/16/16 15/15/15 14/14/14 +f 17/17/17 16/16/16 14/14/14 +f 18/18/18 17/17/17 14/14/14 +f 13/13/13 18/18/18 14/14/14 +f 11/11/11 19/19/19 13/13/13 +f 19/19/19 11/11/11 8/8/8 +f 19/19/19 20/20/20 13/13/13 +f 20/20/20 21/21/21 13/13/13 +f 13/13/13 21/21/21 18/18/18 +f 21/21/21 22/22/22 18/18/18 +f 21/21/21 23/23/23 22/22/22 +f 23/23/23 24/24/24 22/22/22 +f 24/24/24 25/25/25 22/22/22 +f 25/25/25 26/26/26 22/22/22 +f 18/18/18 22/22/22 26/26/26 +f 27/27/27 17/17/17 18/18/18 +f 27/27/27 18/18/18 26/26/26 +f 28/28/28 20/20/20 19/19/19 +f 29/29/29 28/28/28 19/19/19 +f 29/29/29 19/19/19 8/8/8 +f 5/5/5 29/29/29 8/8/8 +f 30/30/30 28/28/28 29/29/29 +f 5/5/5 30/30/30 29/29/29 +f 30/30/30 31/31/31 28/28/28 +f 31/31/31 32/32/32 28/28/28 +f 33/33/33 20/20/20 28/28/28 +f 32/32/32 33/33/33 28/28/28 +f 21/21/21 20/20/20 33/33/33 +f 23/23/23 21/21/21 33/33/33 +f 32/32/32 34/34/34 33/33/33 +f 34/34/34 35/35/35 33/33/33 +f 36/36/36 23/23/23 33/33/33 +f 35/35/35 36/36/36 33/33/33 +f 24/24/24 23/23/23 36/36/36 +f 35/35/35 37/37/37 36/36/36 +f 37/37/37 38/38/38 36/36/36 +f 38/38/38 39/39/39 36/36/36 +f 39/39/39 40/40/40 36/36/36 +f 40/40/40 24/24/24 36/36/36 +f 39/39/39 41/41/41 40/40/40 +f 41/41/41 42/42/42 40/40/40 +f 31/31/31 30/30/30 43/43/43 +f 43/43/43 30/30/30 5/5/5 +f 44/44/44 31/31/31 43/43/43 +f 44/44/44 43/43/43 45/45/45 +f 46/46/46 44/44/44 45/45/45 +f 45/45/45 43/43/43 6/6/6 +f 7/7/7 45/45/45 6/6/6 +f 6/6/6 43/43/43 5/5/5 +f 46/46/46 47/47/47 44/44/44 +f 47/47/47 48/48/48 44/44/44 +f 48/48/48 49/49/49 44/44/44 +f 49/49/49 31/31/31 44/44/44 +f 48/48/48 50/50/50 49/49/49 +f 50/50/50 51/51/51 49/49/49 +f 51/51/51 52/52/52 49/49/49 +f 52/52/52 34/34/34 49/49/49 +f 32/32/32 31/31/31 49/49/49 +f 34/34/34 32/32/32 49/49/49 +f 51/51/51 53/53/53 52/52/52 +f 51/51/51 50/50/50 53/53/53 +f 53/53/53 54/54/54 52/52/52 +f 54/54/54 55/55/55 52/52/52 +f 55/55/55 54/54/54 56/56/56 +f 54/54/54 57/57/57 56/56/56 +f 57/57/57 54/54/54 53/53/53 +f 57/57/57 58/58/58 56/56/56 +f 58/58/58 59/59/59 56/56/56 +f 60/60/60 55/55/55 56/56/56 +f 59/59/59 60/60/60 56/56/56 +f 55/55/55 37/37/37 52/52/52 +f 35/35/35 34/34/34 52/52/52 +f 37/37/37 35/35/35 52/52/52 +f 50/50/50 61/61/61 53/53/53 +f 61/61/61 62/62/62 53/53/53 +f 62/62/62 63/63/63 53/53/53 +f 63/63/63 57/57/57 53/53/53 +f 58/58/58 57/57/57 63/63/63 +f 62/62/62 64/64/64 63/63/63 +f 64/64/64 65/65/65 63/63/63 +f 65/65/65 58/58/58 63/63/63 +f 66/66/66 58/58/58 65/65/65 +f 59/59/59 58/58/58 66/66/66 +f 67/67/67 66/66/66 65/65/65 +f 64/64/64 67/67/67 65/65/65 +f 64/64/64 68/68/68 67/67/67 +f 68/68/68 69/69/69 67/67/67 +f 70/70/70 59/59/59 66/66/66 +f 67/67/67 71/71/71 66/66/66 +f 67/67/67 69/69/69 71/71/71 +f 71/71/71 72/72/72 66/66/66 +f 62/62/62 61/61/61 73/73/73 +f 73/73/73 61/61/61 74/74/74 +f 75/75/75 73/73/73 74/74/74 +f 76/76/76 75/75/75 74/74/74 +f 61/61/61 50/50/50 74/74/74 +f 50/50/50 48/48/48 74/74/74 +f 48/48/48 47/47/47 74/74/74 +f 74/74/74 47/47/47 77/77/77 +f 77/77/77 47/47/47 46/46/46 +f 78/78/78 77/77/77 46/46/46 +f 78/78/78 46/46/46 79/79/79 +f 80/80/80 79/79/79 46/46/46 +f 80/80/80 46/46/46 45/45/45 +f 81/81/81 80/80/80 45/45/45 +f 82/82/82 81/81/81 45/45/45 +f 83/83/83 74/74/74 77/77/77 +f 83/83/83 84/84/84 74/74/74 +f 84/84/84 76/76/76 74/74/74 +f 82/82/82 45/45/45 7/7/7 +f 85/85/85 82/82/82 7/7/7 +f 85/85/85 7/7/7 86/86/86 +f 7/7/7 2/2/2 86/86/86 +f 86/86/86 2/2/2 3/3/3 +f 88/88/88 87/87/87 24/24/24 +f 24/24/24 87/87/87 25/25/25 +f 88/88/88 24/24/24 40/40/40 +f 42/42/42 88/88/88 40/40/40 +f 90/90/90 89/89/89 70/70/70 +f 91/91/91 70/70/70 89/89/89 +f 91/91/91 92/92/92 70/70/70 +f 92/92/92 93/93/93 70/70/70 +f 59/59/59 70/70/70 93/93/93 +f 60/60/60 59/59/59 93/93/93 +f 38/38/38 60/60/60 93/93/93 +f 39/39/39 38/38/38 93/93/93 +f 55/55/55 60/60/60 38/38/38 +f 37/37/37 55/55/55 38/38/38 +f 94/94/94 39/39/39 93/93/93 +f 92/92/92 94/94/94 93/93/93 +f 94/94/94 95/95/95 39/39/39 +f 39/39/39 95/95/95 41/41/41 +f 90/90/90 70/70/70 66/66/66 +f 72/72/72 90/90/90 66/66/66 +f 97/97/97 96/96/96 73/73/73 +f 98/98/98 97/97/97 73/73/73 +f 96/96/96 62/62/62 73/73/73 +f 97/97/97 99/99/99 96/96/96 +f 99/99/99 64/64/64 96/96/96 +f 64/64/64 62/62/62 96/96/96 +f 68/68/68 64/64/64 99/99/99 +f 75/75/75 98/98/98 73/73/73 +f 102/102/102 101/101/101 100/100/100 +f 103/103/103 102/102/102 100/100/100 +f 102/102/102 103/103/103 104/104/104 +f 103/103/103 105/105/105 104/104/104 +f 104/104/104 105/105/105 106/106/106 +f 105/105/105 107/107/107 106/106/106 +f 106/106/106 107/107/107 108/108/108 +f 106/106/106 108/108/108 109/109/109 +f 110/110/110 106/106/106 109/109/109 +f 104/104/104 106/106/106 110/110/110 +f 111/111/111 104/104/104 110/110/110 +f 102/102/102 104/104/104 111/111/111 +f 112/112/112 102/102/102 111/111/111 +f 102/102/102 112/112/112 101/101/101 +f 112/112/112 113/113/113 101/101/101 +f 110/110/110 109/109/109 114/114/114 +f 115/115/115 110/110/110 114/114/114 +f 111/111/111 110/110/110 115/115/115 +f 116/116/116 111/111/111 115/115/115 +f 112/112/112 111/111/111 116/116/116 +f 117/117/117 112/112/112 116/116/116 +f 112/112/112 117/117/117 113/113/113 +f 117/117/117 118/118/118 113/113/113 +f 115/115/115 114/114/114 119/119/119 +f 116/116/116 115/115/115 119/119/119 +f 120/120/120 116/116/116 119/119/119 +f 117/117/117 116/116/116 120/120/120 +f 121/121/121 117/117/117 120/120/120 +f 117/117/117 121/121/121 118/118/118 +f 121/121/121 122/122/122 118/118/118 +f 120/120/120 119/119/119 123/123/123 +f 120/120/120 123/123/123 124/124/124 +f 121/121/121 120/120/120 124/124/124 +f 121/121/121 124/124/124 122/122/122 +f 124/124/124 125/125/125 122/122/122 +f 122/122/122 125/125/125 126/126/126 +f 125/125/125 127/127/127 126/126/126 +f 122/122/122 126/126/126 128/128/128 +f 118/118/118 122/122/122 128/128/128 +f 118/118/118 128/128/128 129/129/129 +f 113/113/113 118/118/118 129/129/129 +f 113/113/113 129/129/129 130/130/130 +f 101/101/101 113/113/113 130/130/130 +f 101/101/101 130/130/130 131/131/131 +f 100/100/100 101/101/101 131/131/131 +f 132/132/132 127/127/127 125/125/125 +f 133/133/133 132/132/132 125/125/125 +f 134/134/134 132/132/132 133/133/133 +f 135/135/135 134/134/134 133/133/133 +f 135/135/135 133/133/133 136/136/136 +f 135/135/135 136/136/136 137/137/137 +f 137/137/137 136/136/136 138/138/138 +f 136/136/136 139/139/139 138/138/138 +f 138/138/138 139/139/139 140/140/140 +f 139/139/139 141/141/141 140/140/140 +f 140/140/140 141/141/141 142/142/142 +f 141/141/141 143/143/143 142/142/142 +f 142/142/142 143/143/143 144/144/144 +f 143/143/143 145/145/145 144/144/144 +f 144/144/144 145/145/145 146/146/146 +f 145/145/145 147/147/147 146/146/146 +f 146/146/146 147/147/147 148/148/148 +f 147/147/147 149/149/149 148/148/148 +f 148/148/148 149/149/149 150/150/150 +f 149/149/149 151/151/151 150/150/150 +f 150/150/150 151/151/151 152/152/152 +f 151/151/151 153/153/153 152/152/152 +f 152/152/152 153/153/153 154/154/154 +f 153/153/153 155/155/155 154/154/154 +f 141/141/141 156/156/156 143/143/143 +f 157/157/157 156/156/156 141/141/141 +f 156/156/156 158/158/158 143/143/143 +f 143/143/143 158/158/158 145/145/145 +f 158/158/158 159/159/159 145/145/145 +f 145/145/145 159/159/159 147/147/147 +f 159/159/159 160/160/160 147/147/147 +f 147/147/147 160/160/160 149/149/149 +f 160/160/160 161/161/161 149/149/149 +f 149/149/149 161/161/161 151/151/151 +f 161/161/161 162/162/162 151/151/151 +f 151/151/151 162/162/162 153/153/153 +f 162/162/162 163/163/163 153/153/153 +f 153/153/153 163/163/163 155/155/155 +f 163/163/163 164/164/164 155/155/155 +f 150/150/150 152/152/152 165/165/165 +f 166/166/166 150/150/150 165/165/165 +f 166/166/166 165/165/165 167/167/167 +f 167/167/167 165/165/165 168/168/168 +f 165/165/165 169/169/169 168/168/168 +f 170/170/170 166/166/166 167/167/167 +f 171/171/171 166/166/166 170/170/170 +f 172/172/172 171/171/171 170/170/170 +f 173/173/173 171/171/171 172/172/172 +f 174/174/174 173/173/173 172/172/172 +f 175/175/175 173/173/173 174/174/174 +f 176/176/176 175/175/175 174/174/174 +f 177/177/177 175/175/175 176/176/176 +f 178/178/178 177/177/177 176/176/176 +f 179/179/179 177/177/177 178/178/178 +f 180/180/180 179/179/179 178/178/178 +f 181/181/181 179/179/179 180/180/180 +f 182/182/182 181/181/181 180/180/180 +f 183/183/183 181/181/181 182/182/182 +f 184/184/184 183/183/183 182/182/182 +f 185/185/185 183/183/183 184/184/184 +f 186/186/186 185/185/185 184/184/184 +f 184/184/184 182/182/182 187/187/187 +f 188/188/188 184/184/184 187/187/187 +f 186/186/186 184/184/184 188/188/188 +f 188/188/188 187/187/187 189/189/189 +f 190/190/190 188/188/188 189/189/189 +f 191/191/191 188/188/188 190/190/190 +f 191/191/191 186/186/186 188/188/188 +f 192/192/192 191/191/191 190/190/190 +f 189/189/189 187/187/187 193/193/193 +f 187/187/187 194/194/194 193/193/193 +f 182/182/182 180/180/180 194/194/194 +f 187/187/187 182/182/182 194/194/194 +f 193/193/193 194/194/194 195/195/195 +f 194/194/194 196/196/196 195/195/195 +f 195/195/195 196/196/196 197/197/197 +f 196/196/196 198/198/198 197/197/197 +f 197/197/197 198/198/198 199/199/199 +f 198/198/198 200/200/200 199/199/199 +f 199/199/199 200/200/200 201/201/201 +f 200/200/200 202/202/202 201/201/201 +f 201/201/201 202/202/202 203/203/203 +f 202/202/202 204/204/204 203/203/203 +f 203/203/203 204/204/204 205/205/205 +f 204/204/204 206/206/206 205/205/205 +f 205/205/205 206/206/206 207/207/207 +f 206/206/206 208/208/208 207/207/207 +f 193/193/193 195/195/195 209/209/209 +f 210/210/210 193/193/193 209/209/209 +f 210/210/210 209/209/209 211/211/211 +f 212/212/212 210/210/210 211/211/211 +f 213/213/213 210/210/210 212/212/212 +f 214/214/214 213/213/213 212/212/212 +f 215/215/215 213/213/213 214/214/214 +f 216/216/216 215/215/215 214/214/214 +f 217/217/217 215/215/215 216/216/216 +f 218/218/218 217/217/217 216/216/216 +f 211/211/211 209/209/209 219/219/219 +f 209/209/209 220/220/220 219/219/219 +f 219/219/219 220/220/220 221/221/221 +f 220/220/220 222/222/222 221/221/221 +f 221/221/221 222/222/222 223/223/223 +f 222/222/222 224/224/224 223/223/223 +f 223/223/223 224/224/224 225/225/225 +f 224/224/224 226/226/226 225/225/225 +f 225/225/225 226/226/226 227/227/227 +f 226/226/226 228/228/228 227/227/227 +f 227/227/227 228/228/228 229/229/229 +f 228/228/228 230/230/230 229/229/229 +f 219/219/219 221/221/221 231/231/231 +f 232/232/232 219/219/219 231/231/231 +f 232/232/232 231/231/231 233/233/233 +f 234/234/234 232/232/232 233/233/233 +f 235/235/235 232/232/232 234/234/234 +f 236/236/236 235/235/235 234/234/234 +f 237/237/237 235/235/235 236/236/236 +f 238/238/238 237/237/237 236/236/236 +f 239/239/239 237/237/237 238/238/238 +f 240/240/240 239/239/239 238/238/238 +f 241/241/241 239/239/239 240/240/240 +f 242/242/242 241/241/241 240/240/240 +f 243/243/243 241/241/241 242/242/242 +f 244/244/244 243/243/243 242/242/242 +f 233/233/233 231/231/231 245/245/245 +f 231/231/231 246/246/246 245/245/245 +f 245/245/245 246/246/246 247/247/247 +f 246/246/246 248/248/248 247/247/247 +f 247/247/247 248/248/248 249/249/249 +f 248/248/248 250/250/250 249/249/249 +f 249/249/249 250/250/250 251/251/251 +f 250/250/250 252/252/252 251/251/251 +f 245/245/245 247/247/247 253/253/253 +f 254/254/254 245/245/245 253/253/253 +f 254/254/254 253/253/253 255/255/255 +f 256/256/256 254/254/254 255/255/255 +f 257/257/257 254/254/254 256/256/256 +f 258/258/258 257/257/257 256/256/256 +f 259/259/259 257/257/257 258/258/258 +f 260/260/260 259/259/259 258/258/258 +f 261/261/261 259/259/259 260/260/260 +f 262/262/262 261/261/261 260/260/260 +f 263/263/263 261/261/261 262/262/262 +f 264/264/264 263/263/263 262/262/262 +f 265/265/265 263/263/263 264/264/264 +f 266/266/266 265/265/265 264/264/264 +f 267/267/267 265/265/265 266/266/266 +f 268/268/268 267/267/267 266/266/266 +f 269/269/269 267/267/267 268/268/268 +f 270/270/270 269/269/269 268/268/268 +f 269/269/269 244/244/244 267/267/267 +f 244/244/244 242/242/242 267/267/267 +f 267/267/267 242/242/242 265/265/265 +f 242/242/242 240/240/240 265/265/265 +f 265/265/265 240/240/240 263/263/263 +f 240/240/240 238/238/238 263/263/263 +f 263/263/263 238/238/238 261/261/261 +f 238/238/238 236/236/236 261/261/261 +f 261/261/261 236/236/236 259/259/259 +f 236/236/236 234/234/234 259/259/259 +f 259/259/259 234/234/234 257/257/257 +f 234/234/234 233/233/233 257/257/257 +f 257/257/257 233/233/233 254/254/254 +f 233/233/233 245/245/245 254/254/254 +f 270/270/270 268/268/268 271/271/271 +f 272/272/272 270/270/270 271/271/271 +f 272/272/272 271/271/271 273/273/273 +f 274/274/274 272/272/272 273/273/273 +f 273/273/273 271/271/271 275/275/275 +f 271/271/271 276/276/276 275/275/275 +f 275/275/275 276/276/276 277/277/277 +f 276/276/276 278/278/278 277/277/277 +f 277/277/277 278/278/278 279/279/279 +f 278/278/278 280/280/280 279/279/279 +f 279/279/279 280/280/280 281/281/281 +f 280/280/280 282/282/282 281/281/281 +f 281/281/281 282/282/282 283/283/283 +f 282/282/282 284/284/284 283/283/283 +f 283/283/283 284/284/284 285/285/285 +f 284/284/284 286/286/286 285/285/285 +f 285/285/285 286/286/286 287/287/287 +f 286/286/286 288/288/288 287/287/287 +f 287/287/287 288/288/288 289/289/289 +f 288/288/288 290/290/290 289/289/289 +f 289/289/289 290/290/290 291/291/291 +f 290/290/290 292/292/292 291/291/291 +f 290/290/290 293/293/293 292/292/292 +f 293/293/293 294/294/294 292/292/292 +f 293/293/293 295/295/295 294/294/294 +f 295/295/295 296/296/296 294/294/294 +f 253/253/253 295/295/295 293/293/293 +f 255/255/255 253/253/253 293/293/293 +f 255/255/255 293/293/293 290/290/290 +f 288/288/288 255/255/255 290/290/290 +f 253/253/253 247/247/247 295/295/295 +f 247/247/247 249/249/249 295/295/295 +f 295/295/295 249/249/249 296/296/296 +f 249/249/249 251/251/251 296/296/296 +f 256/256/256 255/255/255 288/288/288 +f 286/286/286 256/256/256 288/288/288 +f 258/258/258 256/256/256 286/286/286 +f 284/284/284 258/258/258 286/286/286 +f 260/260/260 258/258/258 284/284/284 +f 282/282/282 260/260/260 284/284/284 +f 262/262/262 260/260/260 282/282/282 +f 280/280/280 262/262/262 282/282/282 +f 264/264/264 262/262/262 280/280/280 +f 278/278/278 264/264/264 280/280/280 +f 266/266/266 264/264/264 278/278/278 +f 276/276/276 266/266/266 278/278/278 +f 268/268/268 266/266/266 276/276/276 +f 271/271/271 268/268/268 276/276/276 +f 251/251/251 252/252/252 297/297/297 +f 252/252/252 298/298/298 297/297/297 +f 252/252/252 229/229/229 298/298/298 +f 227/227/227 229/229/229 252/252/252 +f 250/250/250 227/227/227 252/252/252 +f 225/225/225 227/227/227 250/250/250 +f 248/248/248 225/225/225 250/250/250 +f 223/223/223 225/225/225 248/248/248 +f 246/246/246 223/223/223 248/248/248 +f 221/221/221 223/223/223 246/246/246 +f 231/231/231 221/221/221 246/246/246 +f 229/229/229 299/299/299 298/298/298 +f 229/229/229 230/230/230 299/299/299 +f 230/230/230 300/300/300 299/299/299 +f 251/251/251 297/297/297 301/301/301 +f 296/296/296 251/251/251 301/301/301 +f 296/296/296 301/301/301 302/302/302 +f 294/294/294 296/296/296 302/302/302 +f 294/294/294 302/302/302 303/303/303 +f 292/292/292 294/294/294 303/303/303 +f 292/292/292 303/303/303 304/304/304 +f 291/291/291 292/292/292 304/304/304 +f 305/305/305 291/291/291 304/304/304 +f 305/305/305 306/306/306 291/291/291 +f 307/307/307 306/306/306 305/305/305 +f 308/308/308 307/307/307 305/305/305 +f 309/309/309 306/306/306 307/307/307 +f 310/310/310 309/309/309 307/307/307 +f 311/311/311 309/309/309 310/310/310 +f 312/312/312 311/311/311 310/310/310 +f 313/313/313 311/311/311 312/312/312 +f 314/314/314 313/313/313 312/312/312 +f 315/315/315 313/313/313 314/314/314 +f 316/316/316 315/315/315 314/314/314 +f 317/317/317 316/316/316 314/314/314 +f 318/318/318 316/316/316 317/317/317 +f 319/319/319 318/318/318 317/317/317 +f 320/320/320 318/318/318 319/319/319 +f 321/321/321 320/320/320 319/319/319 +f 322/322/322 321/321/321 319/319/319 +f 323/323/323 321/321/321 322/322/322 +f 324/324/324 323/323/323 322/322/322 +f 325/325/325 312/312/312 310/310/310 +f 325/325/325 310/310/310 307/307/307 +f 326/326/326 325/325/325 307/307/307 +f 327/327/327 326/326/326 307/307/307 +f 308/308/308 327/327/327 307/307/307 +f 328/328/328 319/319/319 317/317/317 +f 329/329/329 328/328/328 317/317/317 +f 330/330/330 328/328/328 329/329/329 +f 330/330/330 329/329/329 325/325/325 +f 328/328/328 330/330/330 331/331/331 +f 331/331/331 322/322/322 319/319/319 +f 328/328/328 331/331/331 319/319/319 +f 330/330/330 332/332/332 331/331/331 +f 325/325/325 326/326/326 332/332/332 +f 330/330/330 325/325/325 332/332/332 +f 332/332/332 333/333/333 331/331/331 +f 326/326/326 327/327/327 333/333/333 +f 332/332/332 326/326/326 333/333/333 +f 333/333/333 334/334/334 331/331/331 +f 334/334/334 324/324/324 322/322/322 +f 331/331/331 334/334/334 322/322/322 +f 325/325/325 329/329/329 312/312/312 +f 329/329/329 314/314/314 312/312/312 +f 329/329/329 317/317/317 314/314/314 +f 308/308/308 335/335/335 327/327/327 +f 335/335/335 308/308/308 336/336/336 +f 308/308/308 305/305/305 336/336/336 +f 336/336/336 305/305/305 304/304/304 +f 335/335/335 337/337/337 327/327/327 +f 333/333/333 338/338/338 334/334/334 +f 338/338/338 339/339/339 334/334/334 +f 334/334/334 339/339/339 324/324/324 +f 339/339/339 340/340/340 324/324/324 +f 324/324/324 340/340/340 323/323/323 +f 340/340/340 341/341/341 323/323/323 +f 323/323/323 341/341/341 274/274/274 +f 323/323/323 274/274/274 321/321/321 +f 274/274/274 273/273/273 321/321/321 +f 321/321/321 273/273/273 320/320/320 +f 273/273/273 275/275/275 320/320/320 +f 320/320/320 275/275/275 318/318/318 +f 275/275/275 277/277/277 318/318/318 +f 318/318/318 277/277/277 316/316/316 +f 277/277/277 279/279/279 316/316/316 +f 316/316/316 279/279/279 315/315/315 +f 279/279/279 281/281/281 315/315/315 +f 315/315/315 281/281/281 313/313/313 +f 281/281/281 283/283/283 313/313/313 +f 313/313/313 283/283/283 311/311/311 +f 283/283/283 285/285/285 311/311/311 +f 311/311/311 285/285/285 309/309/309 +f 285/285/285 287/287/287 309/309/309 +f 309/309/309 287/287/287 306/306/306 +f 287/287/287 289/289/289 306/306/306 +f 306/306/306 289/289/289 291/291/291 +f 344/344/344 343/343/343 342/342/342 +f 345/345/345 344/344/344 342/342/342 +f 345/345/345 342/342/342 346/346/346 +f 347/347/347 345/345/345 346/346/346 +f 347/347/347 346/346/346 348/348/348 +f 349/349/349 347/347/347 348/348/348 +f 349/349/349 348/348/348 350/350/350 +f 351/351/351 349/349/349 350/350/350 +f 351/351/351 350/350/350 352/352/352 +f 353/353/353 351/351/351 352/352/352 +f 353/353/353 352/352/352 354/354/354 +f 355/355/355 353/353/353 354/354/354 +f 355/355/355 354/354/354 356/356/356 +f 357/357/357 355/355/355 356/356/356 +f 357/357/357 356/356/356 358/358/358 +f 359/359/359 357/357/357 358/358/358 +f 359/359/359 358/358/358 360/360/360 +f 361/361/361 359/359/359 360/360/360 +f 360/360/360 362/362/362 361/361/361 +f 362/362/362 363/363/363 361/361/361 +f 364/364/364 363/363/363 362/362/362 +f 365/365/365 364/364/364 362/362/362 +f 366/366/366 364/364/364 365/365/365 +f 367/367/367 366/366/366 365/365/365 +f 368/368/368 366/366/366 367/367/367 +f 369/369/369 368/368/368 367/367/367 +f 370/370/370 368/368/368 369/369/369 +f 371/371/371 370/370/370 369/369/369 +f 372/372/372 370/370/370 371/371/371 +f 373/373/373 372/372/372 371/371/371 +f 374/374/374 372/372/372 373/373/373 +f 375/375/375 374/374/374 373/373/373 +f 375/375/375 376/376/376 374/374/374 +f 375/375/375 377/377/377 376/376/376 +f 375/375/375 378/378/378 377/377/377 +f 378/378/378 379/379/379 377/377/377 +f 376/376/376 380/380/380 374/374/374 +f 380/380/380 381/381/381 374/374/374 +f 381/381/381 382/382/382 374/374/374 +f 382/382/382 383/383/383 374/374/374 +f 216/216/216 214/214/214 239/239/239 +f 241/241/241 216/216/216 239/239/239 +f 239/239/239 214/214/214 237/237/237 +f 214/214/214 212/212/212 237/237/237 +f 237/237/237 212/212/212 235/235/235 +f 212/212/212 211/211/211 235/235/235 +f 235/235/235 211/211/211 232/232/232 +f 211/211/211 219/219/219 232/232/232 +f 218/218/218 216/216/216 241/241/241 +f 243/243/243 218/218/218 241/241/241 +f 243/243/243 384/384/384 218/218/218 +f 385/385/385 384/384/384 243/243/243 +f 244/244/244 385/385/385 243/243/243 +f 386/386/386 385/385/385 244/244/244 +f 269/269/269 386/386/386 244/244/244 +f 387/387/387 386/386/386 269/269/269 +f 270/270/270 387/387/387 269/269/269 +f 388/388/388 387/387/387 270/270/270 +f 272/272/272 388/388/388 270/270/270 +f 389/389/389 388/388/388 272/272/272 +f 274/274/274 389/389/389 272/272/272 +f 341/341/341 389/389/389 274/274/274 +f 384/384/384 390/390/390 218/218/218 +f 218/218/218 390/390/390 217/217/217 +f 390/390/390 391/391/391 217/217/217 +f 217/217/217 391/391/391 192/192/192 +f 217/217/217 192/192/192 215/215/215 +f 192/192/192 190/190/190 215/215/215 +f 215/215/215 190/190/190 213/213/213 +f 190/190/190 189/189/189 213/213/213 +f 213/213/213 189/189/189 210/210/210 +f 189/189/189 193/193/193 210/210/210 +f 391/391/391 392/392/392 192/192/192 +f 192/192/192 392/392/392 191/191/191 +f 392/392/392 393/393/393 191/191/191 +f 191/191/191 393/393/393 186/186/186 +f 393/393/393 394/394/394 186/186/186 +f 186/186/186 394/394/394 185/185/185 +f 394/394/394 395/395/395 185/185/185 +f 185/185/185 395/395/395 396/396/396 +f 185/185/185 396/396/396 183/183/183 +f 396/396/396 137/137/137 183/183/183 +f 396/396/396 135/135/135 137/137/137 +f 183/183/183 137/137/137 181/181/181 +f 137/137/137 138/138/138 181/181/181 +f 181/181/181 138/138/138 179/179/179 +f 138/138/138 140/140/140 179/179/179 +f 179/179/179 140/140/140 177/177/177 +f 140/140/140 142/142/142 177/177/177 +f 177/177/177 142/142/142 175/175/175 +f 142/142/142 144/144/144 175/175/175 +f 175/175/175 144/144/144 173/173/173 +f 144/144/144 146/146/146 173/173/173 +f 173/173/173 146/146/146 171/171/171 +f 146/146/146 148/148/148 171/171/171 +f 171/171/171 148/148/148 166/166/166 +f 148/148/148 150/150/150 166/166/166 +f 397/397/397 134/134/134 135/135/135 +f 395/395/395 397/397/397 396/396/396 +f 396/396/396 397/397/397 135/135/135 +f 195/195/195 197/197/197 220/220/220 +f 209/209/209 195/195/195 220/220/220 +f 220/220/220 197/197/197 222/222/222 +f 197/197/197 199/199/199 222/222/222 +f 222/222/222 199/199/199 224/224/224 +f 199/199/199 201/201/201 224/224/224 +f 224/224/224 201/201/201 226/226/226 +f 201/201/201 203/203/203 226/226/226 +f 226/226/226 203/203/203 228/228/228 +f 203/203/203 205/205/205 228/228/228 +f 228/228/228 205/205/205 207/207/207 +f 230/230/230 228/228/228 207/207/207 +f 230/230/230 207/207/207 300/300/300 +f 207/207/207 398/398/398 300/300/300 +f 207/207/207 208/208/208 398/398/398 +f 208/208/208 399/399/399 398/398/398 +f 208/208/208 168/168/168 399/399/399 +f 167/167/167 168/168/168 208/208/208 +f 206/206/206 167/167/167 208/208/208 +f 170/170/170 167/167/167 206/206/206 +f 204/204/204 170/170/170 206/206/206 +f 172/172/172 170/170/170 204/204/204 +f 202/202/202 172/172/172 204/204/204 +f 174/174/174 172/172/172 202/202/202 +f 200/200/200 174/174/174 202/202/202 +f 176/176/176 174/174/174 200/200/200 +f 198/198/198 176/176/176 200/200/200 +f 178/178/178 176/176/176 198/198/198 +f 196/196/196 178/178/178 198/198/198 +f 180/180/180 178/178/178 196/196/196 +f 194/194/194 180/180/180 196/196/196 +f 168/168/168 400/400/400 399/399/399 +f 168/168/168 169/169/169 400/400/400 +f 169/169/169 401/401/401 400/400/400 +f 169/169/169 154/154/154 401/401/401 +f 152/152/152 154/154/154 169/169/169 +f 165/165/165 152/152/152 169/169/169 +f 154/154/154 402/402/402 401/401/401 +f 154/154/154 155/155/155 402/402/402 +f 155/155/155 403/403/403 402/402/402 +f 155/155/155 164/164/164 403/403/403 +f 164/164/164 404/404/404 403/403/403 +f 164/164/164 100/100/100 404/404/404 +f 103/103/103 100/100/100 164/164/164 +f 163/163/163 103/103/103 164/164/164 +f 105/105/105 103/103/103 163/163/163 +f 162/162/162 105/105/105 163/163/163 +f 107/107/107 105/105/105 162/162/162 +f 161/161/161 107/107/107 162/162/162 +f 108/108/108 107/107/107 161/161/161 +f 160/160/160 108/108/108 161/161/161 +f 109/109/109 108/108/108 160/160/160 +f 159/159/159 109/109/109 160/160/160 +f 114/114/114 109/109/109 159/159/159 +f 158/158/158 114/114/114 159/159/159 +f 119/119/119 114/114/114 158/158/158 +f 156/156/156 119/119/119 158/158/158 +f 123/123/123 119/119/119 156/156/156 +f 157/157/157 123/123/123 156/156/156 +f 124/124/124 123/123/123 157/157/157 +f 405/405/405 124/124/124 157/157/157 +f 405/405/405 157/157/157 139/139/139 +f 136/136/136 405/405/405 139/139/139 +f 133/133/133 405/405/405 136/136/136 +f 139/139/139 157/157/157 141/141/141 +f 125/125/125 124/124/124 405/405/405 +f 133/133/133 125/125/125 405/405/405 +f 327/327/327 337/337/337 338/338/338 +f 333/333/333 327/327/327 338/338/338 +f 100/100/100 131/131/131 404/404/404 +f 345/345/345 406/406/406 344/344/344 diff --git a/test-data/AvocadoOut.obj.REMOVED.git-id b/test-data/AvocadoOut.obj.REMOVED.git-id new file mode 100644 index 0000000000..5690950cad --- /dev/null +++ b/test-data/AvocadoOut.obj.REMOVED.git-id @@ -0,0 +1 @@ +d5f560b94a3a8f04d6f8b6e9513d7dcfad9eaca3 \ No newline at end of file diff --git a/test-data/Avocado_baseColor.png.REMOVED.git-id b/test-data/Avocado_baseColor.png.REMOVED.git-id new file mode 100644 index 0000000000..40d5618236 --- /dev/null +++ b/test-data/Avocado_baseColor.png.REMOVED.git-id @@ -0,0 +1 @@ +6f3a8b2309c64a078456423015adb8095bb8a159 \ No newline at end of file diff --git a/test-data/Avocado_normal.png.REMOVED.git-id b/test-data/Avocado_normal.png.REMOVED.git-id new file mode 100644 index 0000000000..eb2c174467 --- /dev/null +++ b/test-data/Avocado_normal.png.REMOVED.git-id @@ -0,0 +1 @@ +73f90d59794e139e876b34d48ee1f29671bfdba2 \ No newline at end of file diff --git a/test-data/Avocado_roughnessMetallic.png.REMOVED.git-id b/test-data/Avocado_roughnessMetallic.png.REMOVED.git-id new file mode 100644 index 0000000000..ff7c5bffd2 --- /dev/null +++ b/test-data/Avocado_roughnessMetallic.png.REMOVED.git-id @@ -0,0 +1 @@ +b83cff933c3502a5f176780e8dc7a437e5449f21 \ No newline at end of file diff --git a/test-data/gen b/test-data/gen new file mode 100755 index 0000000000..dfe708d963 Binary files /dev/null and b/test-data/gen differ diff --git a/test-data/gen2 b/test-data/gen2 new file mode 100755 index 0000000000..9cd46566cf Binary files /dev/null and b/test-data/gen2 differ diff --git a/test-data/generate-obj.rs b/test-data/generate-obj.rs new file mode 100644 index 0000000000..f226a2b9ef --- /dev/null +++ b/test-data/generate-obj.rs @@ -0,0 +1,45 @@ + +extern crate gltf; + +use std::io::Write; + +fn main() { + let path = "test-data/Avocado.gltf"; + let gltf = gltf::Import::from_path(path).sync().unwrap(); + let mesh = gltf.meshes().nth(0).unwrap(); + let primitive = mesh.primitives().nth(0).unwrap(); + let positions: Vec<[f32; 3]> = primitive.positions().unwrap().collect(); + let normals: Vec<[f32; 3]> = primitive.normals().unwrap().collect(); + let mut tex_coords: Vec<[f32; 2]> = vec![]; + let mut indices: Vec = vec![]; + match primitive.tex_coords(0).unwrap() { + gltf::mesh::TexCoords::F32(iter) => tex_coords.extend(iter), + _ => unreachable!(), + } + match primitive.indices().unwrap() { + gltf::mesh::Indices::U16(iter) => indices.extend(iter), + _ => unreachable!(), + } + + let file = std::fs::File::create("Avocado.obj").unwrap(); + let mut writer = std::io::BufWriter::new(file); + for position in &positions { + writeln!(writer, "v {} {} {}", position[0], position[1], position[2]); + } + for normal in &normals { + writeln!(writer, "vn {} {} {}", normal[0], normal[1], normal[2]); + } + for tex_coord in &tex_coords { + writeln!(writer, "vt {} {}", tex_coord[0], tex_coord[1]); + } + let mut i = indices.iter(); + while let (Some(v0), Some(v1), Some(v2)) = (i.next(), i.next(), i.next()) { + writeln!( + writer, + "f {}/{}/{} {}/{}/{} {}/{}/{}", + v0, v0, v0, + v1, v1, v1, + v2, v2, v2, + ); + } +} diff --git a/test-data/generate-test-data b/test-data/generate-test-data new file mode 100755 index 0000000000..b383add654 Binary files /dev/null and b/test-data/generate-test-data differ diff --git a/test-data/generate-test-data.c b/test-data/generate-test-data.c new file mode 100644 index 0000000000..6fa42877ca --- /dev/null +++ b/test-data/generate-test-data.c @@ -0,0 +1,245 @@ + +#include +#include +#include +#include +#include +#include + +#include "mikktspace.h" + +struct tangent { + // Vector. + float v[3]; + + // Sign. + float s; +}; + +struct vertex { + // Borrows from `input.positions`. + float (*position)[3]; + + // Borrows from `input.positions`. + float (*normal)[3]; + + // Borrows from `input.positions`. + float (*tex_coord)[2]; +}; + +struct face { + // Borrows from `positions`, `normals`, and `tex_coords`. + struct vertex vertices[3]; +}; + +struct input { + // Owned. + float (*positions)[3]; + + // Owned. + float (*normals)[3]; + + // Owned. + float (*tex_coords)[2]; + + // Borrows from `positions`, `normals`, and `tex_coords`. + struct face *faces; + + // Number of entries in `positions`, `normals`, and `tex_coords`. + size_t nr_vertices; + + // Number of entries in `faces`. + size_t nr_faces; +} input; + +struct output { + // Borrows from `positions`, `normals`, and `tex_coords`. + struct vertex *vertices; + + // Owned. + struct tangent *tangents; + + // Number of entries in `vertices` and `tangents`. + // Equal to `3 * input.nr_faces`. + size_t nr_vertices; +} output; + +void print_vec2(float (*t)[2]) { + printf("[%f, %f]", (*t)[0], (*t)[1]); +} + +void print_vec3(float (*t)[3]) { + printf("[%f, %f, %f]", (*t)[0], (*t)[1], (*t)[2]); +} + +void print_tangent(const struct tangent *t) { + printf("[%f, %f, %f, %f]", t->v[0], t->v[1], t->v[2], t->s); +} + +int get_num_faces(const SMikkTSpaceContext *x) { + return input.nr_faces; +} + +int get_num_vertices_of_face(const SMikkTSpaceContext *x, int f) { + return 3; +} + +void get_position(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[3] = input.faces[f].vertices[v].position; + memcpy(dst, src, sizeof(*src)); +} + +void get_normal(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[3] = input.faces[f].vertices[v].normal; + memcpy(dst, src, sizeof(*src)); +} + +void get_tex_coord(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[2] = input.faces[f].vertices[v].tex_coord; + memcpy(dst, src, sizeof(*src)); +} + +void set_tspace_basic( + const SMikkTSpaceContext *x, + const float *t, + float s, + int f, + int v +) { + // The index of the last output (vertex, tangent) pair. + static int i = 0; + + struct vertex *in = &input.faces[f].vertices[v]; + + output.vertices[i].position = in->position; + output.vertices[i].normal = in->normal; + output.vertices[i].tex_coord = in->tex_coord; + memcpy(output.tangents[i].v, t, 3 * sizeof(float)); + output.tangents[i].s = s; + + ++i; +} + +void set_tspace( + const SMikkTSpaceContext *x, + const float *t, + const float *b, + float mag_s, + float mag_t, + tbool op, + int f, + int v +) { + assert(!"unreachable"); +} + +int main() { + input.nr_vertices = 406; + input.nr_faces = 682; + output.nr_vertices = 3 * input.nr_faces; + + input.positions = calloc(input.nr_vertices, sizeof(*input.positions)); + input.normals = calloc(input.nr_vertices, sizeof(*input.normals)); + input.tex_coords = calloc(input.nr_vertices, sizeof(*input.tex_coords)); + input.faces = calloc(input.nr_faces, sizeof(*input.faces)); + output.vertices = calloc(output.nr_vertices, sizeof(*output.vertices)); + output.tangents = calloc(output.nr_vertices, sizeof(*output.tangents)); + + FILE *fi = fopen("Avocado.obj", "rb"); + assert(fi); + char buffer[1024]; + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "v %f %f %f", + &input.positions[i][0], + &input.positions[i][1], + &input.positions[i][2] + ); + } + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "vn %f %f %f", + &input.normals[i][0], + &input.normals[i][1], + &input.normals[i][2] + ); + } + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "vt %f %f", + &input.tex_coords[i][0], + &input.tex_coords[i][1] + ); + } + + for (size_t i = 0; i < input.nr_faces; ++i) { + fgets(buffer, sizeof(buffer), fi); + int v[3]; + sscanf( + buffer, + "f %d/%d/%d %d/%d/%d %d/%d/%d", + &v[0], &v[0], &v[0], + &v[1], &v[1], &v[1], + &v[2], &v[2], &v[2] + ); + for (size_t j = 0; j < 3; ++j) { + input.faces[i].vertices[j].position = &input.positions[v[j] - 1]; + input.faces[i].vertices[j].normal = &input.normals[v[j] - 1]; + input.faces[i].vertices[j].tex_coord = &input.tex_coords[v[j] - 1]; + } + } + + SMikkTSpaceInterface interface = { + .m_getNumFaces = get_num_faces, + .m_getNumVerticesOfFace = get_num_vertices_of_face, + .m_getPosition = get_position, + .m_getNormal = get_normal, + .m_getTexCoord = get_tex_coord, + .m_setTSpaceBasic = set_tspace_basic, + .m_setTSpace = NULL, + }; + SMikkTSpaceContext context = { + .m_pInterface = &interface, + .m_pUserData = NULL, + }; + + genTangSpaceDefault(&context); + + printf("{\n \"vlist\": [\n"); + for (size_t i = 0; i < output.nr_vertices; ++i) { + printf(" {\"v\": "); + print_vec3(output.vertices[i].position); + printf(", \"vn\": "); + print_vec3(output.vertices[i].normal); + printf(", \"vt\": "); + print_vec2(output.vertices[i].tex_coord); + printf(", \"vx\": "); + print_tangent(&output.tangents[i]); + if (i == output.nr_vertices - 1) { + printf("}\n"); + } else { + printf("},\n"); + } + } + printf(" ]\n}"); + + fclose(fi); + free(input.positions); + free(input.normals); + free(input.tex_coords); + free(input.faces); + free(output.vertices); + free(output.tangents); + + return 0; +} + diff --git a/test-data/generate-test-obj.c b/test-data/generate-test-obj.c new file mode 100644 index 0000000000..6d4a8ea921 --- /dev/null +++ b/test-data/generate-test-obj.c @@ -0,0 +1,250 @@ + +#include +#include +#include +#include +#include +#include + +#include "mikktspace.h" + +struct tangent { + // Vector. + float v[3]; + + // Sign. + float s; +}; + +struct vertex { + // Borrows from `input.positions`. + float (*position)[3]; + + // Borrows from `input.positions`. + float (*normal)[3]; + + // Borrows from `input.positions`. + float (*tex_coord)[2]; +}; + +struct face { + // Borrows from `positions`, `normals`, and `tex_coords`. + struct vertex vertices[3]; +}; + +struct input { + // Owned. + float (*positions)[3]; + + // Owned. + float (*normals)[3]; + + // Owned. + float (*tex_coords)[2]; + + // Borrows from `positions`, `normals`, and `tex_coords`. + struct face *faces; + + // Number of entries in `positions`, `normals`, and `tex_coords`. + size_t nr_vertices; + + // Number of entries in `faces`. + size_t nr_faces; +} input; + +struct output { + // Borrows from `positions`, `normals`, and `tex_coords`. + struct vertex *vertices; + + // Owned. + struct tangent *tangents; + + // Number of entries in `vertices` and `tangents`. + // Equal to `3 * input.nr_faces`. + size_t nr_vertices; +} output; + +void print_vec2(float (*t)[2]) { + printf("%f %f", (*t)[0], (*t)[1]); +} + +void print_vec3(float (*t)[3]) { + printf("%f %f %f", (*t)[0], (*t)[1], (*t)[2]); +} + +void print_tangent(const struct tangent *t) { + printf("%f %f %f %f", t->v[0], t->v[1], t->v[2], t->s); +} + +int get_num_faces(const SMikkTSpaceContext *x) { + return input.nr_faces; +} + +int get_num_vertices_of_face(const SMikkTSpaceContext *x, int f) { + return 3; +} + +void get_position(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[3] = input.faces[f].vertices[v].position; + memcpy(dst, src, sizeof(*src)); +} + +void get_normal(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[3] = input.faces[f].vertices[v].normal; + memcpy(dst, src, sizeof(*src)); +} + +void get_tex_coord(const SMikkTSpaceContext *x, float *dst, int f, int v) { + float (*src)[2] = input.faces[f].vertices[v].tex_coord; + memcpy(dst, src, sizeof(*src)); +} + +void set_tspace_basic( + const SMikkTSpaceContext *x, + const float *t, + float s, + int f, + int v +) { + // The index of the last output (vertex, tangent) pair. + static int i = 0; + + struct vertex *in = &input.faces[f].vertices[v]; + + output.vertices[i].position = in->position; + output.vertices[i].normal = in->normal; + output.vertices[i].tex_coord = in->tex_coord; + memcpy(output.tangents[i].v, t, 3 * sizeof(float)); + output.tangents[i].s = s; + + ++i; +} + +void set_tspace( + const SMikkTSpaceContext *x, + const float *t, + const float *b, + float mag_s, + float mag_t, + tbool op, + int f, + int v +) { + assert(!"unreachable"); +} + +int main() { + input.nr_vertices = 406; + input.nr_faces = 682; + output.nr_vertices = 3 * input.nr_faces; + + input.positions = calloc(input.nr_vertices, sizeof(*input.positions)); + input.normals = calloc(input.nr_vertices, sizeof(*input.normals)); + input.tex_coords = calloc(input.nr_vertices, sizeof(*input.tex_coords)); + input.faces = calloc(input.nr_faces, sizeof(*input.faces)); + output.vertices = calloc(output.nr_vertices, sizeof(*output.vertices)); + output.tangents = calloc(output.nr_vertices, sizeof(*output.tangents)); + + { + FILE *fi = fopen("Avocado.obj", "rb"); + assert(fi); + char buffer[1024]; + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "v %f %f %f", + &input.positions[i][0], + &input.positions[i][1], + &input.positions[i][2] + ); + } + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "vn %f %f %f", + &input.normals[i][0], + &input.normals[i][1], + &input.normals[i][2] + ); + } + + for (size_t i = 0; i < input.nr_vertices; ++i) { + fgets(buffer, sizeof(buffer), fi); + sscanf( + buffer, + "vt %f %f", + &input.tex_coords[i][0], + &input.tex_coords[i][1] + ); + } + + for (size_t i = 0; i < input.nr_faces; ++i) { + fgets(buffer, sizeof(buffer), fi); + int v[3]; + sscanf( + buffer, + "f %d/%d/%d %d/%d/%d %d/%d/%d", + &v[0], &v[0], &v[0], + &v[1], &v[1], &v[1], + &v[2], &v[2], &v[2] + ); + for (size_t j = 0; j < 3; ++j) { + input.faces[i].vertices[j].position = &input.positions[v[j] - 1]; + input.faces[i].vertices[j].normal = &input.normals[v[j] - 1]; + input.faces[i].vertices[j].tex_coord = &input.tex_coords[v[j] - 1]; + } + } + + fclose(fi); + } + + SMikkTSpaceInterface interface = { + .m_getNumFaces = get_num_faces, + .m_getNumVerticesOfFace = get_num_vertices_of_face, + .m_getPosition = get_position, + .m_getNormal = get_normal, + .m_getTexCoord = get_tex_coord, + .m_setTSpaceBasic = set_tspace_basic, + .m_setTSpace = NULL, + }; + SMikkTSpaceContext context = { + .m_pInterface = &interface, + .m_pUserData = NULL, + }; + + genTangSpaceDefault(&context); + + for (size_t i = 0; i < output.nr_vertices; ++i) { + printf("v "); + print_vec3(output.vertices[i].position); + printf("\n"); + } + + for (size_t i = 0; i < output.nr_vertices; ++i) { + printf("vn "); + print_vec3(output.vertices[i].normal); + printf("\n"); + } + + int k = 1; + for (size_t i = 0; i < output.nr_vertices / 3; ++i) { + int v0 = k++; + int v1 = k++; + int v2 = k++; + printf("f %d//%d %d//%d %d//%d\n", v0, v0, v1, v1, v2, v2); + } + + free(input.positions); + free(input.normals); + free(input.tex_coords); + free(input.faces); + free(output.vertices); + free(output.tangents); + + return 0; +} + diff --git a/test-data/libmikktspace.a b/test-data/libmikktspace.a new file mode 100644 index 0000000000..01df84eade Binary files /dev/null and b/test-data/libmikktspace.a differ diff --git a/test-data/mikktspace.h b/test-data/mikktspace.h new file mode 100644 index 0000000000..52c44a713c --- /dev/null +++ b/test-data/mikktspace.h @@ -0,0 +1,145 @@ +/** \file mikktspace/mikktspace.h + * \ingroup mikktspace + */ +/** + * Copyright (C) 2011 by Morten S. Mikkelsen + * + * This software is provided 'as-is', without any express or implied + * warranty. In no event will the authors be held liable for any damages + * arising from the use of this software. + * + * Permission is granted to anyone to use this software for any purpose, + * including commercial applications, and to alter it and redistribute it + * freely, subject to the following restrictions: + * + * 1. The origin of this software must not be misrepresented; you must not + * claim that you wrote the original software. If you use this software + * in a product, an acknowledgment in the product documentation would be + * appreciated but is not required. + * 2. Altered source versions must be plainly marked as such, and must not be + * misrepresented as being the original software. + * 3. This notice may not be removed or altered from any source distribution. + */ + +#ifndef __MIKKTSPACE_H__ +#define __MIKKTSPACE_H__ + + +#ifdef __cplusplus +extern "C" { +#endif + +/* Author: Morten S. Mikkelsen + * Version: 1.0 + * + * The files mikktspace.h and mikktspace.c are designed to be + * stand-alone files and it is important that they are kept this way. + * Not having dependencies on structures/classes/libraries specific + * to the program, in which they are used, allows them to be copied + * and used as is into any tool, program or plugin. + * The code is designed to consistently generate the same + * tangent spaces, for a given mesh, in any tool in which it is used. + * This is done by performing an internal welding step and subsequently an order-independent evaluation + * of tangent space for meshes consisting of triangles and quads. + * This means faces can be received in any order and the same is true for + * the order of vertices of each face. The generated result will not be affected + * by such reordering. Additionally, whether degenerate (vertices or texture coordinates) + * primitives are present or not will not affect the generated results either. + * Once tangent space calculation is done the vertices of degenerate primitives will simply + * inherit tangent space from neighboring non degenerate primitives. + * The analysis behind this implementation can be found in my master's thesis + * which is available for download --> http://image.diku.dk/projects/media/morten.mikkelsen.08.pdf + * Note that though the tangent spaces at the vertices are generated in an order-independent way, + * by this implementation, the interpolated tangent space is still affected by which diagonal is + * chosen to split each quad. A sensible solution is to have your tools pipeline always + * split quads by the shortest diagonal. This choice is order-independent and works with mirroring. + * If these have the same length then compare the diagonals defined by the texture coordinates. + * XNormal which is a tool for baking normal maps allows you to write your own tangent space plugin + * and also quad triangulator plugin. + */ + + +typedef int tbool; +typedef struct SMikkTSpaceContext SMikkTSpaceContext; + +typedef struct { + // Returns the number of faces (triangles/quads) on the mesh to be processed. + int (*m_getNumFaces)(const SMikkTSpaceContext * pContext); + + // Returns the number of vertices on face number iFace + // iFace is a number in the range {0, 1, ..., getNumFaces()-1} + int (*m_getNumVerticesOfFace)(const SMikkTSpaceContext * pContext, const int iFace); + + // returns the position/normal/texcoord of the referenced face of vertex number iVert. + // iVert is in the range {0,1,2} for triangles and {0,1,2,3} for quads. + void (*m_getPosition)(const SMikkTSpaceContext * pContext, float fvPosOut[], const int iFace, const int iVert); + void (*m_getNormal)(const SMikkTSpaceContext * pContext, float fvNormOut[], const int iFace, const int iVert); + void (*m_getTexCoord)(const SMikkTSpaceContext * pContext, float fvTexcOut[], const int iFace, const int iVert); + + // either (or both) of the two setTSpace callbacks can be set. + // The call-back m_setTSpaceBasic() is sufficient for basic normal mapping. + + // This function is used to return the tangent and fSign to the application. + // fvTangent is a unit length vector. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpaceBasic)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fSign, const int iFace, const int iVert); + + // This function is used to return tangent space results to the application. + // fvTangent and fvBiTangent are unit length vectors and fMagS and fMagT are their + // true magnitudes which can be used for relief mapping effects. + // fvBiTangent is the "real" bitangent and thus may not be perpendicular to fvTangent. + // However, both are perpendicular to the vertex normal. + // For normal maps it is sufficient to use the following simplified version of the bitangent which is generated at pixel/vertex level. + // fSign = bIsOrientationPreserving ? 1.0f : (-1.0f); + // bitangent = fSign * cross(vN, tangent); + // Note that the results are returned unindexed. It is possible to generate a new index list + // But averaging/overwriting tangent spaces by using an already existing index list WILL produce INCRORRECT results. + // DO NOT! use an already existing index list. + void (*m_setTSpace)(const SMikkTSpaceContext * pContext, const float fvTangent[], const float fvBiTangent[], const float fMagS, const float fMagT, + const tbool bIsOrientationPreserving, const int iFace, const int iVert); +} SMikkTSpaceInterface; + +struct SMikkTSpaceContext +{ + SMikkTSpaceInterface * m_pInterface; // initialized with callback functions + void * m_pUserData; // pointer to client side mesh data etc. (passed as the first parameter with every interface call) +}; + +// these are both thread safe! +tbool genTangSpaceDefault(const SMikkTSpaceContext * pContext); // Default (recommended) fAngularThreshold is 180 degrees (which means threshold disabled) +tbool genTangSpace(const SMikkTSpaceContext * pContext, const float fAngularThreshold); + + +// To avoid visual errors (distortions/unwanted hard edges in lighting), when using sampled normal maps, the +// normal map sampler must use the exact inverse of the pixel shader transformation. +// The most efficient transformation we can possibly do in the pixel shader is +// achieved by using, directly, the "unnormalized" interpolated tangent, bitangent and vertex normal: vT, vB and vN. +// pixel shader (fast transform out) +// vNout = normalize( vNt.x * vT + vNt.y * vB + vNt.z * vN ); +// where vNt is the tangent space normal. The normal map sampler must likewise use the +// interpolated and "unnormalized" tangent, bitangent and vertex normal to be compliant with the pixel shader. +// sampler does (exact inverse of pixel shader): +// float3 row0 = cross(vB, vN); +// float3 row1 = cross(vN, vT); +// float3 row2 = cross(vT, vB); +// float fSign = dot(vT, row0)<0 ? -1 : 1; +// vNt = normalize( fSign * float3(dot(vNout,row0), dot(vNout,row1), dot(vNout,row2)) ); +// where vNout is the sampled normal in some chosen 3D space. +// +// Should you choose to reconstruct the bitangent in the pixel shader instead +// of the vertex shader, as explained earlier, then be sure to do this in the normal map sampler also. +// Finally, beware of quad triangulations. If the normal map sampler doesn't use the same triangulation of +// quads as your renderer then problems will occur since the interpolated tangent spaces will differ +// eventhough the vertex level tangent spaces match. This can be solved either by triangulating before +// sampling/exporting or by using the order-independent choice of diagonal for splitting quads suggested earlier. +// However, this must be used both by the sampler and your tools/rendering pipeline. + +#ifdef __cplusplus +} +#endif + +#endif