The '3D Engine' thread

BlitzMax Forums/BlitzMax OpenGL Programming/The '3D Engine' thread

Hi all,

So is anyone working on a 3D Engine?

To my knowledge:
Cyanide is working on one (free AFAIK)
Antony is porting Vivid? (commercial)
Arkon has BasicGL (commercial & Win32 only for now)
BRL will sort something obviously (most likely commercial)
Terrabit sort of has one (I think? :) (free)
Noel Cower started then stopped on one (someone jump start that guy back into action!) (free AFAIK)

Any others in progress?

I'm trying to make one myself, progress is slow as I'm not finding GL that quick to learn, or at least, it doesn't seem as organised as DX and its documentation.

So far I have an entity system like Blitz3Ds, with local/parent/world translation/rotation commands, working hierarchy, a scene graph type render system, render tweening like Blitz3Ds.

The first thing I messed with when I got Max was GLSL / shaders, and I have a module almost ready to drop into this (shaders rock, and Mark agrees! :) Loading and using a shader can be as easy as 3 commands, and set/getting Uniform & Attribute variables in the shader is just as easy:

myShader.SetUI1("myUniformShaderVarName", value)

Anyways, I'm currently learning about multitexturing/meshes & mesh importers/rendering methods.

My target is for something at least of Blitz3Ds calibre (read as *ease of use!*) to start with, something that can be built upon by anyone.

Keep ya posted!
Tom
p.s HunterSD, they found the 11th commandment!! It reads 'Though shalt not free resources by thine self' :P

Hi Tom
How did you learn GLSL? Is there something like the Redbook or NeHe's tutorials?

I don't want to say I'm coding an engine, it's rather playing around a bit with OpenGL.
I've got Stencil shadows working as well as basic lighting, entity / camera / transformation (hey I had to learn about Quaternions) stuff. I'm working on Frustum Culling right now. What I'm aiming at is rather the opposite of your targets: I don't need it to be that easy (don't know whether I'll even release it to the public) but I want it to be as fast as possible.

Maybe we can support each other in this thread. If anybody needs help with Stencils shadows, Quaternions etc. I can try to help or paste some code.

Is there a way to render something faster than building Display Lists?
Would be nice if we had some OpenGL 'pros' helping us Newbies :)

Greetings, Daniel

Edit: A little example code extract:
Mesh = EEMLoader.LoadMesh("Meshes/floor.eeMesh")
Mesh.MergeVertices()
Mesh.UpdateNormals()
Mesh.BuildDisplayList()
Mesh.UpdateDimensions()
Bottom:eeEntity = eeEntity.Create(Mesh)

Bottom.Material = eeMaterial.Create()
Bottom.Material.Shininess = 0.3
Bottom.Material.Roughness = 0.0
Bottom.Material.LoadTexture("Textures/test.bmp")
Bottom.Position(-20,0,-20)


Yeap, I got one in the pipeline.. slow going though, and certainly not in the same league as some of those you mentioned above.

Still, thats all the fun of writing your own engine - improving and adding features as you learn them.

eizdealer: I've not learned the actual language yet, maybe just a little to mess about, I was talking more about how to easily integrate the whole GLSL system, and it's methods of setting & getting variables within the shader (as mark talked about in his latest worklog).

I find GL docs aint written in 'Plain Jane' English :) They seem all over the place to me. Today I've been reading up on extented multitexturing, and how you can (card support permitting) have awesome control of texture blending. The ARB text docs don't explain stuff well, very techy, but now I've got it written down like this, it makes a little more sense to me.

