lightmapping question

Blitz3D Forums/Blitz3D Programming/lightmapping question

I am currently doing some tests with a lightmapping code that I write from scratch, just to try out some ideas I had. Now to fix the problem of the socalled "bleeding seams" I will check the visiblity of a location seen from the lights position (and not the other way round), this solves the issue almost perfectly. A pivot on the surface of the mesh is checked with entityvisible from the lightsource.

But this won't work with alpha and masked textures. How would you mod the EntityVisible method to make it work with alpha and mask modes as well?

nevermind, got it almost working. No sophisticated maths, just a quick'n'dirty hack:

(basicly)
test if light can pick pivot. yes? it's fully lit.
no: entitycolor mapmesh black, fx17, do a 1 pixel renderworld towards light dummy mesh, other than black? no: must be obscured by opaque tri.
yes: must be alpha or masked texels: set entitycolor of mapmesh to white, render the pixel again, then multiply light color * renderd rgb.

There is still a strange problem when I use alpha and/or masked textures then I get eg. strange artefacts on the lightmap. Guess that's only a bug or so.

1 pixel renders? wow that's pretty sneaky ;)

What I did with my vertex lighting to prevent bleeding - or at least 'soften the blow' was to slightly 'blur' the map afterwards. Yes you will still have 'some' bleeding, but it (litterally) softens the effect and you can call it 'global illumation' instead.
And actually I liked the effect of light being able to 'bleed around the corner' just a little.
Perhaps you can use this with the lightmapping as well??

Just a thought..

-Danny

p.s. about the artifacts might that perhaps be because your pivot is EXACTLY IN the surface you're trying to test?
And get similar artifacts when you have 2 planes sharing the same space & try to render them?
If so, maybe offset the pivot by a fraction - along the normal of the surface ??

thanks Danny. I already have a little offset for the pivot, away from the surface normals. So it´s not a z-fight of shared locations.

The one-pixel render is actually pretty fast! But there ´s a problem when you want to detect a lights dummy mesh that is relative far away. Since the entire render is only one pixel, the light dummy mesh must be big enough to be seen. So the choice is: scale the light dummy mesh (may cause unwanted intersections) or use a very high camerazoom that makes distant things bigger in the render.

I do however get these artefacts, it's as if the lightmap is applied to the shady side of the masked parts of the mesh - I don't get it right now. Up to this point the whole thing was straight forward Brain>Code>Screen, but this is really confusing me.

(Simple) global illuination is perfomed in a very easy way: simply take a 32*32 pixel render from the surface pivots position, the cam aligned to the tris normals, then calculate the average RGB of the render, mix this with the other lightmapping texel data. Mixing is a littke tricky since you don't want to add this, so you have to subtract the relative rgb amount to "tone" the texel. It may also be a little darker after "toning" because a filter (like eg. stained glass) will always reduce the light more or less.

Bleeding is ok in some situations (eg. when it looks like a little dirt in the corners), but in some other moments it sucks. So I really wanted to get rid of it in the first place.

Antialiasing would be the first choice. Blur is an option, but it will also blur the shadow contours on geomerty.

I'm not sure, maybe I should post the source as it is. At the other hand I don't want to offend some people who are selling lightmappers around here.

That's pretty cool stuff you're doing with those little renders. Never considered tricks like that - although I've never done anything serious in the lighting department ;)

don't want to offend some people who are selling lightmappers around here
LOL! No you don't want the lightmapping industry to crumble, I can imagine - But I won't stop you ;)

Could you perhaps post an image (before/after?) of the effect you're trying to get rid of?
Is this only happening though alpha/masked textures? Again, I don't know anything about it, but could easily imagine that texture interpolation has a impact on it. Could you for example in stead of a 1 pixel render, do it with a 16x16 render and see if you get similar results (in the hope that a larger render will produce better texture interpolation/render and thus a better/more 'accurate' result???????????)

D.
p.s. You got Fog switched off right?! :)

I say post it, maybe it can be made faster than YAL

I say post it. I've always been interested in lightmapping and raytracing, all that sort of thing, but never had the time to really read up on it.

Well my code is a hack and I got to clean it up a little first, but if some of you will try to fix the strange artefacts issue then I´ll post it ASAP.

Is it some real time sort of thing? Are you rendering textures, or doing it per vertex?

I have YAL working, but it's not optimized at all.
I'm considering dividing the triangles up into 3d bounding boxes that make up the volume of the model, and test for ray collisions on those first.

I'm also considering the unique color scheme, where one applies huge 2048x2048 (or smaller) texture (unique colors), takes a picture from the light source, compares the colors that are seen, and makes them lit for the light map.

I think I need to unweld and remap the UV's so I can properly apply the texture to any b3d model to make it work. My experience with earlier experiments has shown while it works it's grainy and spotty like a bad photo.

Far away from realtime. A simple lightmapper with a GI feature. I was a bit tired of the YAL yet another lightmapper source since the coding style feels somewhat alien to me and I hardly can modify it productively, so I started something from scratch. But as I said, it still needs a ton of optimation and bugfixing.

Ok, here it is! I'd be really happy if some people help to solve the mentioned issues.

; WIP: Lightmapper with GI. By Dieter Marfurt. PickedUVW Functions provided by Mikkel Fredborg.

; The whole thing (at least my part) is rather a quick hack, not yet a useful program. 3 Things  
; have to be done next:
; - Get rid of the lightmap artefacts with masked textures (see red object) - this is where I need help.
; - Optimize Speed by about 10'000 percent :)
; - Add smart UV Packing (DDS is no excuse for not to do so)

; Other than that - have fun!

; Please note in the planar mapping function GOTO is used twice... this is a good exampe for
; a sophisticated, intelligent use of GOTO :o)



; Description of Functionality
; ----------------------------

; First the Mesh is unwelded, so all Triangles have their own 3 Vertices.
; Then a second UV Coordinates set is created, using Planar mapping, so every triangle has it's
; space on the lightmap Texture, relative to it's size in the world.
; THen each triangle is scanned with a given stepwidth. A pivot and a camera is used to
; check the visibility of the lights from every possible location on the surface of the tirangle. A linepick
; is then done for every scanned cell and Fredbords PickedU/V functions will tell us where to paint the texel
; on the texture.


Graphics3D 800,600,32,2
Global workbuffer
workbuffer=BackBuffer()
SetBuffer workbuffer


; some important variables
; *************************

; lightmapers scanner stepwidth. 1.0 would be equal the texture resolution, at least in theory
; (the higher the faster, eg. for draft mode, the lower, the more accurate)
Global sstep#=2.0 

Global csp_ambient=$111111 ; ambient light
Global nlites=1 ; number of randomly created lights (see below) ((zero to number!))
Global tex_maxsize#=512 ; lightmap texture size
Global margin=4 ; lightmappers margin in pixel, helps to prevent bleeding edges

; set global illumination  influence
Global col_shift#=2.1;2.3 ;  be carefull with this "afterburner" (lower=more GI)
Global gi_r=150 ; dominance: 0 to 255
Global gi_g=150
Global gi_b=150

; offset of the lightmappers visibility pivots to the surface
Global piv_offset#=0.001





; some variables used by fredborgs pickedUVW functions 
Type PickedTri
	Field ent,surf,tri				;picked entity, surface and triangle
	Field px#,py#,pz#			    ;picked xyz
	Field pu#[1],  pv#[1]  ,pw#[1]  ;picked uvw x 2
	
	Field vx#[2],  vy#[2]  ,vz#[2]  ;vertex xyz
	Field vnx#[2], vny#[2] ,vnz#[2] ;vertex normals
	Field vu#[5],  vv#[5]  ,vw#[5]  ;vertex uvw x 2
End Type
Global ptri.pickedtri = New pickedtri
; end of some variables used by fredborgs pickedUVW functions 


Const ssstep#=0.0001 ;dummy stepwidth to allow varibale FOR stepwidths



