• 使用3d模型


    引用:http://www.ategpu.com/2009/06/25/%E4%BD%BF%E7%94%A83d%E6%A8%A1%E5%9E%8B.html

    通过前6章的学习你应该完全掌握了使用OpenGL绘制物体的所有内容。这包括对基本图元的渲染,颜色和光照以及上一章所讲述的纹理贴图。运用上面的知识,我们已经具备了完整的渲染任何一个3D模型的能力。但在真正的游戏中,我们要绘制的3D物体往往十分复杂,对于这样的情况,使用代码来创建模型显得即复杂又不直观,难以调试。因此,我们通常的做法都是在专门的建模工具(如3DS Max, Maya等)中创建模型,再将模型导出为特定的格式,然后在我们的程序载入这个模型。这正是本章的主题:3D模型。

    在计算机平台上,常用的3D模型有3DS、OBJ、MD2、MD3、MDL等多种格式。这些格式在存储方式存在很大的差别,但基本思想大同小异。3D模型又分为动态模型和静态模型。在动态模型中除了保存模型的定点和表面数据之外,还有与动画相关的信息。这些动态模型在渲染起来较为复杂。在这里,我们只介绍一种简单的静态模型格式–OBJ模型。

    学习本章,你将了解:

    • 使用建模工具创建模型并输出为OBJ格式

    • OBJ模型文件的文件格式

    • 用于存储材质的MTL文件格式

    • 载入OBJ模型

    • 渲染模型

    7.1 使用建模工具创建模型

    要渲染一个模型,首先要有一个模型文件。目前绝大多数流行的建模软件都支持将模型输出为OBJ文件。这里以3DS Max为例,介绍创建OBJ模型的方法。

    首先,启动3DS Max,并向往常一样创建模型。如图7.1-1所示。

    模型创建完后,执行文件(File)菜单->导出(Export)命令。在弹出的保存对话框中,输入文件名,并在”保存类型”项中,选则”Wavefront Object (*.obj)”项。

    如图7.1-2所示。

    图7.1-1 使用3DS Max创建模型

     

    图7.1-2 保存文件

    单击”保存”按钮后,会弹出如图7.1-3所示的选项对话框。

    图7.1-3 设置模型文件参数

    在这里,请注意在Faces栏中,选择Triangles以使用三角形作为基本图元。还要注意钩去Relative Vertex一项,禁止使用相对顶点索引。这是因为在后面的程序中,我们将不考虑相对顶点索引以简化代码。

    单击”OK”按钮后,会紧接着弹出另一个选项对话框,如图7.1-4所示。

    图7.1-4 设置材质文件参数

    这一步是在设置与此OBJ文件对应的MTL文件(用于保存材质信息),注意要选择”Scene”并勾选”Export”复选框。然后单击”OK”按钮完成设置。这样,在指定的文件夹下,我们得到了两个文件:一个*.OBJ文件,存储了模型的顶点、法线和纹理坐标信息;一个*.MTL文件,存储了该模型所用的材质信息。

    7.2 OBJ文件格式

    OBJ文件格式是非常简单的。这种文件以纯文本的形式存储了模型的顶点、法线和纹理坐标和材质使用信息。OBJ文件的每一行,都有极其相似的格式。在OBJ文件中,每行的格式如下:

    前缀 参数1 参数2 参数3 …

    其中,前缀标识了这一行所存储的信息类型。参数则是具体的数据。OBJ文件的前缀可以有:

    表7.2-1 OBJ文件中的前缀
    前缀

    说明

    v 表示本行指定一个顶点。
    此前缀后跟着3个单精度浮点数,分别表示该定点的X、Y、Z坐标值
    vt 表示本行指定一个纹理坐标。
    此前缀后跟着两个单精度浮点数。分别表示此纹理坐标的U、V值
    vn 表示本行指定一个法线向量。
    此前缀后跟着3个单精度浮点数,分别表示该法向量的X、Y、Z坐标值
    f 表示本行指定一个表面(Face)。
    一个表面实际上就是一个三角形图元。此前缀行的参数格式后面将详细介绍。
    usemtl 此前缀后只跟着一个参数。该参数指定了从此行之后到下一个以usemtl开头的行之间的所有表面所使用的材质名称。该材质可以在此OBJ文件所附属的MTL文件中找到具体信息。
    mtllib 此前缀后只跟着一个参数。该参数指定了此OBJ文件所使用的材质库文件(*.mtl)的文件路径

    我们使用3DS Max创建了一个长方体,并保存为OBJ格式。用写字板打开这个OBJ文件,可以看到如下内容:

    # Max2Obj Version 4.0 Mar 10th, 2001
    #
    mtllib ./Box.mtl
    g
    # object (null) to come …
    #
    v -46.508743 -45.052959 50.796341
    v 49.442947 -45.052959 50.796341
    v -46.508743 -45.052959 -48.019585
    v 49.442947 -45.052959 -48.019585
    v -46.508743 48.034504 50.796341
    v 49.442947 48.034504 50.796341
    v -46.508743 48.034504 -48.019585
    v 49.442947 48.034504 -48.019585
    # 8 vertices

    vt 0.000000 0.000000 0.000000
    vt 1.000000 0.000000 0.000000
    vt 0.000000 1.000000 0.000000
    vt 1.000000 1.000000 0.000000
    vt 0.000000 0.000000 0.000000
    vt 1.000000 0.000000 0.000000
    vt 0.000000 1.000000 0.000000
    vt 1.000000 1.000000 0.000000
    vt 0.000000 0.000000 0.000000
    vt 1.000000 0.000000 0.000000
    vt 0.000000 1.000000 0.000000
    vt 1.000000 1.000000 0.000000
    # 12 texture vertices

    g (null)
    s 2
    f 1/10 3/12 4/11
    f 4/11 2/9 1/10
    s 4
    f 5/9 6/10 8/12
    f 8/12 7/11 5/9
    s 8
    f 1/5 2/6 6/8
    f 6/8 5/7 1/5
    s 16
    f 2/1 4/2 8/4
    f 8/4 6/3 2/1
    s 32
    f 4/5 3/6 7/8
    f 7/8 8/7 4/5
    s 64
    f 3/1 1/2 5/4
    f 5/4 7/3 3/1
    # 12 faces

    g

    仔细观察,你会发现这个文件中包含了一些我们没有提到的前缀,如以”#”开头的注释,以g开头的表示组的前缀等等。但这些前缀并不影响模型的外观,因此我们可以忽略它们。

    在解释以f为前缀的行的格式之前,我们不得不提一个新的概念,这就是顶点索引(Vertex Indices)。我们知道,对于每一个三角形,都需要用3个顶点来表示。例如在上面的立方体模型中,一共有6×2×3=36个顶点。仔细想想就会知道,在这36个顶点中,又相当数量的顶点是重合的。如果把这些重合的顶点都一一表示出来,就太浪费存储空间了。于是,我们提出了顶点索引的想法,解决空间占用问题。顶点索引的思想是建立两个数组,一个数组用于存储模型中所有的顶点坐标值,另一个数组则存储每一个表面所对应的三个顶点在第一个数组中的索引。图 7.2-1显示了这种一一对应的关系。

     

    图7.2-1 顶点索引与顶点数组的对应

    建立这样的顶点索引显然更加节约存储空间。

    假设Indices:array of Integer是顶点索引数组,Vertices:array of TVertex是顶点数组,使用下面的代码段就可以把整个顶点索引对应的所有三角形绘制出来:

    procedure DrawIndex(Indices:array of Integer;Vertices:array of TVertex);

    var i :Integer;

    begin

    glBegin(GL_TRIANGLES);

    for i := 0 to (High(Vertices)+1) div 3 -1 do

    begin

    glVertex3fv(@Vertices[Indices[i*3]]);

    glVertex3fv(@Vertices[Indices[i*3+1]]);

    glVertex3fv(@Vertices[Indices[i*3+2]]);

    end;

    glEnd;

    end;

    以此类推,我们可以为模型中所有的法线、纹理坐标都建立起相应的索引,以节省更多的空间。而事实上,OBJ文件就是这么做的。

    现在,我们再来看一下OBJ文件的结构。在一个OBJ文件中,首先有一些以v、vt或vn前缀开头的行指定了所有的顶点、纹理坐标、法线的坐标。然后再由一些以f开头的行指定每一个三角形所对应的顶点、纹理坐标和法线的索引。在顶点、纹理坐标和法线的索引之间,使用符号”/”隔开的。一个f行可以以下面几种格式出现:

    f 1 2 3

    这样的行表示以第1、2、3号顶点组成一个三角形。

    f 1/3 2/5 3/4

    这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,第二个顶点的纹理坐标的索引值为5,第三个顶点的纹理坐标的索引值为4。

    f 1/3/4 2/5/6 3/4/2

    这样的行表示以第1、2、3号顶点组成一个三角形,其中第一个顶点的纹理坐标的索引值为3,其法线的索引值是4;第二个顶点的纹理坐标的索引值为5,其法线的索引值是6;第三个顶点的纹理坐标的索引值为6,其法线的索引值是2。

    f 1//4 2//6 3//2

    这样的行表示以第1、2、3号顶点组成一个三角形,且忽略纹理坐标。其中第一个顶点的法线的索引值是4;第二个顶点的法线的索引值是6;第三个顶点的法线的索引值是2。

    值得注意的是文件中的索引值是以1作为起点的,这一点与Delphi中以0作为起点有很大的不同。在渲染的时候应注意将从文件中读取的坐标值减去1。

    7.3 用于存储材质信息的MTL文件

    MTL文件与OBJ文件极其相似。只是用于标识行的前缀有所不同。这些前缀的意义如表7.3-1所示。

    表7.3-1 MTL文件中的前缀
    前缀

    说明

    newmtl 表示新建一个材质。
    此前缀后跟一个字符串,表示此材质的名称。
    此行之后的信息都是对这个材质进行设定。
    Ka 指定最后建立的材质的环境光成分。此行包含3个单精度浮点参数。
    Kd 指定最后建立的材质的漫射光成分。此行包含3个单精度浮点参数。
    Ks 指定最后建立的材质的镜面光成分。此行包含3个单精度浮点参数。
    map_Kd 指定最后建立的材质的反射贴图。如果此行的第一个参数为字符串”-s”,则此行将包括5个参数。其中第二、第三和第四个参数为此纹理贴图在U、V、W方向的缩放值,第五个参数为纹理图片的文件名。如果第一个字符串不是”-s”,那么第一个参数就是此纹理图片的文件名。
    map_Ks 指定最后建立的材质的镜面贴图。如果此行的第一个参数为字符串”-s”,则此行将包括5个参数。其中第二、第三和第四个参数为此纹理贴图在U、V、W方向的缩放值,第五个参数为纹理图片的文件名。如果第一个字符串不是”-s”,那么第一个参数就是此纹理图片的文件名。

    由这些信息,我们就足以写一个类,用于读取和渲染OBJ模型了。

    7.4 读取OBJ模型

    为了保存这些模型数据,你最好建立6个动态数组,分别存储顶点数据、顶点索引、法线数据、法线索引、纹理坐标数据、纹理坐标索引。读取文件时,把文件中的数据存入数组中。

    要读取一个文本文件,我们可以使用Delphi中自带的TStringList类。TStringList的用法十分简单,创建实例之后,执行 TStringList.LoadFromFile(FileName)就可以载入文本文件了。

    载入完之后,可以使用TStringList的Strings属性来获取文本文件每一行的值。如

    Line5:=StringList.Strings[4]; //读取第5行

    载入模型的重点是读取这些数据,我们可以建立一个循环,循环判别每一行的前缀,然后根据不同的前缀执行不同的操作。你可以这样写代码:

    procedure LoadFromFile(FileName:String);

    var i:Integer; sl:TStringList; prefix:String;

    begin

    sl:=TStringList.Create;

    for i := 0 to sl.Count-1 do

    begin

    prefix := ReadPrefix(SL.Strings[i]);

    if prefix = ‘v’ then //处理顶点数据

    … //用于读取顶点数据的代码

    else if prefix = ‘vt’ then //处理纹理坐标数据

    … //用于读取纹理坐标数据的代码

    … //处理其他前缀行

    end;

    sl.Free;

    end;

    此外,你应该建立一个函数用于分割和提取每一行中的各个参数。对于这个函数,你可以参考下面的代码。

    function GetSubString(Line: String; ValueCount: Integer): String; 
    //ValueCount参数指定要提取第几个词,如果是0则表示提取前缀。
    var fP,eP:Integer; vn:Integer;
    i:Integer;
    function WordCount(Line:String):Integer; //读取此行中的单词个数
    var i :Integer;
    begin
    Line:=' '+Line;
    Result:=0;
    for i := 1 to Length(Line)-1 do
    begin
    if (Line[i]=' ') and (Line[i+1]<>' ') then
    Result := Result+1;
    end;
    end;
    begin
    vn:=0;
    fp:=1;
    ep:=0;
    Line:=Line+' ';
    if ValueCount>0 then
    for i := 2 to Length(Line)-1 do
    begin
    if (Line[i+1]<>' ')and(Line[i] = ' ') then
    Inc(vn);
    if vn=ValueCount then
    begin
    fp := i+1;
    Break;
    end;
    end;
    if ValueCount
     
    在OBJ文件中,与其关联的MTL文件是由一个以mtllib开头的行指定的。当读到mtllib时,你应该开始载入mtllib行所指定的MTL文件。你可以参考下面的做法:
    
        首先,定义 
    

    TMaterial = record
    Name:String; //名称
    TextureFile:String; //纹理路径
    Texture:THyTexture; //这就是第6章我们创建的纹理贴图类
    Tiling:T3DVector; //纹理缩放因子
    Ambient,Diffuse,Specular:array[1..3] of Single; //材质
    end;

     TMaterials = array of TMaterial;  
    
        用于存储所有的材质。然后使用下面的代码来读取MTL文件:
    
     
     
    7.7  TObjMdl类的完整代码
                
    以下代码已包含在示例程序中。其中引用的单元(GLInit,RapidUI)全部可以在http://program.stedu.net上下载。


    unit untObjModel;
    interface
    uses Classes, SysUtils, OpenGL, GLinit, RapidUI;
    type
    T3DVector = record
    X,Y,Z:Single;
    end;
    T2DVector = record
    X,Y:Single;
    end;
    TFace = record
    MaterialName:String;
    HasNormal:Boolean;
    NormalInfo:record case Integer of
    1:(NormalIdx:array[1..3] of Integer);
    2:(Normal:T3DVector);
    end;
    Vertices:array[1..3] of Integer;
    TexCoords:array[1..3] of Integer;
    end;
    TMaterial = record
    Name:String;
    TextureFile:String;
    Texture:THyTexture;
    Tiling:T3DVector;
    Ambient,Diffuse,Specular:array[1..3] of Single;
    end;
    TMaterials = array of TMaterial;
    TObjModel = class(TObject)
    private
    Vertices:array of T3DVector;
    Normals: array of T3DVector;
    TexCoords: array of T2DVector;
    Faces:array of TFace;
    Materials:TMaterials;
    FMaterialCount:Integer;
    FHasNormals: Boolean;
    FTextured: Boolean;
    FVertexCount,FFaceCount,FNormalCount,FTexCoordCount:Integer;
    Textures : array of THyTexture;
    TextureCount:Integer;
    CommingMaterialName:String;
    procedure ReadLine(Line:String);
    procedure ProcessMtl(Line:String);
    procedure ReadVertex(Line:String);
    procedure ReadNormal(Line:String);
    procedure ReadTexCoord(Line:String);
    procedure ReadFace(Line:String);
    procedure LoadMaterials(FileName:String);
    procedure ReadMTLine(Line:String);
    function GetLinePrefix(Line:String):String;
    function GetSubString(Line:String;ValueCount:Integer):String;
    function GetSingleValue(Line:String;ValueCount:Integer):Single;
    function GetIntegerValue(Line:String;ValueCount:Integer):Integer;
    function UseMaterial(Name: String):Integer;
    public
    constructor Create;
    procedure LoadFromFile(FileName:String);
    procedure Render;
    procedure FreeModel;
    procedure Free;
    property HasNormals:Boolean read FHasNormals;
    property Textured:Boolean read FTextured;
    end;
    function CalcTriangleNormal(P1,P2,P3:T3DVector):T3DVector;
    implementation
    function CalcTriangleNormal(P1,P2,P3:T3DVector):T3DVector;
    var V1,V2:T3DVector;
    function GenVector(EndPos,StartPos:T3DVector):T3DVector;
    begin
    result.X := EndPos.X - StartPos.X;
    Result.Y := EndPos.Y - StartPos.Y;
    Result.Z := EndPos.Z - StartPos.Z;
    end;
    function VectorCross(U,V:T3DVector):T3DVector;
    begin
    Result.X := U.Y*V.Z - U.Z*V.Y;
    Result.Y := U.Z*V.X - U.X*V.Z;
    Result.Z := U.X*V.y - U.Y*V.X
    end;
    function VectorMulLanda(Landa:Single;V:T3DVector):T3DVector;
    begin
    result.X := Landa*V.X;
    Result.Y := Landa*V.Y;
    Result.Z := Landa*V.Z;
    end;
    function VectorLength(V:T3DVector):Single;
    begin
    Result := SQRT(V.X*V.X + V.Y *V.Y + V.Z *V.Z);
    end;
    function Normalize(V:T3DVector):T3DVector;
    begin
    Result := VectorMulLanda(1/VectorLength(V),V);
    end;
    begin
    V1:=GenVector(P2,P1);
    V2:=GenVector(P3,P1);
    Result := Normalize(VectorCross(v1,v2));
    end;
    { TObjModel }
    constructor TObjModel.Create;
    begin
    FNormalCount:=0;
    FVertexCount:=0;
    FFaceCount:=0;
    FTexCoordCount:=0;
    end;
    procedure TObjModel.Free;
    var i :Integer;
    begin
    SetLength(Vertices,0);
    SetLength(Normals,0);
    SetLength(TexCoords,0);
    SetLength(Faces,0);
    for i := 0 to TextureCount-1 do
    Textures[i].Free;
    end;
    function TObjModel.GetIntegerValue(Line: String;
    ValueCount: Integer): Integer;
    var Vs:String;
    begin
    vs:=GetSubString(Line,ValueCount);
    Result := StrToInt(vs);
    end;
    function TObjModel.GetLinePrefix(Line: String): String;
    begin
    Result := GetSubString(Line,0);
    end;
    function TObjModel.GetSingleValue(Line: String;
    ValueCount: Integer): Single;
    var vs:String; V:Single;Code:Integer;
    begin
    vs:=GetSubString(Line,ValueCount);
    Val(vs,V,code);
    if Code = 0 then
    result := V
    else
    Result:=0;
    end;
    function TObjModel.GetSubString(Line: String; ValueCount: Integer): String;
    var fP,eP:Integer; vn:Integer;
    i:Integer;
    function WordCount(Line:String):Integer;
    var i :Integer;
    begin
    Line:=' '+Line;
    Result:=0;
    for i := 1 to Length(Line)-1 do
    begin
    if (Line[i]=' ') and (Line[i+1]<>' ') then
    Result := Result+1;
    end;
    end;
    begin
    vn:=0;
    fp:=1;
    ep:=0;
    Line:=Line+' ';
    if ValueCount>0 then
    for i := 2 to Length(Line)-1 do
    begin
    if (Line[i+1]<>' ')and(Line[i] = ' ') then
    Inc(vn);
    if vn=ValueCount then
    begin
    fp := i+1;
    Break;
    end;
    end;
    if ValueCount0);
    FTextured := (TextureCount>0);
    for i := 0 to FFaceCount-1 do
    begin
    if not Faces[i].HasNormal then
    begin
    Faces[i].NormalInfo.Normal := CalcTriangleNormal(
    Vertices[Faces[i].Vertices[1]-1],Vertices[Faces[i].Vertices[2]-1],
    Vertices[Faces[i].Vertices[3]-1]);
    end;
    end;
    finally
    Sl.Free;
    end;
    end;
    procedure TObjModel.LoadMaterials(FileName: String);
    var sl:TStringList;
    i:Integer;
    begin
    sl:=TStringList.Create;
    try
    sl.LoadFromFile(FileName);
    for i := 0 to sl.Count-1 do
    begin
    ReadMTLine(sl.Strings[i]);
    end;
    finally
    Sl.Free;
    end;
    end;
    procedure TObjModel.ProcessMtl(Line: String);
    begin
    CommingMaterialName:=GetSubString(Line,1);
    end;
    procedure TObjModel.ReadFace(Line: String);
    var i,j:Integer; subS:String;
    vc:Integer;
    nvs:array[1..3] of String;
    function GetValueCount(S:String):Integer;
    var i:Integer;
    begin
    Result := 1;
    for i := 1 to Length(S) do
    begin
    if S[i] = '/' then
    Inc(Result);
    end;
    end;
    function GetValue(S:String;n:Integer):String;
    var i,fp:Integer;ep:Integer;sc:Integer;sp:array[1..2] of Integer;
    begin
    fp := 1;
    ep := 0;
    sc := 0;
    for i := 1 to Length(S) do
    begin
    if S[i]='/' then
    begin
    inc(SC);
    sp[sc] := i;
    end;
    end;
    if n=1 then
    begin
    Result := Copy(s,1,sp[1]-1);
    end
    else if n=2 then
    begin
    Result := Copy(s,sp[1]+1,sp[2]-sp[1]-1);
    end
    else if n= 3 then
    begin
    Result := Copy(s,sp[2]+1,Length(s)-sp[2]);
    end;
    end;
    begin
    Inc(FFaceCount);
    if FFaceCount-1>High(Faces) then
    SetLength(Faces,FFaceCount+50);
    if CommingMaterialName<>'' then
    begin
    Faces[FFaceCount-1].MaterialName := CommingMaterialName;
    CommingMaterialName:='';
    end;
    for i := 1 to 3 do
    begin
    SubS:=GetSubString(Line,i);
    vc := GetValueCount(SubS);
    for j := 1 to vc do
    begin
    nvs[j]:= GetValue(SubS,j);
    if nvs[j]<>'' then
    begin
    if j= 1 then
    begin
    Faces[FFaceCount-1].Vertices[i] := StrToInt(Nvs[1]);
    end
    else
    if j=2 then
    begin
    Faces[FFaceCount-1].TexCoords[i] := StrToInt(Nvs[2]);
    end
    else
    begin
    Faces[FFaceCount-1].HasNormal :=True;
    Faces[FFaceCount-1].NormalInfo.NormalIdx[i] := StrToInt(Nvs[3]);
    end;
    end;
    end;
    end;
    end;
    procedure TObjModel.ReadLine(Line: String);
    var fp:String;
    begin
    fp:=GetLinePrefix(Line);
    if fp = 'mtllib' then
    LoadMaterials(GetSubString(Line,1))
    else
    if fp = 'v' then
    ReadVertex(Line)
    else
    if fp = 'vn' then
    ReadNormal(Line)
    else
    if fp = 'vt' then
    ReadTexCoord(Line)
    else
    if fp = 'f' then
    ReadFace(Line)
    else
    if fp = 'usemtl' then
    ProcessMtl(Line);
    end;
    procedure TObjModel.ReadMTLine(Line: String);
    var pf:String;
    begin
    pf := Lowercase(GetLinePreFix(Line));
    if pf = 'newmtl' then
    begin
    Inc(FMaterialCount);
    SetLength(Materials,FMaterialCount);
    Materials[FMaterialCount-1].Name := GetSubString(Line,1);
    end
    else if pf = 'ka' then
    begin
    Materials[FMaterialCount-1].Ambient[1] := GetSingleValue(Line,1);
    Materials[FMaterialCount-1].Ambient[2] := GetSingleValue(Line,2);
    Materials[FMaterialCount-1].Ambient[3] := GetSingleValue(Line,3);
    end
    else if pf = 'kd' then
    begin
    Materials[FMaterialCount-1].Diffuse[1] := GetSingleValue(Line,1);
    Materials[FMaterialCount-1].Diffuse[2] := GetSingleValue(Line,2);
    Materials[FMaterialCount-1].Diffuse[3] := GetSingleValue(Line,3);
    end
    else if pf = 'ks' then
    begin
    Materials[FMaterialCount-1].Specular[1] := GetSingleValue(Line,1);
    Materials[FMaterialCount-1].Specular[2] := GetSingleValue(Line,2);
    Materials[FMaterialCount-1].Specular[3] := GetSingleValue(Line,3);
    end
    else if pf = 'txt' then
    begin
    Materials[FMaterialCount-1].TextureFile := GetSubString(Line,1);
    if FileExists(Materials[FMaterialCount-1].TextureFile) then
    begin
    Inc(TextureCount);
    SetLength(Textures,TextureCount);
    Textures[TextureCount-1]:=THyTexture.Create;
    Textures[TextureCount-1].LoadFromFile(Materials[FMaterialCount-1].TextureFile);
    Materials[FMaterialCount-1].Texture := Textures[TextureCount-1];
    end
    else
    Materials[FMaterialCount-1].TextureFile := '';
    end
    else if pf = 'map_kd' then
    begin
    if GetSubString(Line,1) = '-s' then
    begin
    Materials[FMaterialCount-1].Tiling.X:= GetSingleValue(Line,2);
    Materials[FMaterialCount-1].Tiling.Y:= GetSingleValue(Line,3);
    Materials[FMaterialCount-1].Tiling.Z:= GetSingleValue(Line,4);
    ReadMTLine('txt ' + GetSubString(Line,5));
    end
    else
    begin
    Materials[FMaterialCount-1].Tiling.X:= 1;
    Materials[FMaterialCount-1].Tiling.Y:= 1;
    Materials[FMaterialCount-1].Tiling.Z:= 1;
    ReadMTLine('txt ' + GetSubString(Line,1));
    end;
    end;
    end;
    procedure TObjModel.ReadNormal(Line: String);
    begin
    Inc(FNormalCount);
    if FNormalCount-1>High(Normals) then
    SetLength(Normals,FNormalCount+50);
    Normals[FNormalCount-1].X := GetSingleValue(Line,1);
    Normals[FNormalCount-1].Y := GetSingleValue(Line,2);
    Normals[FNormalCount-1].Z := GetSingleValue(Line,3);
    end;
    procedure TObjModel.ReadTexCoord(Line: String);
    begin
    Inc(FTexCoordCount);
    if FTexCoordCount-1>High(TexCoords) then
    SetLength(TexCoords,FTexCoordCount+50);
    TexCoords[FTexCoordCount-1].X := GetSingleValue(Line,1);
    TexCoords[FTexCoordCount-1].Y := GetSingleValue(Line,2);
    end;
    procedure TObjModel.ReadVertex(Line: String);
    begin
    Inc(FVertexCount);
    if FVertexCount-1>High(Vertices) then
    SetLength(Vertices,FVertexCount+50);
    Vertices[FVertexCount-1].X := GetSingleValue(Line,1);
    Vertices[FVertexCount-1].Y := GetSingleValue(Line,2);
    Vertices[FVertexCount-1].Z := GetSingleValue(Line,3);
    end;
    function TObjModel.UseMaterial(Name:String):Integer;
    var idx,i:Integer;
    begin
    for i := 0 to FMaterialCount-1 do
    begin
    if Materials[i].Name = Name then
    begin
    idx:=i;
    Result :=i;
    break;
    end;
    end;
    if Materials[idx].TextureFile <>'' then Materials[idx].Texture.Bind;
    glMaterialfv(GL_FRONT,GL_AMBIENT,@Materials[idx].Ambient);
    glMaterialfv(GL_FRONT,GL_DIFFUSE,@Materials[idx].Diffuse);
    glMaterialfv(GL_FRONT,GL_SPECULAR,@Materials[idx].Specular);
    end;
    procedure TObjModel.Render;
    var i,j:Integer; CurMtlIdx:Integer; curMtlTiling:T3DVector;
    begin
    for i := 0 to FFaceCount -1 do
    begin
    curMtlTiling.X:=1;
    curMtlTiling.Y:=1;
    curMtlTiling.Z:=1;
    if Faces[i].MaterialName <>'' then
    begin
    CurMtlIdx:=UseMaterial(Faces[i].MaterialName);
    curMtlTiling:=Materials[CurMtlIdx].Tiling;
    end;
    glBegin(GL_TRIANGLES);
    for j := 1 to 3 do
    begin
    if Faces[i].HasNormal then
    glNormal3fv(@Normals[Faces[i].NormalInfo.NormalIdx[j]-1])
    else
    glNormal3fv(@Faces[i].NormalInfo.Normal);
    if FTextured then
    glTexCoord2d(TexCoords[Faces[i].TexCoords[j]-1].X*curMtlTiling.X,
    TexCoords[Faces[i].TexCoords[j]-1].Y*curMtlTiling.Y);
    glVertex3fv(@Vertices[Faces[i].Vertices[j]-1]);
    end;
    glEnd;
    end;
    end;
    procedure TObjModel.FreeModel;
    var i : Integer;
    begin
    for i := 0 to TextureCount-1 do
    begin
    Textures[i].Free;
    end;
    FNormalCount:=0;
    FVertexCount:=0;
    FFaceCount:=0;
    TextureCount:=0;
    FTexCoordCount:=0;
    FMaterialCount:=0;
    SetLength(Normals,FNormalCount);
    SetLength(Vertices,FVertexCount);
    SetLength(Faces,FFaceCount);
    SetLength(TexCoords,FTexCoordCount);
    SetLength(Materials,FMaterialCount);
    SetLength(Textures,TextureCount);
    end;
    end.
  • 相关阅读:
    RN组件的生命周期
    调试菜单
    React Native 之项目的启动
    React Native 之组件的定义
    Es6 之 const关键字
    2019年开发App记录
    upc-魔法石01字符串ab字符串变换问题——尺取
    中石油训练混合训练第七场
    魔法序列-upc
    MAX 的读书计划——dp
  • 原文地址:https://www.cnblogs.com/sode/p/2342114.html
Copyright © 2020-2023  润新知