[Source] Quake 2 MD2 model loader for OGRE

Everything related to 3D programming
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

[Source] Quake 2 MD2 model loader for OGRE

Post by Mijikai »

Quake 2 MD2 Loader for Ogre (well its generic) :)

The MD2 (Quake 2 model format) is a boneless per frame (mesh) animated 3d model format
introduced in November 1997 by id Software for the Quake 2 game.

Frame based animations are faster than bone based animations (nowdays there is a comeback with blendshapes and shaders).
Bones however can still be used to create animations for MD2 models (they just wont get exported).

The MD2 model format is one of the simplest 3d model formats.
It is still useful ex. for a top down strategy games.

About Quake 2: https://en.wikipedia.org/wiki/Quake_II

Example model from ZDoom (doomguy.md2):
Image

Update 19.02.2023:
- md2Animation() & md2Mesh() now make use of the frame_size variable as it is intended (i calculated it before for no reason)

Update 20.02.2023:
- Added Example load & animate (maybe there is a better way with submeshes...)

The Library (pb3d_md2.pb):

Code: Select all

EnableExplicit

;---------------------------------------------------------------
;Quake 2 (id Tech 2) MD2 (Version 8) model loader for Ogre
;https://github.com/id-Software/Quake-2
;---------------------------------------------------------------
;File: DLL - pb3d_md2.pb
;---------------------------------------------------------------
;Version: alpha
;Author: Mijikai
;License: GPL 2.0
;---------------------------------------------------------------
;Limitation: no support for MD2 commands!
;---------------------------------------------------------------
;MD2 format limits (for 3d modeling):
;- Skins:     32
;- Triangles: 4096
;- Vertices:  2048
;- Texels:    2048
;- Frames:    512
;---------------------------------------------------------------

Structure MD2_TRIANGLE
  v.w[3]
  st.w[3]
EndStructure

Structure MD2_VERTEX
  v.a[3]
  n.a
EndStructure

Structure MD2_TEXEL
  s.w
  t.w
EndStructure

Structure MD2_VECTOR
  x.f
  y.f
  z.f
EndStructure

Structure MD2_FRAME
  scale.MD2_VECTOR
  translate.MD2_VECTOR
  name.a[16]
  vertex.MD2_VERTEX[0]
EndStructure

Structure MD2_SKIN
  name.a[64]
EndStructure

Structure MD2
        magic.l
        version.l
	skin_width.l
	skin_height.l
	frame_size.l
	num_skins.l
	num_vertice.l
	num_texels.l
	num_triangles.l
	num_commands.l
	num_frames.l
	offset_skins.l
	offset_texels.l
	offset_triangles.l
	offset_frames.l
	offset_commands.l
	offset_end.l
EndStructure

Structure LDR_COORD
  x.f
  y.f
EndStructure

Structure LDR_ACCESS
  StructureUnion
    indice.MD2_TRIANGLE[0]
    coord.MD2_TEXEL[0]
    index.MD2_VECTOR[0]
  EndStructureUnion
EndStructure

#MD2_HEADER_MAGIC = $32504449
#MD2_HEADER_VERSION = 8

#LDR_MD2_VERSION = $000001