Global camera
camera=CreateCamera()
CameraRange camera,0.001,100

c_transz#=-10
c_transh#=4
TranslateEntity camera,0,c_transz#,c_transh#
CameraProjMode camera,0
Global minrel#=0



; create some random "lights" fr the test scene
Dim lites_pivot(nlites)
Dim lites_r#(nlites)
Dim lites_g#(nlites)
Dim lites_b#(nlites)

Dim lites_x#(nlites)
Dim lites_y#(nlites)
Dim lites_z#(nlites)
Dim lites_pitch#(nlites)
Dim lites_yaw#(nlites)
Dim lites_roll#(nlites)
Dim lites_range#(nlites)

For i=0 To nlites
 lites_pivot(i)=CreatePivot()
 dummy=CreateCube(lites_pivot(i)) ; this will be used for alpha and maskmode checks
 ScaleEntity dummy,.1,.1,.1
 lites_r(i)=155
 lites_g(i)=155
 lites_b(i)=155
 ; lites_r(i)=Rand(0,1)*255
 ; lites_g(i)=Rand(255)
 ; lites_b(i)=Rand(0,1)*255
 EntityColor dummy,lites_r(i),lites_g(i),lites_b(i)
 PositionEntity lites_pivot(i),Rnd(-10,10),Rnd(2,5),Rnd(-10,10)
 EntityPickMode lites_pivot(i),2
 lites_range#(i)=15;+Rnd(30)
 EntityFX dummy,1
Next


Global pivot=CreatePivot()
Global GI_cam, UV_cam
GI_cam=CreateCamera()
UV_cam=CreateCamera()
CameraProjMode GI_cam,0
CameraProjMode UV_cam,0


Global alpha_cam=CreateCamera()
CameraProjMode alpha_cam,0
CameraViewport alpha_cam,0,0,1,1
CameraZoom alpha_cam,100.0
CameraRange alpha_cam,0.0001,100




RotateEntity gi_cam,-90,0,0
CameraZoom gi_cam,0.85
EntityParent gi_cam,pivot
Global gi_res=16
CameraViewport gi_cam,0,0,gi_res,gi_res
CameraFogMode gi_cam,0
CameraFogRange gi_cam,0,100.0

Global mesh
mesh=csp_MyCreateDummyMap()








Global max_tris
Global x_min#,x_max#,y_min#,y_max#,z_min#,z_max#
Dim tris_arr_u#(0,0,0)
Dim tris_arr_v#(0,0,0)
Dim tris_arr_dir(0,0)
Dim tris_arr_centerxyz#(0,0,2)
Dim tris_arr_normals#(0,0,2)

Global lima_img 
lima_img=CreateImage(tex_maxsize,tex_maxsize)

mesh=csp_unweld(mesh)
EntityPickMode mesh,2
EntityFX mesh,16 ;Or 1
csp_planarmap(mesh)







; lightmap the map....
;*********************
EntityColor mesh, gi_r,gi_g,gi_b ; set global illumination level
EntityFX mesh,1 Or 16
csp_lightmap(mesh)
EntityFX mesh,0
lima=CreateTexture(tex_maxsize,tex_maxsize,48)
CopyRect 0,0,tex_maxsize,tex_maxsize,0,0,ImageBuffer(lima_img),TextureBuffer(lima)
EntityTexture mesh,lima,0,1
TextureCoords lima,1 
TextureBlend lima, 2;5
FreeImage lima_img


EntityColor mesh,255,255,255
EntityFX mesh,17

SetBuffer BackBuffer()

For i=0 To nlites
 ShowEntity lites_pivot(i)
Next





