Code archives/3D Graphics - Effects/Ambient Volume Lib

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

Download source code

Ambient Volume Lib by RifRaf2011
With this lib you can Create and save Ambient Volume Maps which are basically a large 3D grid containing ambient color settings per cell. The lib allows you to make these over your large maps. Once done you can ambient map your level and ambient shade moving and still entities dynamically with very little cpu or gpu cost.

No additional line picks or collision checks will be needed to utilize the volume data once its been created.

You can also drop ambient volume static lights, once i improve the colored light placement im going to add moving lights as well via a second grid holding only lights.

If you make any improvements please post them here. Thanks
Include "amb_volume.bb"
;//----------------------------------------------------------       ----TEST LIB---
;//----------------------------------------------------------       ----TEST LIB---
;//----------------------------------------------------------       ----TEST LIB---
;// PRESS 1 TO DROP A RANDOM COLORED LIGHT
;// click SPACE BAR button to toggle ambient volume map on the level mesh

Print "AMBIENT VOLUME LIB v1.0"
Print ""
Print ""
Print ""
Print "[ SPACEBAR ] : Toggle ambient scene mapping."
Print "[ 1 ]        : Drop a ambient volume light."
Print "[ ESCAPE ]        : Quit program. "
Print "[ MOUSE BUTTONS ] : move around."
Print ""
Print "press any key to start "
WaitKey()

amb_smoothContrastdown_Startat=3
amb_smoothContrastdown_Endat=5

amb_smoothContrastDown#=.95

amb_SmoothContrastup_Startat=2
amb_SmoothContrastup_Endat=3

amb_SmoothContrastup#=1.01


Graphics3D 800,600,0,2

 displayamb=1
 camera=CreateCamera()
 CameraClsColor camera,0,55,125
 CameraRange camera,1,13000
 avatar=CreateSphere()
 EntityFX avatar,1
 
 PositionEntity avatar,0,-2,3
 EntityParent avatar,camera
 mesh=LoadMesh("yourmodelhere.b3d")
 ;toonmeshuv mesh
 EntityPickMode mesh,2
 Amb_width# = MeshWidth(mesh)
 Amb_Height#= MeshHeight(mesh)
 Amb_Depth# = MeshDepth(mesh)
 AmbientLight 190,190,190
 
 PositionEntity camera,-100,-250,0

 ;//if you want to play with different settings in this test, just change 
 ;//the filename here to something that will never exist.. such as "ambientvolume44.dat"
 ;//if you dont then it will only create the volume once, then load it the next time.
 If FileType("ambientvolume.dat")<>1 Then 
     
	 amb_smoothContrastdown_Startat=2
	 amb_smoothContrastdown_Endat=3
	 amb_smoothContrastDown#=.95
	 amb_SmoothContrastup_Startat=3
	 amb_SmoothContrastup_Endat=5
	 amb_SmoothContrastup#=1.5
     smooth_times=5
     lowest_Ambient=15
     x_cells=100
     y_cells=60
     z_cells=100

    ;another setting to try
    ; amb_smoothContrastdown_Startat=3
	; amb_smoothContrastdown_Endat=5
	; amb_smoothContrastDown#=.95
	; amb_SmoothContrastup_Startat=1
	; amb_SmoothContrastup_Endat=5
	; amb_SmoothContrastup#=1.01
    ; smooth_times=5
    ; lowest_Ambient=30
    ; x_cells=120
    ; y_cells=90
    ; z_cells=120

     ;prep and create flipped mesh copy 
	 Amb_prepMeshBeforeCreation(mesh)
    
     ;//the following two calls accomplish the same thing, but can be useful to use area if youre level mesh is not centered in global space 
	 ;//you can specify how many smooths to do in the creaion.. or you can do it later with a direct call to smoothambientvolume()

	 ;createambientvolume_area(-1660,-1250,-1700,1660,1250,1700,smooth_times,lowest_ambient,x_cells,y_cells,z_cells) 
     ;CreateblankAmbientVolume	(mesh,Smooth_Times,Lowest_Ambient,x_cells,y_cells,z_cells)
     CreateAmbientVolume_SizeOfMesh	(mesh,Smooth_Times,Lowest_Ambient,x_cells,y_cells,z_cells)
     ;remove flipped mesh copy    
	 Amb_freesparemeshes()

     ;save and apply the volume
	 saveambientvolume ("ambientvolume.dat")
	 ApplyAmbientVolumeToMesh(mesh)

 Else 
 
     ;if the file exists load and apply volume 
	 loadambientvolume("ambientvolume.dat")
	 ApplyAmbientVolumeToMesh(mesh)
 EndIf



While Not KeyDown(1)
	;hit SPACEBAR  to see with and without volume applied to level mesh
	If KeyHit(57) Then
         displayamb=displayamb-1
         If displayamb<=0 Then displayamb=3
         Select displayamb
         Case 2
             FreeEntity mesh 
			 mesh=LoadMesh("cabin.b3d")
			 EntityPickMode mesh,2
             
			removeambientvolumefrommesh(mesh,0)
			EntityFX mesh,2
         Case 3
			removeambientvolumefrommesh(mesh,1)
  		    applyambientvolumetomesh(mesh)
			EntityFX mesh,2
 		 Case 1
 			 applyambientvolumetomesh(mesh)
			EntityFX mesh,2
		End Select         
	EndIf

    ;press 1 on the keyboard to drop a random colored volume light
	If KeyHit(2) Then
        ;go ahead and prep main mesh (make flipped copy) to help the light accuracy (needs all the help it can get)
        Amb_prepMeshBeforeCreation(mesh)
		r=Rand(120,255)
		g=Rand(120,255)
		b=Rand(120,255)
	    Amb_InsertLight(EntityX(camera),EntityY(Camera),EntityZ(Camera),350,r,g,b)
	    Amb_freesparemeshes()
    	applyambientvolumetomesh(mesh)
	EndIf

    ;allow camera movement with the mouse buttons
	MoveEntity Camera,(KeyDown(205)-KeyDown(203))*5,0,(MouseDown(1)-MouseDown(2))*5
	TurnEntity Camera,MouseYSpeed()*0.1,-MouseXSpeed()*0.1,0
	RotateEntity Camera,EntityPitch(Camera,True),EntityYaw(Camera,True),0

    ;use amb_entitycolor() to show how an enity can be shaded by the ambient volume
	amb_entitycolor(Avatar)
    ;position mouse in middle so mouselook can work
	MoveMouse GraphicsWidth()*.5,GraphicsHeight()*.5
	UpdateWorld()
	RenderWorld()
	Flip
