very basic question - tessellation and fewer vertices than I started

Misha Koshelev <[email protected]> Sat, 24 Jul 2010 17:32:21 -0500
Newsgroups gmane.games.devel.sweng
Message-ID <1280010741.2028.15.camel@misha-d630>
Dear All:

My apologies first of all if my post is too simple for this list.

I am working on the Wine project, specifically on implementing D3DX9.

I have done some work for this bug:
http://bugs.winehq.org/show_bug.cgi?id=22918

implementing D3DXCreateSphere, D3DXCreateCylinder, and D3DXCreateBox.
These can be found on my github site:

http://github.com/misha680/wine

Mainly as a motivation to learn more DirectX 9/OpenGL, but also
hopefully to get it into Wine, I have been doing some work on trying to
reproduct D3DXCreateTeapot (exact vertices and indices) using Newell's
original data:
http://www.sjbaker.org/teapot/teaset.tgz

My current approach mostly lies in figuring out the ordering difference
between the two versions (MS and Newell).

I have succeeded in using ID3DXPatchMesh to tesselate the Newell teapot.
Further, I can rotate and scale the MS teapot to match the Newell (I
have attached tester.cpp - the actual program come, compile -
compilation script for Ubuntu 10.04 using mingw32 4.2.1dfsg-1ubuntu2,
teapot.newell - Newell's original data, and test.bat - program to run
the test).

Further, I have attached a file vertices which simply isolates the
vertices ("points") from Newell's original data.

Now, I find something rather odd, and I thought you might be able to
help me with this.

My tessellation code:
    // create mesh for tesselation
    FLOAT fTessLevel=1.0f;
    DWORD Adaptive=FALSE;
    DWORD NumTriangles,NumVertices;
    if (!
SUCCEEDED(p_mesh->GetTessSize(fTessLevel,Adaptive,&NumTriangles,&NumVertices)))
        assert(FALSE);
    LPD3DXMESH mesh = NULL;
    if (!
SUCCEEDED(D3DXCreateMeshFVF(NumTriangles,NumVertices,D3DXMESH_MANAGED,D3DFVF_XYZ,d3ddev,&mesh)))
        assert(FALSE);
    
    // tesselate
    assert(Adaptive==FALSE);
    if (!SUCCEEDED(p_mesh->Tessellate(fTessLevel,mesh)))
        assert(FALSE);
    printf("Generated tesselated mesh at level %f with %d triangles, %d
vertices\n",fTessLevel,NumTriangles,NumVertices);
    p_mesh->Release();

uses non-adaptive tessellation at level 1.0f. Per
http://msdn.microsoft.com/en-us/library/bb174094%28v=VS.85%29.aspx
fTessLevel [in] 
        FLOAT
        
        Tessellation level. This is the number of vertices introduced
        between existing vertices. The range of this float parameter is
        0 < fTessLevel <= 32.
        
It seems that this would mean at least _one_ extra vertex is inserted
between existing ones.

Now the strange thing. Initially we have 306 vertices, 294 of which are
unique:

misha@misha-d630:~/wine/tester$ cat /tmp/vertices | wc
    306     306    4653
misha@misha-d630:~/wine/tester$ cat /tmp/vertices | sort -u | wc
    294     294    4479

Oddly though, tessellation results in _128_ vertices:
---
Loaded patch teapot.newell with 32 patches and 306 vertices.
Generated tesselated mesh at level 1.000000 with 64 triangles, 128
vertices
Newell teapot has 64 triangles, 128 vertices
MS teapot has 2256 triangles, 1178 vertices

Newell teapot center is (0.046875,1.725,0)
MS teapot center is (1.28115e-007,-7.50032e-006,3.36478e-009)
MS teapot center needs to be translated by
(0.0468749,1.72501,-3.36478e-009)
MS teapot center is now (0.0468749,1.725,3.36478e-009)

Newell teapot extents are (-3,0,-2) (3.3,3.15,2)
MS teapot extents are (-2.99538,-0.0136373,-2) (3.43425,3.13636,2)
---

I am quite confused as to how an operation that _introduces_ vertices
can actually result in fewer unique vertices than at the start.

(For your reference, I am not a complete newbie at this - and have
implemented a very basic tessellator in OpenGL for the same problem:
http://bugs.winehq.org/attachment.cgi?id=29818
)

Perhaps I am missing something simple?

Thank you, and cc on replies much appreciated :)

Thank you and I eagerly await your thoughtful response.

Misha

_______________________________________________
Sweng-Gamedev mailing list
[email protected]
http://lists.midnightryder.com/listinfo.cgi/sweng-gamedev-midnightryder.com
tester.cpp (text/x-c++src, 20.2 KB)
// Main D3DX framework from www.directxtutorial.com (free section) 
#include <assert.h>
#include <stdio.h>
#include <math.h>

// include the basic windows header files and the Direct3D header file
#include <windows.h>
#include <windowsx.h>
#include <d3d9.h>
#include <d3dx9.h>

// define the screen resolution
#define SCREEN_WIDTH 800
#define SCREEN_HEIGHT 600

// include the Direct3D Library files
#pragma comment (lib, "d3d9.lib")
#pragma comment (lib, "d3dx9.lib")

// global declarations
LPDIRECT3D9 d3d;
LPDIRECT3DDEVICE9 d3ddev;
LPD3DXMESH newell_teapot = NULL;
LPD3DXMESH ms_teapot = NULL;

// function prototypes
void initD3D(HWND hWnd);
void render_frame(void);
void cleanD3D(void);
void init_graphics(void);

// the WindowProc function prototype
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);

// the entry point for any Windows program
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nCmdShow)
{
    HWND hWnd;
    WNDCLASSEX wc;

    ZeroMemory(&wc, sizeof(WNDCLASSEX));

    wc.cbSize = sizeof(WNDCLASSEX);
    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.hInstance = hInstance;
    wc.hCursor = LoadCursor(NULL, IDC_ARROW);
    wc.lpszClassName = "WindowClass";

    RegisterClassEx(&wc);

    hWnd = CreateWindowEx(NULL, "WindowClass", "Our Direct3D Program",
                          WS_OVERLAPPEDWINDOW, 0, 0, SCREEN_WIDTH, SCREEN_HEIGHT,
                          NULL, NULL, hInstance, NULL);

    ShowWindow(hWnd, nCmdShow);

    initD3D(hWnd);

    MSG msg;

    while(TRUE)
    {
        while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
        {
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }

        if(msg.message == WM_QUIT)
            break;

        render_frame();
    }

    cleanD3D();

    return msg.wParam;
}