FreeEntity gi_cam
FreeEntity uv_cam
FreeEntity alpha_cam
CameraProjMode camera,1
; ------------- main --------------------------------------------------------------------------------
While KeyDown(1)=0

 PositionEntity camera,Sin(c_a#)*c_transz,c_transh,Cos(c_a#)*c_transz
 PointEntity camera,mesh
 c_a#=(c_a#+1) Mod 360.0
 RenderWorld()
 Flip
Wend

End
;--------------------------------------------------------------------------------------------------------------------------------------------------------








; this will apply planar-mapping to the 2nd UV channel of the mesh. THe biggest triangle will be used to 
; determine the max scaling factor world vs texture. THe function will then try to pack the triangles to the
; lightmap. If there isn't enough space it ill automaticly reduce the scaling factor and try again until
; all triangles fit in tex_maxsize*tex_maxsize.
; The packing is completely unoptimized (other than rotating the tris to a more horizontal shape). Since
; this function is pretty much independent from the lightmapper, the PACKING of the planar may be optimized
; without troubles with the rest of the code.
Function csp_planarmap(mesh)
 CameraProjMode uv_cam,1
;uv_pivot=CreatePivot()
 rel#=csp_lima_minrel#(mesh,tex_maxsize)
 minrel=rel
 csp_GetMaxTris(mesh)
 surfs=CountSurfaces(mesh)
 Dim tris_arr_u#(surfs,max_tris,2)
 Dim tris_arr_v#(surfs,max_tris,2)
 Dim tris_arr_centerxyz#(surfs,max_tris,2)
 Dim tris_arr_normals#(surfs,max_tris,2)

 Dim tris_arr_dir(surfs,max_tris)
 For s=1 To surfs
  surf=GetSurface(mesh,s)
  tris=CountTriangles(surf)
  ;Print max_tris
  ;Print tris
  ;Print "--------"
  For t=0 To tris-1
   ; dtermine primary axis
   x0#=VertexX(surf,TriangleVertex(surf,t,0))
   y0#=VertexY(surf,TriangleVertex(surf,t,0))
   z0#=VertexZ(surf,TriangleVertex(surf,t,0))
   TFormPoint x0,y0,z0,mesh,0 : x0=TFormedX() : y0=TFormedY() : z0=TFormedZ()
   x1#=VertexX(surf,TriangleVertex(surf,t,1))
   y1#=VertexY(surf,TriangleVertex(surf,t,1))
   z1#=VertexZ(surf,TriangleVertex(surf,t,1))
   TFormPoint x1,y1,z1,mesh,0 : x1=TFormedX() : y1=TFormedY() : z1=TFormedZ()
   x2#=VertexX(surf,TriangleVertex(surf,t,2))
   y2#=VertexY(surf,TriangleVertex(surf,t,2))
   z2#=VertexZ(surf,TriangleVertex(surf,t,2))
   TFormPoint x2,y2,z2,mesh,0 : x2=TFormedX() : y2=TFormedY() : z2=TFormedZ()


  ;  we're doing some darn good projectional mapping here! (better than what some tutorials suggest)
  ;  so our Tris will not be distorted on the texture.

   x4#=(x0+x1+x2)/3.0 ; tri center
   y4#=(y0+y1+y2)/3.0
   z4#=(z0+z1+z2)/3.0

   px#=x1-x0 ; determine tri normal
   py#=y1-y0
   pz#=z1-z0

   qx#=x2-x0
   qy#=y2-y0
   qz#=z2-z0

   nx#=(py*qz)-(pz*qy)
   ny#=(pz*qx)-(px*qz)
   nz#=(px*qy)-(py*qx)

   tris_arr_centerxyz#(s,t,0)=x4 ; uhm, probably not in use... however
   tris_arr_centerxyz#(s,t,1)=y4
   tris_arr_centerxyz#(s,t,2)=z4

   tris_arr_normals#(s,t,0)=nx
   tris_arr_normals#(s,t,1)=ny
   tris_arr_normals#(s,t,2)=nz

   PositionEntity uv_cam,x4,y4,z4
   AlignToVector uv_cam,nx,ny,nz,0
   RotateEntity uv_cam,EntityPitch(uv_cam),EntityYaw(uv_cam),0

   MoveEntity uv_cam,0,0,-rel#/4.0 ; make sure to sync this values with the same steps in csp_lightmap(). Probably better use a variable for 4.0...

   CameraProject uv_cam, x0,y0,z0
   u0#=ProjectedX()
   v0#=ProjectedY()
   CameraProject uv_cam, x1,y1,z1
   u1#=ProjectedX()
   v1#=ProjectedY()
   CameraProject uv_cam, x2,y2,z2
   u2#=ProjectedX()
   v2#=ProjectedY()
  

   xdis#=max(Abs(x0-x1),Abs(x0-x2))
   ydis#=max(Abs(y0-y1),Abs(y0-y2))
   zdis#=max(Abs(z0-z1),Abs(z0-z2))

   x_min=min(x_min,min3(x0,x1,x2))
   y_min=min(y_min,min3(y0,y1,y2))
   z_min=min(z_min,min3(z0,z1,z2))

   x_max=max(x_max,max3(x0,x1,x2))
   y_max=max(y_max,max3(y0,y1,y2))
   z_max=max(z_max,max3(z0,z1,z2))
   ;------------

   u_min=min(u_min,min3(u0,u1,u2))
   v_min=min(v_min,min3(v0,v1,v2))

   u_max=max(u_max,max3(u0,u1,u2))
   v_max=max(v_max,max3(v0,v1,v2))

   ;store each tris uvs relative to zero
   tris_arr_u(s,t,0)=u0   -min3(u0,u1,u2)
   tris_arr_u(s,t,1)=u1   -min3(u0,u1,u2)
   tris_arr_u(s,t,2)=u2   -min3(u0,u1,u2)

   tris_arr_v(s,t,0)=v0   -min3(v0,v1,v2)
   tris_arr_v(s,t,1)=v1   -min3(v0,v1,v2)
   tris_arr_v(s,t,2)=v2   -min3(v0,v1,v2)

   Color 255,0,0
   triLine tris_arr_u(s,t,0),tris_arr_v(s,t,0),tris_arr_u(s,t,1),tris_arr_v(s,t,1),tris_arr_u(s,t,2),tris_arr_v(s,t,2)

  Next
 Next


 Color 255,255,255
 Flip ; just some screen output for the user
 


 ; UV PACKER : feel free to optimize this A LOT!!
 ;pack tris on lm...
 largest_tri#=max(u_max-u_min,v_max-v_min)
 ;multiplyer To make sure even the largest tris fits on texture
 mply#=tex_maxsize#/largest_tri#

 .tryagain2 ; restart here if not all tris fit on texture (may use boolen search, instead of reducing scaling, tho this doesn´t seem to be the speed bottleneck here)
 Cls
 xoff#=margin
 yoff#=margin

 max_height#=0
 For s=1 To surfs
  ;surfs
  surf=GetSurface(mesh,s)
  tris=CountTriangles(surf)
  For t=0 To tris-1
   ; arrang tris horizontal
   height#=Abs(   max3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))-min3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))   ) ; tri höhe?
   csp_RotateUVs90Plus(s,t)
   height2#=Abs(   max3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))-min3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))   ) ; tri höhe nach einer 90 grad drehung

   If height2>height Then 
    csp_RotateUVs90Plus(s,t)
   EndIf
   height#=Abs(   max3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))-min3(tris_arr_v(s,t,0),tris_arr_v(s,t,1),tris_arr_v(s,t,2))   )
   If height>max_height Then max_height=height ; remeber max tris height on this line for next line break 

   width#=Abs(   max3(tris_arr_u(s,t,0),tris_arr_u(s,t,1),tris_arr_u(s,t,2))-min3(tris_arr_u(s,t,0),tris_arr_u(s,t,1),tris_arr_u(s,t,2))   )
   .tryagain ; jump here after a "line break"
   ; check if tris still fits on current "line"...
   x0#=(xoff+tris_arr_u(s,t,0)*mply)
   x1#=(xoff+tris_arr_u(s,t,1)*mply)
   x2#=(xoff+tris_arr_u(s,t,2)*mply)

   y0#=(yoff+tris_arr_v(s,t,0)*mply)
   y1#=(yoff+tris_arr_v(s,t,1)*mply)
   y2#=(yoff+tris_arr_v(s,t,2)*mply)

   If max3(x0,x1,x2)>tex_maxsize#-margin ; force linebreak!
    xoff=margin
    yoff=margin+yoff+max_height*mply
    max_height=0
    If KeyDown(1)=1 Then End
    Goto tryagain
   EndIf


   If max3(y0,y1,y2)>tex_maxsize#-margin ; tris won't fit all on texture!
    s=1
    mply=mply*0.99 ; reduce main xyz versus uv scaling factor
    max_height=0
    If KeyDown(1)=1 Then End ; panic button
    Goto tryagain2
   EndIf

   ; visually seek UVs
   TriLine(x0,y0,x1,y1,x2,y2)
   ; and set UVs!
   VertexTexCoords surf,TriangleVertex(surf,t,0),x0/tex_maxsize,y0/tex_maxsize ,0,1 
   VertexTexCoords surf,TriangleVertex(surf,t,1),x1/tex_maxsize,y1/tex_maxsize ,0,1
   VertexTexCoords surf,TriangleVertex(surf,t,2),x2/tex_maxsize,y2/tex_maxsize ,0,1

   xoff=margin+xoff+width*mply
  Next
 Next
 Rect 0,0,tex_maxsize,tex_maxsize,0
 CameraProjMode uv_cam,0

 csp_ShowBackbuffer()

 Color 0,255,0
 Locate 0,GraphicsHeight()-20
 Print "Pretty bad UV packing so far... Press a key to continue"
 Color 255,255,255
 WaitKey()

End Function








Function csp_ShowBackbuffer()
 CopyRect 0,0,GraphicsWidth(),GraphicsHeight(),0,0,BackBuffer(),FrontBuffer()
End Function





Function csp_lightmap(mesh)
 help=csp_CreateHelperQuad() ; used for "near-surface picking"
 Cls
 surfs=CountSurfaces(mesh)
 For s=1 To surfs
  surf=GetSurface(mesh,s)
  tris=CountTriangles(surf)
  For t=0 To tris-1

   x0#=VertexX(surf,TriangleVertex(surf,t,0))
   y0#=VertexY(surf,TriangleVertex(surf,t,0))
   z0#=VertexZ(surf,TriangleVertex(surf,t,0))
   TFormPoint x0,y0,z0,mesh,0
   x0=TFormedX():y0=TFormedY():z0=TFormedZ()

   x1#=VertexX(surf,TriangleVertex(surf,t,1))
   y1#=VertexY(surf,TriangleVertex(surf,t,1))
   z1#=VertexZ(surf,TriangleVertex(surf,t,1))
   TFormPoint x1,y1,z1,mesh,0
   x1=TFormedX():y1=TFormedY():z1=TFormedZ()

   x2#=VertexX(surf,TriangleVertex(surf,t,2))
   y2#=VertexY(surf,TriangleVertex(surf,t,2))
   z2#=VertexZ(surf,TriangleVertex(surf,t,2))
   TFormPoint x2,y2,z2,mesh,0
   x2=TFormedX():y2=TFormedY():z2=TFormedZ()


   x4#=(x0+x1+x2)/3.0 ; tri center
   y4#=(y0+y1+y2)/3.0
   z4#=(z0+z1+z2)/3.0

   px#=x1-x0 ; determine tri normal
   py#=y1-y0
   pz#=z1-z0

   qx#=x2-x0
   qy#=y2-y0
   qz#=z2-z0

   nx#=(py*qz)-(pz*qy)
   ny#=(pz*qx)-(px*qz)
   nz#=(px*qy)-(py*qx)


   PositionEntity uv_cam,x4,y4,z4
   AlignToVector uv_cam,nx,ny,nz,0
   RotateEntity uv_cam,EntityPitch(uv_cam),EntityYaw(uv_cam),0
   TurnEntity uv_cam,180,0,0


   PositionEntity help,x4,y4,z4
   AlignToVector help,nx,ny,nz,0
;   TurnEntity uv_cam,0,0,0 ; wt..?
   MoveEntity help,0,0,.001 



   MoveEntity uv_cam,0,0,-minrel/4.0 
   CameraRange uv_cam,(minrel/4.0)-.0001,(minrel/4.0)+.0001
   CameraProjMode uv_cam,1

   CameraProject uv_cam, x0,y0,z0
   u0#=ProjectedX()
   v0#=ProjectedY()
   CameraProject uv_cam, x1,y1,z1
   u1#=ProjectedX()
   v1#=ProjectedY()
   CameraProject uv_cam, x2,y2,z2
   u2#=ProjectedX()
   v2#=ProjectedY()
  
   CameraProjMode uv_cam,0

   up_min#=min3#(u0,u1,u2)
   vp_min#=min3#(v0,v1,v2)

   up_max#=max3#(u0,u1,u2)
   vp_max#=max3#(v0,v1,v2)

   pick_rad#=0.01;001
   texox#=0; pixel offset on texture to draw
   texoy#=0
   po#=0.01
   AppTitle "surf:"+s+" tri:"+t+" of "+surfs+"/"+tris +" ,v/u: "+v#+" / "+u#

    For v#=vp_min#-1.0 To vp_max#+1.0 Step ssstep# ; v
     For u#=up_min#-1.0 To up_max#+1.0 Step ssstep# ; u
      ;pick  helper quad
      EntityPickMode mesh,0
      EntityPickMode help,2
      p=CameraPick(uv_cam,u,v)
      EntityPickMode mesh,2
      EntityPickMode help,0

      If p=help Then 
       ; pick map tri
       ; remember projection location
       re_x#=EntityX(uv_cam,1)
       re_y#=EntityY(uv_cam,1)
       re_z#=EntityZ(uv_cam,1)
       ; repos camera on near-surface picked loaction
       PositionEntity uv_cam,PickedX(),PickedY(),PickedZ()
       p=CameraPick(uv_cam,GraphicsWidth()/2.0,GraphicsHeight()/2.0) ; pick map surface
       PositionEntity uv_cam,re_x,re_y,re_z,1 ; restore camera
      EndIf
      If KeyDown(1) Then End ; don´t panic :) 
      If p=mesh  Then ; picked main mesh?
       ptris=PickedTriangle()
       If ptris=t ; picked a to-be-scanned triangle?
        HideEntity help
        CameraProjMode uv_cam,0
        CameraProjMode gi_cam,1
        csp_lima_scancell() ; determine light factor on the surface location
        CameraProjMode uv_cam,1
        CameraProjMode gi_cam,0
        ShowEntity help
       EndIf
      EndIf
      u=u+(sstep-ssstep) ; force variable FOR stepwidth
     Next
     v=v+(sstep-ssstep) ; force variable FOR stepwidth
    Next
  Next
 Next
