;VividGL 3D Lite.
;
;(c)Antony Wells 2004.


Const oct_maxnodes=255 
Const oct_maxsurfaces=255 
Const oct_maxlodlevels=255
                   
Type bound
	Field x#,y#,z#,cx#,cy#,cz#,bsize#
	Field w#,h#,d#,size#,radius#,btype
End Type

Type octsurf
	Field srf.sdat,ent.ent,tc,ec
	Field ctris[oct_maxlodlevels],tric,lodc
	Field tribuf[oct_maxlodlevels]
End Type

Type octnode
	Field child.octnode[oct_maxnodes],nodes
	Field devides,vertc
	Field box.bound,tc,ec
	Field nodetype,hasgeo,geolist
	Field srf.octsurf[oct_maxsurfaces],srfc
End Type

Type octbatch
	Field box.bound
	Field srf.octsurf[oct_maxsurfaces],srfc
End Type

Const node_static=1,node_dynamic=2 

Type octtree
	Field root.octnode
	Field devides
End Type

Function Oct_NewTree(entity,subdevides=4) 
	out.octtree=New octtree
	out\root=oct_newnode()
	If subdevides<1 RuntimeError "octal - devide by zero"
	oct_addnodeentity(entity,Handle(out\root),subdevides)
	Return Handle(out)
End Function

Function oct_addnodeentity(entity,oct_node,devides=6)
	oct.octnode=Object.octnode(oct_node)
	in.ent=Object.ent(entity)
	bound_toentity(oct\box,entity)
	oct_gennode(oct,in,devides) 
End Function

Function Oct_RenderTree(octtree) 
	in.octtree=Object.octtree(octtree)
	setupscenecam()
	GetFrustum()
	oct_rendernode(in\root,True)
	If shadersareonnow glshadersoff()
End Function

Global shadersareonnow

Function oct_rendernode(node.octnode,seen=1)
	If seen=1
		box.bound=node\box
		seen=CubeFrustumState(box\cx,box\cy,box\cz,box\bsize) ;Frustraum culling. All done C++ side, so not alterable.sorry.
	EndIf
	If seen>0
		If node\hasgeo=True
			j=node\srfc
			While(j>0)
					j=j-1
					srf.octsurf=node\srf[j]
					setupscene(srf\ent,srf\srf)
					setupsurfaceswithtris(srf\srf,srf\ent,srf\ctris[0],srf\tribuf[0])
					surf.sdat=srf\srf
					eh=Handle(srf\ent)
					If surf\fx[eh]<>Null
					      skinprofile(surf\fx[eh])
					    For so=0 To surf\fx[eh]\sc-1
					      	glshaderon2(surf\fx[eh]\sp[so])
					    	shadersareonnow=True
					   Next
				Else If lockedfx<>Null
					skinprofile(lockedfx)
					For so=0 To lockedfx\sc-1
						glshaderon2(lockedfx\sp[so])
						shadersareonnow=True
					Next
				Else
					If shadersareonnow
						 glshadersoff():shadersareonnow=False
					EndIf
				EndIf
				glDrawElements2(gl_triangles,srf\ec,gl_unsigned_int,0)
			Wend
		Else
			j=node\nodes
			While(j>0)
				j=j-1
			   oct_rendernode(node\child[j],seen)
			Wend
		EndIf
	EndIf
End Function

Function oct_newnodesurface(in.octnode,tris,sdat.sdat,ent.ent,tribuf)
	If BankSize(tris)<12 Return
	in\srf[in\srfc]=New octsurf
	in\srf[in\srfc]\ctris[0]=tris
	in\srf[in\srfc]\srf=sdat
	in\srf[in\srfc]\ent=ent
	in\srf[in\srfc]\tribuf[0]=tribuf
	in\srf[in\srfc]\tc=BankSize(tris)/12
	in\srf[in\srfc]\ec=(BankSize(tris)/12)*3
	in\srfc=in\srfc+1
	;debuglog "tris>"+banksize(tris)/12
End Function