// this is the main message handler for the program
LRESULT CALLBACK WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_DESTROY:
        PostQuitMessage(0);
        return 0;
    }
    
    return DefWindowProc (hWnd, message, wParam, lParam);
}

// this function initializes and prepares Direct3D for use
void initD3D(HWND hWnd)
{
    d3d = Direct3DCreate9(D3D_SDK_VERSION);

    D3DPRESENT_PARAMETERS d3dpp;

    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.hDeviceWindow = hWnd;
    d3dpp.BackBufferFormat = D3DFMT_X8R8G8B8;
    d3dpp.BackBufferWidth = SCREEN_WIDTH;
    d3dpp.BackBufferHeight = SCREEN_HEIGHT;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;

    d3d->CreateDevice(D3DADAPTER_DEFAULT,
                      D3DDEVTYPE_HAL,
                      hWnd,
                      D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                      &d3dpp,
                      &d3ddev);

    init_graphics();

    d3ddev->SetRenderState(D3DRS_LIGHTING, FALSE);    // turn off the 3D lighting
    d3ddev->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);    // turn off culling
    d3ddev->SetRenderState(D3DRS_ZENABLE, TRUE);    // turn on the z-buffer
}

// this is the function used to render a single frame
void render_frame(void)
{
    d3ddev->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);
    d3ddev->Clear(0, NULL, D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(0, 0, 0), 1.0f, 0);

    d3ddev->BeginScene();

    d3ddev->SetFVF(D3DFVF_XYZ);

    // set the view transform
    D3DXMATRIX matView;    // the view transform matrix
    D3DXMatrixLookAtLH(&matView,
    &D3DXVECTOR3 (0.0f, 8.0f, 25.0f),    // the camera position
    &D3DXVECTOR3 (0.0f, 0.0f, 0.0f),      // the look-at position
    &D3DXVECTOR3 (0.0f, 1.0f, 0.0f));    // the up direction
    d3ddev->SetTransform(D3DTS_VIEW, &matView);    // set the view transform to matView

    // set the projection transform
    D3DXMATRIX matProjection;    // the projection transform matrix
    D3DXMatrixPerspectiveFovLH(&matProjection,
                               D3DXToRadian(45),    // the horizontal field of view
                               (FLOAT)SCREEN_WIDTH / (FLOAT)SCREEN_HEIGHT, // aspect ratio
                               1.0f,   // the near view-plane
                               100.0f);    // the far view-plane
    d3ddev->SetTransform(D3DTS_PROJECTION, &matProjection); // set the projection

    // set the world transform
    static float index = 0.0f; index+=0.03f; // an ever-increasing float value
    D3DXMATRIX matRotateY;    // a matrix to store the rotation for each triangle
    D3DXMatrixRotationY(&matRotateY, index);    // the rotation matrix
    d3ddev->SetTransform(D3DTS_WORLD, &(matRotateY));    // set the world transform

    if (newell_teapot) newell_teapot->DrawSubset(0);     
    if (ms_teapot) ms_teapot->DrawSubset(0);     

    d3ddev->EndScene(); 

    d3ddev->Present(NULL, NULL, NULL, NULL);
}

// this is the function that cleans up Direct3D and COM
void cleanD3D(void)
{
    if (ms_teapot) 
    {
        ms_teapot->Release();
        ms_teapot = NULL;
    }
    if (newell_teapot) 
    {
        newell_teapot->Release();
        newell_teapot = NULL;
    }
    d3ddev->Release();
    d3d->Release();
}

#define MAX_PATCHES 1000
#define POINTS_PER_PATCH 16
int num_patches = -1;
int patches[MAX_PATCHES][POINTS_PER_PATCH];

void B_patch(int ii, int a, int b, int c, int d, int e, int f, int g, int h, int i, int j, int k, int l, int m, int n, int o, int p)
{
    assert(ii < MAX_PATCHES);
    patches[ii][0] = a-1;
    patches[ii][1] = b-1;
    patches[ii][2] = c-1;
    patches[ii][3] = d-1;
    patches[ii][4] = e-1;
    patches[ii][5] = f-1;
    patches[ii][6] = g-1;
    patches[ii][7] = h-1;
    patches[ii][8] = i-1;
    patches[ii][9] = j-1;
    patches[ii][10] = k-1;
    patches[ii][11] = l-1;
    patches[ii][12] = m-1;
    patches[ii][13] = n-1;
    patches[ii][14] = o-1;
    patches[ii][15] = p-1;
    assert(POINTS_PER_PATCH==16);
}

#define MAX_POINTS 1000
int num_points = -1;
D3DXVECTOR3 points[MAX_POINTS];

void B_point(int ii, double x, double y, double z)
{
    ii--;
    assert(ii < MAX_POINTS);
    points[ii].x = x;
    /*** Y AND Z FLIPPED ***/
    points[ii].y = z;
    points[ii].z = y;
}

// BEGIN http://www.sjbaker.org/teapot/teaset.tgz

/*
 *  The file input.c  -- Juhana Kouhia, [email protected], Oct. 25, 1991
 *
 *  Load_patch(filename, patches, vertices);
 *    char *filename; int *patches, *vertices;  
 *    A sample program to read Bezier patches in.
 *    Returns count of patches and vertices.
 *  User defined subroutines:
 *    B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p);
 *      int ii, a, b, ..., p;
 *      Defines one Bezier patch with index number ii,
 *      indexes to points are in a, b, c, ..., p.
 *    B_point(ii, x, y, z);
 *      int ii; double x, y, z;
 *      Defines one point with index number ii.
 */

#include <stdio.h>