End Function






















; gets the light factor at a previously pickes location o n a surface 
Function csp_lima_scancell()
 PositionEntity pivot, PickedX(),PickedY(),PickedZ()
 AlignToVector(pivot,PickedNX(),PickedNY(),PickedNZ(),2)
 MoveEntity pivot,0,0,piv_offset#

 rgb1=csp_grab_gi() ; get global illumination factor

 PositionEntity alpha_cam, PickedX(),PickedY(),PickedZ()
 PositionEntity pivot, PickedX(),PickedY(),PickedZ()
 AlignToVector(pivot,PickedNX(),PickedNY(),PickedNZ(),0)
 MoveEntity pivot,0,0,piv_offset#
 For i=0 To nlites
  If EntityVisible(lites_pivot(i),pivot) ; can see light directly = no alpha or masks or opaque obstructors
   ; calc light intensity from the distance
   r0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*lites_r(i)
   g0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*lites_g(i)
   b0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*lites_b(i)

   If r0>255 Then r0=255
   If r0<0 Then r0=0
   If g0>255 Then g0=255
   If g0<0 Then g0=0
   If b0>255 Then b0=255
   If b0<0 Then b0=0

   rr0=rr0+r0
   gg0=gg0+g0
   bb0=bb0+b0
  Else ; may be obstructed, or alpha or mask..
   EntityColor mesh,0,0,0  ; (everything that is obstructing 100% will be totally black)
   EntityFX mesh,17
   PointEntity alpha_cam,lites_pivot(i)
   CameraProjMode alpha_cam,1
   CameraProjMode gi_cam,0
   ;SetBuffer BackBuffer()
   RenderWorld()
   argb=ReadPixel(0,0) And $ffffff
   argb2=0
   If argb <>0 ; is the rendered pixel not black? then it must be an alpha (or mask) texel!
    EntityColor mesh,127,127,127
    RenderWorld()
    argb2=ReadPixel(0,0) And $ffffff
   EndIf
   CameraProjMode alpha_cam,0 ; restore colors etc.
   CameraProjMode gi_cam,1
   EntityColor mesh,gi_r,gi_g,gi_b


   ;determine light factor based on distance to light source  relative to the rendered pixel color
   r0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*((argb2 Shr 16) And $FF)
   g0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*((argb2 Shr 8) And $FF)
   b0=(((lites_range#(i)-(EntityDistance(pivot,lites_pivot(i))))*5.0)/256.0)*((argb2) And $FF)

   If r0>255 Then r0=255
   If r0<0 Then r0=0
   If g0>255 Then g0=255
   If g0<0 Then g0=0
   If b0>255 Then b0=255
   If b0<0 Then b0=0

   rr0=rr0+r0
   gg0=gg0+g0
   bb0=bb0+b0

  EndIf
 Next


 r0=rr0;/(nlites+1)
 g0=gg0;/(nlites+1)
 b0=bb0;/(nlites+1)

 If r0>255 Then r0=255
 If r0<0 Then r0=0
 If g0>255 Then g0=255
 If g0<0 Then g0=0
 If b0>255 Then b0=255
 If b0<0 Then b0=0

 rgb0=(r0 Shl 16) Or (g0 Shl 8) Or (b0)

 ; mix ambient, global illumintaion light and point lights
 rgb=csp_toneRGB(rgb1,rgb0) ; point lights + gi
 rgb=csp_addRGB(rgb,csp_ambient) ; +ambient

 ; finally lot the lightmap texel (to an image for now, for speed reasons)
 Color 0,0,rgb
 SetBuffer ImageBuffer(lima_img)
 ; Plot pickedu(1)*(tex_maxsize#),pickedv(1)*(tex_maxsize#) ; single pixel is more accurate, but requires a higher scan resolution (takes much more time)
 Rect pickedu(1)*(tex_maxsize#)-1,pickedv(1)*(tex_maxsize#)-1,3,3,1            ; is quicker, but also not so accurate
 SetBuffer workbuffer
End Function



















; calculates Global Illumination from a render (camera etc. must be set uo correctly)
Function csp_grab_gi()
 RenderWorld()
 LockBuffer workbuffer
 r_#=0
 g_#=0
 b_#=0
  For j=0 To gi_res-1
   For i=0 To gi_res-1
    rgb=ReadPixelFast(i,j) And $ffffff
    r=(rgb And $ff0000) Shr 16
    g=(rgb And $ff00) Shr 8
    b=(rgb And $ff)
    r_=r_+r
    g_=g_+g
    b_=b_+b
   Next
  Next
 UnlockBuffer workbuffer
 r_=  r_/(Float(gi_res)^col_shift#)
 g_=  g_/(Float(gi_res)^col_shift#)
 b_=  b_/(Float(gi_res)^col_shift#)

 If r_>255 Then r_=255
 If g_>255 Then g_=255
 If b_>255 Then b_=255

 Return (r_ Shl 16) Or (g_ Shl 8) Or (b_)
End Function






Function csp_addRGB(rgb1,rgb2)
 r=(rgb1 Shr 16) + (rgb2 Shr 16)
 g=((rgb1 And $ff00) Shr 8) + ((rgb2 And $ff00) Shr 8)
 b=((rgb1 And $ff)) + ((rgb2 And $ff))

 If r>255 Then r=255
 If g>255 Then g=255
 If b>255 Then b=255

 rgb=(r Shl 16) Or (g Shl 8) Or (b)
 Return rgb
End Function





; teints a given color with the hue of an other color
Function csp_toneRGB(rgb1,rgb2)
 r1=(rgb1 Shr 16)
 g1=((rgb1 And $ff00) Shr 8)
 b1=((rgb1 And $ff))

 r2=(rgb2 Shr 16)
 g2=((rgb2 And $ff00) Shr 8)
 b2=((rgb2 And $ff))

 rgbmin=min3(r1,g1,b1)
 rgbmax=max3(r1,g1,b1)
 rgbdis=(rgbmax-rgbmin)/1.5;/2 ; /2.0 would be the same brightness, 1.5 makes it a little darker
 rp=(r1-(rgbmin+rgbdis))
 gp=(g1-(rgbmin+rgbdis))
 bp=(b1-(rgbmin+rgbdis))

 r=r2+rp
 g=g2+gp
 b=b2+bp

 If r>255 Then r=255
 If g>255 Then g=255
 If b>255 Then b=255

 If r<0 Then r=0
 If g<0 Then g=0
 If b<0 Then b=0

 rgb=(r Shl 16) Or (g Shl 8) Or (b)
 Return rgb
End Function









Function TriLine(x0,y0,x1,y1,x2,y2)
 Line x0,y0,x1,y1
 Line x1,y1,x2,y2
 Line x2,y2,x0,y0
End Function




; flips UVs (rather than rotating..)
Function csp_RotateUVs90Plus(s,t)
 rem_u#=tris_arr_u(s,t,0)
 tris_arr_u(s,t,0)=tris_arr_v(s,t,0)
 tris_arr_v(s,t,0)=rem_u#

 rem_u#=tris_arr_u(s,t,1)
 tris_arr_u(s,t,1)=tris_arr_v(s,t,1)
 tris_arr_v(s,t,1)=rem_u#

 rem_u#=tris_arr_u(s,t,2)
 tris_arr_u(s,t,2)=tris_arr_v(s,t,2)
 tris_arr_v(s,t,2)=rem_u#
End Function



Function csp_RotateUVsPlus(s,t)
 rem_u#=tris_arr_u(s,t,0)
 rem_v#=tris_arr_v(s,t,0)

 tris_arr_u(s,t,0)=tris_arr_u(s,t,1)
 tris_arr_v(s,t,0)=tris_arr_v(s,t,1) 

 tris_arr_u(s,t,1)=tris_arr_u(s,t,2)
 tris_arr_v(s,t,1)=tris_arr_v(s,t,2)

 tris_arr_u(s,t,2)=rem_u
 tris_arr_v(s,t,2)=rem_v
End Function





Function min3#(a#,b#,c#)
 If (a<=b) And (a<=c) Then Return a
 If (b<=a) And (b<=c) Then Return b
 Return c
End Function

Function max3#(a#,b#,c#)
 If (a>=b) And (a>=c) Then Return a
 If (b>=a) And (b>=c) Then Return b
 Return c
End Function




Function min#(a#,b#)
 If a<b Then Return a
 Return b
End Function

Function max#(a#,b#)
 If a>b Then Return a
 Return b
End Function






; saves the max triscount of any surface in the mesh in the global variable max_tris (waste od DIM ram, I know)
Function csp_GetMaxTris(mesh)
 max_tris=0
 surfs=CountSurfaces(mesh)
 For s=1 To surfs
  surf=GetSurface(mesh,s)
  tris=CountTriangles(surf)
  If tris >max_tris Then max_tris=tris
 Next
; Return max_tris
End Function







Function csp_unweld(mesh)

 mesh2=CreateMesh()
 surfs=CountSurfaces(mesh)
 For s=1 To surfs
  surf=GetSurface(mesh,s)

  brush2=GetSurfaceBrush(surf)
  surf2=CreateSurface(mesh2)
  PaintSurface surf2,brush2 
  ;surf2=GetSurface(mesh2,s)
  ;ClearSurface surf2,1,1

  tris=CountTriangles(surf)
  For t=0 To tris-1

   x0#=VertexX(surf,TriangleVertex(surf,t,0))
   y0#=VertexY(surf,TriangleVertex(surf,t,0))
   z0#=VertexZ(surf,TriangleVertex(surf,t,0))
   u0a#=VertexU(surf,TriangleVertex(surf,t,0),0)
   v0a#=VertexV(surf,TriangleVertex(surf,t,0),0)
   u0b#=VertexU(surf,TriangleVertex(surf,t,0),1)
   v0b#=VertexV(surf,TriangleVertex(surf,t,0),1)
   nx0#=VertexNX(surf,TriangleVertex(surf,t,0))
   ny0#=VertexNY(surf,TriangleVertex(surf,t,0))
   nz0#=VertexNZ(surf,TriangleVertex(surf,t,0))
   v0_r#=VertexRed(surf,TriangleVertex(surf,t,0))
   v0_g#=VertexGreen(surf,TriangleVertex(surf,t,0))
   v0_b#=VertexBlue(surf,TriangleVertex(surf,t,0))
   v0_a#=VertexAlpha(surf,TriangleVertex(surf,t,0))


   x1#=VertexX(surf,TriangleVertex(surf,t,1))
   y1#=VertexY(surf,TriangleVertex(surf,t,1))
   z1#=VertexZ(surf,TriangleVertex(surf,t,1))
   u1a#=VertexU(surf,TriangleVertex(surf,t,1),0)
   v1a#=VertexV(surf,TriangleVertex(surf,t,1),0)
   u1b#=VertexU(surf,TriangleVertex(surf,t,1),1)
   v1b#=VertexV(surf,TriangleVertex(surf,t,1),1)
   nx1#=VertexNX(surf,TriangleVertex(surf,t,1))
   ny1#=VertexNY(surf,TriangleVertex(surf,t,1))
   nz1#=VertexNZ(surf,TriangleVertex(surf,t,1))
   v1_r#=VertexRed(surf,TriangleVertex(surf,t,1))
   v1_g#=VertexGreen(surf,TriangleVertex(surf,t,1))
   v1_b#=VertexBlue(surf,TriangleVertex(surf,t,1))
   v1_a#=VertexAlpha(surf,TriangleVertex(surf,t,1))

   x2#=VertexX(surf,TriangleVertex(surf,t,2))
   y2#=VertexY(surf,TriangleVertex(surf,t,2))
   z2#=VertexZ(surf,TriangleVertex(surf,t,2))
   u2a#=VertexU(surf,TriangleVertex(surf,t,2),0)
   v2a#=VertexV(surf,TriangleVertex(surf,t,2),0)
   u2b#=VertexU(surf,TriangleVertex(surf,t,2),1)
   v2b#=VertexV(surf,TriangleVertex(surf,t,2),1)
   nx2#=VertexNX(surf,TriangleVertex(surf,t,2))
   ny2#=VertexNY(surf,TriangleVertex(surf,t,2))
   nz2#=VertexNZ(surf,TriangleVertex(surf,t,2))
   v2_r#=VertexRed(surf,TriangleVertex(surf,t,2))
   v2_g#=VertexGreen(surf,TriangleVertex(surf,t,2))
   v2_b#=VertexBlue(surf,TriangleVertex(surf,t,2))
   v2_a#=VertexAlpha(surf,TriangleVertex(surf,t,2))


   vv0=AddVertex(surf2,x0,y0,z0)
   VertexTexCoords surf2,vv0,u0a,v0a,0,0
   VertexTexCoords surf2,vv0,u0b,v0b,0,1
   VertexColor surf2,vv0,v0_r,v0_g,v0_b,v0_a
   VertexNormal surf2,vv0,nx0,ny0,nz0

   vv1=AddVertex(surf2,x1,y1,z1)
   VertexTexCoords surf2,vv1,u1a,v1a,0,0
   VertexTexCoords surf2,vv1,u1b,v1b,0,1
   VertexColor surf2,vv1,v1_r,v1_g,v1_b,v1_a
   VertexNormal surf2,vv1,nx1,ny1,nz1

   vv2=AddVertex(surf2,x2,y2,z2)
   VertexTexCoords surf2,vv2,u2a,v2a,0,0
   VertexTexCoords surf2,vv2,u2b,v2b,0,1
   VertexColor surf2,vv2,v2_r,v2_g,v2_b,v2_a
   VertexNormal surf2,vv2,nx2,ny2,nz2

   tri=AddTriangle(surf2,vv0,vv1,vv2) 

  Next
 Next


 FreeEntity mesh
 Return mesh2
End Function
;, ********************************************************************************************************************************



End



; freborgs pickedu und pickedv functions

;
; PickedU(), PickedV(), PickedW() commands 
;
; Created by Mikkel Fredborg
; 
; Use as you please, but please include a thank you :)
;

;
;
; Returns the Texture U coordinate of the last successful pick command
; coordset may be set to either 0 or 1
Function PickedU#(coordset = 0)
	
	; if something new has been picked then calculate the new uvw coordinates
	If (PickedX()<>ptri\px) Or (PickedY()<>ptri\py) Or (PickedZ()<>ptri\pz) Or (PickedSurface()<>ptri\surf)
		PickedUVW()
	End If
	
	Return ptri\pu[coordset]
	
End Function

;
; Returns the Texture U coordinate of the last successful pick command
; coordset may be set to either 0 or 1
Function PickedV#(coordset = 0)
	
	; if something new has been picked then calculate the new uvw coordinates
	If (PickedX()<>ptri\px) Or (PickedY()<>ptri\py) Or (PickedZ()<>ptri\pz) Or (PickedSurface()<>ptri\surf)
		PickedUVW()
	End If
	
	Return ptri\pv[coordset]
	
End Function

;
; Returns the Texture U coordinate of the last successful pick command
; coordset may be set to either 0 or 1
Function PickedW#(coordset = 0)
	
	; if something new has been picked then calculate the new uvw coordinates
	If (PickedX()<>ptri\px) Or (PickedY()<>ptri\py) Or (PickedZ()<>ptri\pz) Or (PickedSurface()<>ptri\surf)
		PickedUVW()
	End If
	
	Return ptri\pw[coordset]
	
End Function

;
; Calculates the UVW coordinates of a pick
; Do not call this by yourself, as PickedU(), PickedV(), and PickedW()
; takes care of calling it when nescessary
Function PickedUVW()

	If PickedSurface()
		ptri\ent  = PickedEntity()
		ptri\surf = PickedSurface()
		ptri\tri  = PickedTriangle()
			
		ptri\px = PickedX()
		ptri\py = PickedY()
		ptri\pz = PickedZ()
		
		For i = 0 To 2
			TFormPoint VertexX(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i)),VertexY(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i)),VertexZ(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i)),ptri\ent,0

			ptri\vx[i] = TFormedX()
			ptri\vy[i] = TFormedY()
			ptri\vz[i] = TFormedZ()

			ptri\vnx[i] = VertexNX(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i))
			ptri\vny[i] = VertexNY(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i))
			ptri\vnz[i] = VertexNZ(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i))
					
			ptri\vu[i+0] = VertexU(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),0)
			ptri\vv[i+0] = VertexV(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),0)
			ptri\vw[i+0] = VertexW(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),0)

			ptri\vu[i+3] = VertexU(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),1)
			ptri\vv[i+3] = VertexV(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),1)
			ptri\vw[i+3] = VertexW(ptri\surf,TriangleVertex(ptri\surf,ptri\tri,i),1)
		Next

		; Select which component of xyz coordinates to ignore
		Local coords = 3

		If Abs(PickedNX()) > Abs(PickedNY())
			If Abs(PickedNX())>Abs(PickedNZ()) Then coords = 1
		Else
			If Abs(PickedNY())>Abs(PickedNZ()) Then coords = 2
		EndIf
		
		Local a0#,a1#,b0#,b1#,c0#,c1#
		
		; xy components
		If (coords = 3)
			; edge 0
			a0# = ptri\vx[1] - ptri\vx[0]
			a1# = ptri\vy[1] - ptri\vy[0]
		
			; edge 1
			b0# = ptri\vx[2] - ptri\vx[0]
			b1# = ptri\vy[2] - ptri\vy[0]

			; picked offset from triangle vertex 0
			c0# = PickedX() - ptri\vx[0]
			c1# = PickedY() - ptri\vy[0]
		Else		
			; xz components
			If (coords = 2)
				; edge 0
				a0# = ptri\vx[1] - ptri\vx[0]
				a1# = ptri\vz[1] - ptri\vz[0]
		
				; edge 1
				b0# = ptri\vx[2] - ptri\vx[0]
				b1# = ptri\vz[2] - ptri\vz[0]

				; picked offset from triangle vertex 0
				c0# = PickedX() - ptri\vx[0]
				c1# = PickedZ() - ptri\vz[0]
			Else
				; yz components

				; edge 0
				a0# = ptri\vy[1] - ptri\vy[0]
				a1# = ptri\vz[1] - ptri\vz[0]
		
				; edge 1
				b0# = ptri\vy[2] - ptri\vy[0]
				b1# = ptri\vz[2] - ptri\vz[0]

				; picked offset from triangle vertex 0
				c0# = PickedY() - ptri\vy[0]
				c1# = PickedZ() - ptri\vz[0]
			End If
		End If
						
		;
		; u and v are offsets from vertex 0 along edge 0 and edge 1
		; using these it is possible to calculate the Texture UVW coordinates
		; of the picked XYZ location
		;
		; a0*u + b0*v = c0
		; a1*u + b1*v = c1
		;
		; solve equation (standard equation with 2 unknown quantities)
		; check a math book to see why the following is true
		;
		Local u# = (c0*b1 - b0*c1) / (a0*b1 - b0*a1)
		Local v# = (a0*c1 - c0*a1) / (a0*b1 - b0*a1)
		
		; If either u or v is out of range then the
		; picked entity was not a mesh, and therefore
		; the uvw coordinates cannot be calculated
		If (u<0.0 Or u>1.0) Or (v<0.0 Or v>1.0)
			Return 
		End If
		
		; Calculate picked uvw's for coordset 0 (and modulate them to be in the range of 0-1 nescessary)
		ptri\pu[0] = (ptri\vu[0] + ((ptri\vu[1] - ptri\vu[0]) * u) + ((ptri\vu[2] - ptri\vu[0]) * v)) Mod 1
		ptri\pv[0] = (ptri\vv[0] + ((ptri\vv[1] - ptri\vv[0]) * u) + ((ptri\vv[2] - ptri\vv[0]) * v)) Mod 1
		ptri\pw[0] = (ptri\vw[0] + ((ptri\vw[1] - ptri\vw[0]) * u) + ((ptri\vw[2] - ptri\vw[0]) * v)) Mod 1
		
		; If any of the coords are negative
		If ptri\pu[0]<0.0 Then ptri\pu[0] = 1.0 + ptri\pu[0]
		If ptri\pv[0]<0.0 Then ptri\pv[0] = 1.0 + ptri\pv[0]
		If ptri\pw[0]<0.0 Then ptri\pw[0] = 1.0 + ptri\pw[0]
		
		; Calculate picked uvw's for coordset 1 (and modulate them to be in the range of 0-1 nescessary)
		ptri\pu[1] = (ptri\vu[3] + ((ptri\vu[4] - ptri\vu[3]) * u) + ((ptri\vu[5] - ptri\vu[3]) * v)) Mod 1
		ptri\pv[1] = (ptri\vv[3] + ((ptri\vv[4] - ptri\vv[3]) * u) + ((ptri\vv[5] - ptri\vv[3]) * v)) Mod 1
		ptri\pw[1] = (ptri\vw[3] + ((ptri\vw[4] - ptri\vw[3]) * u) + ((ptri\vw[5] - ptri\vw[3]) * v)) Mod 1

		; If any of the coords are negative
		If ptri\pu[1]<0.0 Then ptri\pu[1] = 1.0 + ptri\pu[1]
		If ptri\pv[1]<0.0 Then ptri\pv[1] = 1.0 + ptri\pv[1]
		If ptri\pw[1]<0.0 Then ptri\pw[1] = 1.0 + ptri\pw[1]
	End If