ProcedureDLL.i md2Open(File.s,*Buffer.MD2)
  Protected *md2.MD2
  Protected hfile.i
  Protected bytes.i
  If *Buffer
    If *Buffer\magic = #MD2_HEADER_MAGIC And *Buffer\version = #MD2_HEADER_VERSION
      bytes = *Buffer\offset_end
      If bytes > SizeOf(MD2)
        *md2 = AllocateMemory(bytes)
        If *md2
          CopyMemory(*Buffer,*md2,bytes)
        EndIf
      EndIf
    EndIf
  ElseIf File
    hfile = ReadFile(#PB_Any,File)
    If IsFile(hfile)
      bytes = Lof(hfile)
      If bytes > SizeOf(MD2)
        *md2 = AllocateMemory(bytes)
        If *md2
          If ReadData(hfile,*md2,bytes) = bytes
            If *md2\magic <> #MD2_HEADER_MAGIC Or *md2\version <> #MD2_HEADER_VERSION
              FreeMemory(*md2)
              *md2 = #Null
            EndIf
          Else
            FreeMemory(*md2)
            *md2 = #Null
          EndIf
        EndIf
      EndIf
      CloseFile(hfile)
    EndIf
  EndIf
  ProcedureReturn *md2
EndProcedure

ProcedureDLL.i md2Texture(*md2.MD2,Index.i,*Texture)
  Protected *skin.MD2_SKIN
  Protected name.s
  With *md2
    If Index < \num_skins And *Texture
      *skin = *md2 + \offset_skins + (Index * SizeOf(MD2_SKIN))
      name = PeekS(@*skin\name[0],64,#PB_Ascii)
      CopyMemory(@name,*Texture,128)
    EndIf
    ProcedureReturn \num_skins
  EndWith
EndProcedure

ProcedureDLL.i md2Animation(*md2.MD2,Index.i,*Animation)
  Protected *frame.MD2_FRAME
  Protected name.s
  With *md2
    If Index < \num_frames And *Animation
      *frame = *md2 + \offset_frames + (Index * \frame_size)
      name = PeekS(@*frame\name[0],16,#PB_Ascii)
      CopyMemory(@name,*Animation,32)
    EndIf
    ProcedureReturn \num_frames
  EndWith
EndProcedure

ProcedureDLL.i md2Info(*md2.MD2,*Meshes.Integer,*Faces.Integer,*Indice.Integer,*Mode.Integer)
  With *md2
    If *Meshes
      *Meshes\i = \num_frames
    EndIf
    If *Faces
      *Faces\i = \num_triangles
    EndIf
    If *Indice
      *Indice\i = 3
    EndIf
    If *Mode
      *Mode\i = #PB_Mesh_TriangleList
    EndIf
    ProcedureReturn #Null
  EndWith
EndProcedure

ProcedureDLL.i md2Mesh(*md2.MD2,Mesh.i,Face.i,Index.i,Blend.f,*TextureCoord.LDR_COORD,*Normal.MD2_VECTOR,*Vertex.MD2_VECTOR)
  Protected *tex.LDR_ACCESS
  Protected *nor.LDR_ACCESS
  Protected *tri.LDR_ACCESS
  Protected *frame1.MD2_FRAME
  Protected *frame2.MD2_FRAME
  Protected vn1.MD2_VECTOR
  Protected vn2.MD2_VECTOR
  Protected vt1.MD2_VECTOR
  Protected vt2.MD2_VECTOR
  With *md2
    *tex = *md2 + \offset_texels
    *nor = ?md2normals
    *tri = *md2 + \offset_triangles
     *frame1 = *md2 + \offset_frames + (Mesh * \frame_size)
    Mesh + 1
    *frame2 = *md2 + \offset_frames + (Mesh * \frame_size)
    If Blend And Mesh < \num_frames
      *TextureCoord\x = *tex\coord[*tri\indice[Face]\st[Index]]\s / \skin_width
      *TextureCoord\y = *tex\coord[*tri\indice[Face]\st[Index]]\t / \skin_height
      vn1\x = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\x
      vn1\y = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\y
      vn1\z = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\z
      vn2\x = *nor\index[*frame2\vertex[*tri\indice[Face]\v[Index]]\n]\x
      vn2\y = *nor\index[*frame2\vertex[*tri\indice[Face]\v[Index]]\n]\y
      vn2\z = *nor\index[*frame2\vertex[*tri\indice[Face]\v[Index]]\n]\z
      vn1\x = vn1\x + Blend * (vn2\x - vn1\x)
      vn1\y = vn1\y + Blend * (vn2\y - vn1\y)
      vn1\z = vn1\z + Blend * (vn2\z - vn1\z)
      vt1\x = (*frame1\vertex[*tri\indice[Face]\v[Index]]\v[0] * *frame1\scale\x) + *frame1\translate\x
      vt1\y = (*frame1\vertex[*tri\indice[Face]\v[Index]]\v[1] * *frame1\scale\y) + *frame1\translate\y
      vt1\z = (*frame1\vertex[*tri\indice[Face]\v[Index]]\v[2] * *frame1\scale\z) + *frame1\translate\z
      vt2\x = (*frame2\vertex[*tri\indice[Face]\v[Index]]\v[0] * *frame2\scale\x) + *frame2\translate\x
      vt2\y = (*frame2\vertex[*tri\indice[Face]\v[Index]]\v[1] * *frame2\scale\y) + *frame2\translate\y
      vt2\z = (*frame2\vertex[*tri\indice[Face]\v[Index]]\v[2] * *frame2\scale\z) + *frame2\translate\z
      vt1\x = vt1\x + Blend * (vt2\x - vt1\x)
      vt1\y = vt1\y + Blend * (vt2\y - vt1\y)
      vt1\z = vt1\z + Blend * (vt2\z - vt1\z)
      *Normal\x = vn1\x
      *Normal\y = vn1\y
      *Normal\z = vn1\z
      *Vertex\x = vt1\x
      *Vertex\y = vt1\y
      *Vertex\z = 1.0 - vt1\z
    Else
      *TextureCoord\x = *tex\coord[*tri\indice[Face]\st[Index]]\s / \skin_width
      *TextureCoord\y = *tex\coord[*tri\indice[Face]\st[Index]]\t / \skin_height
      *Normal\x = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\x
      *Normal\x = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\y
      *Normal\z = *nor\index[*frame1\vertex[*tri\indice[Face]\v[Index]]\n]\z
      *Vertex\x = (*frame1\vertex[*tri\indice[Face]\v[Index]]\v[0] * *frame1\scale\x) + *frame1\translate\x
      *Vertex\y = (*frame1\vertex[*tri\indice[Face]\v[Index]]\v[1] * *frame1\scale\y) + *frame1\translate\y
      *Vertex\z = 1.0 - ((*frame1\vertex[*tri\indice[Face]\v[Index]]\v[2] * *frame1\scale\z) + *frame1\translate\z)
    EndIf
    ProcedureReturn #Null
  EndWith  
EndProcedure

ProcedureDLL.i md2Close(*md2.MD2)
  With *md2
    FreeMemory(*md2)
    ProcedureReturn #Null
  EndWith
EndProcedure

ProcedureDLL.i md2Version()
  ProcedureReturn #LDR_MD2_VERSION
EndProcedure

DataSection
  md2normals:
  Data.f -0.525731, 0.000000, 0.850651 
  Data.f -0.442863, 0.238856, 0.864188 
  Data.f -0.295242, 0.000000, 0.955423 
  Data.f -0.309017, 0.500000, 0.809017 
  Data.f -0.162460, 0.262866, 0.951056 
  Data.f  0.000000, 0.000000, 1.000000 
  Data.f  0.000000, 0.850651, 0.525731 
  Data.f -0.147621, 0.716567, 0.681718 
  Data.f  0.147621, 0.716567, 0.681718 
  Data.f  0.000000, 0.525731, 0.850651 
  Data.f  0.309017, 0.500000, 0.809017 
  Data.f  0.525731, 0.000000, 0.850651 
  Data.f  0.295242, 0.000000, 0.955423 
  Data.f  0.442863, 0.238856, 0.864188 
  Data.f  0.162460, 0.262866, 0.951056 
  Data.f -0.681718, 0.147621, 0.716567 
  Data.f -0.809017, 0.309017, 0.500000 
  Data.f -0.587785, 0.425325, 0.688191 
  Data.f -0.850651, 0.525731, 0.000000 
  Data.f -0.864188, 0.442863, 0.238856 
  Data.f -0.716567, 0.681718, 0.147621 
  Data.f -0.688191, 0.587785, 0.425325 
  Data.f -0.500000, 0.809017, 0.309017 
  Data.f -0.238856, 0.864188, 0.442863 
  Data.f -0.425325, 0.688191, 0.587785 
  Data.f -0.716567, 0.681718,-0.147621 
  Data.f -0.500000, 0.809017,-0.309017 
  Data.f -0.525731, 0.850651, 0.000000 
  Data.f  0.000000, 0.850651,-0.525731 
  Data.f -0.238856, 0.864188,-0.442863 
  Data.f  0.000000, 0.955423,-0.295242 
  Data.f -0.262866, 0.951056,-0.162460 
  Data.f  0.000000, 1.000000, 0.000000 
  Data.f  0.000000, 0.955423, 0.295242 
  Data.f -0.262866, 0.951056, 0.162460 
  Data.f  0.238856, 0.864188, 0.442863 
  Data.f  0.262866, 0.951056, 0.162460 
  Data.f  0.500000, 0.809017, 0.309017 
  Data.f  0.238856, 0.864188,-0.442863 
  Data.f  0.262866, 0.951056,-0.162460 
  Data.f  0.500000, 0.809017,-0.309017 
  Data.f  0.850651, 0.525731, 0.000000 
  Data.f  0.716567, 0.681718, 0.147621 
  Data.f  0.716567, 0.681718,-0.147621 
  Data.f  0.525731, 0.850651, 0.000000 
  Data.f  0.425325, 0.688191, 0.587785 
  Data.f  0.864188, 0.442863, 0.238856 
  Data.f  0.688191, 0.587785, 0.425325 
  Data.f  0.809017, 0.309017, 0.500000 
  Data.f  0.681718, 0.147621, 0.716567 
  Data.f  0.587785, 0.425325, 0.688191 
  Data.f  0.955423, 0.295242, 0.000000 
  Data.f  1.000000, 0.000000, 0.000000 
  Data.f  0.951056, 0.162460, 0.262866 
  Data.f  0.850651,-0.525731, 0.000000 
  Data.f  0.955423,-0.295242, 0.000000 
  Data.f  0.864188,-0.442863, 0.238856 
  Data.f  0.951056,-0.162460, 0.262866 
  Data.f  0.809017,-0.309017, 0.500000 
  Data.f  0.681718,-0.147621, 0.716567 
  Data.f  0.850651, 0.000000, 0.525731 
  Data.f  0.864188, 0.442863,-0.238856 
  Data.f  0.809017, 0.309017,-0.500000 
  Data.f  0.951056, 0.162460,-0.262866 
  Data.f  0.525731, 0.000000,-0.850651 
  Data.f  0.681718, 0.147621,-0.716567 
  Data.f  0.681718,-0.147621,-0.716567 
  Data.f  0.850651, 0.000000,-0.525731 
  Data.f  0.809017,-0.309017,-0.500000 
  Data.f  0.864188,-0.442863,-0.238856 
  Data.f  0.951056,-0.162460,-0.262866 
  Data.f  0.147621, 0.716567,-0.681718 
  Data.f  0.309017, 0.500000,-0.809017 
  Data.f  0.425325, 0.688191,-0.587785 
  Data.f  0.442863, 0.238856,-0.864188 
  Data.f  0.587785, 0.425325,-0.688191 
  Data.f  0.688191, 0.587785,-0.425325 
  Data.f -0.147621, 0.716567,-0.681718 
  Data.f -0.309017, 0.500000,-0.809017 
  Data.f  0.000000, 0.525731,-0.850651 
  Data.f -0.525731, 0.000000,-0.850651 
  Data.f -0.442863, 0.238856,-0.864188 
  Data.f -0.295242, 0.000000,-0.955423 
  Data.f -0.162460, 0.262866,-0.951056 
  Data.f  0.000000, 0.000000,-1.000000 
  Data.f  0.295242, 0.000000,-0.955423 
  Data.f  0.162460, 0.262866,-0.951056 
  Data.f -0.442863,-0.238856,-0.864188 
  Data.f -0.309017,-0.500000,-0.809017 
  Data.f -0.162460,-0.262866,-0.951056 
  Data.f  0.000000,-0.850651,-0.525731 
  Data.f -0.147621,-0.716567,-0.681718 
  Data.f  0.147621,-0.716567,-0.681718 
  Data.f  0.000000,-0.525731,-0.850651 
  Data.f  0.309017,-0.500000,-0.809017 
  Data.f  0.442863,-0.238856,-0.864188 
  Data.f  0.162460,-0.262866,-0.951056 
  Data.f  0.238856,-0.864188,-0.442863 
  Data.f  0.500000,-0.809017,-0.309017 
  Data.f  0.425325,-0.688191,-0.587785 
  Data.f  0.716567,-0.681718,-0.147621 
  Data.f  0.688191,-0.587785,-0.425325 
  Data.f  0.587785,-0.425325,-0.688191 
  Data.f  0.000000,-0.955423,-0.295242 
  Data.f  0.000000,-1.000000, 0.000000 
  Data.f  0.262866,-0.951056,-0.162460 
  Data.f  0.000000,-0.850651, 0.525731 
  Data.f  0.000000,-0.955423, 0.295242 
  Data.f  0.238856,-0.864188, 0.442863 
  Data.f  0.262866,-0.951056, 0.162460 
  Data.f  0.500000,-0.809017, 0.309017 
  Data.f  0.716567,-0.681718, 0.147621 
  Data.f  0.525731,-0.850651, 0.000000 
  Data.f -0.238856,-0.864188,-0.442863 
  Data.f -0.500000,-0.809017,-0.309017 
  Data.f -0.262866,-0.951056,-0.162460 
  Data.f -0.850651,-0.525731, 0.000000 
  Data.f -0.716567,-0.681718,-0.147621 
  Data.f -0.716567,-0.681718, 0.147621 
  Data.f -0.525731,-0.850651, 0.000000 
  Data.f -0.500000,-0.809017, 0.309017 
  Data.f -0.238856,-0.864188, 0.442863 
  Data.f -0.262866,-0.951056, 0.162460 
  Data.f -0.864188,-0.442863, 0.238856 
  Data.f -0.809017,-0.309017, 0.500000 
  Data.f -0.688191,-0.587785, 0.425325 
  Data.f -0.681718,-0.147621, 0.716567 
  Data.f -0.442863,-0.238856, 0.864188 
  Data.f -0.587785,-0.425325, 0.688191 
  Data.f -0.309017,-0.500000, 0.809017 
  Data.f -0.147621,-0.716567, 0.681718 
  Data.f -0.425325,-0.688191, 0.587785 
  Data.f -0.162460,-0.262866, 0.951056 
  Data.f  0.442863,-0.238856, 0.864188 
  Data.f  0.162460,-0.262866, 0.951056 
  Data.f  0.309017,-0.500000, 0.809017 
  Data.f  0.147621,-0.716567, 0.681718 
  Data.f  0.000000,-0.525731, 0.850651 
  Data.f  0.425325,-0.688191, 0.587785 
  Data.f  0.587785,-0.425325, 0.688191 
  Data.f  0.688191,-0.587785, 0.425325 
  Data.f -0.955423, 0.295242, 0.000000 
  Data.f -0.951056, 0.162460, 0.262866 
  Data.f -1.000000, 0.000000, 0.000000 
  Data.f -0.850651, 0.000000, 0.525731 
  Data.f -0.955423,-0.295242, 0.000000 
  Data.f -0.951056,-0.162460, 0.262866 
  Data.f -0.864188, 0.442863,-0.238856 
  Data.f -0.951056, 0.162460,-0.262866 
  Data.f -0.809017, 0.309017,-0.500000 
  Data.f -0.864188,-0.442863,-0.238856 
  Data.f -0.951056,-0.162460,-0.262866 
  Data.f -0.809017,-0.309017,-0.500000 
  Data.f -0.681718, 0.147621,-0.716567 
  Data.f -0.681718,-0.147621,-0.716567 
  Data.f -0.850651, 0.000000,-0.525731 
  Data.f -0.688191, 0.587785,-0.425325 
  Data.f -0.587785, 0.425325,-0.688191 
  Data.f -0.425325, 0.688191,-0.587785 
  Data.f -0.425325,-0.688191,-0.587785 
  Data.f -0.587785,-0.425325,-0.688191 
  Data.f -0.688191,-0.587785,-0.425325
EndDataSection


The Include (pb3d_md2.pbi):

Code: Select all

EnableExplicit

;---------------------------------------------------------------
;Quake 2 (id Tech 2) MD2 (Version 8) model loader for Ogre
;https://github.com/id-Software/Quake-2
;---------------------------------------------------------------
;File: INCLUDE - pb3d_md2.pbi
;---------------------------------------------------------------
;Version: alpha
;Author: Mijikai
;License: GPL 2.0
;---------------------------------------------------------------
;Limitation: no support for MD2 commands!
;---------------------------------------------------------------
;MD2 format limits (for 3d modeling):
;- Skins:     32
;- Triangles: 4096
;- Vertices:  2048
;- Texels:    2048
;- Frames:    512
;---------------------------------------------------------------

Import "pb3d_md2.lib"
  md2Open(File.s,*Buffer = #Null)
  md2Texture(Handle.i,Index.i = #Null,*Texture = #Null)
  md2Animation(Handle.i,Index.i = #Null,*Animation = #Null)
  md2Info(Handle.i,*Meshes,*Faces,*Indice,*Mode = #Null)
  md2Mesh(Handle.i,Mesh.i,Face.i,Index.i,Blend.f,*TextureCoord,*Normal,*Vertex)
  md2Close(Handle.i)
  md2Version.i()
EndImport

#LDR_MD2_VERSION = $000001

Structure LDR_MD2_TEXTURE
  id.s{64}
EndStructure

Structure LDR_MD2_ANIMATION
  id.s{16}
EndStructure

Structure LDR_MD2_TEXEL
  s.f
  t.f
EndStructure

Structure LDR_MD2_VERTEX
  x.f
  y.f
  z.f
EndStructure


Example:

Code: Select all

EnableExplicit

XIncludeFile "pb3d_md2.pbi"

Procedure.i Main(md2File.s,md2Texture.s)
  Protected exit.i
  Protected handle.i
  Protected count.i
  Protected face.i
  Protected faces.i
  Protected index.i
  Protected indice.i
  Protected build.i
  Protected texture.LDR_MD2_TEXTURE
  Protected animation.LDR_MD2_ANIMATION
  Protected Dim texel.LDR_MD2_TEXEL(2)
  Protected Dim normal.LDR_MD2_VERTEX(2)
  Protected Dim vertex.LDR_MD2_VERTEX(2)
  handle = md2Open(md2File)
  If handle
;     count = md2Texture(handle);<- uncomment to see the textures files referred to inside the model
;     For index = 0 To count - 1
;       md2Texture(handle,index,@texture)
;       Debug texture\id
;     Next
;     count = md2Animation(handle);<- uncomment to see the mesh/frame animation tags
;     For index = 0 To count - 1
;       md2Animation(handle,index,@animation)
;       Debug animation\id
;     Next
    If InitEngine3D() And InitSprite()
       Add3DArchive(GetCurrentDirectory(),#PB_3DArchive_FileSystem)
      If OpenWindow(0,0,0,800,600,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
        If OpenWindowedScreen(WindowID(0),0,0,WindowWidth(0),WindowHeight(0))
          SetFrameRate(60)
          CreateMesh(0,#PB_Mesh_TriangleList)
          md2Info(handle,#Null,@faces,@indice);<- how to build a mesh
          indice - 1
          While face < faces
            For index = 0 To indice
              md2Mesh(handle,#Null,face,index,0.0,@texel(index),@normal(index),@vertex(index));<- the Blend parameter allows to interpolate the vertex position between the current and next mesh/frame
              MeshVertexPosition(vertex(index)\x,vertex(index)\y,vertex(index)\z)
              MeshVertexNormal(normal(index)\x,normal(index)\y,normal(index)\z)
              MeshVertexTextureCoordinate(texel(index)\s,texel(index)\t)
            Next
            MeshFace(build,build + 1,build + 2)
            build + 3
            face + 1
          Wend
          FinishMesh(#True)
          NormalizeMesh(0)
          UpdateMeshBoundingBox(0)
          CreateMaterial(0,LoadTexture(0,md2Texture))
          SetMaterialColor(0, #PB_Material_AmbientColor, #PB_Material_AmbientColors)
          CreateEntity(0,MeshID(0),MaterialID(0))
          ScaleEntity(0,8,8,8)
          RotateEntity(0,90,0,0,#PB_Absolute)
          CreateCamera(0,0,0,100,100)
          MoveCamera(0,0,0,1000,#PB_Absolute)
          CreateLight(0,RGB(255,255,255),300,600,-100)
          AmbientColor(RGB(200,200,200))
          Repeat
            Repeat
              Select WindowEvent()
                Case #PB_Event_None
                  Break
                Case #PB_Event_CloseWindow
                  exit = #True
              EndSelect
            ForEver
            RotateEntity(0,0,0,1,#PB_Relative)
            RenderWorld()
            FlipBuffers()
          Until exit
        EndIf 
        CloseWindow(0)
      EndIf
    EndIf
    md2Close(handle)
  EndIf
  ProcedureReturn #Null
EndProcedure

Main("md2\doomguy.md2","md2\doomguy.bmp")
;model downloaded from: https://sourceforge.net/projects/zdoomgl/files/zdoomgl%20models/Full%20Md2%20Pack/

End


Example load & animate (no interpolation):

Code: Select all

EnableExplicit

;---------------------------------------------------------------
;Quake 2 (id Tech 2) MD2 (Version 8) model loader for Ogre
;https://github.com/id-Software/Quake-2
;---------------------------------------------------------------
;File: DLL - pb3d_load_and_animate.pb
;---------------------------------------------------------------
;Version: alpha
;Author: Mijikai
;License: GPL 2.0
;---------------------------------------------------------------
;Limitation: no support for MD2 commands!
;---------------------------------------------------------------
;MD2 format limits (for 3d modeling):
;- Skins:     32
;- Triangles: 4096
;- Vertices:  2048
;- Texels:    2048
;- Frames:    512
;---------------------------------------------------------------

XIncludeFile "pb3d_md2.pbi"

Structure MD2_MODEL
  texture.i
  material.i
  count.i
  Array entity.i(0)
EndStructure

Procedure.i md2Model(Handle.i,Texture.s)
  Protected *model.MD2_MODEL
  Protected mesh.i
  Protected meshes.i
  Protected face.i
  Protected faces.i
  Protected index.i
  Protected indice.i
  Protected frame.i
  Protected winding.i
  Protected Dim texel.LDR_MD2_TEXEL(2)
  Protected Dim normal.LDR_MD2_VERTEX(2)
  Protected Dim vertex.LDR_MD2_VERTEX(2)
  With *model
    If Handle And Texture
      *model = AllocateStructure(MD2_MODEL)
      If *model
        \texture = LoadTexture(#PB_Any,Texture)
        If IsTexture(\texture)
          \material = CreateMaterial(#PB_Any,TextureID(\texture))
          If IsMaterial(\material)
            SetMaterialColor(\material,#PB_Material_AmbientColor,#PB_Material_AmbientColors)
            md2Info(Handle,@meshes,@faces,@indice)
            meshes - 1
            faces - 1
            indice - 1
            ReDim \entity(meshes)
            If ArraySize(\entity()) = meshes
              For mesh = 0 To meshes
                frame = CreateMesh(#PB_Any,#PB_Mesh_TriangleList)
                If frame
                  winding = 0
                  For face = 0 To faces
                    For index = 0 To indice
                      md2Mesh(Handle,mesh,face,index,#Null,@texel(index),@normal(index),@vertex(index))
                      MeshVertexPosition(vertex(index)\x,vertex(index)\y,vertex(index)\z)
                      MeshVertexNormal(normal(index)\x,normal(index)\y,normal(index)\z)
                      MeshVertexTextureCoordinate(texel(index)\s,texel(index)\t)
                    Next
                    MeshFace(winding,winding + 1,winding + 2)
                    winding + 3
                  Next
                  FinishMesh(#True)
                  NormalizeMesh(frame)
                  UpdateMeshBoundingBox(frame)
                  \entity(mesh) = CreateEntity(#PB_Any,MeshID(frame),MaterialID(\material))
                  If IsEntity(\entity(mesh))
                    ScaleEntity(\entity(mesh),8,8,8)
                    HideEntity(\entity(mesh),#True)
                    \count + 1
                  Else
                    FreeMesh(frame)
                  EndIf
                EndIf
              Next
              If \count = meshes + 1
                ProcedureReturn *model
              EndIf
              For mesh = 0 To meshes
                If IsEntity(\entity(mesh))
                  frame = GetEntityMesh(\entity(mesh))
                  FreeEntity(\entity(mesh))
                  FreeMesh(frame)
                EndIf
              Next
            EndIf
            FreeMaterial(\material)
          EndIf
          FreeTexture(\texture)
        EndIf
        FreeStructure(*model)
      EndIf
    EndIf
    ProcedureReturn #Null
  EndWith
EndProcedure

Procedure.i Main(md2File.s,md2Texture.s)
  Protected exit.i
  Protected handle.i
  Protected count.i
  Protected index.i
  Protected rot.f
  Protected texture.LDR_MD2_TEXTURE
  Protected animation.LDR_MD2_ANIMATION
  Protected Dim texel.LDR_MD2_TEXEL(2)
  Protected Dim normal.LDR_MD2_VERTEX(2)
  Protected Dim vertex.LDR_MD2_VERTEX(2)
  Protected *model.MD2_MODEL
  handle = md2Open(md2File)
  If handle
    If InitEngine3D() And InitSprite()
       Add3DArchive(GetCurrentDirectory(),#PB_3DArchive_FileSystem)
      If OpenWindow(0,0,0,800,600,#Null$,#PB_Window_SystemMenu|#PB_Window_ScreenCentered)
        If OpenWindowedScreen(WindowID(0),0,0,WindowWidth(0),WindowHeight(0))
          SetFrameRate(60) 
          *model = md2Model(handle,md2Texture)
          CreateCamera(0,0,0,100,100)
          MoveCamera(0,0,0,1000,#PB_Absolute)
          CreateLight(0,RGB(255,255,255),300,600,-100)
          AmbientColor(RGB(200,200,200))
          Repeat
            Repeat
              Select WindowEvent()
                Case #PB_Event_None
                  Break
                Case #PB_Event_CloseWindow
                  exit = #True
              EndSelect
            ForEver
            index + 1
            If index % 6 = 0
              count + 1
              If count >= md2Animation(handle)
                count = 0  
              EndIf
              index = 1
            EndIf
            rot + 1.5
            HideEntity(*model\entity(count),#False)
            RotateEntity(*model\entity(count),90.0,0.0,rot,#PB_Absolute)
            RenderWorld()
            HideEntity(*model\entity(count),#True)
            FlipBuffers()
          Until exit
        EndIf 
        CloseWindow(0)
      EndIf
    EndIf
    md2Close(handle)
  EndIf
  ProcedureReturn #Null
EndProcedure

Main("md2\doomguy.md2","md2\doomguy.bmp")
;model downloaded from: https://sourceforge.net/projects/zdoomgl/files/zdoomgl%20models/Full%20Md2%20Pack/

End

Have fun 8)
Last edited by Mijikai on Mon Feb 20, 2023 10:52 am, edited 4 times in total.
box_80
Enthusiast
Enthusiast
Posts: 115
Joined: Mon Sep 03, 2012 8:52 pm

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by box_80 »

Thanks for sharing. I agree that even a old 3d format can be useful in games. Depending on the game, A lot features of a newer 3d format is not even used. I welcome any tool that would help with PB ogre 3d.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Mijikai »

Thanks @box_80 :)

Added a new code example (load & animate)
Not sure if its ok how i do it, im still a noob when it comes to Ogre.
Hiding & Unhiding the entity seems strage... and there are also Submeshes which i have not looked into yet.
Maybe Ogre even has a way to manage entities which i havent found yet.
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Caronte3D »

Thank you very much!
Maybe we can do easyly 3d models with animation on Blender? Any other tool? :?
I don't have time right now, but it's definitely worth a try when I have free time.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Mijikai »

Yes, its possible with Blender or MilkShape 3D for example just to name two.

Animate a model using Bones:
Image

Make sure to stay within the MD2 limitations.

Limitations:

Code: Select all

;MD2 format limits (for 3d modeling):
;- Skins:     32
;- Triangles: 4096
;- Vertices:  2048
;- Texels:    2048
;- Frames:    512

If the model has too many Triangles for example it is possible to slice the model into seperate parts.
Later the meshes can be combined again.

This can also be really helpful for example if i only want to change a tool on the roboter or add different weapons to a character.
No need to animate and store the rest of the mesh all over again ;)

Example (choppy GIF + animation doesnt use interpolation):
Image

So yes there are limits but there is a workaround for everything.
Note most Editors require at least one Bone (at the feet or the base of the model) even if the model is static!
I belive that the bone = model origin.

Maybe someone more expierenced can look into submeshes withing Ogre.
I have a feeling that the model loading could be done better (and how to proprtly build the mesh with submeshes from several md2 files).

Also note that most MD2 exporters only support triangles and not triangle fans or other more optimized modes.
But since all models will be relatively simple this should be no problem anyway.
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Caronte3D »

Yeah! The limitations are huge, but maybe a good way to load animated models while we found a better (modern?) way to do it.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Mijikai »

Under LoadMesh() in the helpfile there is a link to a converter library which probably supports more formats.
But there is no list of what formats nor what version of them is supported - so trial and error :!:
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Caronte3D »

Looks like a 3dStudioMax (expensive) converter.
On the past, I was try the Blende exporter indicated on the Ogre web site, but no luck.
We needs a easy way to load animated models otherwise the 3d engine in PB is almost useless.
User avatar
Mijikai
Addict
Addict
Posts: 1517
Joined: Sun Sep 11, 2016 2:17 pm

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Mijikai »

I did a quick test with a *.dae Collada file and i could load my model (i did not test texture/material/animation).
Collada is one the most commonly used formats.
Maybe we could explore some formats in more detail and collect them in a seperate thread :)
User avatar
Caronte3D
Addict
Addict
Posts: 1355
Joined: Fri Jan 22, 2016 5:33 pm
Location: Some Universe

Re: [Source] Quake 2 MD2 model loader for OGRE

Post by Caronte3D »

If the import of collada works also with animations, I think we have winner, because Blender and almost every 3D software have a good Collada exporter :D
Post Reply