A Texture Unit Type, idea being blending modes are set per texture unit, per surface, rather than per texture (I honestly don't knw if this will turn out to be a good or bad thing performance wise, time will tell!)

Good for advanced blend tweaking. For easier blending, you can always use regular blending, or just call some preset functions that do all this crap for you :)

	Field combineRGB:Int
	Field combineAlpha:Int
	'Defines how RGB & Alpha Inputs are Combined to produce the Output Color/Fragment
	'Accepts:
	' - GL_REPLACE						= Arg0
	' - GL_MODULATE <(Default) for RGB & Alpha		= Arg0 * Arg1
	' - GL_ADD						= Arg0 + Arg1
	' - GL_ADD_SIGNED_ARB					= Arg0 + Arg1 - 0.5
 	' - GL_INTERPOLATE_ARB					= Arg0 * Arg2 + Arg1 * (1-Arg2)
	' - GL_SUBTRACT_ARB					= Arg0 - Arg1


	Field source0RGB:Int
	Field source1RGB:Int
	Field source2RGB:Int
	Field source0Alpha:Int
	Field source1Alpha:Int
	Field source2Alpha:Int
	'Defines the Source Input colors for both RGB & Alpha (Arg0, Arg1 & Arg2)
	'Accepts:
	' - GL_TEXTURE<x>_ARB	<(Default for 0)	= The Texture Fragment Color from 'this' Texture
	' - GL_CONSTANT_ARB	<(Default for 2)	= Takes a Color from glColor(), or a Material Color (not 100% sure on this!)
	' - GL_PRIMARY_COLOR_ARB			= Color Array set with glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, texColorArray[4])
	' - GL_PREVIOUS_ARB	<(Default for 1)	= The Texture Fragment Color from the Previous Texture


	Field operand0RGB:Int
	Field operand1RGB:Int
	Field operand2RGB:Int
	'Operands operate on the Source Colors, non-defaults could be used to Invert Source Inputs 
	'Accepts:
	' - GL_SRC_COLOR			<(Default for 0 and 1)
	' - GL_ONE_MINUS_SRC_COLOR
	' - GL_SRC_ALPHA			<(Default for 2)
	' - GL_ONE_MINUS_SRC_ALPHA


	Field operand0Alpha:Int
	Field operand1Alpha:Int
	Field operand2Alpha:Int	
	'Same as above, but affects Alpha
	'Accepts:
	' - GL_SRC_ALPHA			<(Default for 0, 1 and 2)
	' - GL_ONE_MINUS_SRC_ALPHA


	Field rgbScaleArb:Float
	Field alphaScale:Float
	'The Final output RGB & Alpha values for this Texture Unit can be Multiplied
	'by 1.0 = no effect/default, or 2.0 & 4.0.	Effectively a 'Modulate2x/4x' setting that brightens the texture
	'Accepts:
	' - 1.0
	' - 2.0
	' - 4.0
	
	Function Create:tUnit(unit:Int)
		Local t:tUnit = New tUnit
		
		t.combineRGB		= GL_MODULATE
		t.combineAlpha		= GL_MODULATE
		
		t.source0RGB 		= 33984 + unit
		t.source1RGB		= GL_PREVIOUS_ARB
		t.source2RGB		= GL_CONSTANT_ARB

		t.source0Alpha		= 33984 + unit
		t.source1Alpha		= GL_PREVIOUS_ARB
		t.source2Alpha		= GL_CONSTANT_ARB		

		t.operand0RGB		= GL_SRC_COLOR
		t.operand1RGB		= GL_SRC_COLOR
		t.operand2RGB		= GL_SRC_ALPHA

		t.operand0Alpha	= GL_SRC_ALPHA
		t.operand1Alpha	= GL_SRC_ALPHA
		t.operand2Alpha	= GL_SRC_ALPHA

		t.rgbScaleArb		= 1.0
		t.alphaScale		= 1.0
		
		Return t
	End function


Pretty understandable right? If not, go look up the ARB docs on gl_arb_tex_env_combine....come back, and say 'yup...' :)

Tom

What would be the best and fastest way to render a terrain without LOD whose tiles are given as well as the view frustum planes and the camera position / range?

I thought about doing some kind of Quadtree, then putting all the data of the visible Quads into an array and finally rendering it with glDrawArrays or a command alike. But I guess there would be a lot of overhead when compiling from visible Quads to the array. Is there a better way to do it?
Maybe just checking rather large Quads (e.g. 16x16 tiles) that are close to the camera and not subdivide, just draw them?
The camera position would be similar to a top-down RTS game, if this may help. How do 'professional' RTS games do the culling?

Is this a 3D rts game? Seems to me you'd want an octree or BSP tree if its in 3D, or you'll be missing a dimension :D

I think BSP is supposed to be more optimal than an octree, but I couldn't get my head around how to implement it when I first looked at it - maybe something for me to go back to at some point. If you can implement a quadtree, I'm sure you'd have no problem with an octree, just add in an extra 4 branches and use a set minimum number of polygons for subdivision.

