Code archives/3D Graphics - Effects/Unique Hardware-Processed Lighting Environments per entity

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

Download source code

Unique Hardware-Processed Lighting Environments per entity by Drey2004
Controls:
Right Mouse(with movement) makes the cam rotate around the entity

Middle Mouse Resets the camera so it's behide the mesh.

Normal Mouse Rotation steers the mesh around the environment

Mouse Wheel moves the camera away or towards the entity.

M shows light markers

N shows Active Lights

Standard WASD movement

Num 4 and 6 rotates lights
Num 5 stops them and turns on static mode.

Update: Now demoing a better lighting options. This should be saficient for getting how you'll make a lighting engine.
This is still testing code and i'll make a much cleaner system in the future. Still to add is fade in, fade out, light types.

I get about 141fps on my machine.

Update: To better demo the technique, i modded the code a bit to show how it works with many objects on the screen and to better prove how the lighting will work. Now with
100 objects all gettin their unqiue hardware processed lighting.

Basicly, in this demo, 42 "lights" are created. I create a speical types called Objects and MavLights( maverick 2.0 is the name of my engine).

Objects have a max value of 8 lights to work on it at once. MaxLights is a variable tho and can be set from 0 to 8.

Then with that, the nearest number of lights will be turned on and then the object is to be rendered.

It's important to note a few things:

Though the light markers in the demo aren't, everything else has a default of hidden.

The first renderworld doesn't show anything at all, instead, places everything into the Z buffer for later rendering. It's IMPORTANT to change the camera's clsmode to 0,0 after the first renderworld.

The light searching system works pretty well, but i want to make some bigger improvements on it. In fact, this section of code probably won't be used in the engine at all. Because i'm gion to have something called LightPackages. That really help countdown pointless checks and controls a few other properties.

If u plan to make a game using this technique. It's important to consider that if u want directional light and such like that. U'll have to make sure to create those properties in your lighting system.

Also consider what lights u always want to be on. Directional light should probably always be one of your active lights.

I just threw this together. In a few months, u should see more from my engine. For now, i think this techinque is important enough to share.

Any comments are welcome.


To see the true speed of the system, make sure no markers are on.
Graphics3D 800, 600,32, 2
SetBuffer BackBuffer()
HidePointer

Global Cam = CreateCamera()
Global ShowActiveLights

SeedRnd MilliSecs()

MoveMouse GraphicsWidth()/2, GraphicsHeight()/2
FlushMouse()


Type MavObject

	Field Pivot
	Field Mesh ; What's important to hidden
	Field X#
	Field Y#
	Field Z#

	Field  ObjectType
	
	
	Field MaxLights;  How many lights can act on it

	Field AmbR; What just a quick option for custom ambient light
	Field AmbG
	Field AmbB

	
	

End Type 

Type MavLight

	Field Pivot ; Something i might end up using later
	Field Mesh ; Something I might end up using later
	Field X#,Y#,Z#; Placement
	

	Field R, G, B ;  Color Values
	Field LightRange# ;  Sets the Range for the light
	Field TriggerRange# ; Sets the Trigger Range, Used to see if it's even worth goin through the check. this is to help take up some processes 

	Field LightType;  for later use, used for what type of light it is.
	Field AngleIn# ; Used for SpotLights, later use
	Field AngleOut#; Used for SpotLights, later use

	Field Static;
	Field Testing

End Type 


Dim LC(8) ; Light Checkers( MavLight Handles) .  basicly, the lights that will end up being used for the lighting system
Dim Dis#(8) ; Parallel with LC, it saves the distance of the light

Dim DyLC(8)
Dim DyDis#(8)

Dim TempCube(8);  if light markers are on




Repeat

	Locate 0,0
	Cls
	Flip 0
	NumOfLights = Input("NumberOfLights(0 to 8) activiated: " )
	
Until NumOfLights > -1 And NumOfLights < 9


ST = CreateTexture(16,16,  4)
SetBuffer TextureBuffer(ST)
	Color 0,0,0
	Rect 0,0,16,16
	Color 255,255,255

	Oval 0, 0,16,16
SetBuffer BackBuffer()