Function oct_compilenode(in.octnode,ent.ent,istop=False)
Local surfs[oct_maxsurfaces]
		sc=oct_gentris(ent,in,surfs)
		If in\nodes=0
		For j=0 To ent\sc-1
			If BankSize(surfs[j])>0
				id=glcompiletris(surfs[j],Surface_Vertexobjects,glstatic)
				oct_newnodesurface(in,surfs[j],ent\sdat[j],ent,id)
			EndIf
		Next
		cd=CreateCube()
		FitMesh cd,in\box\x,in\box\y,in\box\z,in\box\size,in\box\size,in\box\size
		cd=Entity_ConvB3D(cd)
		Entity_Compile (cd,Compile_Static)
		Entity_ColorMode cd,ColorMode_Color
		Entity_CullMode cd,ColorMode_Vertex
		Entity_Color cd,200,200,200
		Entity_WireFrame(cd,True)
		Batch_AddEntity(demobatch,cd)
		in\hasgeo=True
		in\nodes=0
	Else
		For j=0 To in\nodes-1
			oct_compilenode(in\child[j],ent,False)
		Next
	EndIf
End Function

Function oct_addnodetris(in.octnode,ent.ent)
	
End Function

Global pnode.octnode
Global Vert_Thresh=800
Global Node_Limit=4

Function Oct_NodeLimit(NLimit)
	Node_Limit=NLimit
End Function

Function Oct_VertexLimit(VLimit)
	Vert_Threash=Abs(VLimit)
End Function