Wend
amb_vol_clearall()
EndGraphics()
End
;//----------------------------------------------------------       ----END TEST---
;//----------------------------------------------------------       ----END TEST---
;//----------------------------------------------------------       ----END TEST---

Comments

RifRaf2011
;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;Ambient volume dynamic shading lib. by Jeff Frazier ( rifraf )
;Aug, 2011
;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
;////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
; if you improve on this lib , please update me ( gamemaker04@... ) and/or repost in the original code archives entry 
; thank you.
;/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


Dim ambientVolume(0,0,0)
Dim ambientVolumeSmooth(0,0,0)
Global amb_brightest=255


;the following four globals are used in SmoothAmbientVolume()
;to allow contrasting freedoms per volume created, please review the
;smoothing loop "for rep=" in that function to see how these play a part
Global amb_smoothContrastdown_Endat=5
Global amb_smoothContrastdown_Startat=2
Global amb_smoothContrastDown#=.95
Global amb_SmoothContrastup_Startat=3
Global amb_SmoothContrastup_Endat=5
Global amb_SmoothContrastup#=1.2

;THE DIVISION FOR CURVING THE COLOR OF SHADED ENTITIES
;LARGER NUMBERS = SMOOTHING COLOR TRANSITIONS.. 
Const AMB_ColorShiftFrames=30

Global Amb_default_Ambient%=40

;Used to store temporaty flipped mesh copies before a createvolume() call
Type Amb_flippedmesh
  Field ent
End Type

;used as return values from Amb_splitcolors()
Global amb_split_red%
Global amb_split_green%
Global amb_split_blue%

;Stores an entities scale factors when you call Amb_getmeshscale()
Global amb_XScale#
Global amb_YScale#
Global amb_ZScale#

