Sure do.
Snipped from my engine...
'#Region Copyright and license
' CEngine - 3D engine for graphics and game development
' Copyright 2005 Noel R. Cower
'
' This library is free software; you can redistribute it and/or
' modify it under the terms of the GNU Lesser General Public
' License as published by the Free Software Foundation; either
' version 2.1 of the License, or (at your option) any later version.
'
' This library is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY; without even the implied warranty of
' MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
' Lesser General Public License for more details.
'
' You should have received a copy of the GNU Lesser General Public
' License along with this library; if not, write to the Free Software
' Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
'
' To contact the author, e-mail him at noel_cower@... and/or hooker.with.a.penis@...
'#End Region
Strict
Module CEngine.EngineCore
ModuleInfo "CEngine Graphics-related classes"
ModuleInfo "CEngine is © 2005 Noel R. Cower"
Import CEngine.Math
Import Brl.FileSystem
Import Brl.LinkedList
Import Brl.Pixmap
Import Brl.StandardIO
Import Brl.System
Public
Global CEngine_Global_Device:CDevice = Null
' Primitive type enumerators
Type PrimitiveType Final
Const Triangles = $F000
Const TriangleFan = $F001
Const TriangleStrip = $F002
Const Quads = $F003
Const Points = $F004
Const Lines = $F005
'' Wireframe rendering is likely to be slower than the average render due to extra function calls- index buffers are REQUIRED.
Const TriWireframe = $F006 ' Triangle wireframe- renders a wireframe mesh with triangles, this is likely to be slower than normal drawing with most devices
Const QuadWireframe = $F007 ' Quad wireframe- renders a wireframe mesh with quads, this is likely to be slower than normal drawing with most devices
End Type
Type CDevice Abstract
Field Indices:Int[]
Field Vertices:CVertex[]
Field Material:CMaterial
Field Width:Int,Height:Int
Method SetupGraphics(IWidth:Int,IHeight:Int,BPP:Int=32,Hz:Int=0) Abstract
Method ClearColor(R#,G#,B#,A#) Abstract
Method SetViewport(X%,Y%,Width%,Height%) Abstract
Method GetViewport(X:Int Ptr, Y:Int Ptr, _Width:Int Ptr, _Height:Int Ptr) Abstract
Method DrawPrimitives(PrimitiveType:Int) Abstract
Method SetVertexData(Indices:Int[],Vertices:CVertex[]) Abstract
Method Clear(Enum:Int = CBuffers.Color | CBuffers.Depth) Abstract
Method Present() Abstract
Method SetWorldMatrix(mat:CMatrix) Abstract
Method SetViewMatrix(mat:CMatrix) Abstract
Method SetProjectionMatrix(mat:CMatrix) Abstract
Method GetWorldMatrix:CMatrix() Abstract
Method GetViewMatrix:CMatrix() Abstract
Method GetProjectionMatrix:CMatrix() Abstract
Method FreeObjectResources(i:Object) Abstract
Method LoadObjectResources(i:Object) Abstract
Method LoadVertexShader:CVertexShader(path$) Abstract
Method LoadPixelShader:CPixelShader(path$) Abstract
Method BindShader(i:CShader) Abstract
Method BindTexture(i:CTexture) Abstract
Method SetActiveDevice()
CEngine_Global_Device = Self
End Method
Function GetActiveDevice:CDevice()
Return CEngine_Global_Device
End Function
End Type
Type CBuffers
Const Color = 1 ' Color buffer (back buffer- front buffer access not allowed)
Const Depth = 2 ' Depth buffer
Const Stencil = 4 ' Stencil buffer
End Type
Type CVertex
Field X#,Y#,Z#
Field NX#,NY#,NZ#
Field U0#,V0#
Field Red:Float
Field Green:Float
Field Blue:Float
Field Alpha:Float
Function Create:CVertex(X#=0,Y#=0,Z#=0,U#=0,V#=0,R#=1,G#=1,B#=1,A#=1)
Local vert:CVertex = New CVertex
vert.X = X
vert.Y = Y
vert.Z = Z
vert.U0 = U
vert.V0 = V
vert.Red = R
vert.Green = G
vert.Blue = B
vert.Alpha = A
Return vert
End Function
End Type
Type CAttributeTable
Field VertexStart:Int
Field VertexCount:Int
Field TriangleStart:Int
Field TriangleCount:Int
Field Material:CMaterial
End Type
Type BlendMode Final
Const None = 0
Const Add = 1
Const Multiply = 2
Const Alpha = 3
Const Modulate2X = 4
Const Modulate4X = 5
End Type
Type CMaterial
Field Specularity:Float ' Specularity value
Field Glossiness:Float ' Glossiness value
Field Wireframe:Int ' Wireframe toggle
Field Blend:Int
Field Maps:TList ' List of textures
End Type
Type CShader
Global CShaders:TList
Field _link:TLink
Field VertexShader:CVertexShader
Field PixelShader:CPixelShader
Method New()
_link = CShaders.AddLast(Self)
End Method
Method LoadVertexShader(path$)
VertexShader = CDevice.GetActiveDevice().LoadVertexShader(path$)
End Method
Method LoadPixelShader(path$)
PixelShader = CDevice.GetActiveDevice().LoadPixelShader(path$)
End Method
Method Bind()
CDevice.GetActiveDevice().BindShader(Self)
End Method
Method Free()
RemoveLink _link
CDevice.GetActiveDevice().FreeObjectResources(Self)
' Then you have to do the rest
End Method
Method ToString$()
Return "Shader"
End Method
End Type
CShader.CShaders = New TList
' Prototype.. er.. types for shaders
Type CVertexShader Abstract
End Type
Type CPixelShader Abstract
End Type
' Chunks are organized as CHUNKID, LENGTH (of DATA), DATA
Type ChunkID3DM
Const Header = $1000
Const HeaderAuthor = $1001 ' Null-delimited string containing the author's name and/or copyright information
Const HeaderVersion = $1002 ' 32-bit single precision float representing the version of the model format
Const Materials = $2000
Const Material = $2001
Const MaterialColor = $2002 ' 4 bytes representing color information
Const MaterialSpecular = $2003 ' 32-bit single precision float
Const MaterialGlossiness = $2004 ' 32-bit single precision float
Const MaterialWireframe = $2005 ' 1 byte- either 1 or 0
Const MaterialBlend = $2006
Const MaterialMaps = $2007
Const TextureMap = $2008
Const TexturePath = $2009 ' Null-delimited string containing the path of the texture
Const TextureFlags = $200A ' 32-bit integer containing OR'd flags
Const TextureData = $200B ' Binary texture data- not always saved; data is stored as WIDTH, HEIGHT, COLORS (4 bytes per pixel, going from left to right, top to bottom)
Const Models = $3000
Const Model = $3001
Const ModelColor = $3002 ' 4 bytes representing the entity's color
Const ModelMaterialIndex = $3003
Const ModelVertices = $3004 ' Amount of vertices, 32-bit integer
Const ModelVertexData = $3005 ' Vertex information, first four bytes is an OR'd list of what information is to be read, the rest is the data
Const ModelTriangles = $3006 ' Amount of triangles (3*MT = Indices) 32-bit integer
Const ModelTriangleData = $3007 ' Triangle index information, the length of this chunk is always the data from ModelTriangles*12- each index is a 32-bit integer
Const ModelAttribTables = $3008
Const ModelAttribTable = $3009
Const ModelAttribVertexStart = $300A
Const ModelAttribVertexRange = $300B
Const ModelAttribTriangleStart = $300C
Const ModelAttribTriangleRange = $300D
' OR'd flags that designate what data is read inside of the ModelVertexData chunk
Const VertexDataPosition = 1
Const VertexDataTexCoords = 2
Const VertexDataNormals = 4
Const VertexDataColors = 8
End Type
' EntityFX enumerations
Type EntityFX Final
Const None = 0 ' Do nothing
Const FullBright = 1 ' Disable lighting
Const VertexColors = 2 ' Use vertex coloring
Const Faceted = 4 ' Draw triangles without smooth shading
Const DisableFog = 8 ' Disable fog
Const DisableCulling = 16 ' Disable backface culling
Const ForceAlpha = 32 ' Force alpha blending
End Type
' EntityBlend enumerations
Type EntityBlend Final
Const None = 0
Const Add = 1
Const Alpha = 2
Const Multiply = 3
Const Modulate2X = 4
Const Modulate4X = 5
End Type
Type CEntity
Global CEntities:TList
Field _lnk:TLink ' Link to the object in the entity list, Private data
Field _par:CEntity ' Parent entity- private
Field _chl:TList ' List of children entities- private
Field _ind:Double ' Index/identifier
Method New()
_lnk = ListAddLast(CEntities,Self)
_chl = CreateList()
_ind = MilliSecs()
End Method
Function GetEntity:CEntity(ind:Double)
For Local e:CEntity = EachIn CEntity.CEntities
If e._ind = ind Then Return e
Next
Return Null
End Function
Method Delete()
RemoveLink(_lnk)
End Method
Field X:Float,Y:Float,Z:Float
Field Pitch:Float,Yaw:Float,Roll:Float
Field ScaleX:Float=1,ScaleY:Float=1,ScaleZ:Float=1
Field Hidden:Int
Field A:Byte,R:Byte,G:Byte,B:Byte
Field FX%
Method Position(IX#,IY#,IZ#)
X = IX
Y = IY
Z = IZ
End Method
Method Move(IX#,IY#,IZ#)
Local mat:CMatrix = New CMatrix
Local mat2:CMatrix = New CMatrix
Local mat3:CMatrix = New CMatrix
mat.Rotate(Pitch,Yaw,Roll)
Local nm:CVector = CVector.Create(IX,IY,IZ)
mat.MultiplyVector(nm)
X :+ nm.X
Y :+ nm.Y
Z :+ nm.Z
End Method
Method Translate(IX#,IY#,IZ#)
X :+ IX
Y :+ IY
Z :+ IZ
End Method
Method Rotate(IPitch#,IYaw#,IRoll#)
Pitch = IPitch
Yaw = IYaw
Roll = IRoll
End Method
Method Turn(IPitch#,IYaw#,IRoll#)
Pitch :+ IPitch
Yaw :+ IYaw
Roll :+ IRoll
End Method
Method Compare(other:Object)
Local ent:CEntity = CEntity(other)
If ent <> Null
Local priA:Int = Self.Priority()
Local priB:Int = ent.Priority()
If priA > priB Return 1
If priA = priB Return 0
If priA < priB Return -1
EndIf
Throw "Failed to compare objects: other:Object ("+other.ToString()+") is not an entity"
End Method
' When implementing new entities, they should always be 3 and above
' Priority indicates an entity's rendering priority, in other words what stage
' it will be rendered at. Lights are 0, cameras are 1, and meshes are 2.
Method Priority() Abstract
Method Draw() Abstract
End Type
CEntity.CEntities = CreateList()
Type CMesh Extends CEntity
Field VertexData:CVertex[] ' Vertices used in rendering
Field TriangleIndices:Int[] ' Triangle indices used in rendering
Field TriangleCount:Int
Field Attributes:CAttributeTable[] ' Mimicing the DirectX AttributeTable means of rendering
Field _lock:Int = 0
Function FromFile:CMesh(Path$)
Local ext$
Local i = Path.FindLast(".")
ext = Path[i+1..Path.Length]
ext.ToLower()
If ext$ = "" Then
Print "Unable to get file extension"
Return
ElseIf ext$ = "obj"
Return CMesh.FromObj(path)
ElseIf ext$ = "3dm"
Return CMesh.From3DM(path)
EndIf
End Function
' Load an ASCII Obj file- binary Obj files are not supported
Function FromOBJ:CMesh(Path:String)
Local ins:TStream = ReadFile(Path)
Local triangles:Int = 0
Local vertices:Int = 0
If ins Then
Local m:CMesh = New CMesh
Local vertList:TList = CreateList()
Local triList:Int[]
Local vert:CVertex = Null
Local vt=0,vn=0
While Not Eof(ins)
Local l$ = ReadLine(ins)
Local lst:String[] = SplitString(l," ")
Local key:String = lst[0]
Select key
Case "v"
vertices :+ 1
vert = New CVertex
vert.x = Float(lst[1])
vert.y = Float(lst[2])
vert.z = Float(lst[3])
vert.red = 1
vert.green = 1
vert.blue = 1
vert.alpha = 1
ListAddLast(vertList,vert)
Case "vt"
vert = CVertex(vertList.ValueAtIndex(vt))
vert.u0 = Float(lst[1])
vert.v0 = Float(lst[2])
vt :+ 1
Case "vn"
vert = CVertex(vertList.ValueAtIndex(vn))
vn:+1
vert.nx = Float(lst[1])
vert.ny = Float(lst[2])
vert.nz = Float(lst[3])
vert.red = vert.nx*.5 + .5
vert.green = vert.ny*.5 + .5
vert.blue = vert.nz*.5 + .5
Case "f"
Local s1$ = SplitString(lst[1],"/")[0]
Local s2$ = SplitString(lst[2],"/")[0]
Local s3$ = SplitString(lst[3],"/")[0]
triList = triList[..triList.Length+3]
triList[triangles*3] = s1.ToInt()-1
triList[triangles*3+1] = s2.ToInt()-1
triList[triangles*3+2] = s3.ToInt()-1
triangles :+ 1
End Select
lst = New String[0]
FlushMem
Wend
m.VertexData = CVertex[](ListToArray(vertList))
m.TriangleIndices = triList
vertList = Null
triList = Null
CloseFile ins
CDevice.GetActiveDevice().LoadObjectResources(m)
FlushMem
Return m
EndIf
Print "Failed to open file ~q"+Path+"~q~n"
Return Null
End Function
Method ToString:String()
Return "Mesh"
End Method
Method Priority()
Return 2
End Method
Method Draw()
If VertexData.Length = 0 Or _lock > 0 Then Return
setupMatrices()
Local Device:CDevice = CDevice.GetActiveDevice()
Device.SetVertexData(TriangleIndices,VertexData)
Device.DrawPrimitives(PrimitiveType.Triangles)
End Method
Method setupMatrices()
Local wmat:CMatrix = New CMatrix
wmat.Rotation(Pitch,Yaw,Roll)
wmat.Scale(ScaleX,ScaleY,ScaleZ)
wmat.Translate(X,Y,Z)
Local Device:CDevice = CDevice.GetActiveDevice()
Device.SetWorldMatrix(wmat)
End Method
Method AddVertex(X#=0,Y#=0,Z#=0,U#=0,V#=0)
If _lock = 0 Then Throw "Mesh vertex data must be locked before it can be modified"
VertexData = VertexData[..VertexData.Length+1]
Local vert:CVertex = New CVertex
VertexData[VertexData.Length-1] = vert
vert.X = X
vert.Y = Y
vert.Z = Z
vert.U0 = U
vert.V0 = V
Return VertexData.Length-1
End Method
Method VertexCoords(Index,X#,Y#,Z#)
If _lock = 0 Then Throw "Mesh vertex data must be locked before it can be modified"
Assert Index < VertexData.Length, "Vertex index out of range"
Local vert:CVertex = VertexData[Index]
vert.X = X
vert.Y = Y
vert.Z = Z
End Method
Method VertexTexCoords(Index,U#,V#)
If _lock = 0 Then Throw "Mesh vertex data must be locked before it can be modified"
Assert Index < VertexData.Length, "Vertex index out of range"
Local vert:CVertex = VertexData[Index]
vert.U0 = U
vert.V0 = V
End Method
Method VertexNormals(Index,NX#,NY#,NZ#)
If _lock = 0 Then Throw "Mesh vertex data must be locked before it can be modified"
Assert Index < VertexData.Length, "Vertex index out of range"
Local vert:CVertex = VertexData[Index]
vert.NX = NX
vert.NY = NY
vert.NZ = NZ
End Method
Method VertexColor(Index,R:Float=1,G:Float=1,B:Float=1,A:Float=1)
If _lock = 0 Then Throw "Mesh vertex data must be locked before it can be modified"
Assert Index < VertexData.Length, "Vertex index out of range"
Local vert:CVertex = VertexData[Index]
vert.Red = R
vert.Green = G
vert.Blue = B
vert.Alpha = A
End Method
Method Lock()
_lock :+ 1
If _lock = 1 Then CDevice.GetActiveDevice().FreeObjectResources(Self)
End Method
Method Unlock()
Assert _lock > 0, "Mesh is already unlocked"
_lock :- 1
If _lock = 0 Then CDevice.GetActiveDevice().LoadObjectResources(Self)
End Method
Method AddTriangle(IDX_A,IDX_B,IDX_C)
Local tris = TriangleIndices.Length
TriangleIndices = TriangleIndices[..tris+3]
TriangleIndices[tris] = IDX_A
TriangleIndices[tris+1] = IDX_B
TriangleIndices[tris+2] = IDX_C
TriangleCount = TriangleCount + 1
Return TriangleCount-1
End Method
' Unless you absolutely need to, don't change the VertexData argument
Method To3DM(Path$,Author$="",SaveBinaryTextures=0,VertexData=ChunkID3DM.VertexDataPosition|ChunkID3DM.VertexDataTexCoords|ChunkID3DM.VertexDataNormals|ChunkID3DM.VertexDataColors)
Local out:TStream = WriteFile(Path$)
If out Then
Local i:Int
Local chunkPos:CStack
out.WriteInt ChunkID3DM.Header
out.WriteInt ChunkID3DM.HeaderAuthor
Return
EndIf
Print "Failed to write to ~q"+path+"~q~n"
End Method
Function From3DM:CMesh(Path$)
End Function
End Type
Type CCamera Extends CEntity
Method ToString:String()
Return "Camera"
End Method
Method Draw()
End Method
Method Priority()
Return 1
End Method
End Type
' Normal point light
Type CLight Extends CEntity
Field Range:Float = 1
Field Attenuation:Float = 1
Field SpecR:Byte = 0
Field SpecG:Byte = 0
Field SpecB:Byte = 0
Function Create:CLight()
Local l:CLight = New CLight
Return l
End Function
Method Draw()
End Method
Method ToString:String()
Return "Light"
End Method
Method Priority()
Return 0
End Method
End Type
' Basically, a blank type- that's really all Blitz's pivot does
Type CPivot Extends CEntity
Method Draw()
End Method
Method Priority()
Return 3
End Method
End Type
Type CTexture
Field Width
Field Height
Field ScaleU#=1
Field ScaleV#=1
Field PositionU#
Field PositionV#
Field Rotation#
Field Blend%=0
Field Mipmaps%=1
Field _lock ' Don't modify this
Field _pix:TPixmap ' Pixmax
Method New()
End Method
Method Delete()
CDevice.GetActiveDevice().FreeObjectResources(Self)
End Method
Function Create:CTexture(Width,Height)
Local t:CTexture = New CTexture
t._pix = TPixmap.Create(Width,Height,PF_BGRA8888)
t.Width = Width
t.Height = Height
End Function
' Loads a texture according to its extension
Function FromFile:CTexture(Path$)
Local this:CTexture = New CTexture
this._pix = LoadPixmap(Path$)
this._lock = False
this.Mipmaps = 1
this.Blend = 0
this.Rotation = 0
If this._pix = Null Then
this = Null
FlushMem
Return Null
EndIf
this.Width = this._pix.Width
this.Height = this._pix.Height
CDevice.GetActiveDevice().LoadObjectResources(this)
Return this
End Function
Method LockTexture()
_lock :+ 1
If _lock > 1 Then Return
Local dev:CDevice = CDevice.GetActiveDevice()
' Free the texture while it's being modified
dev.FreeObjectResources(Self)
End Method
Method UnlockTexture()
If _lock = 0 Then Throw "Texture is already unlocked"
_lock :- 1
' If it's still locked after unlocking, don't recreate the object resources
If _lock > 0 Then Return
Local dev:CDevice = CDevice.GetActiveDevice()
' Reload the texture
dev.LoadObjectResources(Self)
End Method
Method WritePixel(X%,Y%,Color%)
_pix.WritePixel(X,Y,Color)
If _lock = 0 Then
Local dev:CDevice = CDevice.GetActiveDevice()
dev.FreeObjectResources(Self)
dev.LoadObjectResources(Self)
EndIf
End Method
Method ReadPixel(X,Y)
Return _pix.ReadPixel(X,Y)
End Method
End Type
Type CAnimTexture Extends CTexture
Field _frames:CTexture[]
Function AnimFromFile(Path$,FWidth%,FHeight%,FCount%)
Local this:CTexture = New CTexture
this._pix = LoadPixmap(Path$)
this._lock = False
this.Mipmaps = 1
this.Blend = 0
this.Rotation = 0
If this._pix = Null Then
this = Null
FlushMem
Return Null
EndIf
this.Width = this._pix.Width
this.Height = this._pix.Height
this._frames=this._frames[..FCount]
For Local i:Int = 0 To FCount-1
'Allocate frames (TPixmap.Window..)
Next
End Function
End Type
That comprises the core of the engine. Needs redesigning so the core has no association with entities though.