Gray = 32



Mesh = CreateSphere(8)
ScaleEntity Mesh, 2.5, 2.5, 2.5




HideEntity Mesh

Ship.MavObject = New MavObject

Ship\Pivot = CreatePivot()
Ship\Mesh = Mesh

SHip\ObjectType = 1
	ship\MaxLights = NumOfLights 


Ship\AmbR = Gray
ship\AmbG = Gray
Ship\AmbB = Gray

;Not important for the techinque
;{
CamPointPivot = CreatePivot()


EntityParent Ship\Mesh, Ship\Pivot

EntityParent CamPointPivot, Ship\Pivot

EntityParent Cam, CamPOintPivot



MoveEntity Cam, 0, 0, -50


PositionEntity Ship\Pivot, 0,0, 0

CameraRange Cam, 0.01, 5000
;}






; This creates the lights, then hiddens them.  I figure it'll be alil after than creating and destroying lights all the time.
Dim Light(8)

For I = 0 To 8

	Light(I) = CreateLight(2)

	HideEntity Light(I)

Next

ShowActiveLights = 1

Sign = 1

For X = -5 To 4
	For Z = 0 To 9
	
		ObjectCount = ObjectCount + 1
		NO.MavObject = New mavobject ; New Object

		NO\Pivot = CreatePivot()
		NO\Mesh = CopyEntity(Mesh)
		HideEntity No\Mesh 
		NO\MaxLights = NumOfLights

		

		NO\AmbR = Gray 
		NO\AmbG =Gray
		NO\AmbB = Gray

		PositionEntity NO\Mesh, X * 45, 0, Z * 45
		

		No\X = EntityX(No\Mesh)
		No\Y = EntityY(no\mesh)
		No\Z = EntityZ(no\mesh)

	Next
Next 

; Light Creation and Placement

RotationPivot = CreatePivot()
PositionEntity RotationPivot, 0, 0, 3 * 75

LightMesh = CreateSprite()
HideEntity LightMesh
EntityTexture LightMesh, ST



	GLightRange = 45



	GTriggerRange = 150


For X = -3 To 3 Step 1
	For  Z =0 To 5
	count = count + 1

	
	
	L.MavLight = New MavLight

	


	

	
		L\R = Rand(255 * Sign)
		L\B = Rand(255 * Sign)
		L\G = Rand(255 * Sign)

		L\LightRange = GLightRange

		L\TriggerRange = GTriggerRange
	
		
		
		L\X = X * (75) 
		L\Y =  7 ;Rand(5, 10)
		L\Z = Z * (75)

		markCube = CopyEntity(LightMesh)
		HideEntity MarkCube
		PositionEntity MarkCube, L\X, L\Y, L\Z
		ScaleEntity MarkCube, .5, .5, .5
		
			EntityColor MarkCube, Abs(L\R), Abs(L\G),Abs( L\B)

		EntityFX MarkCube, 1
		HideEntity markcube
		L\Mesh = MarkCube
	
		L\Pivot = L\Mesh 
		;PositionEntity L\Pivot, L\X, L\Y, L\Z
		EntityParent L\Pivot, RotationPivot
		

		


	Next
Next


LightStatic = 1

MoSpd# = 1.5
MeshSpd# = 1
Show_FPS_Counter=1
While KeyHit(1) < 1 

		
	
	;MUST OCCUR
	;Camera Refreshes it's Z buffer( color buffer is optional )
	CameraClsMode Cam, 1,1
	RenderWorld(); This renders NOTHING, but puts things in the Z buffer, important for when u render More than one Object.
	
	;Must Occur, make sure that the color and z buffer doesn't refresh one bit
	CameraClsMode Cam, 0, 0
	If CreateMarkers
		RenderLightPlaceHolders()
	EndIf 
	LightObjects(); The lighting rountie
	
	;Mouse wheel allows for a up close look at the action
	MouseZSpeed = MouseZSpeed()
	MoveEntity Cam, 0, 0, MouseZSpeed * 5

	MoveEntity Ship\Pivot, (KeyDown(32) - KeyDown(30))*MeshSpd, (KeyDown(57) - KeyDown(29)) * MeshSpd, (KeyDown(17) - KeyDown(31)) * MeshSpd

	If MouseDown(2) ;Mouse Button 2 allows for All angle camera movement
		
		TurnEntity CamPointPivot, -MouseYSpeed() * MoSpd,  -MouseXSpeed() * MoSpd, 0
		RotateEntity CamPointPivot, EntityPitch(CamPointPivot), EntityYaw(CamPointPivot), 0

	Else

		TurnEntity Ship\Pivot,0, -MouseXSpeed() * MoSpd, 0