Dunno if I've helped any there, so let me know if I have, or if I'm just talking jibberish :D

regarding the term Quads, in B3D talk that would be 2 triangles, but of course in GL you can render real quad polygons.

I'm just experimenting with vertex arrays and vertex buffer objects now. But I did ask some guys about whether using GL_QUADS was good or bad, and I was told that GL is optimized for using GL_TRIANGLES.

Just something to bear in mind when rendering.

I switched to quads in my app and have not noticed a difference. Maybe the driver does the conversion but at least it's simpler for me to work with quads for the most part. One thing to watch out for though is non-planer quads, ie, verts of a triangle form a plane but the same may not be true of a quad. Though not a prob if driver do actual split quads into tri's as part of optimisation.

Havn't read the discussion but triangle strips are the most optimized rendering method via GL.

(someone jump start that guy back into action!)


I'm working on another, just no API abstraction this time around. It's too much of a hassle in BlitzMax.

Not gonna do anything particularly advanced either- that would be major overkill.

I'm using ODE, which has been *so* useful and not just
for dynamic physics!

The collision system (one thing I didnt want the nightmare
of coding) is absolutly top notch.

having geoms about that aren't involved in physics, but
do collide, seems to be quite efficient for static mesh's

Theres some useful math helper routines too

Short of terrain culling and special graphics effects
it leaves you with a lot of the tedious work done...

It would be really nice to see a community project along
these lines, but I fear that such an engine might get
so overloaded with features that it will crawl.

Whats needed is somthing simple and lightweight...

$0.02

Tom:
Could you give a small example of how to use and integrate GLSL? I have absolutely no idea of GLSL.
What are Vertex Buffers and how can you use them in BMax?

Kalakian:
I think a QuadTree should be enough for a 3D RTS, because when a tile is visible, usually all objects over this tile are visible as well. I was just wondering about whether it's fast enough to compile visible Quads (by the way I was talking about 'Quads' as part of a Quadtree, nothing you could render) into an array.
What exactly is the advantage of a BSP Tree compared to a QuadTree?


Does anyone know whether glVertex3s is faster than glVertex3f because theoretically you send only half of the Bytes to the graphics card. Of course the Fill Rate is not faster but maybe you could save some nano seconds this way 8)

Yeah, my mistake, I was just thinking generic 3D game there, so no real need for an octree instead of a quadtree.

As for the advantage of using a BSP, I'm not entirely sure. I know its supposed to be more efficient than an octree, but I don't really know how it would compare to a quadtree.

I think your idea of using the quadtree to decide what tiles of the terrain were to be rendered should be efficient enough for an RTS. I would suggest subdivision of the quads though if you're going to draw everything above the tile, but the advantages of this would really depend on what camera angles you would allow. If, as you implied in your earlier post, the camera was static semi-topdown, I think just using an array to store the tiles would probably be optimal. Then, you could simply use the distance formula to check if the tile is within a certain radius away from the point where the camera's lookat vector crosses the terrain. If it is, render it, if not, don't

Thats my opinion anyway, whether its useful or not is an entirely different matter ;)

I had started one based on Terrabits code. I added more blitz3d compatible commands but got stuck on parenting and such, so decided it was too advanced for me and gave up... :/

oh yeah, forgot about this thread. I posted a little showcase entry with some early trinity blizmax engine video clips at:

http://www.blitzbasic.com/Community/posts.php?topic=43408

pretty early but I thought you guys might be interested to see how trinity is coming along.

Hey there again!
I got stuck with the terrain, when it comes to Frustum Culling. Actually it works well, but there are some tiles that don't get clipped although it's obvious that they're out of the view frustum.
I think it has something to do with the formula I'm using for detection whether a tile is in view or not. It's the 'common' CubeInFrustum formula that is described in all Frustum Culling tutorials. It checks whether all points of a cube are behind one of the clipping planes. If so, it can't be visible. But in some cases this detection failes. Usually this should be no problem at all, but in this case it's quite a lot tiles that would be drawn this way.
A little screenshot (blue = as visible detected tiles, yellow = eye), should be self-explaining:
[This was an image]
Any ideas?

Edit:
OK, I got it working now. The problem was in the CubeInFrustum formula - I had to add some stuff (and that was wrong *g*).