// Modified to work with g++
void Load_patch(char *filename, int *patches, int *vertices)
{
  int ii;
  float x,y,z;
  int a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p;

  FILE *fp;

  if (!(fp = fopen(filename,"r"))) {
    fprintf(stderr,"Load_patch: Can't open %s\n",filename);
    exit(1);
  }

  (void)fscanf(fp,"%i\n",patches);
  for (ii = 0; ii < *patches; ii++) {
    (void)fscanf(fp,"%i, %i, %i, %i,",&a,&b,&c,&d);
    (void)fscanf(fp,"%i, %i, %i, %i,",&e,&f,&g,&h);
    (void)fscanf(fp,"%i, %i, %i, %i,",&i,&j,&k,&l);
    (void)fscanf(fp,"%i, %i, %i, %i\n",&m,&n,&o,&p);
    B_patch(ii, a,b,c,d,e,f,g,h,i,j,k,l,m,n,o,p);
  }
  (void)fscanf(fp,"%i\n",vertices);
  for (ii = 1; ii <= *vertices; ii++) {
    (void)fscanf(fp,"%f, %f, %f\n",&x,&y,&z);
    B_point(ii, (double)x,(double)y,(double)z);
  }
}

// END http://www.sjbaker.org/teapot/teaset.tgz

//
// VECTOR FUNCTIONS
//

// "clear" vector
void clear_vec3(D3DXVECTOR3 *p)
{
    p->x = NAN; p->y = NAN; p->z = NAN;   
}

// output vector
void print_vec3(D3DXVECTOR3 p)
{
    printf("(%g,%g,%g)",p.x,p.y,p.z);
}

//
// MESH FUNCTIONS
//

// find mesh center
D3DXVECTOR3 find_mesh_center(LPD3DXMESH mesh)
{
    D3DXVECTOR3 result(0.0f,0.0f,0.0f);

    // lock vertex buffer
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=mesh->GetNumVertices();
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // compute sum of all vertices
    for (int i=0; i<num_vertices; i++)
    {
        result.x += vertices[i].x;
        result.y += vertices[i].y;
        result.z += vertices[i].z;
    }
    
    // unlock vertex buffer
    v_buffer->Unlock();
    v_buffer->Release();        
    
    // divide by number of vertices to make average
    result.x /= num_vertices;
    result.y /= num_vertices;
    result.z /= num_vertices;
    
    return result;
}

// find mesh extents
void find_mesh_extents(LPD3DXMESH mesh, D3DXVECTOR3 *min, D3DXVECTOR3 *max)
{
    assert(min != NULL);
    assert(max != NULL);

    min->x = INFINITY; min->y = INFINITY; min->z = INFINITY;
    max->x = -INFINITY; max->y = -INFINITY; max->z = -INFINITY;

    // lock vertex buffer
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=mesh->GetNumVertices();
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // minima and maxima
    for (int i=0; i<num_vertices; i++)
    {
        min->x = fmin(min->x, vertices[i].x);
        min->y = fmin(min->y, vertices[i].y);
        min->z = fmin(min->z, vertices[i].z);
        max->x = fmax(max->x, vertices[i].x);
        max->y = fmax(max->y, vertices[i].y);
        max->z = fmax(max->z, vertices[i].z);
    }
    
    // unlock vertex buffer
    v_buffer->Unlock();
    v_buffer->Release();        
}

// translate mesh
void translate_mesh(LPD3DXMESH mesh, D3DXVECTOR3 by)
{
    // lock vertex buffer
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=mesh->GetNumVertices();
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // translate vertices
    for (int i=0; i<num_vertices; i++)
    {
        vertices[i].x += by.x;
        vertices[i].y += by.y;
        vertices[i].z += by.z;
    }
    
    // unlock vertex buffer
    v_buffer->Unlock();
    v_buffer->Release();            
}

// scale mesh
void scale_mesh(LPD3DXMESH mesh, D3DXVECTOR3 by)
{
    // lock vertex buffer
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=mesh->GetNumVertices();
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // scale vertices
    for (int i=0; i<num_vertices; i++)
    {
        vertices[i].x *= by.x;
        vertices[i].y *= by.y;
        vertices[i].z *= by.z;
    }
    
    // unlock vertex buffer
    v_buffer->Unlock();
    v_buffer->Release();            
}

#define MAX_CHARS 255
void fprintf_fixed_numchars(FILE *f, FLOAT val, int chars)
{
    char str[MAX_CHARS];
    assert(chars < MAX_CHARS);
    sprintf(str, "%g", val);
    int i=strlen(str);
    for (; i<chars; i++)
    {
        str[i] = ' ';
    }
    str[i] = 0;
    fprintf(f, str);
}

// print vertices to file
void print_vertices(FILE *f, LPD3DXMESH mesh)
{
    // lock vertex buffer
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=mesh->GetNumVertices();
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // output number of vertices
    fprintf(f, "%d\n", num_vertices);

    // output vertices    
    for (int i=0; i<num_vertices; i++)
    {
        fprintf_fixed_numchars(f, vertices[i].x, 10);
        fprintf(f, "\t");
        fprintf_fixed_numchars(f, vertices[i].y, 10);
        fprintf(f, "\t");
        fprintf_fixed_numchars(f, vertices[i].z, 10);
        fprintf(f, "\n");
    }
    
    // unlock vertex buffer
    v_buffer->Unlock();
    v_buffer->Release();            
}

// print indices to file
void print_indices(FILE *f, LPD3DXMESH mesh)
{
    // lock index buffer
    LPDIRECT3DINDEXBUFFER9 i_buffer = NULL;
    if (!SUCCEEDED(mesh->GetIndexBuffer(&i_buffer))) 
        assert(FALSE);
    DWORD num_faces=mesh->GetNumFaces();
    WORD* indices = NULL;
    if (!SUCCEEDED(i_buffer->Lock(0,3*num_faces*sizeof(WORD),(void **)&indices,D3DLOCK_DISCARD)))
        assert(FALSE);

    // output number of indices
    fprintf(f, "%d\n", num_faces);
    
    // output indices
    for (int i=0; i<num_faces*3; i+=3)
    {
        fprintf(f, "%u\t%u\t%u\n", indices[i], indices[i+1], indices[i+2]);
    }

    // unlock index buffer
    i_buffer->Unlock();
    i_buffer->Release();    
}

//
// UNCLEAN CODE
//

