Code archives/3D Graphics - Misc/Zone Occlusion System

This code has been declared by its author to be Public Domain code.

Download source code

Zone Occlusion System by Rogue Vector2004
This is a zone (or cell) based system.

The system works by reading the name of each object in the (.b3d) file hierarchy.

The zone data is extracted from the name and placed into the TZone data structure.

The Level Designer determines which zones can be seen from a particular vantage point in the level map.

He records this data in the object name, before exporting to a (.b3d) file.

Thus, during run-time, if the player is in zone X, simply get the 'can_see' zones from the TZone data.

No need for portals.

No need for an additional script file.

Download the test level here:
www.octanedigitalstudios.com/downloads/Zone_Occlusion.zip

Regards,

Rogue Vector
; ***************************************************************
; PROG:   OCCLUSION SYSTEM  -  (FREEWARE)
; ETHOS:  Simple and Fast
; AUTHOR: Rogue Vector for Octane Digital Studios Ltd
; DATE:   Tuesday 7th December 2004 
; ***************************************************************




; BASIC DESIGN OVERVIEW
; ---------------------

; This is a zone (or cell) based system.

; The system works by reading the name of each object in the (.b3d) file hierarchy. 

; The zone data is extracted from the name and placed into the TZone data structure.

; The Level Designer determines which zones can be seen from a particular vantage point in the level map.

; He records this data in the object name, before exporting to a (.b3d) file.

; Thus, during run-time, if the player is in zone X, simply get the 'can_see' zones from the TZone data.

; No need for portals.

; No need for an additional script file.




;CONSTANTS
Const SUCCESS          = 1
Const FAILURE          = -1
Const EMPTY            = -1
Const VIS_FORMAT$      = "[ zone: # vis: # ]"  ;The basic format of the vis data, in its simplest form.
Const VIS_MAX_ZONES    = 10					   ;The maximum number of zones that can be seen from the current zone.
Const VIS_STOP_SYMBOL$ = "]"			       ;End of line (terminator) used when parsing the vis data.
Const VIS_DELIMITER$   = " "				   ;Words in the vis data are seperated by this character (i.e. space).
Const VIS_INFO_START   = 5					   ;The first vis data begins at the fifth word in.
Const ZONE_IDENT_START = 3



;TYPES
Type TZone

	Field entity
	Field name$
	Field can_see[VIS_MAX_ZONES]
	Field can_see_count
	Field max_X#
	Field max_Y#
	Field max_Z#
	Field min_X#
	Field min_Y#
	Field min_Z#

End Type 



;GLOBALS
Global g_check_format       = True
Global g_current_zone.TZone = Null
Global g_last_zone          = 0




;TEST PROGRAM ************************
Global g_keytimer, g_wireframe, g_time
Global g_gravity# = -1
runtest("level_map.b3d")
;DE-ACTIVATE AS DEFAULT **************