Function oct_gennode(in.octnode,ent.ent,devides#,minverts=580,check=0)
	vertc=bound_vertcount(in,ent)
	in\hasgeo=False
	If vertc=0 Delete in Return 
	If vertc>Vert_Thresh And check<Node_Limit
		box.bound=in\box
		nw#=box\w/devides
		nh#=box\h/devides
		nd#=box\d/devides
		bx#=box\x
		by#=box\y
		bz#=box\z
		endx#=bx+box\w
		endy#=by+box\h
		endz#=bz+box\d
		in\hasgeo=False
		If nw=0 Or nh=0 Or nd=0 Return 
		Repeat
			child.octnode=oct_newnode()
			bound_set(child\box,bx,by,bz,nw,nh,nd)
			child\box\cx=bx+nw/2.
			child\box\cy=by+nh/2.
			child\box\cz=bz+nd/2.
			child\box\bsize=nw/2
			child\box\size=nw
			oct_gennode(child.octnode,ent,devides,minverts,check+1)
			its=its+1
			If child<>Null
				in\child[in\nodes]=child
				in\nodes=in\nodes+1
			EndIf
			bx=bx+nw
			If bx=>endx
				bx=box\x
				by=by+nh
				If by=>endy
					by=box\y
					bz=bz+nd
					If  bz=>endz
						Exit
					EndIf
				EndIf
			EndIf
			If KeyDown(1) End
		Forever
	Else
		Local surfs[oct_maxsurfaces]
		sc=oct_gentris(ent,in,surfs)
		For j=0 To ent\sc-1
			If BankSize(surfs[j])>0
				id=glcompiletris(surfs[j],Surface_Vertexobjects,glstatic)
				oct_newnodesurface(in,surfs[j],ent\sdat[j],ent,id)
			EndIf
		Next
		in\hasgeo=True
	EndIf
		cd=CreateCube()
		FitMesh cd,in\box\x,in\box\y,in\box\z,in\box\size,in\box\size,in\box\size
		cd=Entity_ConvB3D(cd)
		Entity_Compile(cd,Compile_Static)
		Entity_ColorMode cd,ColorMode_Color
		Entity_CullMode cd,CullMode_Disabled
		Entity_Color cd,200,200*in\hasgeo,200
		Entity_WireFrame(cd,True)
		Batch_AddEntity(demobatch,cd)
End Function

Function oct_processnode(in.octnode,ent.ent,devides#=2,minverts=580,check=0)
	;-
	;vertc=bound_vertcount(in,ent)
	;in\vertc=vertc
	;stop
	nonode=False
	If vertc<minverts nonode=True
	
	
	nonode=False
	
	If nonode
		If check=1 Return 
		Delete in
		Return 
	Else;-
		;end
	;	debuglog "node verts>"+vertc
		;-sub devide and process. recursive..is....nice...
		box.bound=in\box
		nw#=box\w/devides
		nh#=box\h/devides
		nd#=box\d/devides
		bx#=box\x
		by#=box\y
		bz#=box\z
		endx#=bx+box\w
		endy#=by+box\h
		endz#=bz+box\d
		If nw=0 Or nh=0 Or nd=0 Return 
		Repeat
			child.octnode=oct_newnode()
			bound_set(child\box,bx,by,bz,nw,nh,nd)
			child\box\size=nw
			oct_processnode(child.octnode,ent,devides,minverts,check+1)
			If child<>Null
				in\child[in\nodes]=child
				in\nodes=in\nodes+1
			EndIf
			bx=bx+nw
			If bx=>endx
				bx=box\x
				by=by+nh
				If by=>endy
					by=box\y
					bz=bz+nd
					If  bz>endz
						Return 
					EndIf
				EndIf
			EndIf
			If KeyDown(1) End
		Forever
	EndIf
End Function

Function oct_gentris(ent.ent,oct.octnode,surfs[oct_maxsurfaces])
Local verts[oct_maxsurfaces]
	For j=0 To	ent\sc-1
		surfs[j]=bound_gentrisfrom(oct\box,ent\sdat[j]\cverts,ent\sdat[j]\ctris,ent\sdat[j]\tc)
		verts[j]=ent\sdat[j]\cverts
		surfc=surfc+1
	Next
;bound_totris(oct\box,surfs,verts,ent\sc-1)

	Return surfc	
End Function
Type tril
	Field tris,id
End Type

Function bound_gentrisfrom(box.bound,verts,tris,tric)

	bank=CreateBank(BankSize(tris))
	For j=0 To tric-1
		v1#=PeekInt(tris,toff)
		v2#=PeekInt(tris,toff+4)
		v3#=PeekInt(tris,toff+8)
		p1=pointbound(PeekFloat(verts,v1*12),PeekFloat(verts,v1*12+4),PeekFloat(verts,v1*12+8),box)
		p2=pointbound(PeekFloat(verts,v2*12),PeekFloat(verts,v2*12+4),PeekFloat(verts,v2*12+8),box)
		p3=pointbound(PeekFloat(verts,v3*12),PeekFloat(verts,v3*12+4),PeekFloat(verts,v3*12+8),box)
		If p1=True Or p2=True Or p3=True
		
			
				trisin=trisin+1
				PokeInt(bank,bankoff,v1)
				PokeInt(bank,bankoff+4,v2)
				PokeInt(bank,bankoff+8,v3)
				bankoff=bankoff+12
	
		
				
		EndIf
		toff=toff+12
	Next
	ResizeBank(bank,bankoff)
	Return bank
End Function

Function bound_vertcount(in.octnode,ent.ent)
	x1#=in\box\x
	y1#=in\box\y
	z1#=in\box\z
	x2#=x1+in\box\w
	y2#=y1+in\box\h
	z2#=z1+in\box\d
	For j=0 To ent\sc-1
		vbank=ent\sdat[j]\cverts
		voff=0
		For k=0 To ent\sdat[j]\vc-1
			x#=PeekFloat(vbank,voff)
			y#=PeekFloat(vbank,voff+4)
			z#=PeekFloat(vbank,voff+8)
			If x>x1 And x<x2
				If z>z1 And z<z2
					If y>y1 And y<y2
						vcount=vcount+1
					EndIf
				EndIf
			EndIf
			voff=voff+12
		Next
	Next
	Return vcount
End Function
 
Function oct_addnodesurface(surface,oct_node)
	in.sdat=Object.sdat(surface)
	oct.octnode=Object.octnode(oct_node)
	
End Function

Function oct_newnode.octnode()
	out.octnode=New octnode
	out\box=New bound
	Return out
End Function

Function bound_set(in.bound,x#,y#,z#,w#,h#,d#) 
	in\x=x
	in\y=y
	in\z=z
	in\w=w
	in\h=h
	in\d=d
End Function

Function pointbound(x#,y#,z#,in.bound)
	If x=>in\x And x<=in\x+in\w
		If y=>in\y And y=<in\y+in\h
			If z=>in\z And z<=in\z+in\d	
				Return True
			EndIf
		EndIf
	EndIf
End Function

Const bound_box=1,bound_cube=2,bound_sphere=3,bound_point=4

Function bound_totris(bound.bound,surfs[oct_maxsurfaces],verts[oct_maxsurfaces],surfc,meth=bound_box)
	Local sx#,sy#,sz#,bx#,by#,bz#
	bs#=0
	cx#=bound\x+bound\w/2
	cy#=bound\y+bound\h/2
	cz#=bound\z+bound\d/2
	For j=0 To surfc-1
		If surfs[j]<>0
		tc=BankSize(surfs[j])/12
		vbank=verts[j]
		If vbank<>0
		For t=0 To tc-1
			For v=0 To 2
				vert=PeekInt(surfs[j],t*12+(v*4))
				x#=PeekFloat(vbank,vert*12)
				y#=PeekFloat(vbank,vert*12+4)
				z#=PeekFloat(vbank,vert*12+8)
				tx#=tx#+x
				ty#=ty#+y
				tz#=tz#+z
				vcc=vcc+1
			Next
		Next
		EndIf
		EndIf
	Next
	cx#=tx/vcc
	cy#=ty/vcc
	cz#=tz/vcc
	
	For j=0 To surfc-1
		If surfs[j]<>0
		tc=BankSize(surfs[j])/12
		vbank=verts[j]
		If vbank<>0
		For t=0 To tc-1
			For v=0 To 2
				vert=PeekInt(surfs[j],t*12+(v*4))
				x#=PeekFloat(vbank,vert*12)
				y#=PeekFloat(vbank,vert*12+4)
				z#=PeekFloat(vbank,vert*12+8)
				Select meth
					Case bound_box
						If Abs(x-cx)>bs bs=Abs(x-cx)
						If Abs(y-cy)>bs bs=Abs(y-cy)
						If Abs(z-cz)>bs bs=Abs(z-cz)
					Case bound_cube
						If x<sx sx=x
						If x>bx bx=x
						If y<sy sy=y
						If y>by by=y
						If z<sz sz=z
						If z>bz bz=z
				End Select
			Next
		Next
		EndIf
		EndIf
	Next

	
	Select meth
	Case bound_box
		
		bound\size=bs*2
		bound\x=cx-bs
		bound\y=cy-bs
		bound\z=cz-bs
		bound\w=bs*2
		bound\h=bs*2
		bound\d=bs*2
		bound\cx=cx
		bound\cy=cy
		bound\cz=cz
		bound\bsize=bs
	Case bound_cube
		bound\x=sx-0.1
		bound\y=sy-0.1
		bound\z=sz-0.1
		
		bound\w=(bx-sx)+0.2
		bound\h=(by-sy)+0.2
		bound\d=(bz-sz)+0.2
	End Select
	

End Function

Function bound_toentity(bound.bound,entity,meth=bound_box)
Local sx#,sy#,sz#,bx#,by#,bz#
	in.ent=Object.ent(entity)
	bs#=0
	For j=0 To in\sc-1
		vbank=in\sdat[j]\cverts
		voff=0
		For k=0 To in\sdat[j]\vc-1
			x#=PeekFloat(vbank,voff)
			y#=PeekFloat(vbank,voff+4)
			z#=PeekFloat(vbank,voff+8)
			tx#=tx#+x
			ty#=ty#+y
			tz#=tz+z
			Select meth
			Case bound_box
				If Abs(x)>bs bs=Abs(x)
				If Abs(y)>bs bs=Abs(y)
				If Abs(z)>bs bs=Abs(z)
				
			Case bound_cube
				If x<sx sx=x
				If x>bx bx=x
				If y<sy sy=y
				If y>by by=y
				If z<sz sz=z
				If z>bz bz=z
			End Select
			
			voff=voff+12
			
		Next
		tv=tv+in\sdat[j]\vc;-1
		
	Next
	cx#=tx/tv
	cy#=ty/tv
	cz#=tz/tv
	Select meth
	Case bound_box
		bs=bs+0.1
		bound\size=bs*2
		bound\x=cx-bs
		bound\y=cy-bs
		bound\z=cz-bs
		bound\cx=cx
		bound\cy=cy
		bound\cz=cz
		bound\bsize=bs
		;bound\size=bs
		
		bound\w=bs*2
		bound\h=bs*2
		bound\d=bs*2
	Case bound_cube
		bound\x=cx-sx-0.1
		bound\y=cy-sy-0.1
		bound\z=cz-sz-0.1
		
		bound\w=(bx-sx)+0.2
		bound\h=(by-sy)+0.2
		bound\d=(bz-sz)+0.2
	End Select
	
End Function