// simplify a mesh in place
void simplify_mesh(LPD3DXMESH *pmesh)
{
    assert(pmesh != NULL);
    LPD3DXMESH mesh = *pmesh;

    // Generate adjacency data
    static FLOAT epsilon = 0.0f; // vertices that differ in position by < epsilon treated as coincident
    DWORD *adjacency = NULL;
    adjacency = (DWORD *)HeapAlloc(GetProcessHeap(), 0, 3*mesh->GetNumFaces()*sizeof(DWORD));
    if (!adjacency)
        assert(FALSE);
    if (!SUCCEEDED(mesh->GenerateAdjacency(epsilon, adjacency)))
        assert(FALSE);
    
    // Assert mesh can be simplified
    LPD3DXBUFFER errorsandwarnings = NULL;
    if (!SUCCEEDED(D3DXValidMesh(mesh, adjacency, &errorsandwarnings)))
        assert(FALSE);

    // Simplify vertices
    LPD3DXMESH new_mesh = NULL;
    if (!SUCCEEDED(D3DXSimplifyMesh(mesh, adjacency, 
                                    NULL, // default weight structure
                                    NULL, // 1.0 vertex weights
                                    306, // desired minimum - not achievable
                                    D3DXMESHSIMP_VERTEX,
                                    &new_mesh)))
        assert(FALSE);
    if (new_mesh)
    {
        mesh->Release();
        *pmesh = new_mesh;
        new_mesh = NULL;
    }

    HeapFree(GetProcessHeap(), 0, (LPVOID)adjacency);
}

//
// MAIN CODE
//