End Function


End

; 
; Test example for
; PickedU(),PickedV(),PickedW() commands
; 
; Created by Mikkel Fredborg
; Inspired by David Bird
;
Graphics3D 640,480
SetBuffer BackBuffer()

lit=CreateLight()
LightColor lit,60,60,60
PositionEntity lit,0,10,0
RotateEntity lit,90,0,0

cam=CreateCamera()
CameraRange cam,.1,1000
PositionEntity cam,0,0,-3
CameraClsColor cam,100,100,100

cubetex=CreateTexture(64,64)
; make some squares on the texture
SetBuffer TextureBuffer(cubetex)
Color 128,128,128
Rect 0,0,TextureWidth(cubetex)/2.0,TextureHeight(cubetex)/2.0,True
Rect TextureWidth(cubetex)/2.0,TextureHeight(cubetex)/2.0,(TextureWidth(cubetex)/2.0)-1,(TextureHeight(cubetex)/2.0)-1,True
SetBuffer BackBuffer()

cube = CreateCube()
PositionEntity cube,1,0,0
EntityTexture cube,cubetex,0,0
EntityPickMode cube,2

; Adjust uv coordinates of the cube to tile the texture
surf_n = CountSurfaces(cube)
For i = 1 To surf_n
	surf = GetSurface(cube,i)
	n_verts = CountVertices(surf)-1
	For j = 0 To n_verts
		VertexTexCoords surf,j,VertexU(surf,j)*5.0,VertexV(surf,j)*5.0,VertexW(surf,j)*5.0
	Next
