Code archives/3D Graphics - Misc/Yet Another Lightmapper Improvement
This code has been declared by its author to be Public Domain code.
Download source code
| |||||
This update to YAL fixes numerous graphical glitches which many lightmappers have. Primarily, it corrects the black edges issue. It does this by determining if the lumels can see the lights rather than if the lights can see the lumels. This means that areas within solid geometry are lit. This correction however caused light to bleed under walls. Which though far less noticeable than the black edges issue, was still annoying. I corrected this issue by illuminating only those lumels which have all four corners in light, rather than just checking the center of each. This increases the lightmap processing time by 400% but results in a more solid feeling world. Another issue was that levels with improperly constructed goemetry, such as a cube sitting on the floor, would have dark edges around said geometry because the bottom face of the cube would block the view of the light for the floor beneath, which is similar to the dark edges problem. To correct this, I offset the position of each lumel from it's surface a little bit using the normal of the surface, and this raises the lumels up just enough to be above gometry sitting flush against floors walls or ceilings. This version of YAL requires the Ray Intersect functions you'll find just below this code archives entry. These functions replace the calls the EntityVisible, so that you don't have to make geometry pickable to lightmap it. It also allows me to add future improvments like light sources that have area and cast soft shadows, and may be somewhat faster than using EntityVisible calls. | |||||
; ID: 514 ; Author: Marcelo ; Date: 2002-11-28 19:49:40 ; Title: YAL - Yet Another LightMapper (update 1.4 ) ; Description: Based on starfox's portable lightmapper ; ; YAL - Yet Another Lightmapper ; Version: 1.4 ; ; Please post any code improvement into blitz basic main site www.blitzbasic.co.nz, and include your data into history ; Thanks to David Dawkins (startfox) and elias_t, that produced the base code for this file. ; ; References: ; http://members.net-tech.com.au/alaneb/lightmapping_tutorial.html (lightmap tutorial) ; http://polygone.flipcode.com/tut_lightmap.htm (lightmap tutorial) ; http://www.blackpawn.com/texts/lightmaps/default.html (lightmap packing) ; ; ; ; To Do: ; - Terrain lightmap precision (seems that the shadows are one or two lumels offset from the correct position) ; - Other light types, such as directional and spot ; - Test with complex meshes to see if the surface self shadowing is working with no problems ; - Merge with the Olive's 1.2 version new features (directional light, etc) ; - Easy way to work out the light coefficients ; - Show the percentage statistics on the terrain too ; ; ; History: ; 1.0 (28/11/2002) - Initial version (marcelo@greenlandstudios.com) ; ; 1.1 (30/11/2002) - Generate surfaces with more than one triangle ; Per light attenuation and brightness ; Functions to apply, save and load the lightmap ; Lightmap sharing (starfox) ; ; 1.3b1(23/12/2002) - Different shadow ray checking, now it generates more precise shadows. ; New LM_DRAWSURFS const to paint the surfaces for debbuging purposes ; SaveLightMap() and LoadLightMap() should work in multiple surface meshes now. (surface fingerprint) ; Weld() the mesh to reduce the number of verts (Peter Scheutz and Terabit) ; Shows the percentage, elapsed and approximate remaining time ; Bug fixes (thanks to Olive), optimizations, etc. ; ; 1.4 (21/6/2003) - Bug fixes and extensive checking made this a stable version ; Compresses the lightmaps based on the contrast (thanks again to elias for the idea) Include "..\..\myprojects\functions\ray_intersect.bb" ; Call example LMExample() ; Set to True to draw the triangle edges on the texture Const LM_DRAWTRIS = False ; True to color each surface Const LM_DRAWSURFS = False Const LM_DISABLESELFSHADOW = False ; Max polys per surface Const LM_SURFTRIS = 256 Const LM_SURFADJTRIS = 256 ; Num verts per poly Const LM_VERTS = 2 ; Angle between normals tolerance Const LM_NORMAL_EPSILON# = 0.997 Const LM_NORMAL_EPSILON2# = 0.984 ; This moves the lumel pivot away from the surface in the direction of it's normal by this distance. ; Increasing this value will prevent objects which are flush against the floor from darkening lumels around ; their base. Const LM_LUMEL_PULL_INWARD# = 0.001 ; Vertex distance tolerance Const LM_VERTPOS_EPSILON# = 0.01 Const LM_VERTPOS_EPSILON2# = 0.05 ; If the intensity is less that it ignore Const LM_INTENSITY_EPSILON# = 0.9999 / 255.0 ; Mapping plane Const LMPLANE_XY = 0 Const LMPLANE_XZ = 1 Const LMPLANE_YZ = 2 ; Mininum texture size Const LM_MINTEXSIZE = 2 ; Types Type LMTriangle ; Vertex info Field OX#[LM_VERTS], OY#[LM_VERTS], OZ#[LM_VERTS] Field X#[LM_VERTS], Y#[LM_VERTS], Z#[LM_VERTS] Field U#[LM_VERTS], V#[LM_VERTS] Field VertIndex[LM_VERTS] ; Normal Field NX#, NY#, NZ# ; Original surface pointer Field Surf ; Surface that owns this triangle Field LMSurf.LMSurface End Type Type LMSurface ; Triangle list Field Tris.LMTriangle[LM_SURFTRIS] Field NTris% Field AdjTris.LMTriangle[LM_SURFADJTRIS] Field NAdjTris% ; Plane Field NX#, NY#, NZ# Field Plane% ; UV Bound Box Field UMin#, UMax#, UDelta# Field VMin#, VMax#, VDelta# ; UV to worldspace transformations Field UEdgeX#, UEdgeY#, UEdgeZ# Field VEdgeX#, VEdgeY#, VEdgeZ# Field OriginX#, OriginY#, OriginZ# ; Misc Field Image Field ImageSize End Type ; Wrapper to sort the surfaces Type LMSortedSurface Field Surf.LMSurface End Type ; Node for the packer Type LMImgNode Field Child.LMImgNode[1] Field Surf.LMSurface Field X1%, Y1% Field X2%, Y2% End Type ; Global parameters Type LMParams Field AmbR, AmbG, AmbB End Type ; Light Type LMLight Field X#, Y#, Z# Field R#, G#, B# Field Range# Field Att#[2] Field Bright# Field CastShadows End Type ; This list contains all entities which should obscure light. Type LMObscurer Field Entity End Type ; Store global parameters Global g_LMParams.LMParams = Null ; ***************** ; ; Public functions ; ; ***************** ; Create and setup global parameters ; AmbR, AmbG, AmbB is the ambient light color Function BeginLightMap(AmbR = 0, AmbG = 0, AmbB = 0) g_LMParams = New LMParams g_LMParams\AmbR = AmbR g_LMParams\AmbG = AmbG g_LMParams\AmbB = AmbB End Function Function LightMapParams(AmbR = 0, AmbG = 0, AmbB = 0) g_LMParams\AmbR = AmbR g_LMParams\AmbG = AmbG g_LMParams\AmbB = AmbB End Function ; Free parameters and stuff Function EndLightMap() If g_LMParams <> Null ; Delete all lights For Light.LMLight = Each LMLight Delete Light Next Delete g_LMParams g_LMParams = Null EndIf End Function ; Create a new Light for lightmapping, only point lights until now ; x, y, z - world space coordinates ; r, g, b - Red, Green and Blue amounts (0..255) ; range - Maximum distance that the light will affect ; (only clamps the distance, If you want a falloff effect use the attenuation coefficients) ; ; bright - Light brightness ; ; att0, att1, att2 - Coefficients for light attenuation (control the falloff curve) ; lumel attenuation# = 1.0 / (att0 + (att1 * dist) + (att2 * dist^2) ; where dist is the distance from light source to lumel Function CreateLMLight.LMLight(x#, y#, z#, r#, g#, b#, range# = 0, castshadows = True, bright# = 10.0, att0# = 0, att1# = 1, att2# = 0, Area#=0, AreaQuality=0) l.LMLight = New LMLight l\X# = x# l\Y# = y# l\Z# = z# l\R# = r# l\G# = g# l\B# = b# l\Range# = range# l\Bright# = bright# l\Att#[0] = att0# l\Att#[1] = att1# l\Att#[2] = att2# l\CastShadows = castshadows If l\Range# = 0 l\Range# = 9999999.0 EndIf Return l End Function ; Apply an lightmap created with LightMapMesh or LightMapTerrain Function ApplyLightMap(mesh, tex, layer = 4) If Not tex Return False EndIf EntityFX(mesh, 1) EntityTexture(mesh, tex, 0, layer) FreeTexture(tex) Weld(mesh) Return True End Function ; Save to a bmp file and a luv file the information about a lightmapped entity Function SaveLightMap(mesh, tex, imgfile$, luvfile$) If Not tex Return False EndIf SaveBuffer(TextureBuffer(tex), imgfile$) CreateLUVs(mesh, luvfile$, 1) End Function ; Load an image file and the luv file into the entity Function LoadLightMap(mesh, imgfile$, luvfile$, layer = 4) Unweld(mesh) If FileType(luvfile$) LoadLUVs(mesh, luvfile$) EndIf tex = LoadTexture(imgfile$) If tex EntityFX(mesh, 1) TextureCoords(tex, 1) EntityTexture(mesh, tex, 0, layer) Weld(mesh) Return tex;FreeTexture(tex) EndIf End Function ; Assigns a 2nd channel planar mapping coordinates to the mesh and returns a packed texture that can be applied for lightmapping ; ; NOTES: ; ; - The world objects must have EntityPickMode() set to produce shadows ; - The mesh is changed in the process (unwelded) ; - Lumel is the equivalent of an texel, but for lightmaps ; - lumelsize# is the size of the lumel in the world units to control the resolution of the lightmap ; Example: If you use the metric system, a 0.2 lumelsize will create a lumel at each 20 centimeters ; - maxmapsize : maximum texture size that the lightmapper can pack (only used if needed) ; - blurradius : blur the resul image by this radius ; Function LightMapMesh(mesh, lumelsize# = 0.5, maxmapsize = 1024, blurradius = 1, TotalInfo$ = "") UnWeld(mesh) ; Run thru all surfaces & triangles storing the info into LMTriangle For surfcount = 1 To CountSurfaces(mesh) surf = GetSurface(mesh, surfcount) For tricount = 0 To CountTriangles(surf) - 1 Tri.LMTriangle = New LMTriangle For i = 0 To LM_VERTS vertn = TriangleVertex(surf, tricount, i) TFormPoint(VertexX(surf, vertn), VertexY(surf, vertn), VertexZ(surf, vertn), mesh, 0) Tri\X[i] = TFormedX() : Tri\Y[i] = TFormedY() : Tri\Z[i] = TFormedZ() Tri\OX[i] = VertexX(surf, vertn) : Tri\OY[i] = VertexY(surf, vertn) : Tri\OZ[i] = VertexZ(surf, vertn) Tri\VertIndex[i] = vertn Next Tri\Surf = Surf GetTriangleNormal(Tri\X[0], Tri\Y[0], Tri\Z[0], Tri\X[1], Tri\Y[1], Tri\Z[1], Tri\X[2], Tri\Y[2], Tri\Z[2]) Tri\NX = TriangleNormalX() : Tri\NY = TriangleNormalY() : Tri\NZ = TriangleNormalZ() Next Next LumelCount = 0 ; Create the surfaces While True ; Find the first unlinked triangle For Tri.LMTriangle = Each LMTriangle If Tri\LMSurf = Null Exit EndIf Next ; No more unlinked tris If Tri = Null Exit EndIf LMSurf.LMSurface = New LMSurface Tri\LMSurf = LMSurf LMSurf\Tris[LMSurf\NTris] = Tri LMSurf\NTris = LMSurf\NTris + 1 ; Search for adjacent tri's with the same caracteristics and append to list ; Loop while no poly's get added While True bNewPoly = False For STri.LMTriangle = Each LMTriangle If STri\LMSurf = Null ; Compare the triangle normal Ang# = ((STri\NX * Tri\NX) + (STri\NY * Tri\NY) + (STri\NZ * Tri\NZ)) If Ang >= LM_NORMAL_EPSILON NSharedVerts = 0 ; Check if it shares vertices with one of the current surface triangles For i = 0 To LMSurf\NTris-1 VTri.LMTriangle = LMSurf\Tris[i] For j = 0 To LM_VERTS For k = 0 To LM_VERTS DX# = STri\X[j] - VTri\X[k] DY# = STri\Y[j] - VTri\Y[k] DZ# = STri\Z[j] - VTri\Z[k] Dist# = Sqr(DX*DX + DY*DY + DZ*DZ) If Dist <= LM_VERTPOS_EPSILON NSharedVerts = NSharedVerts + 1 Exit EndIf Next Next Next If NSharedVerts > 0 STri\LMSurf = LMSurf LMSurf\Tris[LMSurf\NTris] = STri LMSurf\NTris = LMSurf\NTris + 1 bNewPoly = True If LMSurf\NTris > LM_SURFTRIS Exit EndIf EndIf EndIf EndIf Next If Not bNewPoly Exit EndIf If LMSurf\NTris > LM_SURFTRIS Exit EndIf Wend ; Get the averaged normal NX# = 0 : NY# = 0 : NZ# = 0 For i = 0 To LMSurf\NTris-1 GetTriangleNormal(LMSurf\Tris[i]\X[0], LMSurf\Tris[i]\Y[0], LMSurf\Tris[i]\Z[0], LMSurf\Tris[i]\X[1], LMSurf\Tris[i]\Y[1], LMSurf\Tris[i]\Z[1], LMSurf\Tris[i]\X[2], LMSurf\Tris[i]\Y[2], LMSurf\Tris[i]\Z[2]) NX = NX + TriangleNormalX() NY = NY + TriangleNormalY() NZ = NZ + TriangleNormalZ() Next LMSurf\NX = NX / Float(LMSurf\NTris) LMSurf\NY = NY / Float(LMSurf\NTris) LMSurf\NZ = NZ / Float(LMSurf\NTris) ; Search for directly adjacent triangles (that can be hidden on self shadow check) If LM_DISABLESELFSHADOW For STri.LMTriangle = Each LMTriangle If STri\LMSurf <> LMSurf ; Compare the triangle normal Ang# = ((STri\NX * LMSurf\NX) + (STri\NY * LMSurf\NY) + (STri\NZ * LMSurf\NZ)) If Ang >= LM_NORMAL_EPSILON2 NSharedVerts = 0 ; Check if it shares vertices with one of the current surface triangles For i = 0 To LMSurf\NTris-1 VTri.LMTriangle = LMSurf\Tris[i] For j = 0 To LM_VERTS For k = 0 To LM_VERTS DX# = STri\X[j] - VTri\X[k] DY# = STri\Y[j] - VTri\Y[k] DZ# = STri\Z[j] - VTri\Z[k] Dist# = Sqr(DX*DX + DY*DY + DZ*DZ) If Dist <= LM_VERTPOS_EPSILON2 NSharedVerts = NSharedVerts + 1 Exit EndIf Next Next Next If NSharedVerts > 0 LMSurf\AdjTris[LMSurf\NAdjTris] = STri LMSurf\NAdjTris = LMSurf\NAdjTris + 1 If LMSurf\NAdjTris > LM_SURFADJTRIS Exit EndIf EndIf EndIf EndIf Next EndIf LMSetupSurface(LMSurf, lumelsize, blurradius) LumelCount = LumelCount + LMSurf\ImageSize Wend lcount = 0 count = 0 SpdSum# = 0 InitialTime = MilliSecs() If Not LM_DRAWSURFS ClsColor(0, 0, 0) Cls() Print(TotalInfo$) Print("Percentage : 0%") Print("Time : 0s (0s to go)") Flip() For LMSurf.LMSurface = Each LMSurface Time = MilliSecs() ; Create the light texture LMLightSurface(LMSurf, lumelsize) ; Blur resulting image If blurradius > 0 LMBlurImage(LMSurf\Image, blurradius) EndIf lcount = lcount + LMSurf\ImageSize count = count + 1 Now = MilliSecs() Elapsed = Now - Time If Elapsed > 0 Spd# = Float(LMSurf\ImageSize) / Float(Elapsed) * 1000 SpdSum# = SpdSum# + Spd EndIf AvgSpd# = SpdSum / Float(count) Est = Float(LumelCount - lcount) / AvgSpd# ; Display status ClsColor(0, 0, 0) Cls() Print(TotalInfo$) Print("Percentage : " + (Float(lcount) / Float(LumelCount) * 100) + "%") Print("Time : " + ((Now - InitialTime)/1000) + "s (" + Est + "s to go)") Flip() Next Else SeedRnd(MilliSecs()) EndIf ; First sort it by image size, larger images enter first For LMSurf.LMSurface = Each LMSurface ; Search for a lower image size For SLMSurf.LMSortedSurface = Each LMSortedSurface If SLMSurf\Surf\ImageSize <= LMSurf\ImageSize Exit EndIf Next NLMSurf.LMSortedSurface = New LMSortedSurface NLMSurf\Surf = LMSurf If SLMSurf <> Null Insert NLMSurf Before SLMSurf EndIf Next ; Get the mininum map size possible lmapsize% = LMPacker_FitTexSize(maxmapsize) ; Pack into a big texture Tex = LMPacker_Pack(lmapsize%) ; Free temporary stuff For LMSurf.LMSurface = Each LMSurface FreeImage(LMSurf\Image) Delete LMSurf Next Delete Each LMSortedSurface Delete Each LMTriangle SetBuffer(BackBuffer()) Return Tex End Function ; ; Same as the lightmapmesh, but for terrains. detail% is the texture map size ; Function LightMapTerrain(terrain, detail% = 0, blurradius% = 1) TSize# = TerrainSize(terrain) If detail = 0 detail = TSize EndIf ; Get the entity scale vx# = GetMatElement(terrain, 0, 0) vy# = GetMatElement(terrain, 0, 1) vz# = GetMatElement(terrain, 0, 2) XScale# = Sqr(vx*vx + vy*vy + vz*vz) vx# = GetMatElement(terrain, 1, 0) vy# = GetMatElement(terrain, 1, 1) vz# = GetMatElement(terrain, 1, 2) YScale# = Sqr(vx*vx + vy*vy + vz*vz) vx# = GetMatElement(terrain, 2, 0) vy# = GetMatElement(terrain, 2, 1) vz# = GetMatElement(terrain, 2, 2) ZScale# = Sqr(vx*vx + vy*vy + vz*vz) ; Relation between detail and texture size Scale# = 1 If detail < TSize Scale# = Float(detail)/Float(TSize) EndIf LMSize = detail Img = CreateImage(LMSize, LMSize) ImgBuf = ImageBuffer(Img) SetBuffer(ImgBuf) ; Set the ambient light ClsColor(g_LMParams\AmbR, g_LMParams\AmbG, g_LMParams\AmbB) Cls() ClsColor(0, 0, 0) LockBuffer(ImgBuf) LightPivot = CreatePivot() LumelPivot = CreatePivot() EntityPickMode(LumelPivot, 1) EntityRadius(LumelPivot, 0.625) xpos# = EntityX(terrain) : ypos# = EntityY(terrain) : zpos# = EntityZ(terrain) For Light.LMLight = Each LMLight PositionEntity(LightPivot, Light\X, Light\Y, Light\Z) For z% = 0 To LMSize-1 For x% = 0 To LMSize-1 zp% = TSize - z y# = TerrainHeight(terrain, x+1, zp) LumX# = (xpos + Float(x) * XScale) / Scale LumY# = (ypos + Float(y) * YScale) / Scale LumZ# = (zpos + Float(zp) * ZScale) / Scale PositionEntity(LumelPivot, LumX, LumY, LumZ) Dist# = EntityDistance(LightPivot, LumelPivot) ; If this light can light this lumel If (Dist <= Light\Range) And (Dist > 0) LMLightProcess(x, z, Light, LumX, LumY, LumZ, Dist, 1.0, LumelPivot, LightPivot) EndIf Next ; x Next ; z Next UnlockBuffer(ImgBuf) ; Blur resulting image If blurradius > 0 LMBlurImage(Img, blurradius) EndIf Tex = CreateTexture(LMSize, LMSize, 512) CopyRect(0, 0, LMSize, LMSize, 0, 0, ImageBuffer(Img), TextureBuffer(Tex)) TextureCoords(Tex, 1) ScaleTexture(Tex, TSize, TSize) FreeImage(Img) SetBuffer(BackBuffer()) FreeEntity(LightPivot) FreeEntity(LumelPivot) Return Tex End Function ; ****************** ; ; Private functions ; ; ****************** ; Lightmap packing functions Function LMPacker_Pack(lmapsize) Tex = CreateTexture(lmapsize, lmapsize, 512) SetBuffer(TextureBuffer(Tex)) ; Set the ambient light ClsColor(g_LMParams\AmbR, g_LMParams\AmbG, g_LMParams\AmbB) Cls() ClsColor(0, 0, 0) LMRoot.LMImgNode = New LMImgNode LMRoot\X1 = 0 : LMRoot\Y1 = 0 LMRoot\X2 = lmapsize : LMRoot\Y2 = lmapsize LMRoot\Surf = Null SurfCnt = 0 For SLMSurf.LMSortedSurface = Each LMSortedSurface ; Insert in the best location Img.LMImgNode = LMPacker_Insert(LMRoot, SLMSurf\Surf) If Img <> Null LMSurf.LMSurface = Img\Surf IW = ImageWidth(LMSurf\Image) IH = ImageHeight(LMSurf\Image) If LM_DRAWSURFS Color(Rand(0,220), Rand(0,220), Rand(0,220)) Rect(Img\X1, Img\Y1, IW, IH, True) Color(0, 0, 0) Text(Img\X1 + IW/2, Img\Y1 + IH/2, Handle(LMSurf), True, True) Else CopyRect(0, 0, IW, IH, Img\X1, Img\Y1, ImageBuffer(LMSurf\Image), TextureBuffer(Tex)) EndIf ; Scale the original UV's to the new position and scale DX# = Float(Img\X1) / Float(lmapsize) DY# = Float(Img\Y1) / Float(lmapsize) ScaleU# = Float(IW) / Float(lmapsize) ScaleV# = Float(IH) / Float(lmapsize) For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS LMSurf\Tris[i]\U[j] = (LMSurf\Tris[i]\U[j] * ScaleU) + DX LMSurf\Tris[i]\V[j] = (LMSurf\Tris[i]\V[j] * ScaleV) + DY VertexTexCoords(LMSurf\Tris[i]\Surf, LMSurf\Tris[i]\VertIndex[j], LMSurf\Tris[i]\U[j], LMSurf\Tris[i]\V[j], 0, 1) Next Next ; Draw debug stuff if needed If LM_DRAWTRIS ; Triangles Color(255, 255, 255) For i = 0 To LMSurf\NTris-1 x1% = LMSurf\Tris[i]\U[0] * Float(lmapsize) y1% = LMSurf\Tris[i]\V[0] * Float(lmapsize) x2% = LMSurf\Tris[i]\U[1] * Float(lmapsize) y2% = LMSurf\Tris[i]\V[1] * Float(lmapsize) Line(x1, y1, x2, y2) x1% = LMSurf\Tris[i]\U[1] * Float(lmapsize) y1% = LMSurf\Tris[i]\V[1] * Float(lmapsize) x2% = LMSurf\Tris[i]\U[2] * Float(lmapsize) y2% = LMSurf\Tris[i]\V[2] * Float(lmapsize) Line(x1, y1, x2, y2) x1% = LMSurf\Tris[i]\U[2] * Float(lmapsize) y1% = LMSurf\Tris[i]\V[2] * Float(lmapsize) x2% = LMSurf\Tris[i]\U[0] * Float(lmapsize) y2% = LMSurf\Tris[i]\V[0] * Float(lmapsize) Line(x1, y1, x2, y2) Next EndIf SurfCnt = SurfCnt + 1 Else DebugLog("Lightmap doesn't fit into the maxmapsize, increase the lumelsize or increase the maxmapsize") Exit EndIf Next TextureCoords(Tex, 1) SetBuffer(BackBuffer()) For LMNode.LMImgNode = Each LMImgNode Delete LMNode Next Return Tex End Function ; ; Find of the minimum texture size up to maxmapsize% that will fit all the lightmap images ; Function LMPacker_FitTexSize%(maxmapsize%) lmapsize = LM_MINTEXSIZE While lmapsize <= maxmapsize LMRoot.LMImgNode = New LMImgNode LMRoot\X1 = 0 : LMRoot\Y1 = 0 LMRoot\X2 = lmapsize : LMRoot\Y2 = lmapsize LMRoot\Surf = Null bFit = True For SLMSurf.LMSortedSurface = Each LMSortedSurface Img.LMImgNode = LMPacker_Insert(LMRoot, SLMSurf\Surf) If Img = Null bFit = False Exit EndIf Next For LMNode.LMImgNode = Each LMImgNode Delete LMNode Next If bFit Return lmapsize EndIf lmapsize = lmapsize * 2 Wend Return maxmapsize End Function ; ; Recursive function to pack the lightmaps ; Function LMPacker_Insert.LMImgNode(Node.LMImgNode, LMSurf.LMSurface) ; We are not in a leaf If (Node\Child[0] <> Null) And (Node\Child[1] <> Null) ; Try first child NewNode.LMImgNode = LMPacker_Insert(Node\Child[0], LMSurf) If NewNode <> Null Return NewNode ; No room, use the second Return LMPacker_Insert(Node\Child[1], LMSurf) Else ; Already have a lightmap here If Node\Surf <> Null If LM_DRAWSURFS Return Null EndIf ; If the lightmap is the same image use it If LMImageAlike(Node\Surf\Image, LMSurf\Image) Node\Surf = LMSurf Return Node Else Return Null EndIf EndIf IW% = ImageWidth(LMSurf\Image) IH% = ImageHeight(LMSurf\Image) NW% = Node\X2 - Node\X1 NH% = Node\Y2 - Node\Y1 ; Check if image doesn't fit this node If (IW > NW) Or (IH > NH) Return Null EndIf ; If it fits perfectly If (IW = NW) And (IH = NH) Node\Surf = LMSurf Return Node EndIf ; We need to spit the node Node\Child[0] = New LMImgNode Node\Child[1] = New LMImgNode DW% = NW - IW DH% = NH - IH ; Choose the best axis to split If DW > DH Node\Child[0]\X1 = Node\X1 Node\Child[0]\Y1 = Node\Y1 Node\Child[0]\X2 = Node\X1 + IW Node\Child[0]\Y2 = Node\Y2 Node\Child[1]\X1 = Node\X1 + IW Node\Child[1]\Y1 = Node\Y1 Node\Child[1]\X2 = Node\X2 Node\Child[1]\Y2 = Node\Y2 Else Node\Child[0]\X1 = Node\X1 Node\Child[0]\Y1 = Node\Y1 Node\Child[0]\X2 = Node\X2 Node\Child[0]\Y2 = Node\Y1 + IH Node\Child[1]\X1 = Node\X1 Node\Child[1]\Y1 = Node\Y1 + IH Node\Child[1]\X2 = Node\X2 Node\Child[1]\Y2 = Node\Y2 EndIf Return LMPacker_Insert(Node\Child[0], LMSurf) EndIf End Function Function LMImageAlike(img1, img2) ;Check if imagess are congruent width1 = ImageWidth(img1) width2 = ImageWidth(img2) If width1 <> width2 Then Return False height1 = ImageHeight(img1) height2 = ImageHeight(img2) If height1 <> height2 Then Return 0 LockBuffer(ImageBuffer(img1)) LockBuffer(ImageBuffer(img2)) For y = 0 To height1-1 For x = 0 To width1-1 rgb1 = ReadPixelFast(x, y, ImageBuffer(img1)) And $FFFFFF rgb2 = ReadPixelFast(x, y, ImageBuffer(img2)) And $FFFFFF If rgb1 <> rgb2 UnlockBuffer(ImageBuffer(img1)) UnlockBuffer(ImageBuffer(img2)) Return 0 EndIf Next Next UnlockBuffer(ImageBuffer(img1)) UnlockBuffer(ImageBuffer(img2)) Return True End Function Function LMImageMeasureContrast%(img) ;minvalue = 255 ;maxvalue = 0 minvalue_r = 255 minvalue_g = 255 minvalue_b = 255 width = ImageWidth(img) height = ImageHeight(img) LockBuffer(ImageBuffer(img)) For y = 0 To height-1 For x = 0 To width-1 rgb1 = ReadPixelFast(x, y, ImageBuffer(img)) And $FFFFFF r1 = (rgb1 Shr 16 And %11111111) g1 = (rgb1 Shr 8 And %11111111) b1 = (rgb1 And %11111111) ; If r1 > maxvalue Then maxvalue = r1 ; If g1 > maxvalue Then maxvalue = g1 ; If b1 > maxvalue Then maxvalue = b1 ; If r1 < minvalue Then minvalue = r1 ; If g1 < minvalue Then minvalue = g1 ; If b1 < minvalue Then minvalue = b1 ; (sswift) ; What you really want to measure is the contrast of each channel, and then select the channel with the ; max contrast. What you're doing above would assume that an image that is pure red has a lot of contrast ; because blue and green are 0 and red is 255. If r1 > maxvalue_r Then maxvalue_r = r1 If g1 > maxvalue_g Then maxvalue_g = g1 If b1 > maxvalue_b Then maxvalue_b = b1 If r1 < minvalue_r Then minvalue_r = r1 If g1 < minvalue_g Then minvalue_g = g1 If b1 < minvalue_b Then minvalue_b = b1 Next Next UnlockBuffer(ImageBuffer(img)) ;(sswift) contrast_r = maxvalue_r - minvalue_r contrast_g = maxvalue_g - minvalue_g contrast_b = maxvalue_b - minvalue_b If (contrast_r > contrast_g) And (contrast_r > contrast_b) Then Return contrast_r If (contrast_g > contrast_r) And (contrast_g > contrast_b) Then Return contrast_g Return contrast_b ;Return maxvalue - minvalue End Function ; Setup the surface ; (Map the surface's UV's to a plane aligned with a world axis.) Function LMSetupSurface.LMSurface(LMSurf.LMSurface, lumelsize#, blurradius#) ; Find out the best plane to map on (which have the largest normal) NX# = Abs(LMSurf\NX) : NY# = Abs(LMSurf\NY) : NZ# = Abs(LMSurf\NZ) If (NZ > NX) And (NZ > NY) LMSurf\Plane = LMPLANE_XY Else If (NY > NX) And (NY > NZ) LMSurf\Plane = LMPLANE_XZ Else LMSurf\Plane = LMPLANE_YZ EndIf Select LMSurf\Plane Case LMPLANE_XY For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS LMSurf\Tris[i]\U#[j] = LMSurf\Tris[i]\X#[j] LMSurf\Tris[i]\V#[j] = LMSurf\Tris[i]\Y#[j] Next Next Case LMPLANE_XZ For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS LMSurf\Tris[i]\U#[j] = LMSurf\Tris[i]\X#[j] LMSurf\Tris[i]\V#[j] = LMSurf\Tris[i]\Z#[j] Next Next Case LMPLANE_YZ For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS LMSurf\Tris[i]\U#[j] = LMSurf\Tris[i]\Y#[j] LMSurf\Tris[i]\V#[j] = LMSurf\Tris[i]\Z#[j] Next Next End Select ; Measure the UV bound box LMSurf\UMin = LMSurf\Tris[0]\U[0] : LMSurf\UMax = LMSurf\Tris[0]\U[0] LMSurf\VMin = LMSurf\Tris[0]\V[0] : LMSurf\VMax = LMSurf\Tris[0]\V[0] For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS If LMSurf\Tris[i]\U[j] < LMSurf\UMin Then LMSurf\UMin = LMSurf\Tris[i]\U[j] If LMSurf\Tris[i]\U[j] > LMSurf\UMax Then LMSurf\UMax = LMSurf\Tris[i]\U[j] If LMSurf\Tris[i]\V[j] < LMSurf\VMin Then LMSurf\VMin = LMSurf\Tris[i]\V[j] If LMSurf\Tris[i]\V[j] > LMSurf\VMax Then LMSurf\VMax = LMSurf\Tris[i]\V[j] Next Next ; Reduce black borders ; (sswift) ; Multiplying by 3.0 eradicates the light bleeding from adjacent lightmaps, but I don't know why, or what ; effect changing the lumel size will have. ;DT# = lumelsize * Float(blurradius + 1) ;DT# = LumelSize# * Float(BlurRadius# + 1.0) * 3.0 DT# = LumelSize# * Float(BlurRadius# + 5.0) LMSurf\UMax# = LMSurf\UMax# + DT# LMSurf\VMax# = LMSurf\VMax# + DT# LMSurf\UMin# = LMSurf\UMin# - DT# LMSurf\VMin# = LMSurf\VMin# - DT# ; Bound Box size LMSurf\UDelta# = LMSurf\UMax# - LMSurf\UMin# LMSurf\VDelta# = LMSurf\VMax# - LMSurf\VMin# ; Normalize the UV's, making it range from 0.0 to 1.0 For i = 0 To LMSurf\NTris-1 For j = 0 To LM_VERTS ; Translate it to the origin LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\U[j] - LMSurf\UMin# LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\V[j] - LMSurf\VMin# ; Normalize LMSurf\Tris[i]\U[j] = LMSurf\Tris[i]\U[j] / LMSurf\UDelta# LMSurf\Tris[i]\V[j] = LMSurf\Tris[i]\V[j] / LMSurf\VDelta# Next Next ; ; Calculate the UV space to world space equations ; ; Distance of the plane Dist# = -(LMSurf\NX * LMSurf\Tris[0]\X[0] + LMSurf\NY * LMSurf\Tris[0]\Y[0] + LMSurf\NZ * LMSurf\Tris[0]\Z[0]) Local UVX#, UVY#, UVZ# Local V1X#, V1Y#, V1Z# Local V2X#, V2Y#, V2Z# ; Messy stuff based on the plane equation: Ax + By + Cz + D = 0 Select LMSurf\Plane Case LMPLANE_XY Z# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NY * LMSurf\VMin + Dist) / LMSurf\NZ UVX# = LMSurf\UMin : UVY# = LMSurf\VMin : UVZ# = Z Z# = -(LMSurf\NX * LMSurf\UMax + LMSurf\NY * LMSurf\VMin + Dist) / LMSurf\NZ V1X# = LMSurf\UMax : V1Y# = LMSurf\VMin : V1Z# = Z Z# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NY * LMSurf\VMax + Dist) / LMSurf\NZ V2X# = LMSurf\UMin : V2Y# = LMSurf\VMax : V2Z# = Z Case LMPLANE_XZ Y# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NY UVX# = LMSurf\UMin : UVY# = Y : UVZ# = LMSurf\VMin Y# = -(LMSurf\NX * LMSurf\UMax + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NY V1X# = LMSurf\UMax : V1Y# = Y : V1Z# = LMSurf\VMin Y# = -(LMSurf\NX * LMSurf\UMin + LMSurf\NZ * LMSurf\VMax + Dist) / LMSurf\NY V2X# = LMSurf\UMin : V2Y# = Y : V2Z# = LMSurf\VMax Case LMPLANE_YZ X# = -(LMSurf\NY * LMSurf\UMin + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NX UVX# = X : UVY# = LMSurf\UMin : UVZ# = LMSurf\VMin X# = -(LMSurf\NY * LMSurf\UMax + LMSurf\NZ * LMSurf\VMin + Dist) / LMSurf\NX V1X# = X : V1Y# = LMSurf\UMax : V1Z# = LMSurf\VMin X# = -(LMSurf\NY * LMSurf\UMin + LMSurf\NZ * LMSurf\VMax + Dist) / LMSurf\NX V2X# = X : V2Y# = LMSurf\UMin : V2Z# = LMSurf\VMax End Select LMSurf\UEdgeX = V1X - UVX : LMSurf\UEdgeY = V1Y - UVY : LMSurf\UEdgeZ = V1Z - UVZ LMSurf\VEdgeX = V2X - UVX : LMSurf\VEdgeY = V2Y - UVY : LMSurf\VEdgeZ = V2Z - UVZ LMSurf\OriginX = UVX# : LMSurf\OriginY = UVY# : LMSurf\OriginZ = UVZ# ; Create image size based on the lumel density LMSizeX% = (LMSurf\UDelta / lumelsize) LMSizeY% = (LMSurf\VDelta / lumelsize) ; Mininum texture size If LMSizeX < LM_MINTEXSIZE Then LMSizeX = LM_MINTEXSIZE If LMSizeY < LM_MINTEXSIZE Then LMSizeY = LM_MINTEXSIZE LMSurf\Image = CreateImage(LMSizeX, LMSizeY) LMSurf\ImageSize = LMSizeX * LMSizeY Return LMSurf End Function ; ; Create the lightmap texture ; Function LMLightSurface(LMSurf.LMSurface, lumelsize#) ; Move poly to far away ; (sswift) ; This is unneccessary. Moving the lumel slightly away from the surface prevents any possiblity of the ; polygons in the surface self shadowing themselves, and you WANT the other polygons in a surface to shadow ; the others if you have a large enough angle for polygon combining. ; For i = 0 To LMSurf\NTris-1 ; For j = 0 To LM_VERTS ; VertexCoords(LMSurf\Tris[i]\Surf, LMSurf\Tris[i]\VertIndex[j], 99999, 99999, 99999) ; Next ; Next ; For i = 0 To LMSurf\NAdjTris-1 ; For j = 0 To LM_VERTS ; VertexCoords(LMSurf\AdjTris[i]\Surf, LMSurf\AdjTris[i]\VertIndex[j], 99999, 99999, 99999) ; Next ; Next LMSizeX% = ImageWidth(LMSurf\Image) LMSizeY% = ImageHeight(LMSurf\Image) ImgBuf = ImageBuffer(LMSurf\Image) SetBuffer(ImgBuf) ; Set the ambient light ClsColor(g_LMParams\AmbR, g_LMParams\AmbG, g_LMParams\AmbB) Cls() ClsColor(0, 0, 0) LockBuffer(ImgBuf) LightPivot = CreatePivot() LumelPivot = CreatePivot() ;EntityPickMode(LightPivot, 1, False) ;EntityRadius(LightPivot, 0.625) ; Found by trial and error ;EntityRadius(LightPivot, 10.0) ;EntityPickMode(LumelPivot, 1) ;EntityRadius(LumelPivot, 0.625) ; Found by trial and error ;EntityRadius(LumelPivot, 0.3) ;EntityRadius(LumelPivot, lumelsize*2) ; (sswift) ; Calculate one half the width of a lumel, in UV coordinates. centeruvoffset# = (1.0 / Float(LMSizeX)) / 2.0 For Light.LMLight = Each LMLight PositionEntity(LightPivot, Light\X, Light\Y, Light\Z) For Y = 0 To LMSizeY-1 For X = 0 To LMSizeX-1 ; Find the UV ; (sswift) Of the TOP LEFT corner of the lumel! Not the center! U# = Float(x) / Float(LMSizeX) V# = Float(y) / Float(LMSizeY) ; (sswift) ; Offset the UV coordinates so that we are at the center of the lumel. U# = U# + CenterUVOffset# V# = V# + CenterUVOffset# ; Transform to world coordinates N_UEdgeX# = LMSurf\UEdgeX# * u# : N_UEdgeY# = LMSurf\UEdgeY# * u# : N_UEdgeZ# = LMSurf\UEdgeZ# * u# N_VEdgeX# = LMSurf\VEdgeX# * v# : N_VEdgeY# = LMSurf\VEdgeY# * v# : N_VEdgeZ# = LMSurf\VEdgeZ# * v# LumX# = (LMSurf\OriginX# + N_UEdgeX# + N_VEdgeX#) LumY# = (LMSurf\OriginY# + N_UEdgeY# + N_VEdgeY#) LumZ# = (LMSurf\OriginZ# + N_UEdgeZ# + N_VEdgeZ#) PositionEntity(LumelPivot, LumX#, LumY#, LumZ#) RotateEntity(LumelPivot, 0, 0, 0) AlignToVector(LumelPivot, LMSurf\UEdgeX#, LMSurf\UEdgeY#, LMSurf\UEdgeZ#, 1) AlignToVector(LumelPivot, LMSurf\VEdgeX#, LMSurf\VEdgeY#, LMSurf\VEdgeZ#, 3) Dist# = EntityDistance(LightPivot, LumelPivot) ; If this light can light this lumel If (Dist# <= Light\Range#) And (Dist# > 0) ; Normal vector between lumel and light NX# = (LumX# - Light\X#) / Dist# NY# = (LumY# - Light\Y#) / Dist# NZ# = (LumZ# - Light\Z#) / Dist# ; Dot product to find the cosine angle between the surface normal and incident light normal CosAngle# = (NX# * LMSurf\NX#) + (NY# * LMSurf\NY#) + (NZ# * LMSurf\NZ#) ; Poly face front of the light If CosAngle# > 0 LMLightProcess(x, y, Light, LumX#, LumY#, LumZ#, Dist#, CosAngle#, LumelPivot, LightPivot, LumelSize#) EndIf EndIf ; Dist < Light\Range Next ; x Next ; y Next ;Light UnlockBuffer(ImgBuf) SetBuffer(BackBuffer()) ; Move it back ; For i = 0 To LMSurf\NTris-1 ; For j = 0 To LM_VERTS ; VertexCoords(LMSurf\Tris[i]\Surf, LMSurf\Tris[i]\VertIndex[j], LMSurf\Tris[i]\OX[j], LMSurf\Tris[i]\OY[j], LMSurf\Tris[i]\OZ[j]) ; Next ; Next ; For i = 0 To LMSurf\NAdjTris-1 ; For j = 0 To LM_VERTS ; VertexCoords(LMSurf\AdjTris[i]\Surf, LMSurf\AdjTris[i]\VertIndex[j], LMSurf\AdjTris[i]\OX[j], LMSurf\AdjTris[i]\OY[j], LMSurf\AdjTris[i]\OZ[j]) ; Next ; Next FreeEntity(LightPivot) FreeEntity(LumelPivot) If (LMSizeX > 2) And (LMSizeY > 2) TFormFilter(True) Contrast = LMImageMeasureContrast(LMSurf\Image) NSizeX = LMSizeX NSizeY = LMSizeY Select True Case (Contrast <= 4) NSizeX = 2 : NSizeY = 2 Case (Contrast > 4) And (Contrast <= 20) NSizeX = NSizeX / 4 NSizeY = NSizeY / 4 Case (Contrast > 20) And (Contrast <= 80) NSizeX = NSizeX / 2 NSizeY = NSizeY / 2 End Select If NSizeX < 2 Then NSizeX = 2 If NSizeY < 2 Then NSizeY = 2 If (NSizeX <> LMSizeX) Or (NSizeY <> LMSizeY) ResizeImage(LMSurf\Image, NSizeX, NSizeY) EndIf EndIf LMSurf\ImageSize = ImageWidth(LMSurf\Image) * ImageHeight(LMSurf\Image) End Function ; LumelRadius# is in world units, not texture UV. Function LMLightProcess(x%, y%, Light.LMLight, LumX#, LumY#, LumZ#, Dist#, CosAngle#, LumelPivot, LightPivot, LumelRadius#=0) ; Measure attenuation Att# = 1.0 / (Light\Att#[0] + (Light\Att#[1] * Dist#) + (Light\Att#[2] * Dist# * Dist#)) ; Lambert + attenuation Intensity# = (Light\Bright# * CosAngle#) * Att# If (Intensity# < 0.0) Then Intensity# = 0.0 If (Intensity# > 1.0) Then Intensity# = 1.0 If Intensity# > LM_INTENSITY_EPSILON# NHits = 0 NFired = 0 If Light\CastShadows NFired = NFired + 1 Obscured = False For LumelCorner = 1 To 4 Select LumelCorner ; Top left Case 1 TFormPoint -LumelRadius#, LM_LUMEL_PULL_INWARD#, -LumelRadius#, LumelPivot, 0 ; Top right Case 2 TFormPoint LumelRadius#, LM_LUMEL_PULL_INWARD#, -LumelRadius#, LumelPivot, 0 ; Bottom left Case 3 TFormPoint -LumelRadius#, LM_LUMEL_PULL_INWARD#, LumelRadius#, LumelPivot, 0 ; Bottom right Case 4 TFormPoint LumelRadius#, LM_LUMEL_PULL_INWARD#, LumelRadius#, LumelPivot, 0 End Select ; Get the location of this corner. Px# = TFormedX#() Py# = TFormedY#() Pz# = TFormedZ#() ; Calculate the vector between this corner and the light. Dx# = Light\X# - Px# Dy# = Light\Y# - Py# Dz# = Light\Z# - Pz# ; Check each obscuring object to see if it is blocking the light. ; Exit early if one is found. ; Might get some additional speed with a lot of obscurers by sorting them so that those ; nearest the light source or the lumel are examined first. For ThisObscurer.LMObscurer = Each LMObscurer If Ray_Intersect_Mesh(ThisObscurer\Entity, Px#, Py#, Pz#, Dx#, Dy#, Dz#, False, True) Obscured = True Exit EndIf Next If Obscured Then Exit Next If Not Obscured Then NHits = NHits + 1 Else ; This light does not cast shadows. NHits = 1 NFired = 1 EndIf ; If this lumel is illuminated... If (NHits > 0) Intensity# = Intensity# * Float(NHits) / Float(NFired) ; Add the incident light the pixel ARGB = ReadPixelFast(x, y) And $FFFFFF R = (ARGB Shr 16 And %11111111) G = (ARGB Shr 8 And %11111111) B = (ARGB And %11111111) R = R + (Light\R * Intensity) G = G + (Light\G * Intensity) B = B + (Light\B * Intensity) If R > 255 Then R = 255 If G > 255 Then G = 255 If B > 255 Then B = 255 RGB = B Or (G Shl 8) Or (R Shl 16) WritePixelFast(x, y, RGB) EndIf EndIf End Function ; ; Blur an image using radius ; Function LMBlurImage(Image, radius = 1) TmpImg = CopyImage(Image) TmpBuf = ImageBuffer(TmpImg) ImgBuf = ImageBuffer(Image) LockBuffer(ImgBuf) LockBuffer(TmpBuf) W% = ImageWidth(Image) H% = ImageHeight(Image) ; Go thru all the pixels For y% = 0 To H-1 For x% = 0 To W-1 ; Measure the box to get the pixel samples from ix1 = x - radius iy1 = y - radius ix2 = x + radius iy2 = y + radius ; Prevent it going out of bound If ix1 < 0 Then ix1 = 0 If iy1 < 0 Then iy1 = 0 If ix2 > W-1 Then ix2 = W-1 If iy2 > H-1 Then iy2 = H-1 r = 0 : g = 0 : b = 0 num = 0 ; Run thru all the sampled box For y2% = iy1 To iy2 For x2% = ix1 To ix2 ; Sum the sampled pixel argb = ReadPixelFast(x2, y2, TmpBuf) And $FFFFFF ar = (argb Shr 16 And %11111111) ag = (argb Shr 8 And %11111111) ab = (argb And %11111111) r = r + ar g = g + ag b = b + ab num = num + 1 Next Next ; Get the average value r = r / num g = g / num b = b / num ; Clamp ; (sswift: Impossible to get RGB value greater than 255 with averaging!) ;If r > 255 Then r = 255 ;If g > 255 Then g = 255 ;If b > 255 Then b = 255 rgb = b Or (g Shl 8) Or (r Shl 16) WritePixelFast(x, y, rgb, ImgBuf) Next Next UnlockBuffer(TmpBuf) UnlockBuffer(ImgBuf) FreeImage(TmpBuf) End Function ; ; Helper functions ; Global g_TriNormalX#, g_TriNormalY#, g_TriNormalZ# Function GetTriangleNormal(x1#, y1#, z1#, x2#, y2#, z2#, x3#, y3#, z3#) ux# = x1# - x2# uy# = y1# - y2# uz# = z1# - z2# vx# = x3# - x2# vy# = y3# - y2# vz# = z3# - z2# nx# = (uy# * vz#) - (vy# * uz#) ny# = (uz# * vx#) - (vz# * ux#) nz# = (ux# * vy#) - (vx# * uy#) ; Normalize it NormLen# = Sqr((nx*nx) + (ny*ny) + (nz*nz)) If NormLen > 0 nx = nx/NormLen : ny = ny/NormLen: nz = nz/NormLen Else nx = 0 : ny = 0 : nz = 1 EndIf g_TriNormalX = nx g_TriNormalY = ny g_TriNormalZ = nz End Function Function TriangleNormalX#() Return g_TriNormalX End Function Function TriangleNormalY#() Return g_TriNormalY End Function Function TriangleNormalZ#() Return g_TriNormalZ End Function Function CreateLUVs(mesh,filename$,coordset=1) file = WriteFile(filename) WriteInt(file, CountSurfaces(mesh)) For surfcount = 1 To CountSurfaces(mesh) surf = GetSurface(mesh,surfcount) fprint = SurfaceFingerPrint(mesh, surf) WriteInt(file, fprint) count = CountVertices(surf) WriteInt(file, count) For vercount = 0 To count-1 WriteFloat(file,VertexU(surf,vercount,coordset)) WriteFloat(file,VertexV(surf,vercount,coordset)) Next Next WriteInt(file, 0) CloseFile file End Function Function LoadLUVs(mesh,filename$,coordset=1) file = ReadFile(filename) surfcount = ReadInt(File) If surfcount <> CountSurfaces(mesh) DebugLog "Wrong number of surfaces" CloseFile(file) Return False EndIf fprint = ReadInt(file) While fprint surf = FindSurfFingerPrint(mesh, fprint) If surf count = ReadInt(file) For vercount = 0 To count-1 u# = ReadFloat(file) v# = ReadFloat(file) VertexTexCoords(surf,vercount,u,v,0,coordset) Next Else DebugLog "Surface fingerprint " + fprint + " not found" count = ReadInt(file) For vercount = 0 To count-1 ReadFloat(file):ReadFloat(file) Next EndIf fprint = ReadInt(file) Wend CloseFile file End Function Function FindSurfFingerPrint(mesh, fingerprint) For surfcount = 1 To CountSurfaces(mesh) surf = GetSurface(mesh, surfcount) If SurfaceFingerPrint(mesh, surf) = fingerprint Return surf EndIf Next Return 0 End Function Function SurfaceFingerPrint%(mesh, surf) tricount = CountTriangles(surf) CoordSum = 0 For tri = 0 To tricount - 1 For i = 0 To 2 in = TriangleVertex(surf, tri, i) s$ = VertexX(surf, in) Pos = Instr(s, ".") If Pos <> 0 x# = Left(s$, pos + 3) Else x# = s$ EndIf s$ = VertexY(surf, in) Pos = Instr(s, ".") If Pos <> 0 y# = Left(s$, pos + 3) Else y# = s$ EndIf s$ = VertexZ(surf, in) Pos = Instr(s, ".") If Pos <> 0 z# = Left(s$, pos + 3) Else z# = s$ EndIf CoordSum = CoordSum + Abs(x * 3 * (i+1)) CoordSum = CoordSum + Abs(y * 2 * (i+1)) CoordSum = CoordSum + Abs(z * 1 * (i+1)) Next Next Return CoordSum End Function Function Unweld(mesh) ;Unweld a mesh, retaining all of its textures coords and textures For surfcount = 1 To CountSurfaces(mesh) surf = GetSurface(mesh,surfcount) count = CountTriangles(surf) bank = CreateBank((15*count)*4) For tricount = 0 To count-1 off = (tricount*15)*4 in = TriangleVertex(surf,tricount,0) x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in) u# = VertexU(surf,in):v#=VertexV(surf,in) PokeFloat(bank,off,x) PokeFloat(bank,off+4,y) PokeFloat(bank,off+8,z) PokeFloat(bank,off+12,u) PokeFloat(bank,off+16,v) in = TriangleVertex(surf,tricount,1) x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in) u# = VertexU(surf,in):v#=VertexV(surf,in) PokeFloat(bank,off+20,x) PokeFloat(bank,off+24,y) PokeFloat(bank,off+28,z) PokeFloat(bank,off+32,u) PokeFloat(bank,off+36,v) in = TriangleVertex(surf,tricount,2) x# = VertexX(surf,in):y#=VertexY(surf,in):z#=VertexZ(surf,in) u# = VertexU(surf,in):v#=VertexV(surf,in) PokeFloat(bank,off+40,x) PokeFloat(bank,off+44,y) PokeFloat(bank,off+48,z) PokeFloat(bank,off+52,u) PokeFloat(bank,off+56,v) Next ClearSurface(surf,True,True) For tricount = 0 To count-1 off = (tricount*15)*4 x# = PeekFloat(bank,off) y# = PeekFloat(bank,off+4) z# = PeekFloat(bank,off+8) u# = PeekFloat(bank,off+12) v# = PeekFloat(bank,off+16) a = AddVertex(surf,x,y,z,u,v) x# = PeekFloat(bank,off+20) y# = PeekFloat(bank,off+24) z# = PeekFloat(bank,off+28) u# = PeekFloat(bank,off+32) v# = PeekFloat(bank,off+36) b = AddVertex(surf,x,y,z,u,v) x# = PeekFloat(bank,off+40) y# = PeekFloat(bank,off+44) z# = PeekFloat(bank,off+48) u# = PeekFloat(bank,off+52) v# = PeekFloat(bank,off+56) c = AddVertex(surf,x,y,z,u,v) AddTriangle(surf,a,b,c) Next FreeBank bank Next UpdateNormals mesh Return mesh End Function Dim txv(3) Type TRIS Field x0# Field y0# Field z0# Field u0# Field v0# Field U20# Field V20# Field x1# Field y1# Field z1# Field u1# Field v1# Field U21# Field V21# Field x2# Field y2# Field z2# Field u2# Field v2# Field U22# Field V22# Field surface End Type Function Weld(mish) Dim txv(3) For nsurf = 1 To CountSurfaces(mish) su=GetSurface(mish,nsurf) For tq = 0 To CountTriangles(su)-1 txv(0) = TriangleVertex(su,tq,0) txv(1) = TriangleVertex(su,tq,1) txv(2) = TriangleVertex(su,tq,2) vq.TRIS = New TRIS vq\x0# = VertexX(su,txv(0)) vq\y0# = VertexY(su,txv(0)) vq\z0# = VertexZ(su,txv(0)) vq\u0# = VertexU(su,txv(0),0) vq\v0# = VertexV(su,txv(0),0) vq\u20# = VertexU(su,txv(0),1) vq\v20# = VertexV(su,txv(0),1) vq\x1# = VertexX(su,txv(1)) vq\y1# = VertexY(su,txv(1)) vq\z1# = VertexZ(su,txv(1)) vq\u1# = VertexU(su,txv(1),0) vq\v1# = VertexV(su,txv(1),0) vq\u21# = VertexU(su,txv(1),1) vq\v21# = VertexV(su,txv(1),1) vq\x2# = VertexX(su,txv(2)) vq\y2# = VertexY(su,txv(2)) vq\z2# = VertexZ(su,txv(2)) vq\u2# = VertexU(su,txv(2),0) vq\v2# = VertexV(su,txv(2),0) vq\u22# = VertexU(su,txv(2),1) vq\v22# = VertexV(su,txv(2),1) Next ClearSurface su For vq.tris = Each tris vt1=findvert(su,vq\x0#,vq\y0#,vq\z0#,vq\u0#,vq\v0#,vq\u20#,vq\v20#) If vt1=-1 Then vt1=AddVertex(su,vq\x0#,vq\y0#,vq\z0#,vq\u0#,vq\v0#) VertexTexCoords su,mycount,vq\u20#,vq\v20#,0,1 vt1 = mycount mycount = mycount +1 EndIf vt2=findvert(su,vq\x1#,vq\y1#,vq\z1#,vq\u1#,vq\v1#,vq\u21#,vq\v21#) If Vt2=-1 Then vt2=AddVertex( su,vq\x1#,vq\y1#,vq\z1#,vq\u1#,vq\v1#) VertexTexCoords su,mycount,vq\u21#,vq\v21#,0,1 vt2 = mycount mycount = mycount +1 EndIf vt3=findvert(su,vq\x2#,vq\y2#,vq\z2#,vq\u2#,vq\v2#,vq\u22#,vq\v22#) If vt3=-1 Then vt3=AddVertex(su,vq\x2#,vq\y2#,vq\z2#,vq\u2#,vq\v2#) VertexTexCoords su,mycount,vq\u22#,vq\v22#,0,1 vt3 = mycount mycount = mycount +1 EndIf AddTriangle su,vt1,vt2,vt3 Next Delete Each tris mycount=0 Next End Function Function findvert(su,x2#,y2#,z2#,u2#,v2#,u22#,v22#) Local thresh# =0.001 For t=0 To CountVertices(su)-1 If Abs(VertexX(su,t)-x2#)<thresh# Then If Abs(VertexY(su,t)-y2#)<thresh# Then If Abs(VertexZ(su,t)-z2#)<thresh# Then If Abs(VertexU(su,t,0)-u2#)<thresh# Then If Abs(VertexV(su,t,0)-v2#)<thresh# Then If Abs(VertexU(su,t,1)-u22#)<thresh# Then If Abs(VertexV(su,t,1)-v22#)<thresh# Then Return t EndIf EndIf EndIf EndIf EndIf EndIf EndIf Next Return -1 End Function ; ; Example function ; ; Hold 2th mouse button do turn the cam ; Use the arrrows to move ; Click on a object to lightmap it ; Press F2 to load the saved lightmap Function LMExample() Graphics3D 1024, 768, 32, 2 SetBuffer(BackBuffer()) ; Create some stuff in the world camera = CreateCamera() PositionEntity(camera, 17, 18, 18) cube1 = CreateCube() FlipMesh(cube1) PositionMesh(cube1, 0, 1.0, 0) ScaleEntity(cube1, 20, 5, 20) EntityPickMode(cube1, 2) NameEntity(cube1, "cube1") PointEntity(camera, cube1) ; Look at the cube cube2 = CreateCube() ;PositionMesh(cube2, 0, 2, 0) PositionMesh(cube2, 0, 1, 0) ;PositionMesh(cube2, 0, 0.5, 0) ScaleEntity(cube2, 2, 2, 2) EntityPickMode(cube2, 2) NameEntity(cube2, "cube2") AmbientLight(50, 50, 50) light = CreateLight(1) RotateEntity(light, 45, 30, 0) ThisObscurer.LMObscurer = New LMObscurer ThisObscurer\Entity = Cube1 ThisObscurer.LMObscurer = New LMObscurer ThisObscurer\Entity = Cube2 ; Timing control OldTime% = MilliSecs() While Not KeyHit(1) ; Time elapsed between last frame Time% = MilliSecs() DeltaTime# = Float(Time - OldTime) / 1000 ; in seconds OldTime% = Time ; Camera movement CamSpd# = 10 * DeltaTime MoveEntity(camera, Float(KeyDown(205) - KeyDown(203)) * CamSpd, 0, Float(KeyDown(200) - KeyDown(208)) * CamSpd) If MouseDown(2) TurnSpeed# = 0.8 TurnEntity(camera, Float(MouseYSpeed()) * TurnSpeed#, 0, 0, False) TurnEntity(camera, 0, -Float(MouseXSpeed()) * TurnSpeed#, 0, True) Else MouseXSpeed() : MouseYSpeed() EndIf ; Lightmap the picked entity If MouseHit(1) ent = CameraPick(camera, MouseX(), MouseY()) If ent ;EntityPickMode(cube1, 0) BeginLightMap(40, 40, 40) CreateLMLight( -8, 3, -8, 219, 219, 255, 0, True, 3) CreateLMLight( 8, 3, 3, 255, 255, 219, 0, True, 3) ;(mesh, lumelsize# = 0.5, maxmapsize = 1024, blurradius = 1, TotalInfo$ = "") ;tex = LightMapMesh(ent, 0.25, 1024, 1, "Lightmapping " + EntityName(ent)) tex = LightMapMesh(ent, 0.25, 1024, 1, "Lightmapping " + EntityName(ent)) If tex SaveLightMap(ent, tex, EntityName(ent) + "_lm.bmp", EntityName(ent) + ".luv") ApplyLightMap(ent, tex) EndIf EndLightMap() ;EntityPickMode(cube1, 2) EndIf EndIf If KeyHit(60) ; F2 key ent = CameraPick(camera, MouseX(), MouseY()) If ent LoadLightMap(ent, EntityName(ent) + "_lm.bmp", EntityName(ent) + ".luv") EndIf EndIf UpdateWorld() RenderWorld() ent = CameraPick(camera, MouseX(), MouseY()) If ent Then Text 0,0, EntityName$(ent) Flip() Wend EndGraphics() End Function |
Comments
| ||
cant find the include file |
Code Archives Forum