`jGfx Plugin: GInst Example - Dynamic Batch Rendering
`=======================================================================================
`This example demonstrates the plugin's dynamic instance batching.
`Batch rendering reduces draw call overhead by pooling instance data into a single buffer
`before they are submitted to the graphics pipeline.
`
`Batching allows the plugin to render a scene using one drawcall per-GInst LOD rather
`than one drawcall per-GInstBuffer. This encourages using larger numbers of smaller
`GInstBuffers in order to provide better culling.
`
`Batching is a CPU-side optimization and which will only increase FPS if the application is CPU bound.
`If no performance gains are observed, try enabling VSync and watch CPU usage in the task manager.
`
`To enable batch rendering, call GInst_MakeNew() with the new bDynamicBatching flag set to true.
`
Sw = Desktop Width()
Sh = Desktop Height()
Set Display Mode Sw,Sh,32,0
Sync On : Sync Rate 0
Color Backdrop 0 : Autocam Off
Set Camera Range 10,10000
Position Camera -100, 420, -100
Rotate Camera 25, 45, 0



`Example Settings:
`----------------------------

`Optional Flags (CPU optimization)
bUseBatching = 1		`True if the GInst should be setup to render instances using dynamic batching
bBuildQuadtree = 1		`True if a group-based quadtree should be built for culling

`Instance placement settings (required even if bBuildQuadtree = 0)
WorldSizeX = 5000		`Size of instance placement area
WorldSizeZ = 5000
WorldSubdiv = 5			`Number of times to subdivide instance placement area (2^5 = 32x32 grid = 1024 instance buffers)
Global NumInstances		`Number of instances per buffer (1024 buffers * 5 = 5120 Instances)
NumInstances = 5		`Defined as global for access by example's CreateGInstBuffer() function







`Create a source object which contains 4 levels of detail
ObjectID = 1
Make Object Sphere ObjectID, 50, 12, 24	`LOD0

TmpObj=2 : TmpMesh=1
Make Object Sphere TmpObj, 50, 9, 18	`LOD1
Make Mesh From Object TmpMesh, TmpObj
Delete Object TmpObj
Add Limb ObjectID, 1, TmpMesh
Delete Mesh TmpMesh

Make Object Sphere TmpObj, 50, 6, 12	`LOD2
Make Mesh From Object TmpMesh, TmpObj
Delete Object TmpObj
Add Limb ObjectID, 2, TmpMesh
Delete Mesh TmpMesh

Make Object Sphere TmpObj, 50, 4, 8		`LOD3
Make Mesh From Object TmpMesh, TmpObj
Delete Object TmpObj
Add Limb ObjectID, 3, TmpMesh
Delete Mesh TmpMesh

Convert Object FVF ObjectID, 0x002 || 0x010 || 0x100 `XYZ + NORMAL + TEX1


`Load and apply texture
Load Image "Media\wall1.dds", 1
Texture Object ObjectID, 0, 1

`Load GInst compatible effect
GInstFx = 1
Load Effect "Media\GInst_Diffuse.fx", GInstFx, 0



`Create new GInst using the optional bDynamicBatching flag
pGInst = GInst_MakeNew(bUseBatching)
GInst_SetObject pGInst, ObjectID
GInst_SetEffect pGInst, GInstFx

`Setup LOD
GInst_SetLODLevels pGInst, 4			`4 Levels (0,1,2 and 3)
GInst_SetLODDistance pGInst, 1, 500		`LOD Level 1 begins rendering at 500 units
GInst_SetLODDistance pGInst, 2, 1000	`LOD Level 2 begins rendering at 1000 units
GInst_SetLODDistance pGInst, 3, 1500	`LOD Level 3 begins rendering at 1500 units
GInst_SetMeshLOD pGInst, 1, 1			`Mesh1 belongs to LOD level 1
GInst_SetMeshLOD pGInst, 2, 2			`Mesh2 belongs to LOD level 2
GInst_SetMeshLOD pGInst, 3, 3			`Mesh3 belongs to LOD level 3





`Create GInstBuffers
if bBuildQuadtree
	`Build a group-based quadtree and fill it with GInstBuffers
	QuadTree_Build(pGInst, 0, 0, WorldSizeX, WorldSizeZ, WorldSubdiv)	`Example function
Else
	`Create GInstBuffers individually
	GroupsPerAxis = 2^WorldSubdiv				`number of buffers to create along each axis based on world subdivision
	BufferSizeX# = WorldSizeX/GroupsPerAxis		`size of each buffer
	BufferSizeZ# = WorldSizeZ/GroupsPerAxis
	
	`Create instance groups
	for BufferX=0 to GroupsPerAxis-1
		BufferPosX# = BufferX * BufferSizeX#
		for BufferZ=0 to GroupsPerAxis-1
			BufferPosZ# = BufferZ * BufferSizeZ#
			pGInstBuffer = CreateGInstBuffer(pGInst, BufferPosX#, BufferPosZ#, BufferSizeX#, BufferSizeZ#) `Example function
		Next BufferZ
	Next BufferX
Endif




