zoukankan      html  css  js  c++  java
  • 50. umf模型文件格式

    12.4 使用UMF文件
           
    为 www.UltimateGameProgramming.com 创建的最终模型格式(Ultimate Model Format,UMF),它的模型文件作为一种常用的文件格式,用于保存几何图形数据。该文件格式是一种简单的二进制格式,可以保存顶点位置、纹理坐标、法线值、顶点颜色和三角形索引。UMF文件格式旨在创建静态网格并将其显示在屏幕上。将来,还会有其他版本可以指定动画信息的格式。

    UMF格式在www.UltimateGameProgramming.com得到广泛使用。该文件格式使用简单,既包含了小的静态对象和角色,也包含了大的静态对象和角色。同样还提供了一种创建自己的格式并从其他文件格式将其导出的方法。有时候为了满足要求,需要在自己的游戏中创建适合数据的格式。

    12.4.1 UMF文件格式介绍
           
    UMF模型格式开始先是文件ID,用于检查正在加载的文件是否是有效的模型文件。之后,分块列出其余的数据。像X模型格式一样,开始是模型中的网格总数,每个模型可以有多个其关联的网格。网格总数之后完整地列出每个网格的内容。开始是网格的材质信息。在基本的材质信息之后,读取顶点位置,然后是顶点法线、顶点颜色、纹理坐标及三角形数据。读取完三角形数据,读取下一个网格的材质信息。一直持续到加载完所有的网格为止,停止加载过程。程序清单12.23给出了UMF文件的布局。

    程序清单12.23 UMF文件格式

    File id (must be 9000)

    Number of meshes in model

    (Mesh 1)

    Diffuse, specular, and power material data

    Number of vertices

       Vertices data

    Number of normals

       Normals data

    Number of colors

       Colors data

    Number of texture coordinates

       Coordinates data

    Number of faces

       Faces data

    (Mesh 2)

    Diffuse, specular, and power material data

    Number of vertices

       Vertices data

    (And so on until we read the last mesh)

    UMF文件的版本ID可以更改文件格式,而不必担心读取UMF文件时程序崩溃。如果用于加载文件的函数能够验证要加载的文件是兼容的,那么就可以继续后面的工作。文件中的所有数值都是整型,每个整型四个字节。涉及到模型几何图形的所有信息由浮点值构成。模型中的网格数紧跟在版本号之后。一旦读取了网格总数,就可以读取每个网格。开始先是材质信息、法线等,一直到读完整个网格为止。这些网格的数值提供了一种选择,即可以指定某些属性而无需指定保存在文件中的全部属性。法线数目应该和颜色数目、顶点、纹理坐标等匹配。如同所见到的一样,UMF文件格式非常明了。如果想方便地在Direct3D和OpenGL中读取和使用某些内容的话,它是一种很好的选择。本章将介绍在Direct3D中加载UMF模型以及讲其显示到屏幕上的方法。

    12.4.2 UMF模型加载演示程序
           
    加载UMF文件的演示程序可以在本书配套光盘上的CHAPTER12文件夹中找到,名为UMF Models。演示程序包含三个头文件和源文件。这些文件分别是main.cpp源文件、UMFLoader.h头文件和UMFloader.cpp源文件。同样还有一个名为Shperes.umf的模型文件,其中包含一个盒子和三个球的场景。

    UMFLoader.h

    #ifndef _UMF_LOADER_H_
    #define _UMF_LOADER_H_

    #include<stdio.h>
    #include<math.h>


    struct stVector
    {
    stVector() : x(0), y(0), z(0) {}
    float x, y, z;
    };


    struct stTexCoord
    {
    stTexCoord() : tu(0), tv(0) {}
    // tu, tv texture coordinates.
    float tu, tv;
    };


    struct stFace
    {
    stFace() { indices[0] = indices[1] = indices[2] = 0; }

    // Vertex indexes and a surface normal.
    unsigned int indices[3];
    stVector normal;
    };


    struct stUMFModel
    {
    stUMFModel() : vertices(0), faces(0), normals(0), colors(0),
    totalVertices(0), totalFaces(0), power(0),
    texCoords(0) {}

    // Material data. 材质信息
    stVector diffuse;
    stVector specular;
    int power;

    // Model data.
    stVector *vertices; // 顶点
    stTexCoord *texCoords; // 纹理坐标
    stVector *normals; // 顶点法线
    stVector *colors; // 顶点颜色
    stFace *faces; // 三角形数据

    // Array counters.
    int totalVertices; // 顶点总数
    int totalFaces; // 三角形总数

    // Bounding box data.
    stVector bbMin, bbMax; // 包围盒
    };


    bool SaveUMF(char *file, stUMFModel *meshes, int numMeshes);
    bool LoadUMF(char *file, stUMFModel **model, int *totalModels);
    void FreeModel(stUMFModel *mesh);
    void FreeModel(stUMFModel *meshes, int numMeshes);

    #endif

    UMFLoader.cpp

    #include"UMFLoader.h"


    bool SaveUMF(char *file, stUMFModel *meshes, int numMeshes)
    {
    FILE *fp;
    int id = 9000;
    int f = 0;

    // Make sure we have valid data.
    if(!file) return false;
    if(!meshes || numMeshes <= 0) return false;

    // Open file.
    fp = fopen(file, "wb");
    if(!fp) return false;

    // Write ID (9000).
    fwrite(&id, 4, 1, fp);

    // Write total meshes.
    fwrite(&numMeshes, 4, 1, fp);

    // Save each mesh to the file.
    for(int m = 0; m < numMeshes; m++)
    {
    // Write material data.
    fwrite(&meshes[m].diffuse, 4 * 3, 1, fp);
    fwrite(&meshes[m].specular, 4 * 3, 1, fp);
    fwrite(&meshes[m].power, 4, 1, fp);

    // Write number of verts then vertices.
    fwrite(&meshes[m].totalVertices, 4, 1, fp);
    fwrite(meshes[m].vertices, sizeof(stVector) * meshes[m].totalVertices, 1, fp);

    // Write normals. Start with a flag indicating there is data.
    if(!meshes[m].normals)
    fwrite(&f, 4, 1, fp);
    else
    {
    fwrite(&meshes[m].totalVertices, 4, 1, fp);
    fwrite(meshes[m].normals, sizeof(stVector) * meshes[m].totalVertices, 1, fp);
    }

    // Write colors.
    if(!meshes[m].colors) fwrite(&f, 4, 1, fp);
    else
    {
    fwrite(&meshes[m].totalVertices, 4, 1, fp);
    fwrite(meshes[m].colors, sizeof(stVector) * meshes[m].totalVertices, 1, fp);
    }

    // Write texture coords.
    if(!meshes[m].texCoords)
    fwrite(&f, 4, 1, fp);
    else
    {
    fwrite(&meshes[m].totalVertices, 4, 1, fp);
    fwrite(meshes[m].texCoords, sizeof(stTexCoord) * meshes[m].totalVertices, 1, fp);
    }

    // Write total faces, and face data.
    fwrite(&meshes[m].totalFaces, 4, 1, fp);
    for(int i = 0; i < meshes[m].totalFaces; i++)
    {
    fwrite(meshes[m].faces[i].indices, 4 * 3, 1, fp);
    fwrite(&meshes[m].faces[i].normal, sizeof(stVector), 1, fp);
    }
    }

    fclose(fp);
    return true;
    }


    bool LoadUMF(char *file, stUMFModel **model, int *totalModels)
    {
    FILE *fp = NULL;
    int id = 0, temp = 0, numVerts = 0, numMeshes = 0;
    stVector *tVec = NULL;
    stTexCoord *pTexC = NULL;
    stFace *pFaces = NULL;
    stUMFModel *meshes = NULL;

    if(!file || !model) return false;

    fp = fopen(file, "rb");
    if(!fp) return false;

    // 前四个字节是id
    // All UMF files have an id of 9000.
    fread(&id, 4, 1, fp);
    if(id != 9000) return false;

    //再往下四个字节是网格数目
    fread(&numMeshes, 4, 1, fp);
    if(!numMeshes) { fclose(fp); return false; }

    meshes = new stUMFModel[numMeshes];
    if(!meshes) { fclose(fp); return false; }

    // 循环读取每个网格的数据
    for(int i = 0; i < numMeshes; i++)
    {
    // 读取材质数据
    fread(&meshes[i].diffuse, 4 * 3, 1, fp);
    fread(&meshes[i].specular, 4 * 3, 1, fp);
    fread(&meshes[i].power, 4, 1, fp);

    // 读取顶点数目
    fread(&numVerts, 4, 1, fp);
    meshes[i].totalVertices = numVerts;

    // 读取顶点数据
    tVec = new stVector[numVerts];
    fread(tVec, sizeof(stVector) * numVerts, 1, fp);
    meshes[i].vertices = tVec;

    // 读取法线数据
    fread(&temp, 4, 1, fp);
    if(temp > 0)
    {
    tVec = new stVector[numVerts];
    fread(tVec, sizeof(stVector) * numVerts, 1, fp);
    meshes[i].normals = tVec;
    }

    // 读取顶点颜色数据
    fread(&temp, 4, 1, fp);
    if(temp > 0)
    {
    tVec = new stVector[numVerts];
    fread(tVec, sizeof(stVector) * numVerts, 1, fp);
    meshes[i].colors = tVec;
    }

    // 读取纹理坐标
    fread(&temp, 4, 1, fp);
    if(temp > 0)
    {
    pTexC = new stTexCoord[numVerts];
    fread(pTexC, sizeof(stTexCoord) * numVerts, 1, fp);
    meshes[i].texCoords = pTexC;
    }

    // 读取三角形数据
    fread(&temp, 4, 1, fp);
    if(temp > 0)
    {
    // 三角面个数
    meshes[i].totalFaces = temp;

    pFaces = new stFace[meshes[i].totalFaces];

    for(int f = 0; f < meshes[i].totalFaces; f++)
    {
    fread(pFaces[f].indices, 4 * 3, 1, fp); // 三角面的三个顶点的索引
    fread(&pFaces[f].normal, sizeof(stVector), 1, fp); // 三角面的法线数据
    }

    meshes[i].faces = pFaces;
    }


    // 计算模型的包围盒
    tVec = meshes[i].vertices;
    for(int k = 0; k < numVerts; k++)
    {
    if(tVec[k].x < meshes[i].bbMin.x) meshes[i].bbMin.x = tVec[k].x;
    if(tVec[k].y < meshes[i].bbMin.y) meshes[i].bbMin.y = tVec[k].y;
    if(tVec[k].z < meshes[i].bbMin.z) meshes[i].bbMin.z = tVec[k].z;

    if(tVec[k].x > meshes[i].bbMax.x) meshes[i].bbMax.x = tVec[k].x;
    if(tVec[k].y > meshes[i].bbMax.y) meshes[i].bbMax.y = tVec[k].y;
    if(tVec[k].z > meshes[i].bbMax.z) meshes[i].bbMax.z = tVec[k].z;
    }
    }

    // Save information to pointer parameters.
    *model = meshes;
    if(totalModels)
    *totalModels = numMeshes;

    fclose(fp);
    return true;
    }


    void FreeModel(stUMFModel *mesh)
    {
    if(!mesh) return;

    // Release all resources.
    if(mesh->faces) delete[] mesh->faces;
    mesh->faces = NULL;

    if(mesh->vertices) delete[] mesh->vertices;
    mesh->vertices = NULL;

    if(mesh->texCoords) delete[] mesh->texCoords;
    mesh->texCoords = NULL;

    if(mesh->colors) delete[] mesh->colors;
    mesh->colors = NULL;

    if(mesh->normals) delete[] mesh->normals;
    mesh->normals = NULL;
    }


    void FreeModel(stUMFModel *meshes, int numMeshes)
    {
    if(!meshes || numMeshes <= 0) return;

    for(int i = 0; i < numMeshes; i++)
    FreeModel(&meshes[i]);
    }

    main.cpp

    #include<d3d9.h>
    #include<d3dx9.h>
    #include"UMFLoader.h"

    #pragma comment(lib, "d3d9.lib")
    #pragma comment(lib, "d3dx9.lib")


    #define WINDOW_CLASS "UGPDX"
    #define WINDOW_NAME "UMF Model Loading"
    #define WINDOW_WIDTH 640
    #define WINDOW_HEIGHT 480
    #define FULLSCREEN 0

    // Function Prototypes...
    bool InitializeD3D();
    bool InitializeObjects();
    void RenderScene();
    void Shutdown();


    // Global window handle.
    HWND g_hwnd = 0;


    // Direct3D object and device.
    LPDIRECT3D9 g_D3D = NULL;
    LPDIRECT3DDEVICE9 g_D3DDevice = NULL;


    // Matrices.
    D3DXMATRIX g_projection;
    D3DXMATRIX g_worldMatrix;
    D3DXMATRIX g_ViewMatrix;


    // Vertex buffer to hold the geometry.
    LPDIRECT3DVERTEXBUFFER9 *g_vertexBuffer = NULL;


    // A structure for our custom vertex type
    struct stD3DVertex
    {
    float x, y, z;
    float nx, ny, nz;
    unsigned long color;
    };

    // Our custom FVF, which describes our custom vertex structure
    #define D3DFVF_VERTEX (D3DFVF_XYZ | D3DFVF_NORMAL | D3DFVF_DIFFUSE)


    // Model we are loading.
    stUMFModel *g_model;
    int g_totalMeshes;

    // These are the x and y rotations for our object.
    float g_xRot = 0.0f;
    float g_yRot = 0.0f;


    LRESULT WINAPI MsgProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
    {
    static POINT oldMousePos;
    static POINT currentMousePos;
    static bool isMouseActive;

    switch(msg)
    {
    case WM_DESTROY:
    case WM_CLOSE:
    PostQuitMessage(0);
    return 0;
    break;

    case WM_KEYUP:
    if(wParam == VK_ESCAPE) PostQuitMessage(0);
    break;

    case WM_LBUTTONDOWN:
    oldMousePos.x = currentMousePos.x = LOWORD(lParam);
    oldMousePos.y = currentMousePos.y = HIWORD(lParam);
    isMouseActive = true;
    break;

    case WM_LBUTTONUP:
    isMouseActive = false;
    break;

    case WM_MOUSEMOVE:
    currentMousePos.x = LOWORD (lParam);
    currentMousePos.y = HIWORD (lParam);

    if(isMouseActive)
    {
    g_xRot -= (currentMousePos.x - oldMousePos.x);
    g_yRot -= (currentMousePos.y - oldMousePos.y);
    }

    oldMousePos.x = currentMousePos.x;
    oldMousePos.y = currentMousePos.y;
    break;
    }

    return DefWindowProc(hWnd, msg, wParam, lParam);
    }


    int WINAPI WinMain(HINSTANCE hInst, HINSTANCE prevhInst, LPSTR cmdLine, int show)
    {
    // Register the window class
    WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
    GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
    WINDOW_CLASS, NULL };
    RegisterClassEx(&wc);

    // Create the application's window
    HWND hWnd = CreateWindow(WINDOW_CLASS, WINDOW_NAME, WS_OVERLAPPEDWINDOW,
    100, 100, WINDOW_WIDTH, WINDOW_HEIGHT,
    GetDesktopWindow(), NULL, wc.hInstance, NULL);

    // Show the window
    ShowWindow(hWnd, SW_SHOWDEFAULT);
    UpdateWindow(hWnd);

    // Record for global.
    g_hwnd = hWnd;

    // Initialize Direct3D
    if(InitializeD3D())
    {
    // Enter the message loop
    MSG msg;
    ZeroMemory(&msg, sizeof(msg));

    while(msg.message != WM_QUIT)
    {
    if(PeekMessage(&msg, NULL, 0U, 0U, PM_REMOVE))
    {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
    }
    else
    RenderScene();
    }
    }

    // Release any and all resources.
    Shutdown();

    // Unregister our window.
    UnregisterClass(WINDOW_CLASS, wc.hInstance);
    return 0;
    }


    bool InitializeD3D()
    {
    D3DDISPLAYMODE displayMode;

    // Create the D3D object.
    g_D3D = Direct3DCreate9(D3D_SDK_VERSION);
    if(g_D3D == NULL) return false;

    // Get the desktop display mode.
    if(FAILED(g_D3D->GetAdapterDisplayMode(D3DADAPTER_DEFAULT, &displayMode)))
    return false;

    // Set up the structure used to create the D3DDevice
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));

    if(FULLSCREEN)
    {
    d3dpp.Windowed = FALSE;
    d3dpp.BackBufferWidth = WINDOW_WIDTH;
    d3dpp.BackBufferHeight = WINDOW_HEIGHT;
    }
    else
    d3dpp.Windowed = TRUE;
    d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
    d3dpp.BackBufferFormat = displayMode.Format;
    d3dpp.BackBufferCount = 1;
    d3dpp.EnableAutoDepthStencil = TRUE;
    d3dpp.AutoDepthStencilFormat = D3DFMT_D16;


    // Create the D3DDevice
    if(FAILED(g_D3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, g_hwnd,
    D3DCREATE_HARDWARE_VERTEXPROCESSING | D3DCREATE_PUREDEVICE,
    &d3dpp, &g_D3DDevice))) return false;

    // Initialize any objects we will be displaying.
    if(!InitializeObjects()) return false;

    return true;
    }


    bool InitializeObjects()
    {
    // Set default rendering states.
    g_D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    g_D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    g_D3DDevice->SetRenderState(D3DRS_ZENABLE, TRUE);


    // Load the model file.
    if(!LoadUMF("Spheres.umf", &g_model, &g_totalMeshes))
    return false;
    if(!g_model || g_totalMeshes <= 0) return false;


    // Create a list of vertex buffers, one for every mesh.
    g_vertexBuffer = new LPDIRECT3DVERTEXBUFFER9[g_totalMeshes];
    if(!g_vertexBuffer)
    return false;

    // Loop through and fill vertex buffer.
    for(int m = 0; m < g_totalMeshes; m++)
    {
    // Allocate temp D3D array for model data.
    stD3DVertex *objData = new stD3DVertex[g_model[m].totalFaces * 3];
    int size = sizeof(stD3DVertex) * (g_model[m].totalFaces * 3);

    if(!objData) continue;

    // Copy model data into vertex buffer.
    for(int i = 0, f = 0; i < g_model[m].totalFaces * 3; i+=3, f++)
    {
    for(int k = 0; k < 3; k++)
    {
    int r = 0, g = 0, b = 0;
    int index = g_model[m].faces[f].indices[k];

    objData[i+k].x = g_model[m].vertices[index].x;
    objData[i+k].y = g_model[m].vertices[index].y;
    objData[i+k].z = g_model[m].vertices[index].z;
    objData[i+k].nx = g_model[m].faces[f].normal.x;
    objData[i+k].ny = g_model[m].faces[f].normal.y;
    objData[i+k].nz = g_model[m].faces[f].normal.z;

    if(g_model[m].colors)
    {
    r = (int)(g_model[m].colors[index].x * 255);
    g = (int)(g_model[m].colors[index].y * 255);
    b = (int)(g_model[m].colors[index].z * 255);
    }

    if(r < 0) r = 0;
    if(g < 0) g = 0;
    if(b < 0) b = 0;

    objData[i+k].color = D3DCOLOR_XRGB(r, g, b);
    }
    }

    // Create the vertex buffer.
    if(FAILED(g_D3DDevice->CreateVertexBuffer(size, 0,
    D3DFVF_VERTEX, D3DPOOL_DEFAULT,
    &g_vertexBuffer[m], NULL))) return false;

    // Fill the vertex buffer.
    void *ptr;
    if(FAILED(g_vertexBuffer[m]->Lock(0, size, (void**)&ptr, 0))) return false;
    memcpy(ptr, objData, size);
    g_vertexBuffer[m]->Unlock();

    if(objData) delete[] objData;
    }


    // Set the projection matrix.
    D3DXMatrixPerspectiveFovLH(&g_projection, D3DX_PI / 4,
    WINDOW_WIDTH/WINDOW_HEIGHT, 1.0f, 1000.0f);

    g_D3DDevice->SetTransform(D3DTS_PROJECTION, &g_projection);


    // Define camera information.
    D3DXVECTOR3 cameraPos(0.0f, -200.0f, 50.0f);
    D3DXVECTOR3 lookAtPos(0.0f, 0.0f, 0.0f);
    D3DXVECTOR3 upDir(0.0f, 1.0f, 0.0f);

    // Build view matrix.
    D3DXMatrixLookAtLH(&g_ViewMatrix, &cameraPos,
    &lookAtPos, &upDir);

    return true;
    }


    void RenderScene()
    {
    // Clear the backbuffer.
    g_D3DDevice->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER,
    D3DCOLOR_XRGB(0,0,0), 1.0f, 0);

    // Begin the scene. Start rendering.
    g_D3DDevice->BeginScene();

    // Apply the view (camera).
    g_D3DDevice->SetTransform(D3DTS_VIEW, &g_ViewMatrix);

    // Used to rotate by mouse.
    D3DXMATRIX rot, rotX, rotY;

    // Set the rot value to the matrix. Convert deg to rad.
    D3DXMatrixRotationX(&rotX, -g_yRot / 180.0f * 3.141592654f);
    D3DXMatrixRotationY(&rotY, g_xRot / 180.0f * 3.141592654f);

    // Set the rotation matrix.
    rot = rotX * rotY;
    g_D3DDevice->SetTransform(D3DTS_WORLD, &rot);

    // Draw the model.
    for(int i = 0; i < g_totalMeshes; i++)
    {
    g_D3DDevice->SetStreamSource(0, g_vertexBuffer[i], 0, sizeof(stD3DVertex));
    g_D3DDevice->SetFVF(D3DFVF_VERTEX);
    g_D3DDevice->DrawPrimitive(D3DPT_TRIANGLELIST, 0, g_model[i].totalFaces);
    }

    // End the scene. Stop rendering.
    g_D3DDevice->EndScene();

    // Display the scene.
    g_D3DDevice->Present(NULL, NULL, NULL, NULL);
    }


    void Shutdown()
    {
    if(g_D3DDevice != NULL)
    g_D3DDevice->Release();
    g_D3DDevice = NULL;

    if(g_D3D != NULL)
    g_D3D->Release();
    g_D3D = NULL;

    for(int i = 0; i < g_totalMeshes; i++)
    {
    if(g_vertexBuffer[i] != NULL)
    g_vertexBuffer[i]->Release();
    g_vertexBuffer[i] = NULL;
    }

    if(g_vertexBuffer)
    delete[] g_vertexBuffer;
    g_vertexBuffer = NULL;

    if(g_model)
    {
    FreeModel(g_model, g_totalMeshes);
    delete[] g_model;
    g_model = NULL;
    }
    }





  • 相关阅读:
    C#.NET常见问题(FAQ)-如何给Listbox添加右键菜单
    C#.NET常见问题(FAQ)-如何捕捉窗体关闭的事件,弹窗确认是否退出
    C#.NET常见问题(FAQ)-控制台程序如何输出Messagebox
    C#.NET常见问题(FAQ)-TabControl如何隐藏和显示页面
    C#.NET常见问题(FAQ)-SplitPanel如何设置上下和左右
    Oracle 存储过程
    Oracle的存储过程
    Oracle通用分页存储过程的创建与使用
    winform窗体间传值
    多线程下访问控件的方式
  • 原文地址:https://www.cnblogs.com/kex1n/p/2187842.html
Copyright © 2011-2022 走看看