EndIf

	If MouseDown(3) ; Somewhat recenters the camera

		RotateEntity CamPointPivot, 25, 0,0
		
	EndIf 

	TurnEntity RotationPivot, 0, rotationspeed/100.0, 0

	If KeyDown(76)

		RotationSpeed = 0
		LightStatic = 1
		static(LightStatic)

	EndIf 

	RotationTest = (KeyDown(75) - KeyDown(77))

	If RotationTest <> 0
	
		Rotationspeed = rotationspeed + RotationTest
		
		If LightStatic = 1 Then
			LightStatic = 0
			Static(0)
		EndIf
	EndIf 
	
	;recenters mouse
	MoveMouse GraphicsWidth()/2, GraphicsHeight()/2




	NewLightRange# = (KeyDown(205) - KeyDown(203))

	If newLightRange <> 0 Then
	
		GLightRange  = GLightRange + NewLightRange
		AddLightRangeAll(NewLightRange)

	EndIf
	
	NewTriggerRange# =  (KeyDown(200) - KeyDown(208))


	If NewTriggerRange <> 0 Then

		GTriggerRange = GTriggerRange + NewTriggerRange
		AddTriggerRangeAll(NewTriggerRange)

	EndIf 


	If KeyHit(50) Then
		CreateMarkers = Not createMarkers
	EndIf

	If KeyHit(49) Then
		ShowActiveLights = Not ShowActiveLights
	EndIf 

	;Cheap instaneous FPS rate
    If Show_FPS_Counter = True Then
       
        EndingFPS = MilliSecs()
        MilliDif% = EndingFPS - StartingFPS
       
        If MilliDif < 1 Then
            MilliDif = 1
       EndIf

       FPS_Count = 1000/MilliDif
       
       Text 0,0, "FPS: " + FPS_Count
      
    EndIf

StartingFPS = MilliSecs()
If (MilliSecs() - MilliLast) > 1000
	AverFPS = FPSCount
	MilliLast = MilliSecs()
	FPSCount = 0
Else
	FPSCount = FPSCount + 1
EndIf

	Text 0, 20, "Object Count: " + ObjectCount
	Text 0, 10, "Lights: " +  count 
	
	Text 0, 40, "General Light Range: " +  GLightRange
	Text 0, 50, "General Trigger Range: " + GTriggerRange
	Text 0, 60, "Rotation Speed: " + RotationSpeed
	Text 0, 70, "Aver FPS: " + AverFPS

	For I = 0 To ( NumOfLights -1  ) 

		Text 0, I * 10 + 80, "Light " + DyLC(I)  + " Dis: " + DyDis(I)

	Next 
	
	

Flip False
 




Wend
ShowPointer

End