;FUNCTIONS
Function Occlusion_Initialise(v_level)

	Local node         = 0
	Local node_name$   = ""
	Local token$       = ""
	Local surface      = 0
	Local max_surfaces = 0
	Local vis_index    = VIS_INFO_START
	Local child_count  = CountChildren(v_level)
	Local Vx#=0.0, Vy#=0.0, Vz#=0.0

	If (child_count = 0) RuntimeError("ERROR!... NO SCENE ELEMENTS FOUND")	
	
	;Iterate through every child object in the mesh hierarchy and populate the TZone objects
	For i = 1 To child_count
		
		;Find the zones in the level mesh hierarchy.
		node = GetChild(v_level,i)
		
		;Get the name of the node.
		node_name$ = EntityName$(node)

		;Check formatting (SLOW - switched off by default).
		If (g_check_format) CheckZoneInfoFormat(node_name)
				
		;Create zone object to hold data.
		zone.TZone  = New TZone
			
		;Populate object with initial values.
		zone\entity        = node
		zone\name          = node_name 
		zone\max_X         = -1000000.0
		zone\max_Y         = -1000000.0
		zone\max_Z         = -1000000.0
		zone\min_X         = 1000000.0
		zone\min_Y         = 1000000.0 
		zone\min_Z         = 1000000.0
		zone\can_see_count = 0

		;Set default values for can_see array.
		For p=0 To VIS_MAX_ZONES
		
			zone\can_see[p] = EMPTY
				
		Next
						
		;Create a bounding box around the zone.
		max_surfaces = CountSurfaces(zone\entity)
			
		For k=1 To max_surfaces 
					
			surface = GetSurface(zone\entity, k)
			
			For m = 0 To CountVertices(surface) - 1
			
				;Transform points to world (global) space
				TFormPoint VertexX(surface, m), VertexY(surface, m), VertexZ(surface, m), zone\entity, 0
				
				Vx# = TFormedX#()
				Vy# = TFormedY#()
				Vz# = TFormedZ#()
				
				If (Vx# > zone\max_X) Then zone\max_X = Vx#
				If (Vy# > zone\max_Y) Then zone\max_Y = Vy#
				If (Vz# > zone\max_Z) Then zone\max_Z = Vz#
										
				If (Vx# < zone\min_X) Then zone\min_X = Vx#
				If (Vy# < zone\min_Y) Then zone\min_Y = Vy#
				If (Vz# < zone\min_Z) Then zone\min_Z = Vz#
								
			Next

		Next
							
		;Check that there is a stopping condition symbol in the zone info string.
		If Instr(zone\name, VIS_STOP_SYMBOL)

			;Parse initial vis data into the internal array. 
			token = GetWord(node_name, vis_index, " ")
							
			If Not(Int(token) => 0) RuntimeError("ERROR!... DATA IN VIS ARRAY IS NOT OF THE REQUIRED TYPE.")

			Repeat
								
					zone\can_see[vis_index - VIS_INFO_START] = Int(token)
								
					vis_index = vis_index + 1
				
					token = GetWord(zone\name, vis_index, " ")
				
			Until (token = VIS_STOP_SYMBOL)

			;Reset vis array index
			vis_index = VIS_INFO_START

		Else
		
			RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")

		EndIf

	Next
	
			
	;Modify the data. Need to convert the zone numbers to the corresponding entity id's.
	;Also, need to count the number of zones that can be seen from this zone and store in 'can_see_count'.
	;This is a preparatory stage to help keep Update function small and fast.
	Local tmp_entity = 0
	
	For l_tmp.TZone = Each TZone
	
		For index=0 To VIS_MAX_ZONES
	
			If (l_tmp\can_see[index] = EMPTY) Exit
			
			tmp_entity = FindEntity(l_tmp\can_see[index])
			
			l_tmp\can_see[index] = tmp_entity
		
			l_tmp\can_see_count  = l_tmp\can_see_count + 1
							
		Next
	
	Next
			
		
	;Check formatting of zone data structures.
	If (g_check_format) CheckZoneDataStructure()
	
	
End Function





Function Occlusion_Update(v_playerX#, v_playerY#, v_playerZ#)

	Text 5,30, "X: " + v_playerX
	Text 5,40, "Y: " + v_playerY
	Text 5,50, "Z: " + v_playerZ
	
	Local n   = 0

	For g_current_zone = Each TZone
			
		If (IsInsideZone(v_playerX, v_playerY, v_playerZ, g_current_zone)) 
		
			n = n + 1
			
			Text 5, 70+(n*20), "Zone name: " + g_current_zone\name
						
			If Not(g_last_zone = Handle g_current_zone)
					
				HideAllZones()
				
				ShowEntity g_current_zone\entity
								
				For l_index = 0 To g_current_zone\can_see_count - 1
				
					ShowEntity 	g_current_zone\can_see[l_index]
					
				Next
				
				g_last_zone = Handle g_current_zone
				
				Exit
			
			EndIf
										
		EndIf

	Next

End Function





Function Occlusion_ClearAll()

	Local l_tmp.TZone = Null
	
	For l_tmp = Each TZone
	
		Delete l_tmp
		
	Next

	Return SUCCESS

End Function





Function FindEntity(v_number)

	Local l_tmp.TZone = Null
	Local token$      = ""
	
	For l_tmp = Each TZone
	
		token = GetWord(l_tmp\name, ZONE_IDENT_START, VIS_DELIMITER)
		If (Int(token) = v_number) Return l_tmp\entity 
		
	Next

	Return FAILURE

End Function





Function FindZone.TZone(v_number)

	Local l_tmp.TZone = Null
	Local token$      = ""
	
	For l_tmp = Each TZone
	
		token = GetWord(l_tmp\name, ZONE_IDENT_START, VIS_DELIMITER)
		If (Int(token) = v_number) Return l_tmp 
		
	Next

	Return Null

End Function





Function HideAllZones()
		
	For l_tmp.TZone = Each TZone
	
		For l_index = 0 To MAX_VIS_ZONES
		
			HideEntity l_tmp\can_see[l_index]
			HideEntity l_tmp\entity
						
		Next
	
	Next

	Return SUCCESS

End Function




Function EntityAlphaLevel(v_amount#)

	For tmp.TZone = Each TZone
	
		EntityAlpha tmp\entity, v_amount
	
	Next
	
End Function





Function CheckZoneInfoFormat(v_name$)
	
	Print "CHECKING FORMAT OF VIS DATA"
		
	Print "Analysing Zone: [ " + GetWord(v_name, 3, VIS_DELIMITER) + " ]"
	
	
	If  Not(GetWord(v_name, 1 , VIS_DELIMITER) = GetWord(VIS_FORMAT, 1, VIS_DELIMITER)) Then RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")
	If  Not(GetWord(v_name, 2 , VIS_DELIMITER) = GetWord(VIS_FORMAT, 2, VIS_DELIMITER)) Then RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")
	If  Not(GetWord(v_name, 4 , VIS_DELIMITER) = GetWord(VIS_FORMAT, 4, VIS_DELIMITER)) Then RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")
	If  Not(Int(GetWord(v_name, 3 , VIS_DELIMITER)) => 0) Then RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")
	If  Not(Int(GetWord(v_name, 5 , VIS_DELIMITER)) => 0) Then RuntimeError("ERROR!...VIS DATA FORMAT INCORRECT")
	
	Print "        Result: [ PASS ]"
	Print "------------------------"

	Delay 200
 
	Return SUCCESS

End Function





Function CheckZoneDataStructure()
	
	Print
	Print
	Print "CHECKING ZONE DATA STRUCTURE"
		
	For l_tmp.TZone = Each TZone
	
		Print "Analysing Zone: [ " + GetWord(l_tmp\name, 3, VIS_DELIMITER) + " ]"
		Print "  Entity Ident: [ " + Str(l_tmp\entity) + " ]"
		Print "   BoundingBox: [ max_X = " + Str(l_tmp\max_X) + " ]"		
		Print "                [ max_Y = " + Str(l_tmp\max_Y) + " ]"
		Print "                [ max_Z = " + Str(l_tmp\max_Z) + " ]"
		Print "                [ min_X = " + Str(l_tmp\min_X) + " ]"
		Print "                [ min_Y = " + Str(l_tmp\min_Y) + " ]"
		Print "                [ min_Z = " + Str(l_tmp\min_Z) + " ]"
		
		For p=0 To VIS_MAX_ZONES
	
			If Not(l_tmp\can_see[p] = -1) Print "       Can See: [ " + Str(l_tmp\can_see[p]) + " ]"
		
		Next
		
		Print "---------------------"
		
		;WaitKey
		Delay 200
		
	Next

	Print
	Print "FINISHED ANALYSIS..."
	Print
	Print "HIT A KEY TO CONTINUE"
	Print
	
	Return SUCCESS

End Function





Function IsInsideZone%(v_objectspaceX#, v_objectspaceY#, v_objectspaceZ#, v_zone.TZone) 

	Return (v_objectspaceX > v_zone\min_X) And (v_objectspaceX < v_zone\max_X) And (v_objectspaceY > v_zone\min_Y) And (v_objectspaceY < v_zone\max_Y) And (v_objectspaceZ > v_zone\min_Z) And (v_objectspaceZ < v_zone\max_Z)

End Function






Function GetWord$(InputString$, WordNum, Seperators$=" ") ;by sswift

	FoundWord  = False
	WordsFound = 0

	; Loop through each character in the input string.
	For CharLoop = 1 To Len(InputString$)

		; Get the character at this location in the string.
		ThisChar$ = Mid$(InputString$, CharLoop, 1)

		; If the character at this position is one of the characters in the seperator list...
		If Instr(Seperators$, ThisChar$, 1)
		
			; If a word has been started...
			If FoundWord
		
				; ...then this character must mark the end of a word.

				; Increment the number of words we've found.
				WordsFound = WordsFound + 1

				; Is this word the word we want?
				If WordsFound = WordNum
				
					; Yes!  Exit the function and return the word.
					Return Word$
			
				Else
				
					; No.  Discard this word.
					Word$ = ""
					FoundWord = False
				
				EndIf
				
			Else
			
				; Ignore this character.  We have either not reached a word yet, or are between words.
			
			EndIf				
					
		Else
		
			; This is not a character in our seperator list.  Add it to our word.
			FoundWord = True
			Word$ = Word$ + ThisChar$
			
		EndIf
		
	Next	
		
	; We have finished looking through the string.  Was the last word we were on the one we were looking for?
	If (WordsFound+1) = WordNum

		; Yes! 
		; Return the word that at the end of the string which didn't have any seperators after it.
		Return Word$

	Else
	
		; No. 
		; The word number passed to the function was greater than the number of words in the string. 
		; Return an empty string.
		Return ""

	EndIf
	
End Function





Function QuickTexture()

	tex=CreateTexture(512,512)
	ScaleTexture tex,.2,.5
	SetBuffer TextureBuffer(tex)
	
	Color 50,50,50
	
	Rect 0,0,512,512
	
	Color 200,200,200
	Rect 8,8,496,496
	
	Color 255,255,255
	SetBuffer BackBuffer()
	 
	For tmp.TZone = Each TZone
	
		EntityTexture tmp\entity, tex 
			
	Next
	
	Return tex
	
End Function





Function SuperCam(cam,ent,cspeed#,dist#,hite#,xrot#,tilt#) ;by PsychicParrot

	TFormPoint 0,hite#,-dist#,ent,0
	
	cx#=(TFormedX()-EntityX(cam))*cspeed#
	cy#=(TFormedY()-EntityY(cam))*cspeed#
	cz#=(TFormedZ()-EntityZ(cam))*cspeed#
	
	TranslateEntity cam,cx,cy,cz
	PointEntity cam,ent
	RotateEntity cam,xrot#,EntityYaw(cam),tilt#
	
End Function





Function DoWireFrame(v_key)

	g_time = MilliSecs()
	
	If (g_KeyTimer + 200 < g_time)

		If (KeyDown(v_key)) 
		
			g_wireFrame = 1 - g_wireFrame
			WireFrame g_wireFrame
			g_keyTimer = g_time : Return SUCCESS
		
		EndIf
 		
	EndIf

End Function





Function RunTest(v_level_filename$)

	AppTitle "Occlusion Test Program","Are you sure you want to quit?"
	Graphics3D 800,600,16,2
	SetBuffer BackBuffer()
	
	C_PLAYER   = 1
	C_LEVEL	   = 2
	C_TRIGGER  = 3
	
	Collisions C_PLAYER,C_LEVEL,2,2
	
	level = LoadAnimMesh(v_level_filename)
	
	EntityType level,C_LEVEL,True
	
	;Initialise Occlusion system
	Occlusion_Initialise(level)
		
	If (g_check_format) WaitKey
	
	player  = CreateSphere(8) ;the player
	
	ScaleMesh player, 1,1,1
	MoveEntity player, 0,2,0
	TurnEntity player, 0,90,0
	EntityColor player, 255,0,0
	EntityType player,C_PLAYER
	EntityRadius player, 1 
	
	camera = CreateCamera()
	
	PositionEntity camera, -200,50,-200
	PointEntity camera, player
		
	light = CreateLight()
	RotateEntity light, 60,30,0
	
	texture = QuickTexture()	
		
	Repeat
		
		DoWireFrame(17)						 			;W for wireframe
			
		If (KeyDown(200)) MoveEntity player, 0,0,1.5	;Up arrow	
		If (KeyDown(208)) MoveEntity player, 0,0,-1.5	;Down arrow
		If (KeyDown(203)) TurnEntity player, 0,3.8,0	;Left arrow
		If (KeyDown(205)) TurnEntity player, 0,-3.8,0	;Right arrow
		If (KeyDown(57 )) MoveEntity player, 0,3, 0	    ;Space to jump
		
		SuperCam(camera,player,0.5,12,5,0,2)

		MoveEntity player, 0, g_gravity, 0
				
		UpdateWorld()
		
		RenderWorld()
				
		Occlusion_Update(EntityX(player,True),EntityY(player,True),EntityZ(player,True))
		
		Text 5,5, "Triangles Rendered: " + TrisRendered()
		
		Flip
		
	Until KeyHit(1)
	
	Occlusion_ClearAll()
	
	FreeEntity  level
	FreeTexture texture
	ClearWorld()
	End

End Function

Comments

Jeroen2004
smart!!!!
Very nice. Easy to implement also, well done.


sting2005
Hey there this looks great! I know its a bit old now but does anyone still have the test zip? Thanks.


Code Archives Forum