`TBM setup
TimeSpeed# = 0.5
LastTime = Timer()
Do
	Set Cursor 0,0
	Print "FPS = ",Screen Fps()
	Print "Draw Calls = ",GInstCam_GetDrawCalls(0)
	Print "Draw Instances = ",GInstCam_GetDrawInstances(0)
	Print "Draw Primitives = ",GInstCam_GetDrawPrimitives(0)
	Print "Use the mouse and arrowkeys to control the camera"
	Print "Press W to toggle wireframe"
	
	
	`TBM
	Now=Timer()
	TimePassed# = (Now-LastTime)*TimeSpeed#
	LastTime = Now

	`Camera Control
	Move Camera (UpKey()-DownKey())*TimePassed#
	Move Camera Right (RightKey()-LeftKey())*TimePassed#
	Rotate Camera Wrapvalue(Camera Angle X()+MouseMoveY()),  Wrapvalue(Camera Angle Y()+MouseMoveX()), 0



	if Keystate(17)
		if WHeld=0
			WHeld=1
			bWireframe = 1-bWireframe
			if bWireframe
				Set effect technique GInstFx,"Wireframe"
			else
				Set effect technique GInstFx,"Diffuse"
			endif
		endif
	Else
		WHeld=0
	endif



	Sync
Loop



`Assembles a GInstGroup-based quadtree and fills it with instances
Function QuadTree_Build(pGInst, PosX#, PosZ#, SizeX#, SizeZ#, Levels)
	pRootGroup = GInstGroup_MakeNew()
	QuadTree_SubdivideGroup(pGInst, pRootGroup, PosX#, PosZ#, SizeX#, SizeZ#, Levels-1)
EndFunction pRootGroup

`Subdivides the specified group into 4 quadrants (either child groups or buffers)
Function QuadTree_SubdivideGroup(pGInst, pGInstGroup, PosX#, PosZ#, SizeX#, SizeZ#, Levels)
	ChildSizeX# = SizeX#*0.5
	ChildSizeZ# = SizeZ#*0.5
	If Levels>0
		`make GInstGroup child nodes
		pChildGroup = GInstGroup_MakeNew(pGInstGroup)
		QuadTree_SubdivideGroup(pGInst, pChildGroup, PosX#, PosZ#, ChildSizeX#, ChildSizeZ#, Levels-1)
			
		pChildGroup = GInstGroup_MakeNew(pGInstGroup)
		QuadTree_SubdivideGroup(pGInst, pChildGroup, PosX#+ChildSizeX#, PosZ#, ChildSizeX#, ChildSizeZ#, Levels-1)
			
		pChildGroup = GInstGroup_MakeNew(pGInstGroup)
		QuadTree_SubdivideGroup(pGInst, pChildGroup, PosX#, PosZ#+ChildSizeZ#, ChildSizeX#, ChildSizeZ#, Levels-1)
			
		pChildGroup = GInstGroup_MakeNew(pGInstGroup)
		QuadTree_SubdivideGroup(pGInst, pChildGroup, PosX#+ChildSizeX#, PosZ#+ChildSizeZ#, ChildSizeX#, ChildSizeZ#, Levels-1)
	Else
		`make GInstBuffer leaf nodes
		pGInstBuffer = CreateGInstBuffer(pGInst, PosX#, PosZ#, ChildSizeX#, ChildSizeZ#)
		GInstBuffer_SetGroup pGInstBuffer, pGInstGroup
		
		pGInstBuffer = CreateGInstBuffer(pGInst, PosX#+ChildSizeX#, PosZ#, ChildSizeX#, ChildSizeZ#)
		GInstBuffer_SetGroup pGInstBuffer, pGInstGroup
		
		pGInstBuffer = CreateGInstBuffer(pGInst, PosX#, PosZ#+ChildSizeZ#, ChildSizeX#, ChildSizeZ#)
		GInstBuffer_SetGroup pGInstBuffer, pGInstGroup
		
		pGInstBuffer = CreateGInstBuffer(pGInst,PosX#+ChildSizeX#, PosZ#+ChildSizeZ#, ChildSizeX#, ChildSizeZ#)
		GInstBuffer_SetGroup pGInstBuffer, pGInstGroup
	Endif
EndFunction

//Creates a GInstBuffer and fills it with the number of instances specified by the global varaiable "NumInstances"
Function CreateGInstBuffer(pGInst, PosX#, PosZ#, SizeX#, SizeZ#)
	`Create a GInstBuffer
	pGInstBuffer = GInstBuffer_MakeNew( pGInst, NumInstances )

	`Setup Instances
	For i=1 To NumInstances
		`get random transforms
		InstPosX# = Rnd(SizeX#)+PosX#
		InstPosZ# = Rnd(SizeZ#)+PosZ#
		YAng = Rnd(360)
		Scale# = 100 //(50+Rnd(50))

		GInstBuffer_SetPosition pGInstBuffer, i, InstPosX#, 0, InstPosZ#
		GInstBuffer_SetRotation pGInstBuffer, i, 0, YAng, 0
		GInstBuffer_SetScale pGInstBuffer, i, Scale#

		GInstBuffer_UpdateInstance pGInstBuffer, i
	Next i
	GInstBuffer_Update pGInstBuffer
EndFunction pGInstBuffer