;used as return values from createambientvolume() and loadambientvolume(()
;these pertain the the volume dementions and space it takes up
Global AMB_Vol_MaxGridX=1
Global AMB_Vol_MaxGridY=1
Global AMB_Vol_MaxGridZ=1
Global AMB_VOL_SPACEX#=1
Global AMB_VOL_SPACEY#=1
Global AMB_VOL_SPACEZ#=1

;used as return values from createambientvolume() and loadambientvolume(()
;these values are needed to use Amb_InsertLight()
Global Amb_width#
Global Amb_Height#
Global Amb_Depth#

;Return values from the coordinate convertion functions
Global Grid_getx#
Global Grid_gety#
Global Grid_getz#
Global ent_Gridx%
Global ent_Gridy%
Global ent_Gridz%


;used store entities that are colored via Amb_Entitycolor() but the type list is handled by the lib
Type Amb_RGB
	Field r
	Field g
	Field b
End Type



Function Delete_AmbEntityColor(ent)
    ;NOTE if you free an entity that could have a amb_rbg type assosiated with it then
    ;you must call this function before you free the entity else the next call here 
    ;for that entity will MAV
	This.amb_RGB = Object.amb_RGB( EntityName( Ent ) )
	If this<>Null Then Delete this
End Function 


Global Recent_EntityRed%,Recent_EntityGreen%,Recent_EntityBlue%
Function amb_entitycolor_update(ent,r,g,b)

	This.amb_RGB = Object.amb_RGB( EntityName( Ent ) )
	If this<>Null Then 
    	recent_entityred%=   amb_curve(r,this\r,AMB_ColorShiftFrames)
	    recent_entitygreen%= amb_curve(g,this\g,AMB_ColorShiftFrames)
    	recent_entityblue%=  amb_curve(b,this\b,AMB_ColorShiftFrames)
	    Amb_EntityAnimColor ent,Recent_EntityRed%,Recent_EntityGreen%,Recent_EntityBlue%
    	this\r=Recent_EntityRed%
	    this\g=Recent_EntityGreen%
    	this\b=Recent_EntityBlue%
	Else
		This.amb_RGB = New amb_RGB
		This\R = r
		This\G = g
		This\B = b
		NameEntity Ent, Handle( This )
		Amb_EntityAnimColor ent,r,g,b
	EndIf
End Function 

;use this on entities that will be moving around so they can change shades smoothly
Function amb_entitycolor(ent)
	GetEntityGrid(ent)
	amb_splitcolors(ambientvolume(ent_gridx,ent_gridy,ent_gridz))
	amb_entitycolor_update ent,amb_split_Red,amb_split_green,amb_split_blue
End Function 

;use this one if you dont want gradually shade shifting.. for example on scenery items dropped into place 
;durring map loading.
Function amb_entitycolor_instant(ent,plusr=-1,plusg=-1,plusb=-1)
 EntityFX ent,0
 EntityBlend ent,1
	PositionEntity projectilepivot,EntityX(ent,1),EntityY(Ent,1)+10,EntityZ(Ent,1)
	GetEntityGrid(projectilepivot)
	amb_splitcolors(ambientvolume(ent_gridx,ent_gridy,ent_gridz))
    If plusr>-1 Or plusg>-1 Or plusb>-1 Then
	    Newr#=((amb_split_red+plusr)/2)
	    Newg#=((amb_split_green+plusg)/2)
    	Newb#=((amb_split_blue+plusb)/2)
    Else
        newr=amb_split_red
        newg=amb_split_green
        newb=amb_split_blue
    EndIf
	Amb_EntityAnimColor ent,newr,newg,newb
End Function 

;returns where an entity is in the volume array
;storing the result in  ent_gridx,ent_gridy,ent_gridz
Function GetEntityGrid(entity)
    gx#=EntityX(entity,1)
    gy#=EntityY(entity,1)+4
    gz#=EntityZ(entity,1)
	ent_gridX=Int((gx/AMB_VOL_SPACEX))+(AMB_Vol_MaxGridX/2)
	ent_gridY=Int((gy/AMB_VOL_SPACEY))+(AMB_Vol_MaxGridY/2)
	ent_gridZ=Int((gz/AMB_VOL_SPACEZ))+(AMB_Vol_MaxGridZ/2)
    If ent_gridx<0 Or ent_gridx>AMB_Vol_MaxGridx Then ent_gridx=0
    If ent_gridy<0 Or ent_gridy>AMB_Vol_MaxGridy Then ent_gridy=0
    If ent_gridz<0 Or ent_gridz>AMB_Vol_MaxGridz Then ent_gridz=0
End Function 

;returns the 3d space real coordinates of a volume array space
;stores the result in grid_getx,grid_getx,grid_getz
Function GridToCoords(gx,gy,gz)
	grid_getx#=((gx*AMB_VOL_SPACEX)-(AMB_Vol_MaxGridX/2)*AMB_VOL_SPACEX)
	grid_gety#=((gy*AMB_VOL_SPACEY)-(AMB_Vol_MaxGridY/2)*AMB_VOL_SPACEY)
	grid_getz#=((gz*AMB_VOL_SPACEZ)-(AMB_Vol_MaxGridZ/2)*AMB_VOL_SPACEZ)
End Function 

;returns the volume array space of a real global x,y,z coordinate
;storing the result in  ent_gridx,ent_gridy,ent_gridz
Function CoordsToGrid(gx#,gy#,gz#)
	ent_gridX=Int((gx/AMB_VOL_SPACEX))+(AMB_Vol_MaxGridX/2)
	ent_gridY=Int((gy/AMB_VOL_SPACEY))+(AMB_Vol_MaxGridY/2)
	ent_gridZ=Int((gz/AMB_VOL_SPACEZ))+(AMB_Vol_MaxGridZ/2)
   ;error check
    If ent_gridx<0 Or ent_gridx>AMB_Vol_MaxGridx Then ent_gridx=0
    If ent_gridy<0 Or ent_gridy>AMB_Vol_MaxGridy Then ent_gridy=0
    If ent_gridz<0 Or ent_gridz>AMB_Vol_MaxGridz Then ent_gridz=0
End Function 

Function CoordsToGridBetter(gx#,gy#,gz#)
    ;this snaps the coors to grid, but also checks the vertex coordinat distance from all
    ;neightbor grids to make sure we arent landing near the line of a neighbor cell and are closer to its
    ;center than the default snap location. . used in creating lights.. but not for real time dynamic entity shading
	ent_gridX=Int((gx/AMB_VOL_SPACEX))+(AMB_Vol_MaxGridX/2)
	ent_gridY=Int((gy/AMB_VOL_SPACEY))+(AMB_Vol_MaxGridY/2)
	ent_gridZ=Int((gz/AMB_VOL_SPACEZ))+(AMB_Vol_MaxGridZ/2)
    Dister#=999999
    For GridX=-2 To 2
    	For Gridz=-2 To 2
    		For gridY=-2 To 2
              gridtocoords(ent_gridx+gridx,ent_gridy+gridy,ent_gridz+gridz)
              checkd#= amb_distance(grid_getx,grid_Gety,grid_getz,gx,gy,gz)		
              If checkd#<dister# Then
                 dister#=checkd#
                 winnerx=ent_gridx+gridx                 	
                 winnery=ent_gridy+gridy                 	
                 winnerz=ent_gridz+gridz                 	
             EndIf
            Next
         Next
    Next
   ;error check
    ent_gridx=winnerx
    ent_gridy=winnery
    ent_gridz=winnerz
    If ent_gridx<0 Or ent_gridx>AMB_Vol_MaxGridx Then ent_gridx=0
    If ent_gridy<0 Or ent_gridy>AMB_Vol_MaxGridy Then ent_gridy=0
    If ent_gridz<0 Or ent_gridz>AMB_Vol_MaxGridz Then ent_gridz=0
End Function 


;save the volume for later use.. much faster than recalculating each time you need it 
Function SaveAmbientVolume(fn$)
	F=WriteFile(fn$)
	WriteFloat f,Amb_width#
	WriteFloat f,Amb_Height#
	WriteFloat f,Amb_Depth#

	WriteInt F,AMB_Vol_MaxGridX
	WriteInt F,AMB_Vol_MaxGridY
	WriteInt F,AMB_Vol_MaxGridZ
	WriteInt F,AMB_VOL_SPACEX
	WriteInt F,AMB_VOL_SPACEY
	WriteInt F,AMB_VOL_SPACEZ
    For i=0 To AMB_Vol_MaxGridX
   		 For ii=0 To AMB_Vol_MaxGridY
			 For iii=0 To AMB_Vol_MaxGridZ
			   WriteInt f,ambientvolume(i,ii,iii)
			 Next
		 Next
	Next
	CloseFile f
	Return 
End Function 



;loading of volume
Function LoadAmbientVolume(fn$,scalex#=1.0,scaley#=1.0,scalez#=1.0,loadpower#=1.0)
 If FileType(fn$)=0 Then 
     Use_AmbVolume=False
     Return
  EndIf  
 F=ReadFile(fn$)
 Amb_width# = ReadFloat(f)*scalex#
 Amb_Height#= ReadFloat(f)*scaley#
 Amb_Depth# = ReadFloat(f)*scalez#
 AMB_Vol_MaxGridX=ReadInt(F)
 AMB_Vol_MaxGridY=ReadInt(F)
 AMB_Vol_MaxGridZ=ReadInt(F)
 AMB_VOL_SPACEX=ReadInt(F)*scalex
 AMB_VOL_SPACEY=ReadInt(F)*scaley
 AMB_VOL_SPACEZ=ReadInt(F)*scalez
 Dim AMBIENTVOLUME(AMB_Vol_MaxGridX,AMB_Vol_MaxGridY,AMB_Vol_MaxGridZ)
 For ax=0 To AMB_Vol_MaxGridX
  For ay=0 To  AMB_Vol_MaxGridY
  For az=0 To  AMB_Vol_MaxGridZ
    cv=ReadInt(f)
    amb_splitcolors(cv)
    amb_split_red=amb_split_Red*loadpower
    amb_split_green=amb_split_green*loadpower
    amb_split_blue=amb_split_blue*loadpower
    cv=amb_Tricolor(amb_split_Red,amb_split_green,amb_split_blue)
    ambientVolume(ax,ay,az)=cv
  Next
  Next
 Next
CloseFile f
Return
End Function 



;The slowest part of this library, smooths a newly created volume to look much much better
Function SmoothAmbientVolume(amount=1,offset#=1)
	Dim ambientVolumeSmooth(AMB_Vol_MaxGridX,AMB_Vol_MaxGridy,AMB_Vol_MaxGridz)
For rep=1 To amount
totalseen=0
newr=0
newg=0
newb=0
multi#=1.0
 For ax=1 To AMB_Vol_MaxGridX-1
 	For ay=1 To  AMB_Vol_MaxGridY-1
	  For az=1 To  AMB_Vol_MaxGridZ-1
			amb_splitcolors(ambientvolume(ax,ay,az))
	            r0=amb_split_Red
    	        g0=amb_split_green
        	    b0=amb_split_blue
				    ;newr=newr+r0
                   ; newg=newg+g0
                   ; newb=newb+b0
					;totalseen=totalseen+1
       ;     If r0=255 And b0=255 And g0=255 Then 
                    If REP=>amb_smoothContrastdown_Startat And rep<= amb_smoothContrastdown_endat Then 	multi#= amb_smoothContrastDown# ;.95
					;Else 
					If REP=>amb_SmoothContrastup_Startat And rep<= amb_SmoothContrastup_endat Then multi=amb_SmoothContrastup# ;1.1
        ;    EndIf

	   		If Grids_Inview(ax,ay,az,ax+1,ay,az) Then
				    amb_splitcolors(ambientvolume(ax+1,ay,az))
		            r1=amb_split_Red
    		        g1=amb_split_green
        		    b1=amb_split_blue
                    newr=newr+(r1*multi#)
                    newg=newg+(g1*multi#)
                    newb=newb+(b1*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax-1,ay,az) Then
					amb_splitcolors(ambientvolume(ax-1,ay,az))
		            r2=amb_split_Red
    		        g2=amb_split_green
        		    b2=amb_split_blue
                    newr=newr+(r2*multi#)
                    newg=newg+(g2*multi#)
                    newb=newb+(b2*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax,ay,az+1) Then
				    amb_splitcolors(ambientvolume(ax,ay,az+1))
		            r3=amb_split_Red
    		        g3=amb_split_green
        		    b3=amb_split_blue
                    newr=newr+(r3*multi#)
                    newg=newg+(g3*multi#)
                    newb=newb+(b3*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax,ay,az-1) Then
				    amb_splitcolors(ambientvolume(ax,ay,az-1))
		            r4=amb_split_Red
    		        g4=amb_split_green
        		    b4=amb_split_blue
                    newr=newr+(r4*multi#)
                    newg=newg+(g4*multi#)
                    newb=newb+(b4*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax+1,ay,az-1) Then
				    amb_splitcolors(ambientvolume(ax+1,ay,az-1))
		            r5=amb_split_Red
    		        g5=amb_split_green
        		    b5=amb_split_blue
                    newr=newr+(r5*multi#)
                    newg=newg+(g5*multi#)
                    newb=newb+(b5*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax+1,ay,az+1) Then
				    amb_splitcolors(ambientvolume(ax+1,ay,az+1))
			        r6=amb_split_Red
    	    	    g6=amb_split_green
        	    	b6=amb_split_blue
                    newr=newr+(r6*multi#)
                    newg=newg+(g6*multi#)
                    newb=newb+(b6*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax-1,ay,az-1) Then
				    amb_splitcolors(ambientvolume(ax-1,ay,az-1))
		            r7=amb_split_Red
    		        g7=amb_split_green
        		    b7=amb_split_blue
                    newr=newr+(r7*multi#)
                    newg=newg+(g7*multi#)
                    newb=newb+(b7*multi#)
					totalseen=totalseen+1
			EndIf
			If Grids_Inview(ax,ay,az,ax-1,ay,az+1) Then
				    amb_splitcolors(ambientvolume(ax-1,ay,az+1))
		            r8=amb_split_Red
    		        g8=amb_split_green
        		    b8=amb_split_blue
                    newr=newr+(r8*multi#)
                    newg=newg+(g8*multi#)
                    newb=newb+(b8*multi#)
					totalseen=totalseen+1
			EndIf
			If totalseen>0 Then 
            newr=(newr/totalseen)*offset
            newg=(newg/totalseen)*offset
            newb=(newb/totalseen)*offset
			EndIf
			totalseen=0
            If newr>255 Then newr=255
            If newg>255 Then newg=255
            If newb>255 Then newb=255

            If newr<amb_default_Ambient Then newr=amb_default_ambient
            If newg<amb_default_Ambient Then newg=amb_default_ambient
            If newb<amb_default_Ambient Then newb=amb_default_ambient

            ambientvolumesmooth(ax,ay,az)=amb_tricolor(newr,newg,newb)
		    Next
    Next
    
  Next 
 For ax=1 To AMB_Vol_MaxGridX-1
 	For ay=1 To  AMB_Vol_MaxGridY-1
	  For az=1 To  AMB_Vol_MaxGridZ-1
            ambientvolume(ax,ay,az)=ambientvolumesmooth(ax,ay,az)
      Next
    Next
 Next

Next;rep
  Dim ambientVolumeSmooth(0,0,0)
End Function 

Function CreateAmbientVolume_Area(x1#,y1#,z1#,x2#,y2#,z2#,Smoothing=3,AmbC=30,maxx=90,maxy=90,maxz=90,replacemesh=0)
tempmesh=CreateCube()
ScaleMesh tempmesh,1,1,1
sizex#=x2-x1
sizey#=y2-y1
sizez#=z2-z1
FitMesh tempmesh,x1,y1,z1,sizex,sizey,sizez,False
FlipMesh tempmesh
EntityColor tempmesh,255,0,0
CreateAmbientVolume_SizeOfMesh(tempmesh,Smoothing,AmbC,maxx,maxy,maxz)
FreeEntity tempmesh 
End Function 

Function Amb_prepMeshBeforeCreation(mesh,flipit=True)
    ;//NOTICE YOU WILL AT LEAST WANT TO RUN THIS ON YOUR MAIN LEVEL MESH
    ;//BECORE THE CREATION PROCESS.. it is not required though for a load from file ambvolume

	;copy the mesh and flip it
	;our smoothing is done by doing vision checks from one cell to the other
	;these checks will be inacurate if the map mesh is one sided
	;any objects that you want to inpact the volume creation need to have a pickmode of 2 
    ;however large complex objects should be ran through this prepmesh function 

    ;basically we record a flipped version of the entity so that picks dont go though walls if they are
    ;going from the backface to a light source , and proper light source obsuring accurs
     Ambf.Amb_flippedmesh=New Amb_flippedmesh
	 ambf\ent=CopyMesh (mesh)
	 If flipit=True Then FlipMesh ambf\ent
	 EntityPickMode ambf\ent,2
	 PositionEntity ambf\ent,EntityX(mesh,1),EntityY(mesh,1),EntityZ(mesh,1)
	 RotateEntity ambf\ent,EntityPitch(mesh,1),EntityYaw(mesh,1),EntityRoll(mesh,1)
	 EntityPickMode mesh,2
End Function 


Function Amb_FreeSpareMeshes()
     For Ambf.Amb_flippedmesh=Each Amb_flippedmesh
		FreeEntity ambf\ent
        Delete ambf
     Next		 
End Function 

Function CreateBlankambientvolume(Mesh,Smoothing=3,AmbC=30,maxx=90,maxy=90,maxz=90)
	Amb_width# = MeshWidth(mesh)*4
	Amb_Height#= MeshHeight(mesh)*4
	Amb_Depth# = MeshDepth(mesh)*4
	;The default setting gives you 90 cells on all axi = 90x90x90 cell resoluton
	;all created around your mesh. This consumes about 6 megs or ram, and a 6 meg save file
	;you can reduce this my reducing the cell counts on one or all axis.  Be careful using higher values
	;as theres no limit to the ram you can use.. ive used up to 400 megs on very high res volumes
	;those sizes also take hours to calculate and smooth. On my system this default volume res takes about 1 minute

	;copy the mesh and flip it
	;our smoothing is done by doing vision checks from one cell to the other
	;these checks will be inacurate if the map mesh is one sided
	;also any objects that you want to inpact the volume creation need to have a pickmode of 2 before you call this 
	;function

	Amb_default_Ambient=ambc
	If abv_amb<0 Then amv_ambient=0
	If abv_amb>255 Then amv_ambient=255

	AMB_Vol_MaxGridX=MAXX
	AMB_Vol_MaxGridY=MAXY
	AMB_Vol_MaxGridZ=MAXZ

	AMB_VOL_SPACEX=amb_width  / (maxx/2)
	AMB_VOL_SPACEY=amb_height/ (maxy/2)
	AMB_VOL_SPACEZ=amb_depth  / (maxz/2)

	Dim AMBIENTVOLUME(AMB_Vol_MaxGridX,AMB_Vol_MaxGridY,AMB_Vol_MaxGridZ)
	;set all cells to default ambient color
	For ix=0 To maxx:For iy=0 To maxy:For iz=0 To maxz
		ambientvolume(ix,iy,iz)=amb_tricolor(Amb_default_Ambient,Amb_default_Ambient,Amb_default_Ambient)
	Next:Next:Next

End Function 

Function amb_FillVolumeColor(Cvalr,cvalg,cvalb)
 For i=0 To amb_col_maxgridx
	 For ii=0 To amb_col_maxgridx
		 For ii=0 To amb_col_maxgridx
			ambientvolume(i,i,ii)=amb_tricolor(cvalr,cvalg,cvalb)
		Next
	Next
Next
End Function 

Function CreateAmbientVolume_SizeOfMesh(Mesh,Smoothing=3,AmbC=30,maxx=90,maxy=90,maxz=90)
	Amb_width# = MeshWidth(mesh)
	Amb_Height#= MeshHeight(mesh)
	Amb_Depth# = MeshDepth(mesh)
	;The default setting gives you 90 cells on all axi = 90x90x90 cell resoluton
	;all created around your mesh. This consumes about 6 megs or ram, and a 6 meg save file
	;you can reduce this my reducing the cell counts on one or all axis.  Be careful using higher values
	;as theres no limit to the ram you can use.. ive used up to 400 megs on very high res volumes
	;those sizes also take hours to calculate and smooth. On my system this default volume res takes about 1 minute

	;copy the mesh and flip it
	;our smoothing is done by doing vision checks from one cell to the other
	;these checks will be inacurate if the map mesh is one sided
	;also any objects that you want to inpact the volume creation need to have a pickmode of 2 before you call this 
	;function

	Amb_default_Ambient=ambc
	If abv_amb<0 Then amv_ambient=0
	If abv_amb>amb_brightest Then amv_ambient=amb_brightest

	AMB_Vol_MaxGridX=MAXX
	AMB_Vol_MaxGridY=MAXY
	AMB_Vol_MaxGridZ=MAXZ

	AMB_VOL_SPACEX=amb_width  / (maxx/2)
	AMB_VOL_SPACEY=amb_height/ (maxy/2)
	AMB_VOL_SPACEZ=amb_depth  / (maxz/2)

	Dim AMBIENTVOLUME(AMB_Vol_MaxGridX,AMB_Vol_MaxGridY,AMB_Vol_MaxGridZ)
	;set all cells to default ambient color
	For ix=0 To maxx:For iy=0 To maxy:For iz=0 To maxz
		ambientvolume(ix,iy,iz)=amb_tricolor(Amb_default_Ambient,Amb_default_Ambient,Amb_default_Ambient)
	Next:Next:Next
    ;pick down from just the top most Height of the volume.. where the pick lands.. we assign that column all the way 
    ;up from the pick to the top of the volume to full bright, then we smooth out the entire volume to finish it up
	For ix=0 To maxx:For iz=0 To maxz
		iy=maxy
        gridtocoords(ix,iy,iz)
        xloop=grid_getx#
        yloop=grid_gety#
        zloop=grid_getz#
		pk= LinePick(xloop,yloop+400,zloop,0 ,-7500 ,0 ,.1) 
        pkx#=PickedX()
        pky#=PickedY()+Float(amb_vol_spacey)*.2
        pkz#=PickedZ()
        coordstogrid(pkx,pky,pkz)
        If pk=0 Then ent_gridy=0
        For colmn=ent_gridy To maxy Step 1
        	ambientvolume(ent_gridx,colmn,ent_gridz)=amb_tricolor(amb_brightest,amb_brightest,amb_brightest)
        Next
        Next
	Next

	smoothambientvolume(smoothing)
End Function 


Function MatchVert_toEnt(mesh)
     ;if you entity has a rotation other than 0,0,0 or scale other than 1,1,1 you 
     ;must pass it though this if you want to use ApplyAmbientVolumetoMesh() so the verts will fall into
     ;the proper volmues and be colored correctly.
	 RotateMesh mesh,EntityPitch(mesh,1),EntityYaw(mesh,1),EntityRoll(mesh,1)
     amb_getmeshscale(mesh)
	 ScaleMesh mesh,amb_XScale#,amb_yScale#,amb_zccale#
     ;we dont need to positionmesh because we can use entityxyz offsets in the applyambientcolumetomesh() call
	 ;PositionMesh 
End Function



Function  ApplyAmbientVolumeToMesh(ent,power#=1.0)
 ;//NOTE this will not work on entiies that were copied via copyentity()
 ;to work on several copies you must use copymesh()
 EntityFX ent,2
 scount=CountSurfaces(ent)
 For sloop=1 To scount
     surf=GetSurface(ent,sloop) 
     v=CountVertices(surf)-1
     For vloop=0 To v
        vnx#=VertexNX(surf,vloop) 
        vny#=VertexNY(surf,vloop) 
        vnz#=VertexNZ(surf,vloop) 
     	vx#=VertexX(surf,vloop)+EntityX(ent)+(Sgn(vnx)*(AMB_VOL_SPACEx/2))  ;
     	vy#=VertexY(surf,vloop)+EntityY(ent)+(Sgn(vny)*(AMB_VOL_SPACEY/2))  ;move up or down slightly
     	vz#=VertexZ(surf,vloop)+EntityZ(ent)+(Sgn(vnz)*(AMB_VOL_SPACEz/2))  ;
		CoordsToGrid(vx#,vy#,vz#)
        gridtocoords(ent_gridx,ent_gridy,ent_gridz)
		amb_splitcolors(ambientvolume(ent_gridx,ent_gridy,ent_gridz))
   	    va#=VertexAlpha(surf,vloop)
		    finish_Red#=Float(amb_split_red)*power
            finish_green#=Float(amb_split_green)*power
            finish_blue#=Float(amb_split_blue)*power
            If finish_red>255 Then finish_Red=255
            If finish_green>255 Then finish_green=255
            If finish_blue>255 Then finish_blue=255
        VertexColor(surf,vloop,finish_Red,finish_green,finish_blue,va)
	
     Next
 Next

End Function         


;useless function I made for fun.. kept it in because you can really see the ambient volume mapping this way
Function  ToonmeshUV(ent)
 scount=CountSurfaces(ent)
 For sloop=1 To scount
     surf=GetSurface(ent,sloop) 
     v=CountVertices(surf)-1
     For vloop=0 To v
		VertexTexCoords (surf,vloop,.4,.4)
     Next
 Next
UpdateNormals(ent)
End Function 

;useless in a game, but useful in editing ambient volumes
Function  RemoveAmbientVolumeFromMesh(ent,cleartextures=0)
 scount=CountSurfaces(ent)
 For sloop=1 To scount
     surf=GetSurface(ent,sloop) 
     If cleartextures<>0 Then 
	     b=CreateBrush(255,255,255)
    	 PaintSurface surf,b
	     FreeBrush b
     EndIf
     v=CountVertices(surf)-1
     For vloop=0 To v
        va#=VertexAlpha(surf,vloop)
        VertexColor(surf,vloop,255,255,255,va)
     Next
 Next
End Function         



Function amb_tricolor(r,g,b)
    Return $FF000000 Or b Or g Shl 8 Or r Shl 16
End Function 

Function amb_splitcolors(val%)
		
			amb_Split_Red=  (val% Shr 16) And $ff	
			amb_split_Green=(val% Shr 8) And $ff
			amb_split_Blue=  val% And $ff
End Function 


Function Amb_Distance#( x#, y#, z#, x2#, y2#, z2# )
	value#=Sqr((x#-x2#)*(x#-x2#)+(y#-y2#)*(y#-y2#)+(z#-z2#)*(z#-z2#))
	Return value#
End Function

Function Amb_Distance2d#( v#, v2#)
	value#=Sqr((v#-v2#)*(v#-v2#))
	Return value#
End Function

Function Amb_Curve#(newvalue#,oldvalue#,increments#)
    If increments>1 Then oldvalue#=oldvalue#-(oldvalue#-newvalue#)/increments
	If increments<=1 Then oldvalue=newvalue
	Return oldvalue#
End Function


;///NOTE THIS FUNCTION COULD BE BETTER.. IF ANY SMART BLITZERS WANT TO IMPROVE ON THIS, PLEASE DO.
Function Amb_InsertLight(x#,y#,z#,range#,r,g,b)
;you must have any light obscuring meshes set to pick mode 2

;convert range from coordinate range To volume cell range
average_Space#=((AMB_VOL_SPACEX+AMB_VOL_SPACEZ+AMB_VOL_SPACEY)/3)
range= range / average_space

temppiv3=CreateCube()
temppiv2=CreateCube()
temppiv=CreateCube()


EntityRadius temppiv,1
EntityPickMode temppiv,1
ScaleEntity temppiv,1,1,1
EntityRadius temppiv2,1
EntityPickMode temppiv2,1
ScaleEntity temppiv2,1,1,1

;//AQUIRE 3D ASPECT -----------
        ;because the same amount of Cells represent varied length width and height 
		; of the light volume depending on the size of level mesh used
        ; we must sort out the 3D apsect ratio to get a circular light range  
		rangex=range
		rangey=range
		rangez=range
        If amb_width>amb_depth Then
           dif#=amb_width / amb_Depth  
           ratio_x#=1.0  
           ratio_z#=dif#  
           rangex=range
           rangez=range*dif 
           If amd_height<amb_width Then
           		dif#=amb_width / amb_height 
        	    rangey=range*dif 
	            ratio_y#=dif#  
 		   Else
 	           dif#=amb_height / amb_width  
               ratio_y#=1.0  
               ratio_x#=dif#  
    	       rangey=range
        	   rangex=range*dif 
          EndIf
        ;//
        Else
        ;// 
           dif#= amb_depth / amb_width
           rangez=range
           ratio_z#=1.0  
           ratio_x#=dif#  
           rangex=range*dif 
           If amd_height<amb_depth Then
           		dif#=amb_depth / amb_height 
        	    rangey=range*dif 
                ratio_y#=dif#
 		   Else
 	           dif#=amb_height / amb_depth
    	       rangey=range
        	   rangez=range*dif 
               ratio_y#=1.0
               ratio_z#=dif#  
          EndIf

        EndIf
        ;// ASPECT RATIOS CALCULATED CORRECTLY

        halfrangex=rangex/2 
        halfrangey=rangey/2 
        halfrangez=rangez/2 
        CoordsToGrid(x,y,z)
		;//LOOP THROUGH THE REGION
        For xl=ent_gridx-halfrangex To ent_gridx+halfrangex
	        For yl=ent_gridy+halfrangey To ent_gridy-halfrangey Step -1
				       For zl=ent_gridz-halfrangez To ent_gridz+halfrangez
    	                If xl>0 And xl< AMB_Vol_MaxGridX And yl>0 Or yl< AMB_Vol_MaxGridY And zl>0 And zl< AMB_Vol_MaxGridZ Then
							;ok inside the volume and inside the range of the light
							;//CALCULATE THE POWER OF THE LIGHT OUTWARD FROM ITS CENTER -----------
                            ;//PERHAPS  B3D MATH WIZARD CAN IMPROVE ON THIS ROUTING
                            ddx#=amb_distance2d(xl,ent_gridx)*(1.0/ratio_x)
                            ddy#=amb_distance2d(yl,ent_gridy)*(1.0/ratio_y)
                            ddz#=amb_distance2d(zl,ent_gridz)*(1.0/ratio_z)
                            dd#=(ddx+ddy+ddz)

                            power#=(1.2/range) * (dd/2)
                            If power#<0 Then power=0
                            If power>1 Then power=1

                        
                            amb_splitcolors(ambientvolume(xl,yl,zl))

                            totalr#=Float(amb_split_red)*  (1.0-power)   
                            totalg#=Float(amb_split_green)*(1.0-power) 
                            totalb#=Float(amb_split_blue)* (1.0-power)  
							totalr=totalr + (Float(r)*power)
 							totalg=totalg + (Float(g)*power)
							totalb=totalb + (Float(b)*power)
                            
                             ;dont allow the light to bring a color below the ambient
                             If totalr<amb_split_red Then totalr=amb_split_Red
                             If totalg<amb_split_green Then totalg=amb_split_green
                             If totalb<amb_split_blue Then totalb=amb_split_blue

                             If totalr>255 Then totalr=255
                             If totalg>255 Then totalg=255
                             If totalb>255 Then totalb=255

                                

					 	    ;dy#=DeltaYaw(temppiv,temppiv2)
						    ;dp#=DeltaPitch(temppiv,temppiv2)

                             ;Since the volume is a rigid grid and mapping fluid flowing vertex patterns are not, then
                             ;you will likely get some vertex bleeding.. the below is my attempt to best set the
                             ;volumes for added colored lights so that mesh mapping can look alright.
  		                     ;gridtocoords(xl,yl,zl)
                             ;PositionEntity temppiv,x,y,z
                             ;PositionEntity temppiv3,x,y,z
                             ;PositionEntity temppiv2,grid_getx,grid_gety,grid_getz
                             ;PointEntity temppiv2,temppiv
                             ;PointEntity temppiv,temppiv2
                              ;dlp#=EntityDistance(temppiv,temppiv2)
                               ;  ;MoveEntity temppiv2,0,0,dlp/10 
                                ; MoveEntity temppiv,0,0,dlp/5
            				  	 ;If EntityPick (temppiv,2000)=temppiv2  Then
				 			  	 ;If EntityPick (temppiv2,2000)=temppiv  Then  
									ambientvolume(xl,yl,zl)=amb_tricolor(totalr,totalg,totalb)
								 ;EndIf	
				       		 ;EndIf
 						EndIf
				  Next
			Next
		Next

        FreeEntity temppiv
        FreeEntity temppiv2
        FreeEntity temppiv3

End Function 

;returns whether or not two grid spaces have a line of sight to each other from any of the straight lines
Function Grids_Inview(x1,y1,z1,x2,y2,z2)
   If grid_linepick(x1,y1,z1,x2,y2,z2)<>0 Then 
	   If grid_linepick(x1+1,y1,z1,x2,y2,z2)<>0 Then 
		   If grid_linepick(x1,y1,z1+1,x2,y2,z2)<>0 Then 
					   If grid_linepick(x1-1,y1,z1,x2,y2,z2)<>0 Then 
					   		If grid_linepick(x1,y1,z1-1,x2,y2,z2)<>0 Return	0
						EndIf
			EndIf
		EndIf
	EndIf
Return 1

End Function 

;performs a line pick from two volume cells(grids)
Function Grid_linepick(x1#,y1#,z1#,x2#,y2#,z2#)

		 overshotx=AMB_VOL_SPACEX
		 overshoty=AMB_VOL_SPACEy
		 overshotz=AMB_VOL_SPACEz
         GridToCoords(x1,y1,z1)
         x1#=grid_getx#
         y1#=grid_gety#
         z1#=grid_getz#

         GridToCoords(x2,y2,z2)
         x2#=grid_getx#
         y2#=grid_gety#
         z2#=grid_getz#
         dx#=Sgn(x2-x1)
         dy#=Sgn(y2-y1)
         dz#=Sgn(z2-z1)
 		 Pck=LinePick(x1-(dx*overshotx),Y1-(dy*overshoty),Z1-(dz*overshotz),dx+(dx*overshotx),dy+(dx*overshoty),dz+(dx*overshotz),.1)	



         Return pck+pck2
End Function 

;color an entity and all its children, you can filter out children by entityname$ .. if you dont want all children colored
Function Amb_EntityAnimColor(ent,r,g,b)
 EntityColor ent,r,g,b
 numc=CountChildren(ent)
 For c=1 To numc
  this=GetChild(ent,c)
  If EntityClass$(this)="Mesh" Then
	  ename$=Lower$(EntityName$(this))
; And Instr(ename$,"classbox")=0
;Instr(ename$,"hbar")=0
    	If ename$<>"flag"   And Instr(ename$,"rel")=0   And Instr(ename$,"rank")=0  Then
	  	EntityColor this,r,g,b
	  	amb_entityanimcolor this,r,g,b
	  EndIf
  EndIf
 Next
End Function 


Function amb_getmeshscale(mesh)
	vx# = GetMatElement#(Mesh, 0, 0)
	vy# = GetMatElement#(Mesh, 0, 1)
	vz# = GetMatElement#(Mesh, 0, 2)
	amb_XScale# = Sqr(vx# * vx# + vy# * vy# + vz# * vz#)
	vx# = GetMatElement#(Mesh, 1, 0)
	vy# = GetMatElement#(Mesh, 1, 1)
	vz# = GetMatElement#(Mesh, 1, 2)
	amb_YScale# = Sqr(vx# * vx# + vy# * vy# + vz# * vz#)
	vx# = GetMatElement#(Mesh, 2, 0)
	vy# = GetMatElement#(Mesh, 2, 1)
	vz# = GetMatElement#(Mesh, 2, 2)
	amb_ZScale# = Sqr(vx# * vx# + vy# * vy# + vz# * vz#)
End Function 

	
;free the lib
Function Amb_Vol_ClearAll()
 Amb_FreeSpareMeshes()
 Delete Each amb_rgb
 Dim ambientVolume(0,0,0)
 AMB_Vol_MaxGridX=1
 AMB_Vol_MaxGridY=1
 AMB_Vol_MaxGridZ=1
 AMB_VOL_SPACEX=1
 AMB_VOL_SPACEY=1
 AMB_VOL_SPACEZ=1
End Function 



RifRaf2012
ok I updated this lib.

Above in the first post is the testing code, just add your own model to try it. A level map works best

The second post is the library itself "amb_volume.bb"


Blitzplotter2012
This is very nice code - I've been mulling over how to create some terrain from some GPS data - this nicely fits the bill. I just need to invoke some logic to even out the 'spikes'....


Code Archives Forum