Next

spheretex=CreateTexture(256,256)
sphere = CreateSphere()
PositionEntity sphere,-1,0,0
EntityTexture sphere,spheretex,0,0
EntityPickMode sphere,2

plane = CreatePlane()
PositionEntity plane,0,-2,0
EntityPickMode plane,2

Global dotred# = 0.0
Global dotgrn# = 0.0
Global dotblu# = 0.0

ang# = 0
While Not KeyDown(1)

	xm = MouseX()
	ym = MouseY()

	If MouseDown(1) Then
		Select CameraPick(cam,xm,ym)
			Case cube:    PaintRedDot(cubetex  ,PickedU()*(TextureWidth(cubetex)-1)  ,PickedV()*(TextureHeight(cubetex)-1))
			Case sphere:  PaintRedDot(spheretex,PickedU()*(TextureWidth(spheretex)-1),PickedV()*(TextureHeight(spheretex)-1))
		End Select
	Else
		If MouseDown(2) Then
			Select CameraPick(cam,xm,ym)
				Case cube:    ReadDot(cubetex  ,PickedU()*(TextureWidth(cubetex)-1)  ,PickedV()*(TextureHeight(cubetex)-1))
				Case sphere:  ReadDot(spheretex,PickedU()*(TextureWidth(spheretex)-1),PickedV()*(TextureHeight(spheretex)-1))
			End Select
		End If
	End If
	
	ang = (ang + 0.1) Mod 360
	TurnEntity cube,-Sin(ang)*0.3,-Cos(ang)*0.3,0
	TurnEntity sphere,Sin(ang)*0.1,Cos(ang)*0.1,0
	
	UpdateWorld 
	RenderWorld

	; draw mouse cursor
	Line xm-8,ym,xm-4,ym
	Line xm+8,ym,xm+4,ym
	Line xm,ym-8,xm,ym-4
	Line xm,ym+8,xm,ym+4
	Oval xm-4,ym-4,9,9,False	

	; write some info
	Text 0,  0,"Use left mousebutton to paint the cube and the sphere"
	Text 0, 20,"ent = "+PickedEntity()
	Text 0, 35,"surf = "+PickedSurface()
	Text 0, 50,"tri = "+PickedTriangle()
			
	Text 0, 80,"x = "+PickedX()
	Text 0, 95,"y = "+PickedY()
	Text 0,110,"z = "+PickedZ()
	
	Text 0,140,"u = "+PickedU()
	Text 0,155,"v = "+PickedV()
	Text 0,170,"w = "+PickedW()

	Text 0,200,"nx= "+PickedNX()
	Text 0,215,"ny= "+PickedNY()
	Text 0,230,"nz= "+PickedNZ()
	
	Text 14,460,"Use right mousebutton to pick up a texture color"
	Color dotred,dotgrn,dotblu
	Rect 0,460,12,12,True
	Color 255,255,255
	Flip 
