www.dreamspaced.com/WOT360.rar
Alpha of the map editor for World of Tomorrow. Although it can be used by any game using the map engine, which is not tied to the twot code base.
The map engine now supports 360 Rotational scrolling as well as regular scrolling.
Features:
Load,Save - Custom format. The .map file compresses and saves any tiles used in it so it is self-contained.
PasteTile,Line Tile,Hollow Box, Filled Box,Tile Picker and Clear tools.
It has a global ini file that saves all current settings when you quit.
You can create new maps of any size by clicking on new map.
The W and D keys cycle through tiles. Tile window is there, but not implemented.
To load a folder of tiles, click on load tile folder and pick a folder. It'll load any images without "nm" or "bump" in the file name.
If you place a NM file of a tile, the map engine will use it for normal mapping.
Hold in rmb and move the mouse to rotate the map, hold in 3rd mouse button and move mouse to scroll map. alternatively move to the edge of the screen.
Lmb = Active tool.
Presently you cannot edit a map that is rotated as the tile picker hasn't been updated to take rotation into account.
Scroll has though..so it's natural.
Here's the engine code for you to to load use the maps.
Example usage,
Although not yet implemented in the map editor, you can render lightmaps manually in code, add light sources, with per pixel normal mapping.
Tile1.jpg
needs a tile called nmTile1.jpg
This is a regular 3d normal map, you can produce them in deep exploration (Free download. The 2d image part doesn't run out.)
Report any non obvious bugs please.
Alpha of the map editor for World of Tomorrow. Although it can be used by any game using the map engine, which is not tied to the twot code base.
The map engine now supports 360 Rotational scrolling as well as regular scrolling.
Features:
Load,Save - Custom format. The .map file compresses and saves any tiles used in it so it is self-contained.
PasteTile,Line Tile,Hollow Box, Filled Box,Tile Picker and Clear tools.
It has a global ini file that saves all current settings when you quit.
You can create new maps of any size by clicking on new map.
The W and D keys cycle through tiles. Tile window is there, but not implemented.
To load a folder of tiles, click on load tile folder and pick a folder. It'll load any images without "nm" or "bump" in the file name.
If you place a NM file of a tile, the map engine will use it for normal mapping.
Hold in rmb and move the mouse to rotate the map, hold in 3rd mouse button and move mouse to scroll map. alternatively move to the edge of the screen.
Lmb = Active tool.
Presently you cannot edit a map that is rotated as the tile picker hasn't been updated to take rotation into account.
Scroll has though..so it's natural.
Here's the engine code for you to to load use the maps.
Example usage,
Local Test:Map = Map.Load("MyMap.map") Repeat cls Test.Rotate( 180 ) Test.Scroll( 5,0 ) Test.Draw() flip forever
Strict ' ' 'Map Engine V0.1. '(c)Dreamspace 2005. Type Base Field x:Float,y:Float Field z:Float Method Draw(x,y) Abstract Method Pasted( mapx,mapy,Mapz,ax,ay) Abstract End Type Type Light Extends Base Field r:Float,g:Float,b:Float Field castShadow:Byte Field radius:Float Global Lights:TList Function Create:Light( r,g,b ) Local out:light = New light out.r=r out.g=g out.b=b out.castShadow=True ListAddLast Light.Lights,out Return out End Function Method Color(nr,ng,nb) r=nr g=ng b=nb End Method Method SetRadius( nRadius) radius = nradius End Method Method Shadows( enable:Byte = True) castShadow = enable End Method Method Draw(x,y) Final Local tr,tg,tb GetColor(tr,tg,tb) Local tblend = GetBlend() Local alpha:Float =GetAlpha() SetBlend AlphaBlend SetAlpha 0.5 SetColor r,g,b DrawOval x-Radius/2,y-radius/2,radius,radius SetBlend SolidBlend SetColor tr,tg,tb SetAlpha alpha End Method Method pasted(mapx,mapy,Mapz,ax,ay) Final x=ax y=ay z = mapz End Method End Type Light.Lights = CreateList() Type Occluder Field map:Byte[,] Function Create:Occluder(Width,Height) Local out:occluder = New occluder out.map = New Byte[width,height] Return out End Function Method FromPixMap( pix:TPixmap ) For Local x=0 To PixmapWidth( pix ) - 1 For Local y=0 To PixmapHeight( pix) - 1 Local p:Byte Ptr = pix.PixelPtr(x,y) Local lum = p[2]+p[1]+p[3]/3 If lum<30 map[x,y]=False Else map[x,y]=True EndIf Next Next End Method End Type Type ColMap Field Pix:Byte[,,] Field Hi:Byte[,] Field w,h Function Create:ColMap( Width,Height ) Local out:colMap = New colmap out.pix = New Byte[width,height,3] out.hi = New Byte[width,height] out.w=width out.h=height Return out End Function Method FromPixMap( raw:TPixMap ) For Local x=0 To w-1 For Local y=0 To h-1 Local p:Byte Ptr = raw.PixelPtr(x,y) pix[x,y,0] = p[2] pix[x,y,1] = p[1] pix[x,y,2] = p[0] Next Next End Method Method LumFromPix( raw:TPixMap ) For Local x=0 To w-1 For Local y=0 To h-1 Local p:Byte Ptr = raw.pixelPtr(x,y) hi[x,y] =(p[2]+p[1]+p[0])/3 Next Next End Method End Type Type BumpMap Field Col:ColMap Function Create:BumpMap(width,height ) Local out:bumpmap = New bumpmap out.col = colMap.create( width,height ) Return out End Function Function Load:BumpMap( file:String ) Local out:Bumpmap = New bumpmap Local pix:TPixmap = LoadPixmap( file ) If pix=Null Print "Bump map not found" Return Null EndIf ConvertPixmap( pix,PF_BGRA8888) out.col = Colmap.create(PixmapWidth(pix),PixmapHeight(pix) ) out.col.frompixmap( pix ) Return out End Function Method GetPixels:Byte[,,]( ) Return col.pix End Method End Type Type Tile Field image:TImage Field raw:TPixmap Field isAlphaAdjancent:Byte Field isWalkable:Byte Field cFrame:Float Field cMaxFrame:Float Field cAnimSpd:Float Field Width,Height Field shape:Occluder Field Name:String Field Bump:BumpMap Method mSize:Long() Local siz=0 siz:+width*height*4 siz:+SizeOf(width)*4 siz:+SizeOf(name) siz:+SizeOf(cMaxFrame) siz:+SizeOf(cAnimSpd) Return siz End Method Method ReadTile( Stream:TStream ) width = Readint(stream) height = Readint(stream) Local slen=Readint(stream) name=ReadString(stream,sLen) cMaxFrame=Readint(Stream) cAnimSpd=ReadFloat(Stream) image = CreateImage( width,height,1,MASKEDIMAGE) raw = LockImage( image,0,False,True ) For Local x=0 To width-1 For Local y=0 To height-1 Local p:Byte Ptr = raw.pixelptr(x,y) p[0]=ReadByte(stream) p[1]=ReadByte(stream) p[2]=ReadByte(stream) p[3]=ReadByte(stream) Next Next Local crc = Readint( stream ) If crc<>-1 RuntimeError "Tile load crc mismatch" Print "Loaded tile Width:"+width+" Height:"+height UnlockImage(image) End Method Method WriteTile( stream:TStream ) Local at = StreamPos(stream) WriteInt stream,width WriteInt stream,height WriteInt stream,Len( name ) WriteString stream,name WriteInt stream,cMaxFrame WriteFloat stream,cAnimSpd Local raw:TPixmap = LockImage( image,0,True,False ) For Local x=0 To width-1 For Local y=0 To height-1 Local p:Byte Ptr = raw.pixelPtr(x,y) WriteByte stream,p[0] WriteByte stream,p[1] WriteByte stream,p[2] WriteByte stream,p[3] Next Next WriteInt stream,-1 Print "Tile was "+String(StreamPos(stream)-at)+" Bytes" End Method Function Load:Tile(file:String) Local out:Tile = New tile If FileType(file)<>1 RuntimeError "Cannot load Tile "+file+" Not a valid file." out.image = LoadImage( file,MASKEDIMAGE ) If out.image = Null RuntimeError "Cannot load Image "+File out.raw = LoadPixmap( file ) If out.raw = Null RuntimeError "Image loaded but pixal map failed." out.raw = ConvertPixmap( out.raw,PF_BGRA8888) out.cFrame=0 out.width = ImageWidth(out.image) out.height = ImageHeight(out.image) out.name = StripAll( file ) Local t1:String = ExtractDir( file )+"\nm"+out.name+".bmp" Local t2:String = ExtractDir( file )+"bump"+out.name+"."+".bmp" 'ExtractExt( file ) out.bump = BumpMap.Load( t1 ) If out.bump = Null RuntimeError t1+" not present." ' out.bump = BumpMap.Create( out.width,out.height ) EndIf ' Local braw:TPixMap = LoadPixmap( t2 ) ' If braw = Null ' Print "No height map" ' out.bump.col.lumFromPix( out.raw ) ' Else ' out.bump.col.lumFromPix( braw ) ' EndIf out.shape = Occluder.Create( out.width,out.height ) out.shape.fromPixMap( out.raw ) Return out End Function Function Create:Tile(Width,Height,Name:String ="Custom Tile") Local out:tile = New Tile out.name = name out.width=width out.height = height out.raw = CreatePixmap(width,height,PF_BGRA8888) Return out End Function Method process() image = CreateImage(width,height,cMaxFrame+1,DYNAMICIMAGE | MASKEDIMAGE ) Local traw:TPixmap = LockImage(image,0,False,False) Local p1:Byte Ptr Local p2:Byte Ptr For Local x=0 To width-1 For Local y=0 To height-1 ' WritePixel( traw,x,y,ReadPixel(raw,x,y) ) p1 = traw.PixelPtr(x,y) p2 = raw.pixelptr(x,y) p1[0]=p2[2] p1[1]=p2[1] p1[2]=p2[0] p1[3]=p2[3] Next Next UnlockImage( image,0 ) End Method Method AlphaArea( enable:Byte ) isalphaAdjancent = enable End Method Method Walkable( enable:Byte ) isWalkable = True End Method Method GetRaw:TPixmap() Return raw End Method Method Update() If(cmaxFrame>0) cFrame:+cAnimSpd If(cFrame>cmaxFrame) cFrame=0 EndIf End If End Method Method Draw(x:Int,y:Int) DrawImage image,x,y,cFrame End Method Method getWidth() Return width End Method Method getHeight() Return height End Method End Type Type ObjLayer Field obj:TList End Type Type Layer Field Lay:Tile[] Field Obj:ObjLayer[] Field isHigh:Byte Method Init( Layers:Short) Lay = New Tile[Layers] Obj = New ObjLayer[Layers] For Local j=0 To layers-1 obj[j] = New ObjLayer obj[j].obj = CreateList() Next End Method Method Highlight( enable:Byte ) isHigh = enable End Method Method AddObj(depth,this:base) ListAddLast obj[depth].obj,this End Method Method GetObjList:TList(depth) Return obj[depth].obj End Method Method ResetObjs(depth) ClearList obj[depth].obj End Method End Type Type Row Field Col:Layer[] Method Init( Rows:Short,depth:Short ) Col = New Layer[rows] For Local j=0 To rows-1 col[j] = New Layer col[j].init( depth ) Next End Method End Type Type Map Field mWidth,mHeight,mDepth Field col:Row[] Function Load:Map( file:String ) Local out:map = New map out.loadFile( file ) Return out End Function Function Create:Map(width:Short,height:Short,depth:Short) Local out:Map = New Map out.twidth=-1 out.vwidth = GraphicsWidth() out.vheight = GraphicsHeight() out.mWidth = width out.mHeight = height out.mDepth = depth out.populate() Return out End Function Method populate() col = New Row[mwidth] For Local j=0 To mwidth-1 col[j] = New Row col[j].init( mheight,mdepth ) Next End Method Method FindId( tiles:TList,til:Tile ) Local index=0 For Local ct:Tile = EachIn tiles If ct = til Return index EndIf index:+1 Next Return -1 End Method Method ListHas( list:TList,obj:Object ) For Local ob:Object = EachIn list If ob = obj Return True Next Return False End Method Method LoadFile( file:String ) Local tiles:TList = CreateList() Local fStream = ReadFile( file ) Local oSize = Readint( fstream) Local cSize = Readint( fstream ) Local tb = CreateBank( cSize ) ReadBank( tb,fStream,0,cSize ) Local nb = CreateBank( oSize*2 ) oSize=oSize*2 uncompress( BankBuf(nb),oSize,BankBuf(tb),cSize ) Local crc = Readint( fStream ) If crc<>-1 RuntimeError "Map corrupted." Print "Orig Size:"+osize Print "Com Siz:"+cSize Local ts:TbankStream = CreateBankStream( nb ) Local tileC:Int = Readint( fStream ) While tileC>0 Local nTile:Tile = New Tile ListAddLast tiles,ntile ntile.readTile( ts ) tWidth = ntile.width theight = ntile.height tileC:-1 Wend mWidth = Readint( fstream ) mHeight = Readint( fstream ) mDepth = Readint( fstream ) Local TileL:Object[] = ListToArray( tiles ) populate() For Local x=0 To mWidth-1 For Local y=0 To mHeight-1 For Local z=0 To mDepth-1 Local id =Readint(fstream) If id<>-1 PasteTile( x,y,z,Tile( tileL[ id ] ) ) EndIf Next Next Next crc = Readint(fstream) If crc<>-2 RuntimeError "Crc failed on map load." CloseFile fstream Print "Loaded "+CountList( tiles)+" Tiles" Print "Map sucesfully loaded." vwidth=GraphicsWidth() vheight = GraphicsHeight() setUp() End Method Method Save( file:String ) Local Tiles:TList = CreateList() Local tSize:Long =0 For Local x=0 To mWidth-1 For Local y=0 To mHeight-1 For Local z=0 To mDepth-1 Local tmp:tile = col[x].col[y].lay[z] If tmp<>Null If Not ListContains( Tiles,tmp ) ListAddLast tiles,tmp tSize:+tmp.mSize() EndIf EndIf Next Next Next Print "Predicted Size:"+tSize+" Bytes For "+CountList(tiles)+" Unique Tiles" Local tb:TBank = CreateBank( tSize+1024 ) Local bStream:TBankStream=CreateBankStream( tb ) For Local Til:Tile = EachIn Tiles til.writeTile( bStream ) Next Print "Tiles pooled. Compressing." Local n:TBank = CreateBank( tSize ) Local dLen = tSize compress2( BankBuf(n),Dlen,BankBuf(tb),tSize,9) Print "Compressed Size:"+dLen Print "Saved "+String(tSize-dLen)+" Bytes" Local fStream:TStream = WriteFile(file) If Not fStream RuntimeError "Could not write file "+file WriteInt fstream,tSize WriteInt fstream,dLen WriteBank( n,fstream,0,dLen ) WriteInt fStream,-1 WriteInt fStream,CountList( tiles ) WriteInt fStream,mWidth WriteInt fstream,mHeight WriteInt fStream,mDepth For Local x=0 To mWidth-1 For Local y=0 To mHeight-1 For Local z=0 To mDepth-1 If col[x].col[y].lay[z]<>Null Local Id = FindId( tiles,col[x].col[y].lay[z] ) If id=-1 RuntimeError "map could not be wrote." WriteInt fstream,id Else WriteInt fstream,-1 EndIf Next Next Next WriteInt fstream,-2 CloseFile(fStream) Print "Map Saved." End Method Method Size( x:Short,y:Short,width:Short,height:Short) xview = x yview = y vwidth = width vheight = height Setup() End Method Method PasteTile(x:Short,y:Short,z:Short,actual:Tile) col[x].col[y].lay[z] = actual If(tWidth=-1) tWidth = actual.width tHeight = actual.height SetUp() EndIf End Method Method PasteFill( Layer:Byte,actual:tile ) For Local j=0 To mWidth-1 For Local k=0 To mHeight-1 PasteTile(j,k,layer,actual) Next Next End Method Method PasteBox( x:Short,y:Short,w:Short,h:Short,layer:Short,actual:tile,solid:Byte = True) For Local ax=x To x+w-1 For Local ay=y To y+h-1 If(solid=True) pasteTile( ax,ay,layer,actual ) Else If ax=x Or ax=x+w-1 Or ay=y Or ay=y+h-1 pasteTile( ax,ay,layer,actual ) EndIf EndIf Next Next End Method Method Highlight( x1,y1,x2,y2 ) For Local j=x1 To x2 For Local k=y1 To y2 col[j].col[k].isHigh=True Next Next End Method Method PasteLine( x:Float,y:Float,x2:Float,y2:Float,layer:Short,actual:tile ) Local xd:Float = x2-x Local yd:Float = y2-y Local steps:Float=0 If Abs(xd)>Abs(yd) steps = Abs(xd) Else steps = Abs(yd) Local xi:Float = xd/steps Local yi:Float = yd/steps While steps>0 pasteTile( x,y,layer,actual ) x:+xi y:+yi steps:-1 Wend End Method Method LineLight( x:Float,y:Float,x2:Float,y2:Float ) Local xd:Float = x2-x Local yd:Float = y2-y Local steps:Float=0 If Abs(xd)>Abs(yd) steps = Abs(xd) Else steps = Abs(yd) Local xi:Float = xd/steps Local yi:Float = yd/steps While steps>0 'pasteTile( x,y,layer,actual ) col[x].col[y].isHigh=True x:+xi y:+yi steps:-1 Wend End Method Method SetUp() xLen = vWidth/tWidth+2 yLen = vHeight/tHeight+2 If xLen<3 Or yLen<3 RuntimeError "size mismatch, cannot initiate map" EndIf Print "" Print "XLen:"+xLen Print "YLen:"+yLen End Method Method getTile:Tile(x:Short,y:Short,z:Short) Return col[x].col[y].lay[z] End Method Field AtX,AtY Method LayerAt:Layer( px,py ) If tWidth<1 Or tHeight<1 Return Local tilex = xp*tWidth-xinc Local tiley = yp*tHeight-yinc Local lx = (tilex+px)/tWidth Local ly = (tiley+py)/theight atx=lx aty=ly If lx>-1 And ly>-1 And lx<mwidth And ly<mheight Return col[lx].col[ly] EndIf Return col[0].col[0] Local sx:Float=-1 Local sy:Float=-1 For Local tx= xp-1 To xp+xLen Local dx:Float = sx+tWidth+xInc For Local ty= yp-1 To yp+yLen Local dy:Float = sy*tHeight+yInc If px>=dx And px<=dx+tWidth If py>=dy And py<=dy+tHeight atX = tx atY = ty If tx>-1 And ty>-1 And tx<mWidth And ty<mHeight Return col[tx].col[ty] Else Return Null EndIf EndIf EndIf Next sy=-1 sx:+1 Next atx=0 aty=0 Return Null Print "Mouse is not registering with any tiles." WaitKey End End Method Field Rot:Float Method Rotate( val:Float ) rot = val End Method Method GetRotate:Float() Return rot End Method Method Draw() If tWidth=-1 Or tWidth=0 Return 'RuntimeError "Map has no known tile asscoitation." EndIf SetRotation rot Local ax:Float,ay:Float Local cx:Float = GraphicsWidth()/2 Local cy:Float = GraphicsHeight()/2 Local tw#=-1*tWidth+xInc Local th#=-1*Theight+yInc Local ew#=(xLen+2)*tWidth+xInc Local eh#=(yLen+2)*tHeight+yinc Local mx#=(ew-tw)/2 Local my#=(eh-th)/2 ax=-3 ay=-3 For Local td=0 To mDepth-1 ax=-5 ay=-5 For Local tx = xp-5 To xp+xLen+7 Local dx:Float = ax*tWidth+xInc For Local ty = yp-5 To yp+yLen+7 Local dy:Float = ay*tHeight+yInc If(tx>-1 And ty>-1) Local xo:Float = dx-mx Local yo:Float = dy-my Local nx:Float,ny:Float nx=Cos (rot)*xo - Sin (rot)*yo ny=Sin (rot)*xo + Cos (rot)*yo Render( tx,ty,td,mx+nx,my+ny ) If td=mdepth-1 If tx<mWidth And ty<mHeight If Col[tx].col[ty].isHigh col[tx].col[ty].isHigh=False Local lr,lg,lb Local lAlpha:Float Local lBlend lblend = GetBlend() lAlpha = GetAlpha() GetColor(lr,lg,lb) SetColor 0,255,255 SetBlend ShadeBlend SetAlpha 0.5 DrawRect mx+nx,my+ny,tWidth,tHeight SetColor lr,lg,lb SetBlend lBlend SetAlpha lAlpha EndIf EndIf EndIf EndIf ay:+1 Next ay=-5 ax:+1 Next ax=-3 ay=-3 For Local tx = xp-1 To xp+xLen Local dx:Float = ax*tWidth+xInc For Local ty = yp-1 To yp+yLen Local dy:Float = ay*tHeight+yInc If(tx>-1 And ty>-1) RenderObjects( tx,ty,td,dx,dy ) EndIf ay:+1 Next ay=-1 ax:+1 Next Next SetRotation 0 End Method Method Scroll( xMov:Float,yMov:Float ) If xMov<-tWidth-1 xMov=-tWidth-1 If xMov>tWidth xMov=tWidth-1 If yMov<-tHeight-1 yMov=-tHeight-1 If yMov>tHeight-1 yMov=tHeight-1 mover( Varptr xInc,Varptr xLen,Varptr xp,xmov,twidth,mWidth ) mover( Varptr yInc,Varptr yLen,Varptr yp,ymov,theight,mHeight ) End Method Method Mover( vInc:Float Ptr,vLen:Float Ptr,vp:Float Ptr,val:Float,size:Float,total:Float ) vInc[0]:+val If vInc[0]<0 vInc[0] = size-Abs(vInc[0]) vp[0]:+1 If vp[0]>total-vLen[0]-1 vp[0]=total-vLen[0]-1 vInc[0] = 0 EndIf EndIf If vInc[0]>size vInc[0]:-size vp[0]:-1 If vp[0]<1 vp[0] =1 vInc[0] =size EndIf EndIf End Method Method RenderObjects(tileX,tileY,tileZ,xPix,yPix) If tileX<0 Or tileX>mWidth-1 Or tileY>mHeight-1 Or tileY<0 Return EndIf If col[tilex] = Null Return If col[tilex].col[tiley] = Null Return Local lay:Tile[] = col[tilex].col[tiley].lay For Local obj:base = EachIn col[tilex].col[tiley].obj[tileZ].obj obj.Draw(xPix+tWidth/2,yPix+tHeight/2) Next End Method Method Render( tileX,tileY,TileZ,xPix,yPix) If tileX=>mWidth Return If tileY=>mHeight Return If tileX<0 Return If tileY<0 Return If col[tilex] = Null Return If col[tilex].col[tiley] = Null Return Local lay:Tile[] = col[tilex].col[tiley].lay If lay[tileZ]<>Null col[tilex].col[tiley].lay[tileZ].Draw( xPix,yPix ) EndIf End Method Method PasteObject( x,y,d,this:Base) ListAddLast col[x].col[y].obj[d].obj,this this.pasted( x,y,d,x*tWidth+tWidth/2,y*tHeight+tHeight/2 ) Print "Added Object At "+X+","+Y+","+d+" Count:"+CountList( col[x].col[y].obj[d].obj ) End Method Field tWidth,tHeight Field xInc:Float,yInc:Float Field xP:Float,yP:Float Field xlen:Float,ylen:Float Field xview:Float,yview:Float Field vwidth,vheight End Type Const FlatLight=1,BumpLight =2,NormalLight=3 Type LightMap Global LightModel Global pMap:Map Global eMap:occluder Function DoEnvMap() Local z=0 Local nx:Float Local ny:Float Local nz:Float For Local x:Float=0 To 255 For Local y:Float=0 To 255 nx = (x-128)/128 ny = (y-128)/128 nz = 1 - Sqr(nx*nx+ny*ny) If nz<0 nz=0 emap.map[x,y] = nz * 256 Next Next End Function Function RenderMap:Map( from:Map ) Local st = MilliSecs() If LightModel = BumpLight emap = Occluder.Create( 256,256 ) LightMap.DoEnvMap() EndIf LightMap.pMap = from Local out:Map = Map.Create( from.mWidth,from.mHeight,from.mDepth ) For Local d=0 To from.mDepth-1 For Local x=0 To from.mWidth-1 For Local y=0 To from.mHeight-1 If from.col[x].col[y].lay[d]<>Null ' Print "Processing Tile at "+X+","+Y+","+"Z:"+d out.col[x].col[y].lay[d] = Tile.Create( from.tWidth,from.tHeight ) RenderTile( out,x,y,d,from.col[x].col[y].lay[d] ) EndIf FlushMem Next Next Next out.twidth = from.twidth out.theight = from.theight out.setUp() Print "Took "+String(MilliSecs()-St)+" To render Lightmap" Return out End Function Function RenderTile( from:map,x,y,d,orig:Tile ) Local this:tile = from.col[x].col[y].lay[d] Local raw:TPixmap = this.raw Local tileX:Float,tileY:Float tileX = x*orig.width tileY = y*orig.height Local revert=0 Local bmap:bumpMap=Null Local bpix:Byte[,,] Local hm:Byte[,] Local llist:TList = Light.lights Local lModel = LightMap.LightModel LightMap.LightModel = NORMALLIGHT If orig.bump<>Null bmap = orig.bump bpix = bmap.col.pix hm = bmap.col.hi Else End ' If LightMap.LightModel = NormalLight ' LightMap.LightModel = FlatLight ' Revert=True ' EndIf End If Local r,g,b Local p:Byte Ptr Local ra,ga,ba Local nx:Float,ny:Float,nz:Float Local mag:Float,lt:Float Local lscale:Float Local px:Float,py:Float,pz:Float For x=0 To this.width-1 For y=0 To this.height-1 p=orig.raw.PixelPtr(x,y) b=p[0] g=p[1] r=p[2] Select lModel Case FlatLight Local ra=0,ga=0,ba=0 End For Local Lgt:Light = EachIn Light.Lights Local xd:Float,yd:Float xd = lgt.x-(tileX+x) yd = lgt.y-(tileY+y) Local td:Float = Sqr( xd*xd+yd*yd ) If td>lgt.radius Continue Local col = LightMap.RayCast( LightMap.pMap,tileX+x,tileY+y,lgt.x,lgt.y,lgt.z,d) If col=True Continue Local lScale:Float = 1-td/lgt.radius If lScale>1 lScale=1 If lScale<0 lScale=0 ra:+lgt.r*lScale ga:+lgt.g*lScale ba:+lgt.b*lScale If ra<0 ra=0 If ga<0 ga=0 If ba<0 ba=0 If ra>255 ra=255 If ga>255 ga=255 If ba>255 ba=255 Next Local rm:Float = ra/255.0 Local gm:Float = ga/255.0 Local bm:Float = ba/255.0 r=r*(rm*2) g=g*(gm*2) b=b*(bm*2) Case BumpLight RuntimeError "What The FUCK." Local ra,ga,ba If x>0 And y>0 And x<orig.width-1 And y<orig.height-1 For Local lgt:Light = EachIn light.lights Local nx:Float = hm[x+1,y]-hm[x-1,y] Local ny:Float = hm[x,y+1]-hm[y,y-1] Local lx:Float = (tilex+x)-lgt.x Local ly:Float = (tiley+y)-lgt.y Local mag:Float = Sqr(lx*lx+ly*ly) nx:-lx ny:-ly nx:+128 ny:+128 If nx<0 Or nx>255 nx=0 If ny<0 Or ny>255 ny=0 Local lScale:Float = LightMap.emap.map[lx,ly] ra:+lgt.r*(lscale/255.0) ga:+lgt.g*(lscale/255.0) ba:+lgt.b*(lscale/255.0) Next EndIf Local rm:Float = ra/255.0 Local gm:Float = ga/255.0 Local bm:Float = ba/255.0 r=r*(rm) g=g*(gm) b=b*(bm) r=ra g=ga b=ba Case NormalLight ra=0 ga=0 ba=0 For Local lgt:Light = EachIn lList nx = lgt.x-(tilex+x) ny = lgt.y-(tiley+y) nz = 65 mag = Sqr( nx*nx+ny*ny+nz*nz ) lt = Sqr(nx*nx+ny*ny) nx = nx/mag ny = ny/mag nz = nz/mag px =bpix[x,y,0]/255.0 py =bpix[x,y,1]/255.0 pz =bpix[x,y,2]/255.0 Local ft:Double =1-mag/lgt.radius lt = 1-lt/lgt.radius If ft<0 ft=0 px=(-1+px*2)*1.7 py=(-1+py*2)*1.7 pz=(-1+pz*2)*1.7 Local tag:Float = Sqr(px*px+py*py+pz*pz) px=px/tag py=py/tag pz=pz/tag lscale =((px * nx) + (py * ny) + (pz * nz))*lt If lscale>0 If Not LightMap.RayCast( LightMap.pMap,tileX+x,tileY+y,lgt.x,lgt.y,lgt.z,d) ra:+lgt.r*lscale ga:+lgt.g*lscale ba:+lgt.b*lscale EndIf EndIf Next r=r*((ra/255.0)*2) g=g*((ga/255.0)*2) b=b*((ba/255.0)*2) End Select If r>255 r=255 If g>255 g=255 If b>255 b=255 WritePixel raw,x,y,r Shl 16 | g Shl 8 | b | 255 Shl 24 Next Next 'FlushMem this.process() If revert =True LightMap.LightModel = BumpLight ' Print "Finished processing Tile in "+String(MilliSecs()-start)+" Millisecs" End Function Function RayCast( on:map,sx:Float,sy:Float,dx:Float,dy:Float,z:Byte,mz:Byte) Local xd:Float = dx-sx Local yd:Float = dy-sy If mz = z Return False Local steps:Float =0 If Abs(xd)>Abs(yd) steps = Abs(xd) Else steps = Abs(yd) Local xi:Float,yi:Float xi = xd/steps yi = yd/steps Local tWidth = LightMap.pMap.tWidth Local tHeight = LightMap.pMap.tHeight 'Local pMap:map = LightMap.pMap Local col:Row[] = LightMap.pMap.col Local tileX,tileY,xo,yo Local lastx,lasty,lz Local sm:Byte[,]=Null Local do:Byte=0 While steps>0 TileX = sx / tWidth TileY = sy / tHeight If tileX<>lastx Or tiley<>lasty lastx=tilex lasty=tiley If col[tilex].col[tiley].lay[z]<>Null sm = col[tilex].col[tiley].lay[z].shape.map do=True Else do=False EndIf EndIf xo = sx-(tileX*tWidth) yo = sy-(tileY*tHeight) sx:+xi*4 sy:+yi*4 steps:-4 If do = False Or sm[xo,yo]=False Continue Return True Wend Return False End Function Function GenRgb( red,green,blue,alpha=255 ) Return red Shl 16 | green Shl 8 | blue | alpha Shl 24 End Function Function GetRGB(argb:Int,a Var,r Var, g Var, b Var) a=(argb Shr 24) r=(argb Shr 16) g=(argb Shr 8) b=argb End Function End Type LightMap.LightModel = NormalLight
Although not yet implemented in the map editor, you can render lightmaps manually in code, add light sources, with per pixel normal mapping.
Tile1.jpg
needs a tile called nmTile1.jpg
This is a regular 3d normal map, you can produce them in deep exploration (Free download. The 2d image part doesn't run out.)
Report any non obvious bugs please.