// this is the function that puts the 3D models into video RAM
void init_graphics(void)
{
    //
    // NEWELL TEAPOT
    //

    // load patch
    char filename[255];
    sprintf(filename,"teapot.newell");
    Load_patch(filename,&num_patches,&num_points);
    printf("Loaded patch %s with %d patches and %d vertices.\n",
           filename,num_patches,num_points);

    // create declarator from FVF
    D3DVERTEXELEMENT9 inDecl[MAX_FVF_DECL_SIZE];
    if (!SUCCEEDED(D3DXDeclaratorFromFVF(D3DFVF_XYZ,inDecl)))
        assert(FALSE);    

    // create patch mesh
    LPD3DXPATCHMESH p_mesh;
    D3DXPATCHINFO info;
    info.PatchType = D3DXPATCHMESH_RECT;
    info.Degree = D3DDEGREE_CUBIC;
    info.Basis = D3DBASIS_BEZIER;

    if (!SUCCEEDED(D3DXCreatePatchMesh(&info,num_patches,num_points,0,inDecl,d3ddev,&p_mesh)))
        assert(FALSE);
    assert(p_mesh->GetControlVerticesPerPatch()==POINTS_PER_PATCH);

    // copy vertices
    LPDIRECT3DVERTEXBUFFER9 v_buffer = NULL;
    if (!SUCCEEDED(p_mesh->GetVertexBuffer(&v_buffer))) 
        assert(FALSE);
    D3DXVECTOR3* vertices = NULL;
    DWORD num_vertices=p_mesh->GetNumVertices();
    assert(num_vertices==num_points);
    if (!SUCCEEDED(v_buffer->Lock(0,num_vertices*sizeof(D3DXVECTOR3),(void **)&vertices,D3DLOCK_DISCARD)))
        assert(FALSE);
    for (int i=0; i<num_points; i++)
    {
        vertices[i].x = points[i].x;
        vertices[i].y = points[i].y;
        vertices[i].z = points[i].z;
    }
    v_buffer->Unlock();
    v_buffer->Release();    

    // copy indices
    LPDIRECT3DINDEXBUFFER9 i_buffer = NULL;
    if (!SUCCEEDED(p_mesh->GetIndexBuffer(&i_buffer))) 
        assert(FALSE);
    D3DINDEXBUFFER_DESC i_buffer_desc;
    if (!SUCCEEDED(i_buffer->GetDesc(&i_buffer_desc)))
        assert(FALSE);
    assert(i_buffer_desc.Size==num_patches*POINTS_PER_PATCH*sizeof(WORD));
    WORD* indices = NULL;
    if (!SUCCEEDED(i_buffer->Lock(0,0,(void **)&indices,D3DLOCK_DISCARD)))
        assert(FALSE);
    int idx=0;
    for (int i=0; i<num_patches; i++)
    {
        for (int j=0; j<POINTS_PER_PATCH; j++)
        {
            indices[idx] = patches[i][j];
            idx++;
        }
    }
    i_buffer->Unlock();
    i_buffer->Release();    

    // create mesh for tesselation
    FLOAT fTessLevel=1.0f;
    DWORD Adaptive=FALSE;
    DWORD NumTriangles,NumVertices;
    if (!SUCCEEDED(p_mesh->GetTessSize(fTessLevel,Adaptive,&NumTriangles,&NumVertices)))
        assert(FALSE);
    LPD3DXMESH mesh = NULL;
    if (!SUCCEEDED(D3DXCreateMeshFVF(NumTriangles,NumVertices,D3DXMESH_MANAGED,D3DFVF_XYZ,d3ddev,&mesh)))
        assert(FALSE);
    
    // tesselate
    assert(Adaptive==FALSE);
    if (!SUCCEEDED(p_mesh->Tessellate(fTessLevel,mesh)))
        assert(FALSE);
    printf("Generated tesselated mesh at level %f with %d triangles, %d vertices\n",fTessLevel,NumTriangles,NumVertices);
    p_mesh->Release();

    // output
    newell_teapot = mesh;
    mesh = NULL;
    printf("Newell teapot has %d triangles, %d vertices\n",newell_teapot->GetNumFaces(),newell_teapot->GetNumVertices());

    //
    // MS TEAPOT
    //

    // ms teapot
    D3DXCreateTeapot(d3ddev, &mesh, NULL);
    assert(mesh->GetFVF() == D3DFVF_XYZ | D3DFVF_NORMAL);
    if (!SUCCEEDED(mesh->GetIndexBuffer(&i_buffer))) 
        assert(FALSE);
    if (!SUCCEEDED(i_buffer->GetDesc(&i_buffer_desc)))
        assert(FALSE);
    assert(i_buffer_desc.Size==mesh->GetNumFaces()*3*sizeof(WORD));
    i_buffer->Unlock();
    i_buffer->Release();    

    // clone without normals, releasing old mesh
    ms_teapot = NULL;
    if (!SUCCEEDED(mesh->CloneMeshFVF(mesh->GetOptions(),D3DFVF_XYZ,d3ddev,&ms_teapot)))
        assert(FALSE);
    assert(ms_teapot->GetFVF() == D3DFVF_XYZ);
    if (!SUCCEEDED(mesh->Release()))
        assert(FALSE);
    printf("MS teapot has %d triangles, %d vertices\n",ms_teapot->GetNumFaces(),ms_teapot->GetNumVertices());

    //
    // SCALING
    //

    D3DXVECTOR3 scale_ms_mesh_by(2.0f,2.0f,2.0f); // from prior calculations
    scale_mesh(ms_teapot, scale_ms_mesh_by);
    
    // 
    // TRANSLATION
    //
    
    printf("\n");
    D3DXVECTOR3 newell_center(NAN,NAN,NAN);
    newell_center = find_mesh_center(newell_teapot);
    printf("Newell teapot center is ");
    print_vec3(newell_center);
    printf("\n");

    D3DXVECTOR3 ms_center(NAN,NAN,NAN);
    ms_center = find_mesh_center(ms_teapot);
    printf("MS teapot center is ");
    print_vec3(ms_center);
    printf("\n");

    D3DXVECTOR3 ms_translate_by(0.0f,0.0f,0.0f);
    D3DXVec3Subtract(&ms_translate_by, &newell_center, &ms_center);
    printf("MS teapot center needs to be translated by ");
    print_vec3(ms_translate_by);
    printf("\n");
    
    clear_vec3(&ms_center);
    translate_mesh(ms_teapot, ms_translate_by);
    ms_center = find_mesh_center(ms_teapot);
    printf("MS teapot center is now ");
    print_vec3(ms_center);
    printf("\n");    

    //
    // EXTENTS
    //

    printf("\n");
    D3DXVECTOR3 newell_min(NAN,NAN,NAN), newell_max(NAN,NAN,NAN);
    find_mesh_extents(newell_teapot, &newell_min, &newell_max);
    printf("Newell teapot extents are ");
    print_vec3(newell_min);
    printf(" ");
    print_vec3(newell_max);
    printf("\n");

    D3DXVECTOR3 ms_min(NAN,NAN,NAN), ms_max(NAN,NAN,NAN);
    find_mesh_extents(ms_teapot, &ms_min, &ms_max);
    printf("MS teapot extents are ");
    print_vec3(ms_min);
    printf(" ");
    print_vec3(ms_max);
    printf("\n");

    //
    // SIMPLIFY MS TEAPOT
    //

    simplify_mesh(&ms_teapot);

    //
    // OUTPUT
    //

    FILE *f = NULL;

    f = fopen("mesh.newell", "wt");
    print_vertices(f, newell_teapot);
    print_indices(f, newell_teapot);
    fclose(f);

    f = fopen("mesh.ms", "wt");
    print_vertices(f, ms_teapot);
    print_indices(f, ms_teapot);
    fclose(f);
    
    assert(FALSE);
}
teapot.newell (text/plain, 6.4 KB)
32
1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16
4,17,18,19,8,20,21,22,12,23,24,25,16,26,27,28
19,29,30,31,22,32,33,34,25,35,36,37,28,38,39,40
31,41,42,1,34,43,44,5,37,45,46,9,40,47,48,13
13,14,15,16,49,50,51,52,53,54,55,56,57,58,59,60
16,26,27,28,52,61,62,63,56,64,65,66,60,67,68,69
28,38,39,40,63,70,71,72,66,73,74,75,69,76,77,78
40,47,48,13,72,79,80,49,75,81,82,53,78,83,84,57
57,58,59,60,85,86,87,88,89,90,91,92,93,94,95,96
60,67,68,69,88,97,98,99,92,100,101,102,96,103,104,105
69,76,77,78,99,106,107,108,102,109,110,111,105,112,113,114
78,83,84,57,108,115,116,85,111,117,118,89,114,119,120,93
121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136
124,137,138,121,128,139,140,125,132,141,142,129,136,143,144,133
133,134,135,136,145,146,147,148,149,150,151,152,69,153,154,155
136,143,144,133,148,156,157,145,152,158,159,149,155,160,161,69
162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177
165,178,179,162,169,180,181,166,173,182,183,170,177,184,185,174
174,175,176,177,186,187,188,189,190,191,192,193,194,195,196,197
177,184,185,174,189,198,199,186,193,200,201,190,197,202,203,194
204,204,204,204,207,208,209,210,211,211,211,211,212,213,214,215
204,204,204,204,210,217,218,219,211,211,211,211,215,220,221,222
204,204,204,204,219,224,225,226,211,211,211,211,222,227,228,229
204,204,204,204,226,230,231,207,211,211,211,211,229,232,233,212
212,213,214,215,234,235,236,237,238,239,240,241,242,243,244,245
215,220,221,222,237,246,247,248,241,249,250,251,245,252,253,254
222,227,228,229,248,255,256,257,251,258,259,260,254,261,262,263
229,232,233,212,257,264,265,234,260,266,267,238,263,268,269,242
270,270,270,270,279,280,281,282,275,276,277,278,271,272,273,274
270,270,270,270,282,289,290,291,278,286,287,288,274,283,284,285
270,270,270,270,291,298,299,300,288,295,296,297,285,292,293,294
270,270,270,270,300,305,306,279,297,303,304,275,294,301,302,271
306
1.4,0.0,2.4
1.4,-0.784,2.4
0.784,-1.4,2.4
0.0,-1.4,2.4
1.3375,0.0,2.53125
1.3375,-0.749,2.53125
0.749,-1.3375,2.53125
0.0,-1.3375,2.53125
1.4375,0.0,2.53125
1.4375,-0.805,2.53125
0.805,-1.4375,2.53125
0.0,-1.4375,2.53125
1.5,0.0,2.4
1.5,-0.84,2.4
0.84,-1.5,2.4
0.0,-1.5,2.4
-0.784,-1.4,2.4
-1.4,-0.784,2.4
-1.4,0.0,2.4
-0.749,-1.3375,2.53125
-1.3375,-0.749,2.53125
-1.3375,0.0,2.53125
-0.805,-1.4375,2.53125
-1.4375,-0.805,2.53125
-1.4375,0.0,2.53125
-0.84,-1.5,2.4
-1.5,-0.84,2.4
-1.5,0.0,2.4
-1.4,0.784,2.4
-0.784,1.4,2.4
0.0,1.4,2.4
-1.3375,0.749,2.53125
-0.749,1.3375,2.53125
0.0,1.3375,2.53125
-1.4375,0.805,2.53125
-0.805,1.4375,2.53125
0.0,1.4375,2.53125
-1.5,0.84,2.4
-0.84,1.5,2.4
0.0,1.5,2.4
0.784,1.4,2.4
1.4,0.784,2.4
0.749,1.3375,2.53125
1.3375,0.749,2.53125
0.805,1.4375,2.53125
1.4375,0.805,2.53125
0.84,1.5,2.4
1.5,0.84,2.4
1.75,0.0,1.875
1.75,-0.98,1.875
0.98,-1.75,1.875
0.0,-1.75,1.875
2.0,0.0,1.35
2.0,-1.12,1.35
1.12,-2.0,1.35
0.0,-2.0,1.35
2.0,0.0,0.9
2.0,-1.12,0.9
1.12,-2.0,0.9
0.0,-2.0,0.9
-0.98,-1.75,1.875
-1.75,-0.98,1.875
-1.75,0.0,1.875
-1.12,-2.0,1.35
-2.0,-1.12,1.35
-2.0,0.0,1.35
-1.12,-2.0,0.9
-2.0,-1.12,0.9
-2.0,0.0,0.9
-1.75,0.98,1.875
-0.98,1.75,1.875
0.0,1.75,1.875
-2.0,1.12,1.35
-1.12,2.0,1.35
0.0,2.0,1.35
-2.0,1.12,0.9
-1.12,2.0,0.9
0.0,2.0,0.9
0.98,1.75,1.875
1.75,0.98,1.875
1.12,2.0,1.35
2.0,1.12,1.35
1.12,2.0,0.9
2.0,1.12,0.9
2.0,0.0,0.45
2.0,-1.12,0.45
1.12,-2.0,0.45
0.0,-2.0,0.45
1.5,0.0,0.225
1.5,-0.84,0.225
0.84,-1.5,0.225
0.0,-1.5,0.225
1.5,0.0,0.15
1.5,-0.84,0.15
0.84,-1.5,0.15
0.0,-1.5,0.15
-1.12,-2.0,0.45
-2.0,-1.12,0.45
-2.0,0.0,0.45
-0.84,-1.5,0.225
-1.5,-0.84,0.225
-1.5,0.0,0.225
-0.84,-1.5,0.15
-1.5,-0.84,0.15
-1.5,0.0,0.15
-2.0,1.12,0.45
-1.12,2.0,0.45
0.0,2.0,0.45
-1.5,0.84,0.225
-0.84,1.5,0.225
0.0,1.5,0.225
-1.5,0.84,0.15
-0.84,1.5,0.15
0.0,1.5,0.15
1.12,2.0,0.45
2.0,1.12,0.45
0.84,1.5,0.225
1.5,0.84,0.225
0.84,1.5,0.15
1.5,0.84,0.15
-1.6,0.0,2.025
-1.6,-0.3,2.025
-1.5,-0.3,2.25
-1.5,0.0,2.25
-2.3,0.0,2.025
-2.3,-0.3,2.025
-2.5,-0.3,2.25
-2.5,0.0,2.25
-2.7,0.0,2.025
-2.7,-0.3,2.025
-3.0,-0.3,2.25
-3.0,0.0,2.25
-2.7,0.0,1.8
-2.7,-0.3,1.8
-3.0,-0.3,1.8
-3.0,0.0,1.8
-1.5,0.3,2.25
-1.6,0.3,2.025
-2.5,0.3,2.25
-2.3,0.3,2.025
-3.0,0.3,2.25
-2.7,0.3,2.025
-3.0,0.3,1.8
-2.7,0.3,1.8
-2.7,0.0,1.575
-2.7,-0.3,1.575
-3.0,-0.3,1.35
-3.0,0.0,1.35
-2.5,0.0,1.125
-2.5,-0.3,1.125
-2.65,-0.3,0.9375
-2.65,0.0,0.9375
-2.0,-0.3,0.9
-1.9,-0.3,0.6
-1.9,0.0,0.6
-3.0,0.3,1.35
-2.7,0.3,1.575
-2.65,0.3,0.9375
-2.5,0.3,1.125
-1.9,0.3,0.6
-2.0,0.3,0.9
1.7,0.0,1.425
1.7,-0.66,1.425
1.7,-0.66,0.6
1.7,0.0,0.6
2.6,0.0,1.425
2.6,-0.66,1.425
3.1,-0.66,0.825
3.1,0.0,0.825
2.3,0.0,2.1
2.3,-0.25,2.1
2.4,-0.25,2.025
2.4,0.0,2.025
2.7,0.0,2.4
2.7,-0.25,2.4
3.3,-0.25,2.4
3.3,0.0,2.4
1.7,0.66,0.6
1.7,0.66,1.425
3.1,0.66,0.825
2.6,0.66,1.425
2.4,0.25,2.025
2.3,0.25,2.1
3.3,0.25,2.4
2.7,0.25,2.4
2.8,0.0,2.475
2.8,-0.25,2.475
3.525,-0.25,2.49375
3.525,0.0,2.49375
2.9,0.0,2.475
2.9,-0.15,2.475
3.45,-0.15,2.5125
3.45,0.0,2.5125
2.8,0.0,2.4
2.8,-0.15,2.4
3.2,-0.15,2.4
3.2,0.0,2.4
3.525,0.25,2.49375
2.8,0.25,2.475
3.45,0.15,2.5125
2.9,0.15,2.475
3.2,0.15,2.4
2.8,0.15,2.4
0.0,0.0,3.15
0.0,-0.002,3.15
0.002,0.0,3.15
0.8,0.0,3.15
0.8,-0.45,3.15
0.45,-0.8,3.15
0.0,-0.8,3.15
0.0,0.0,2.85
0.2,0.0,2.7
0.2,-0.112,2.7
0.112,-0.2,2.7
0.0,-0.2,2.7
-0.002,0.0,3.15
-0.45,-0.8,3.15
-0.8,-0.45,3.15
-0.8,0.0,3.15
-0.112,-0.2,2.7
-0.2,-0.112,2.7
-0.2,0.0,2.7
0.0,0.002,3.15
-0.8,0.45,3.15
-0.45,0.8,3.15
0.0,0.8,3.15
-0.2,0.112,2.7
-0.112,0.2,2.7
0.0,0.2,2.7
0.45,0.8,3.15
0.8,0.45,3.15
0.112,0.2,2.7
0.2,0.112,2.7
0.4,0.0,2.55
0.4,-0.224,2.55
0.224,-0.4,2.55
0.0,-0.4,2.55
1.3,0.0,2.55
1.3,-0.728,2.55
0.728,-1.3,2.55
0.0,-1.3,2.55
1.3,0.0,2.4
1.3,-0.728,2.4
0.728,-1.3,2.4
0.0,-1.3,2.4
-0.224,-0.4,2.55
-0.4,-0.224,2.55
-0.4,0.0,2.55
-0.728,-1.3,2.55
-1.3,-0.728,2.55
-1.3,0.0,2.55
-0.728,-1.3,2.4
-1.3,-0.728,2.4
-1.3,0.0,2.4
-0.4,0.224,2.55
-0.224,0.4,2.55
0.0,0.4,2.55
-1.3,0.728,2.55
-0.728,1.3,2.55
0.0,1.3,2.55
-1.3,0.728,2.4
-0.728,1.3,2.4
0.0,1.3,2.4
0.224,0.4,2.55
0.4,0.224,2.55
0.728,1.3,2.55
1.3,0.728,2.55
0.728,1.3,2.4
1.3,0.728,2.4
0.0,0.0,0.0
1.5,0.0,0.15
1.5,0.84,0.15
0.84,1.5,0.15
0.0,1.5,0.15
1.5,0.0,0.075
1.5,0.84,0.075
0.84,1.5,0.075
0.0,1.5,0.075
1.425,0.0,0.0
1.425,0.798,0.0
0.798,1.425,0.0
0.0,1.425,0.0
-0.84,1.5,0.15
-1.5,0.84,0.15
-1.5,0.0,0.15
-0.84,1.5,0.075
-1.5,0.84,0.075
-1.5,0.0,0.075
-0.798,1.425,0.0
-1.425,0.798,0.0
-1.425,0.0,0.0
-1.5,-0.84,0.15
-0.84,-1.5,0.15
0.0,-1.5,0.15
-1.5,-0.84,0.075
-0.84,-1.5,0.075
0.0,-1.5,0.075
-1.425,-0.798,0.0
-0.798,-1.425,0.0
0.0,-1.425,0.0
0.84,-1.5,0.15
1.5,-0.84,0.15
0.84,-1.5,0.075
1.5,-0.84,0.075
0.798,-1.425,0.0
1.425,-0.798,0.0
compile (application/x-shellscript, 160 B) - not displayed
vertices (text/plain, 4.5 KB)
1.4,0.0,2.4
1.4,-0.784,2.4
0.784,-1.4,2.4
0.0,-1.4,2.4
1.3375,0.0,2.53125
1.3375,-0.749,2.53125
0.749,-1.3375,2.53125
0.0,-1.3375,2.53125
1.4375,0.0,2.53125
1.4375,-0.805,2.53125
0.805,-1.4375,2.53125
0.0,-1.4375,2.53125
1.5,0.0,2.4
1.5,-0.84,2.4
0.84,-1.5,2.4
0.0,-1.5,2.4
-0.784,-1.4,2.4
-1.4,-0.784,2.4
-1.4,0.0,2.4
-0.749,-1.3375,2.53125
-1.3375,-0.749,2.53125
-1.3375,0.0,2.53125
-0.805,-1.4375,2.53125
-1.4375,-0.805,2.53125
-1.4375,0.0,2.53125
-0.84,-1.5,2.4
-1.5,-0.84,2.4
-1.5,0.0,2.4
-1.4,0.784,2.4
-0.784,1.4,2.4
0.0,1.4,2.4
-1.3375,0.749,2.53125
-0.749,1.3375,2.53125
0.0,1.3375,2.53125
-1.4375,0.805,2.53125
-0.805,1.4375,2.53125
0.0,1.4375,2.53125
-1.5,0.84,2.4
-0.84,1.5,2.4
0.0,1.5,2.4
0.784,1.4,2.4
1.4,0.784,2.4
0.749,1.3375,2.53125
1.3375,0.749,2.53125
0.805,1.4375,2.53125
1.4375,0.805,2.53125
0.84,1.5,2.4
1.5,0.84,2.4
1.75,0.0,1.875
1.75,-0.98,1.875
0.98,-1.75,1.875
0.0,-1.75,1.875
2.0,0.0,1.35
2.0,-1.12,1.35
1.12,-2.0,1.35
0.0,-2.0,1.35
2.0,0.0,0.9
2.0,-1.12,0.9
1.12,-2.0,0.9
0.0,-2.0,0.9
-0.98,-1.75,1.875
-1.75,-0.98,1.875
-1.75,0.0,1.875
-1.12,-2.0,1.35
-2.0,-1.12,1.35
-2.0,0.0,1.35
-1.12,-2.0,0.9
-2.0,-1.12,0.9
-2.0,0.0,0.9
-1.75,0.98,1.875
-0.98,1.75,1.875
0.0,1.75,1.875
-2.0,1.12,1.35
-1.12,2.0,1.35
0.0,2.0,1.35
-2.0,1.12,0.9
-1.12,2.0,0.9
0.0,2.0,0.9
0.98,1.75,1.875
1.75,0.98,1.875
1.12,2.0,1.35
2.0,1.12,1.35
1.12,2.0,0.9
2.0,1.12,0.9
2.0,0.0,0.45
2.0,-1.12,0.45
1.12,-2.0,0.45
0.0,-2.0,0.45
1.5,0.0,0.225
1.5,-0.84,0.225
0.84,-1.5,0.225
0.0,-1.5,0.225
1.5,0.0,0.15
1.5,-0.84,0.15
0.84,-1.5,0.15
0.0,-1.5,0.15
-1.12,-2.0,0.45
-2.0,-1.12,0.45
-2.0,0.0,0.45
-0.84,-1.5,0.225
-1.5,-0.84,0.225
-1.5,0.0,0.225
-0.84,-1.5,0.15
-1.5,-0.84,0.15
-1.5,0.0,0.15
-2.0,1.12,0.45
-1.12,2.0,0.45
0.0,2.0,0.45
-1.5,0.84,0.225
-0.84,1.5,0.225
0.0,1.5,0.225
-1.5,0.84,0.15
-0.84,1.5,0.15
0.0,1.5,0.15
1.12,2.0,0.45
2.0,1.12,0.45
0.84,1.5,0.225
1.5,0.84,0.225
0.84,1.5,0.15
1.5,0.84,0.15
-1.6,0.0,2.025
-1.6,-0.3,2.025
-1.5,-0.3,2.25
-1.5,0.0,2.25
-2.3,0.0,2.025
-2.3,-0.3,2.025
-2.5,-0.3,2.25
-2.5,0.0,2.25
-2.7,0.0,2.025
-2.7,-0.3,2.025
-3.0,-0.3,2.25
-3.0,0.0,2.25
-2.7,0.0,1.8
-2.7,-0.3,1.8
-3.0,-0.3,1.8
-3.0,0.0,1.8
-1.5,0.3,2.25
-1.6,0.3,2.025
-2.5,0.3,2.25
-2.3,0.3,2.025
-3.0,0.3,2.25
-2.7,0.3,2.025
-3.0,0.3,1.8
-2.7,0.3,1.8
-2.7,0.0,1.575
-2.7,-0.3,1.575
-3.0,-0.3,1.35
-3.0,0.0,1.35
-2.5,0.0,1.125
-2.5,-0.3,1.125
-2.65,-0.3,0.9375
-2.65,0.0,0.9375
-2.0,-0.3,0.9
-1.9,-0.3,0.6
-1.9,0.0,0.6
-3.0,0.3,1.35
-2.7,0.3,1.575
-2.65,0.3,0.9375
-2.5,0.3,1.125
-1.9,0.3,0.6
-2.0,0.3,0.9
1.7,0.0,1.425
1.7,-0.66,1.425
1.7,-0.66,0.6
1.7,0.0,0.6
2.6,0.0,1.425
2.6,-0.66,1.425
3.1,-0.66,0.825
3.1,0.0,0.825
2.3,0.0,2.1
2.3,-0.25,2.1
2.4,-0.25,2.025
2.4,0.0,2.025
2.7,0.0,2.4
2.7,-0.25,2.4
3.3,-0.25,2.4
3.3,0.0,2.4
1.7,0.66,0.6
1.7,0.66,1.425
3.1,0.66,0.825
2.6,0.66,1.425
2.4,0.25,2.025
2.3,0.25,2.1
3.3,0.25,2.4
2.7,0.25,2.4
2.8,0.0,2.475
2.8,-0.25,2.475
3.525,-0.25,2.49375
3.525,0.0,2.49375
2.9,0.0,2.475
2.9,-0.15,2.475
3.45,-0.15,2.5125
3.45,0.0,2.5125
2.8,0.0,2.4
2.8,-0.15,2.4
3.2,-0.15,2.4
3.2,0.0,2.4
3.525,0.25,2.49375
2.8,0.25,2.475
3.45,0.15,2.5125
2.9,0.15,2.475
3.2,0.15,2.4
2.8,0.15,2.4
0.0,0.0,3.15
0.0,-0.002,3.15
0.002,0.0,3.15
0.8,0.0,3.15
0.8,-0.45,3.15
0.45,-0.8,3.15
0.0,-0.8,3.15
0.0,0.0,2.85
0.2,0.0,2.7
0.2,-0.112,2.7
0.112,-0.2,2.7
0.0,-0.2,2.7
-0.002,0.0,3.15
-0.45,-0.8,3.15
-0.8,-0.45,3.15
-0.8,0.0,3.15
-0.112,-0.2,2.7
-0.2,-0.112,2.7
-0.2,0.0,2.7
0.0,0.002,3.15
-0.8,0.45,3.15
-0.45,0.8,3.15
0.0,0.8,3.15
-0.2,0.112,2.7
-0.112,0.2,2.7
0.0,0.2,2.7
0.45,0.8,3.15
0.8,0.45,3.15
0.112,0.2,2.7
0.2,0.112,2.7
0.4,0.0,2.55
0.4,-0.224,2.55
0.224,-0.4,2.55
0.0,-0.4,2.55
1.3,0.0,2.55
1.3,-0.728,2.55
0.728,-1.3,2.55
0.0,-1.3,2.55
1.3,0.0,2.4
1.3,-0.728,2.4
0.728,-1.3,2.4
0.0,-1.3,2.4
-0.224,-0.4,2.55
-0.4,-0.224,2.55
-0.4,0.0,2.55
-0.728,-1.3,2.55
-1.3,-0.728,2.55
-1.3,0.0,2.55
-0.728,-1.3,2.4
-1.3,-0.728,2.4
-1.3,0.0,2.4
-0.4,0.224,2.55
-0.224,0.4,2.55
0.0,0.4,2.55
-1.3,0.728,2.55
-0.728,1.3,2.55
0.0,1.3,2.55
-1.3,0.728,2.4
-0.728,1.3,2.4
0.0,1.3,2.4
0.224,0.4,2.55
0.4,0.224,2.55
0.728,1.3,2.55
1.3,0.728,2.55
0.728,1.3,2.4
1.3,0.728,2.4
0.0,0.0,0.0
1.5,0.0,0.15
1.5,0.84,0.15
0.84,1.5,0.15
0.0,1.5,0.15
1.5,0.0,0.075
1.5,0.84,0.075
0.84,1.5,0.075
0.0,1.5,0.075
1.425,0.0,0.0
1.425,0.798,0.0
0.798,1.425,0.0
0.0,1.425,0.0
-0.84,1.5,0.15
-1.5,0.84,0.15
-1.5,0.0,0.15
-0.84,1.5,0.075
-1.5,0.84,0.075
-1.5,0.0,0.075
-0.798,1.425,0.0
-1.425,0.798,0.0
-1.425,0.0,0.0
-1.5,-0.84,0.15
-0.84,-1.5,0.15
0.0,-1.5,0.15
-1.5,-0.84,0.075
-0.84,-1.5,0.075
0.0,-1.5,0.075
-1.425,-0.798,0.0
-0.798,-1.425,0.0
0.0,-1.425,0.0
0.84,-1.5,0.15
1.5,-0.84,0.15
0.84,-1.5,0.075
1.5,-0.84,0.075
0.798,-1.425,0.0
1.425,-0.798,0.0