Wend

ClearWorld
End

;
; Plots a red dot :)
Function PaintRedDot(tex,x,y)
	SetBuffer TextureBuffer(tex)
	
	maxwidth  = TextureWidth(tex)
	maxheight = TextureHeight(tex)
	
	red# = 255
	grn# = 0
	blu# = 0
	WritePixel x,y,blu Or (grn Shl 8) Or (red Shl 16)
	
	For xx = -1 To 1
		For yy = -1 To 1
			If (xx=0) Xor (yy=0)
				If (xx<0 And x>0) Or (xx>0 And x<maxwidth) Or (yy<0 And y>0) Or (yy>0 And y<maxheight)
					argb = ReadPixel(x+xx,y+yy)
					red# =  64 + (argb Shr 16 And %11111111)
					grn# =   0 + (argb Shr 8 And %11111111)
					blu# =   0 + (argb Shr 8 And %11111111)

					If red>255 Then red = 255
					If grn>255 Then grn = 255
					If blu>255 Then blu = 255
		
					WritePixel x+xx,y+yy,blu Or (grn Shl 8) Or (red Shl 16)
				End If
			End If
		Next
	Next
	
	SetBuffer BackBuffer()
End Function

;
; Reads a pixel in a texture
Function ReadDot(tex,x,y)
	SetBuffer TextureBuffer(tex)
	
	maxwidth  = TextureWidth(tex)
	maxheight = TextureHeight(tex)
	
	If x<maxwidth And y<maxheight
		argb = ReadPixel(x,y)
		dotred# = (argb Shr 16 And %11111111)
		dotgrn# = (argb Shr 8 And %11111111)
		dotblu# = (argb Shr 8 And %11111111)
	End If
	
	SetBuffer BackBuffer()
End Function





; end of freborgs pickedu / pickedv functions