Function LightObjects()



	;For every mesh u put in MavObjects
	For Obj.MavObject = Each MavObject 

		;Of course, this is important
		ShowEntity Obj\Mesh 

		;for now, i just slapped this on the mesh
		AmbientLight Obj\AmbR, obj\AmbG , obj\AmbB
		
		;Dims all 
		Dim LC(obj\maxlights)
		;A cheap lil way to make sure it grabs some near by lights.  
				
		For L.MavLight = Each MavLight


		ActiveShown = 0
				
			NewDis = EntityDistance(Obj\Mesh, L\Pivot)
				
				
				
				
			If NewDis < (L\TriggerRange)

				If Not L\Static
					TFormPoint 0,0,0, L\Pivot, 0
					
					L\X = TFormedX()
					L\Y = TFormedY()
					L\Z = TFormedZ()
				EndIf
				
					;This system here see how the NewDistance Compares with the current once.
					;The system is built on a stack idea.
					;Example:
					;ArrayNumber - Handle - Distance
					;   0-1-30
					;   1-2-40
					;   2-26-50
					;If NewDis = 35 and MavLight Handle is 5, this basicly happens
					; 0-1-30
					; 1-5-35
					; 2-2-40
					;The 26-50 gets pushed upward.  The idea is the closest light is 0 and the farthest is Obj\MaxLights-1
				

					
					
					For X = 0 To Obj\MaxLights - 1
				
						

							If LC(X) = 0 Then

								DIS(X) = NewDis
								LC(X) = Handle(L)

        Exit

							Else 

								If Dis(X) >= NewDis And LC(X) <> Handle(L)
								
									
									;The stack push system
									
									For II = Obj\maxLights - 2 To X Step -1

										LC(II + 1 ) = LC(II )
										Dis(II + 1) = Dis(II)
									Next
									Dis(X) = NewDis
									LC(X) = Handle(L)
									
									
									Exit

								EndIf
								
							EndIf
							
						Next
						
				EndIf
				
		Next
		
		For I = 0 To Obj\MaxLights -1
		
			CurMavLight.MavLight = Object.MavLight( LC( I ) )
			
			If CurMavLight <> Null
				; If U want the current lights to have temp makers

				LightCounter = LightCounter + 1
				If ShowActiveLights 

					If Obj\ObjectType Then

						

						ActiveShown = 1
						TempCube(i) = CreateCube()
						EntityColor TempCube(i), CurMavLight\R, CurMavLight\G, CurMavLight\B
						PositionEntity TempCube(i), CurMavLight\X, CurMavLight\Y, CurMavLight\Z
				
						EntityFX TempCube(i), 1
						;ScaleEntity TempCube(i), 1.5, 1.5, 1.5
					EndIf 
				EndIf

				;Where all the light placement happens
				PositionEntity Light(I), CurMavLight\X, CurMavLight\Y, CurMavLight\Z
				LightColor Light(I), CurMavLight\R, CurMavLight\G, CurMavLight\B
				LightRange Light(I), CurMavLight\LightRange
				ShowEntity Light(I)
				
			EndIf
			
		Next
		
		
		RenderWorld()


		;resets everything
		If ActiveShown
		
			For I = 0 To LightCounter -1
				FreeEntity TempCube(i)
			Next 

		EndIf 


		If  Obj\ObjectType = 1

			For I = 0 To Obj\MaxLights - 1

				DyLC(I) = LC(I)
				DyDis(I) = Dis(I)

			Next

		EndIf 
		
		For i = 0 To Obj\MaxLights

				HideEntity Light(I)
			
		Next


		;Hides the Entity
		HideEntity Obj\Mesh 

		
		
	Next
	

End Function

Function RenderLightPlaceHolders()

	For CurLight.MavLight = Each MavLight

		ShowEntity CurLight\Mesh 

		RenderWorld()

		HideEntity CurLight\Mesh

	Next
	
End Function

Function AddLightRangeALL(AddThis#)

	For L.MavLight = Each MavLight

		L\LightRange = L\LightRange + AddThis

	Next 

End Function

Function AddTriggerRangeALL(AddThis#)

	For L.MavLight = Each MavLight

		L\TriggerRange = L\TriggerRange + AddThis

	Next 

End Function

Function Static(value)

	
	For L.MavLight = Each MavLight

		L\Static = value

		If  Value = 0
		
			L\X = EntityX(L\Pivot)
			L\Y = EntityY(L\Pivot)
			L\Z = EntityZ(L\Pivot)
			
		EndIf 

	Next 


End Function

Comments

JoshK2005
This is the correct way to do lighting for moving characters, vehicles, etc.


Drey2005
Thanks. After looking at games like Half Life 2 and unreal. I figured they must be using a system similar to this.

Also, thanks for your help with the Z buffer Halo.


puki2005
Mmm.

I thought this was a bit naff at first - then, instead of 8 active lights, I just used 1 and now I see the effect. Best to start with 1 and increase the number to see the effect.

I like this - in fact, excellent stuff.


Code Archives Forum