OK, new post new problem.
The Quadtree works very well now, it lists Quads that are inside the view volume. Each of these Quads is made up of several terrain tiles. I'm looping through every Quad and then through every terrain tile of that Quad to render the tile with glbegin etc. right now (just to see whether it works). The problem with that is of course the speed (although it's resulting in ~400 FPS with a relatively large terrain, which is surprisingly fast).

My first idea was to use Vertex Arrays of course - compile all terrain tiles into an array, then draw that array. This should work OK with one terrain texture. But what if you want to have the possibility to have different textures on the terrain? How could you avoid the sharp edges between different textures and let them fade out smoothly?

I think a good solution is to assign a texture to each terrain vertex and then blend the texture over the tile, if the tile's other vertices have different textures. A little example picture, these are two terrain tiles, the circles at the edge vertices show the 'texture' (in this case it's just a color), the first tile has blending enabled, because its vertices have different textures, the second tile has blending disabled:


To get to the actual problem: How do I combine this blending technique and the terrain rendering efficiently?
I thought a good way was pushing all vertex coordinates, vertex texture coordinates and vertex normals to the graphics card using glVertexPointer etc., saving indices and then figuring out which indices need to be drawn for each texture. First without blending (solid base tiles so to say) then blended tiles. But what's the fastest way to do it? I've been trying for hours now with different approaches but I just don't get it working. Couldn't find a tutorial that covers this either. The tutorials always explain how a Quadtree works and stuff but never this part :/

Anybody has any ideas? Or does this sound completely stupid (English is not my native language and I often have problems to express myself about complex coding stuff like this even in my native language...)?
Thank you for reading by the way ;)

Any news from the front?

My one was a 'Get you started' one that doesn't work too well on the Mac yet.

Anyone wanting to play can get it Here

Works lovely on the PC, needs a Mac man to look at why it doesn't *quite* work on the Mac.

Thanks TeraBit it was great help im currently working on my own i can't wait for mark any more any one know where i can finde something about opengl terrain more then nehe ?

thanks - how about under linux on x86?

Well i have played around a lot with opengl now and i found it great like people woulde really need to start a opengl3d engine prodject like where we all start at the base and add more and more a open source prodject like
some one write the function start!
ex

InitGL()
positioncam()

well this is just stuppid idea but i coulde be great or every one coulde add more to Terabits engine
i have tryide but can't really finde out how to add key strokes so you can move camera!

Smurfpuss: NO. No more community projects! Save us from the community projects!

On another note, how do you feel about me writing tutorials on engine programming for BlitzMax in the Blitz Newsletter?

well sorry!
and no you don't need to write a tutorial on engine programming in the blitz Newsletter

Oki it was a bad idea so you don't need to be rude
and i will spare every one the talk about community prodjects!

:(

Like the unfriendly respons on this board is just bad
like not every one is great coders but we are trying to learn

The idea wasn't that bad. If it happens spontanously, why not. I've seen a lot of spontanous Community projects that have been a success. Like Alien Breed or FLE.

The fastest way of rendering is definitly not just triangle strips...

You can further optimize a series of gl calls to create an object by creating display lists. These basically run all your gl commands through the driver and store the raw output so that all you need to do is passthis data to the card, skipping the translation phase. This only works well with static geo.

Nearly as fast as display lists, but less versatile in some ways are Vertex arrays. The primary benifit is you can modify vertices very quickly. You create a few arrays storing position, color, uvs, etc, and pass pointers to them to gl. then you can modify the data in them, whatever each frame, and tell gl to bulk render the arrays. Still has to translate the instructions to hardware, but since its done in bulk its a ton better than calling a function for every vertex. A further enhancment is to use an index array which can decrease the amount of data sent by sharing vertices among the triangles, beating the number of shared vertices of strips or fans.

Using extensions it should also be possible to have vertex bufffers, basically arrays stored on the graphics card. In other words, crazy speed :)

Er, wrong thread? Ignore this.

Hmmm OK I see that OPenGL will be quite useful but I need to ask an ignorant question... Which engine will be DX9+ for BMAX??? Will any??? Are we (as a group) turning our backs on DX??? Did I miss a memo???

Boy do I know how to kill a thread or what???

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.