Function csp_lima_minrel#(mesh,texsize)
; gives relation between biggest triangle side and texture size...
msize#=0
surfs=CountSurfaces(mesh)
For s=1 To surfs
 surf=GetSurface(mesh,s)
 tris=CountTriangles(surf)
 For t=0 To tris-1

  x0#=VertexX(surf,TriangleVertex(surf,t,0))
  y0#=VertexY(surf,TriangleVertex(surf,t,0))
  z0#=VertexZ(surf,TriangleVertex(surf,t,0))
  x1#=VertexX(surf,TriangleVertex(surf,t,1))
  y1#=VertexY(surf,TriangleVertex(surf,t,1))
  z1#=VertexZ(surf,TriangleVertex(surf,t,1))
  x2#=VertexX(surf,TriangleVertex(surf,t,2))
  y2#=VertexY(surf,TriangleVertex(surf,t,2))
  z2#=VertexZ(surf,TriangleVertex(surf,t,2))

  x0n#=VertexNX(surf,TriangleVertex(surf,t,0))
  y0n#=VertexNY(surf,TriangleVertex(surf,t,0))
  z0n#=VertexNZ(surf,TriangleVertex(surf,t,0))
  x1n#=VertexNX(surf,TriangleVertex(surf,t,1))
  y1n#=VertexNY(surf,TriangleVertex(surf,t,1))
  z1n#=VertexNZ(surf,TriangleVertex(surf,t,1))
  x2n#=VertexNX(surf,TriangleVertex(surf,t,2))
  y2n#=VertexNY(surf,TriangleVertex(surf,t,2))
  z2n#=VertexNZ(surf,TriangleVertex(surf,t,2))

  xsize#=max3(x0,x1,x2)-min3(x0,x1,x2)
  ysize#=max3(y0,y1,y2)-min3(y0,y1,y2)
  zsize#=max3(z0,z1,z2)-min3(z0,z1,z2)

  size#=max3(xsize,ysize,zsize)
  If size>msize Then msize=size
 Next
Next

rel#=Float(texsize)/msize
Return rel

End Function









Function csp_CreateHelperQuad()
Local mesh
 mesh=CreateMesh()
b=CreateBrush(255,0,255)
surface=CreateSurface(mesh,b)

v0= AddVertex(surface,-1, 1,0)
v1= AddVertex(surface, 1, 1,0)
v2= AddVertex(surface, 1,-1,0)
v3= AddVertex(surface,-1,-1,0)
AddTriangle surface,v0,v1,v3
AddTriangle surface,v1,v2,v3
RotateMesh mesh,0,180,0
ScaleMesh mesh,100,100,0
;EntityFX mesh,16
Return mesh
End Function








; creates a dummy scene mesh with several materials
Function csp_MyCreateDummyMap()
Local mesh
s=128
ttex=CreateTexture(s,s,4)
SetBuffer TextureBuffer(ttex)
LockBuffer()
For j = 0 To s-1
For i = 0 To s-1
a=(((i) And 31)*8)-1
g=255
rgb=(a Shl 24) Or (g Shl 16) Or (g Shl 8) Or (g)
WritePixelFast i,j,rgb
Next
Next
UnlockBuffer()
SetBuffer workbuffer


s=128
ttex2=CreateTexture(s,s,3 Or 512)
SetBuffer TextureBuffer(ttex2)
LockBuffer()
For j = 0 To s-1
For i = 0 To s-1
a=127+j ;((i Or j)*4) And $255
g=255
rgb=(a Shl 24) Or (g Shl 16) Or (g Shl 8) Or (g)
WritePixelFast i,j,rgb
Next
Next
UnlockBuffer()
SetBuffer workbuffer


mesh=CreateCylinder()
brush1=CreateBrush(255,0,0)
BrushTexture brush1,ttex
PaintMesh mesh, brush1





ground=CreateCube()
ScaleMesh ground,4,0.1,4
PositionMesh ground,0,-.3,0

sp=CreateSphere()
PositionMesh sp,-2,0.5,2
brush2=CreateBrush(0,255,0)
;brush2=CreateBrush(255,255,255)
;ttex2=LoadTexture("test_alpha.bmp",2 Or 512)
;BrushTexture brush2,ttex2
;BrushBlend brush2,3
;BrushAlpha brush2,0.999999
PaintMesh sp, brush2


AddMesh sp,mesh
AddMesh ground, mesh
;UpdateNormals mesh


FreeEntity sp
FreeEntity ground


Return mesh
End Function



BTW, Wayne ("...I'm also considering the unique color scheme, where one applies huge 2048x2048 (or smaller) texture (unique colors), takes a picture from the light source, compares the colors that are seen, and makes them lit for the light map....)"

Wow, this could be great, got to think about it... to bad I started with the above thing :) The planar mapping may however be useful for both, as well as the still missing UV packing.

EDIT: currently I have no idea how you could use the ICU scheme together with alpha textures and/or geometry... any ideas? (Where for masked textures it's perfect!)

knock knock

I went thru the code and played around with it some.

It appeared to me that alpha and masked surfaces are being light mapped.

How would you like alpha and masked surfaces to behave ?

Do you want alpha and masked textures to receive shadows?

Ran pretty well overall

I agree packing needs work
8 )



Do you want alpha and masked to cast shadows (colored) ?

I like it!
Wayne and jfk gets their heads together over a whole new lightmapping system.
Go for it, guys.

I examined the red object more closely and I see what you mean. The black artifacts.

Yeah, thanks!

sorry, been playing with the code, but still having trouble trying to figure out what you're doing and how ;) - let alone trying to figure out what might cause those artifacts...


d.

I saved the lightmap (lima_img) to a bmp file to check it out.
In that image itself I think you can see what's causing at least some of these artifacts...
Even on straight horizontal edges in the bmp you will see gaps and irregularities - and chunky pixels on slopes.
I'm nearly sure this is to rounding & inaccuracy of the routine that's supposed to decide from 'where' to start checking for light input.

If you're familiar with Lightwave you might recognise the term 'Relative gap size' when you let LW create an atlas UV map - in stead of placing all the tri's in the uv map straight together - it will create a small gap in between the poly's.
I think the solution is that besides prepping your uv map like that, you then need to calculate the light not only ON the edges of poly's but also 'slightly OUTSIDE' those edges (filling that relative gap size in your uv map). So when mapped back on your geometry - you won't see these 'dirty edges' as you see them now.
Not sure though if this will also fix those smudges you got especially on the red cylinder...

Hope this helps,
Danny

Thanks for your help Danny. I think the gaps and irregularities you see are caused by the draft-set scanner stepwidth. You have to lower this stepwidth "sstep", probably down to 0.5 to make sure every texel is also scanned. Once reduced to 0.5 you may also replace the RECT texel drawing code by the PLOT texel drawing code. but be aware, a stepwidth of 0.5 is going to take 16 times longer to render than a stepwidth of 2.0.

This is why it really needs to be speed-optimized.

There is already a variable named "margin" that allows to set a gap between the edges. And the scanner will start scanning at x1-1 to x2+1, so the whole triangle should be covered. It may be neccessary to use multiple picks, or a higher picking radius to make sure the edges are picked accurately, in this point I agree with you. But I guess this is not the cause for the masked textures problem.

Well, Waynes Idea of the ICU lightmapper is still in my head and as soon as I have an idea on how to solve the alpha problem I will try to prototype it.

Any news about this?

I'm testing this with new ray casting code. ODE and Newton.

Thanks Wayne. new ray casting code sounds good.

A Packing function is still required, for those who have too much time.

As for the checkerboard artefacts I think I know the cause: It's a rounding error sawtheeth artefact by the alpha/mask determination: at a certain distance the light dummy mesh may be seen by one texel, and not by it's neighbour and so on. So the bad guy in here is the one-pixel render. This may be solved by doing eg. a 16*16 pixel render - maybe. But this would be even slower.

Right now I tend to try your ICU suggestion and simply skip the alpha support. Masks would still be supported this way. This is so dead simple and incredibly fast. Imagine: only one six-sided render for every light, then do some readpixelfast, all done! This must take not longer than a few hundred millisecs to lightmap an entire map.

Again I see probems with rounding errors, a well as with texture filtering that may cause false colors (flag 256 may or may not fix this).

sorry jfk, I haven't had much time to play these days.

But yes, what you say is what I suspected earlier. Still, if increasing the resolution (16x16 pixel render) is very slow - you could use it as an option for 'final quality render'.
Sure long render times suck, but if the end result is worth it; Still better than the alternatives I think..

Sounds great - I hope this project continues :)