Stairstepping terrain

Blitz3D Forums/Blitz3D Beginners Area/Stairstepping terrain

Mikorians(Posted 2013) [#1]
Box filter smoothing with cliff detect-
I tried out the cool Landscape Editor by Rifraf
BUT...
While the sample .b3d is smooth, the saved .b3d isn't :(
I only have a rudimentary understanding of what this averaging technique means, but no idea how to deploy it in the ablend.bb program.
Anybody?
I shelled out for a height map generator.


RemiD(Posted 2013) [#2]
Search for "blur" routine in the codes archives, there are several examples.
Here :
http://www.blitzbasic.com/codearcs/codearcs.php?code=169
Here :
http://www.blitzbasic.com/codearcs/codearcs.php?code=696
and several others.


Mikorians(Posted 2013) [#3]
Not blur image, blur MESH- But---
I figured it out- use a 16 bit format instead of 8


Rick Nasher(Posted 2013) [#4]
@Mikorians:
Ehm, sorry for asking but what Landscape Editor are you referring to?


Mikorians(Posted 2013) [#5]
I've patched up the so called 'free landscape editor' by Rifraf (jeff someone) for examination, eliminated most of the myriad bugs.
Fixed it up so it saves both mesh layers to a single b3d, etc...


Rick Nasher(Posted 2013) [#6]
Link? :-)

Does it save hightmaps too?


Mikorians(Posted 2014) [#7]
Heh!
Sorry for the delay, Rick. Pretty solid now.
It saves b3d files (now a SINGLE .b3d) based on the height map it loads.
It lets you actually airbrush them with 6 different maps!
I finished, but abandoned the project because even with 24 bit height maps, I found myself looking at stairstep terraces (albeit smoothed ones). About 10-30k tris.
This is because of the shallow shaded nature of the height map set I am using.
Your results WILL differ.

If people want this thing, where should I toss it?
I'll be happy to. But I don't want to offend any of the authors of this patchwork, though.
Last author was RifRaf. He'd said he fixed up someone else's code too.
I don't wanna get slapped.

I now use a 3ds max 5 macroscript for mesh terrain generation from heightmaps.
Great results (about 3K tris) still figuring out painting it.
Without vertex alpha layering, it doesn't look as cool as FLE's paint jobs.
But I'll live with it. :)


Mikorians(Posted 2014) [#8]
Well, here is the update, I guarantee you'll recognize it!
You'll need this to make it work:
http://www.melog.ch/dl/fle_rr4.zip
There's also a disclaimer in the original .bb about using the bit maps (.jpg's)-
They're somebody else's, too.

This is only a fixed up ablend3.bb:
I can add a few other features if there's enough interest.
I was going to add a 'save current progress' thing (BIG file).
Circular airbrush... Vertex altitude raise, lower...
Those would be easy enough I'd do it.

Graphics3D 800,600,32,2
SeedRnd MilliSecs() 
WireFrame False

; Simple alpha blending demo.
; by Jeff Frazier.. Aka RIFRAF


; SaveB3D Code added by jfk, utilizing  code of:
; welding by TeraBit, optimized by philippe c
; BlitzSys by Rob Hutchinson & Joseph Cox
; B3D SDK by Mark Sibly

; Thoughts:
; It's becoming more and more a useful Landscape Editor to build Mesh-based Terrains
; using VertexAlpha to smoothly spray additional materials onto it.
; Thus - the World is saved once again thanks to the - FLE - Free Landscape Editor ;-)
; A Blitzbasic pretty open source project.

; Now someone add a nice GUI. Some CONSTANTS may be chanched to GLOBALS to allow altering them 
; at runtime in the GUI (eg. "fadespeed" that is the airbrush intesity)

; Additionally a special Format would be useful that would allow to Save and Load a WIP
; layers collection, so one could end the App and continue work on a landscape the next day...

; *****************************************************************
; More bugs squashed by Joseph D. Bockholt - This is NOT MY APP!
; If you wish it removed, post a reply, have the board email me,
; ANYTHING, and I will have this post removed any way I can with
; my FULLEST, most PROFUSE apologies!!!
; I only do these things to provide a service for everyone struggling
; with BlitzBasic3D- and WE are numerous!!!
;
; >>>>>To use this code, you'll need the rest of the zip file<<<<<<<
; http://www.melog.ch/dl/fle_rr4.zip
;
; ******************************************************************


; ------------includes and stuff added by jfk:----------
Include "weld2_inc.bb"
Include "b3dfile.bb" ; used for B3D Saving
Include "blitzsys.bb" ; used for Blitzsys Fileselector
If DLLBlitzSysInitialise() = False Then RuntimeError("blitzsys.dll not found - Please contact Bill Gates! :P")
Global hWnd = DLLFindBlitzRuntimeHwnd(appname$)
Global c_surfs
mamax=100
Dim c_surf(mamax)
Dim c_brush(mamax)
Dim c_tex(mamax)
Dim c_tex_name$(mamax),use_tex$(mamax),use_img(mamax)

; Notes: tex 1 and 2 are used for the slope texturing function
;        tex 3 is used for seaground function


use_tex$(1)="grass.jpg"    ;tex1.png
use_tex$(2)="rock1.jpg"    ;tex2.png
use_tex$(3)="stones.jpg"   ;tex3.png
use_tex$(4)="mudgrass.jpg" ;tex4.png
use_tex$(5)="rocky.jpg"    ;tex5.png
use_tex$(6)="forest.jpg"   ;tex6.png
;---------------------------------





;-----------------------------
;GLOBALS AND CONSTANTS

;layer you are working with
Global selectedlayer=6,brushsize=1
;higher number layers are closer to surface
;number of layers, and segments
Const numlayers=6
Const lsegments=100 ; currently this is about the max size due to the 64k Vertices Limit in DX (I guess that it was Billy Boy once again who said "Nobody ever needs more than 64k Vertices in a Surface"...)
Const lscale#=50
Const fadespeed#=.06; once .006
;the layers
Dim l_index(lsegments,lsegments)
Dim layer(numlayers)
Dim layertexture(numlayers)
Dim layerbrush(numlayers)
Dim layertexscale#(numlayers)

createlayers()
;testing stuff-----------------------------------
;------------------------------------------------

;light and camera
AmbientLight 150,150,150
Global camera=CreateCamera()
;CameraRange camera,.01,5000
;CameraFogMode camera,1
;CameraFogColor camera,200,200,200
;CameraFogRange camera,1,35
Global light=CreateLight(1)
LightRange light,2000

PositionEntity light,0,1000,0
CameraClsColor camera,100,75,220
PositionEntity camera,10,20,0
PointEntity camera,layer(1)



;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------
;----------------------------------------main test loop--------------------------------------------
;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------
;--------------------------------------------------------------------------------------------------
While Not KeyDown(1)
 ;select current layer to mess with via 1-6 keys
 k=GetKey()
 Select Chr$(k)
  Case "1"
    selectedlayer=1
  Case "2"
    selectedlayer=2
  Case "3"
    selectedlayer=3
  Case "4"
    selectedlayer=4
  Case "5"
    selectedlayer=5
  Case "6"
    selectedlayer=6
  Case "s"
    SaveLandscape()
 End Select 

 ;if you press A the texture scale for current layer will increase
 If KeyDown(30)
  layertexscale#(selectedlayer)=layertexscale#(selectedlayer)+.0001
  ScaleTexture layertexture(selectedlayer),layertexscale#(selectedlayer),layertexscale#(selectedlayer)
  BrushTexture layerbrush(selectedlayer),layertexture(selectedlayer)
  PaintMesh layer(selectedlayer),layerbrush(selectedlayer)
 EndIf

 ;if you press Z the texture scale for current layer will decrease
 If KeyDown(44)
  layertexscale#(selectedlayer)=layertexscale#(selectedlayer)-.0001
  ScaleTexture layertexture(selectedlayer),layertexscale#(selectedlayer),layertexscale#(selectedlayer)
  BrushTexture layerbrush(selectedlayer),layertexture(selectedlayer)
  PaintMesh layer(selectedlayer),layerbrush(selectedlayer)
 EndIf

 ;adjust brushsize with - and + keys
 If KeyHit(12) Or KeyHit(74)
  brushsize=brushsize-1
  If brushsize<0 Then brushsize=0
 EndIf
 If KeyHit(13) Or KeyHit(78)
  brushsize=brushsize+1
  If brushsize>20 Then brushsize=20
 EndIf

 If KeyHit(68) ; f10, rockyslope
  RockySlope(2)
 EndIf

 If KeyHit(87) ; f11, seaground
  MapTopology(3,0,3.3)
 EndIf

 ;MOUSE 1 fades selected layer into view.. kind of a kludge
 If MouseDown(1) Or KeyDown(82) Then bringup(brushsize) ;Numpad 0

 UpdateWorld()
 RenderWorld()
 DrawBlock use_img(selectedlayer),GraphicsWidth()-128,0
 Color 255,255,255
 Text 10,10,"Selected Layer is # "+selectedlayer+" Brush Size is "+brushsize
 Text 10,30,"Use Mouse to steer, Mbutton1 to fade layer, Mbutton2 or Cursorkeys to move"
 Text 10,50,"Use A and Z to scale layer texture"
 Text 10,70,"Use 1 though 6 to select layer"
 Text 10,90,"s=save, f10=slope,f11=seaground"
 Text 10,110,"tris rendered "+TrisRendered()
 Text 10,130,"FPS: "+Floor(fps#)
 Oval GraphicsWidth()/2,GraphicsHeight()/2,10,10,0
 Flip()
 tt=MilliSecs()
 fps#=1000.0/(tt-tt2)
 tt2=tt
 mouselook(camera)
 If MouseDown(2) Then MoveEntity camera,0,0,.5
 If KeyDown(72) Then MoveEntity camera, 0,0,.25  								 	;Up
 If MouseDown(3) Or KeyDown(80) Then MoveEntity camera, 0,0,-.25	;Dn
 If KeyDown(77) Then mouselook(camera,True,1)    									;Rt
 If KeyDown(75) Then mouselook(camera,True,-1)   									;Lt
 If KeyDown(73) Then mouselook(camera,True,0,-1) 									;PgU
 If KeyDown(81) Then mouselook(camera,True,0,1)  									;PgD
Wend
End


 
Function assigntexture(ln,texfile$)
 ; Jeff, I had to alter this to use Brushes to make it work together with the saving code
 layertexture(ln)=LoadTexture(texfile$)
 use_img(ln)=LoadImage(texfile$)
 w=ImageWidth(use_img(ln))
 h=ImageHeight(use_img(ln))
 ResizeImage use_img(ln),128,128
 SetBuffer ImageBuffer(use_img(ln))
 Color 0,0,0
 Text 0,0,w+"*"+h
 Color 255,255,255
 Text 1,1,w+"*"+h
 SetBuffer BackBuffer()
 layerbrush(ln)=CreateBrush()
 ScaleTexture layertexture(ln),.01,.01
 layertexscale#(ln)=.01
 layerbrush(ln)=CreateBrush()
 BrushTexture layerbrush(ln),layertexture(ln)
 PaintMesh layer(ln),layerbrush(ln)
End Function  
 




;Creates a single sided segmented set of faces
;THIS FUNCTION WAS TAKEN FROM CODE ARCHIVES.
;I would give credit to the author, but I can't seem to find the
;function again in archives.

Function CreateLayer(segs=100,parent=0,ls#=1)
	mesh=CreateMesh( parent )
	surf=CreateSurface( mesh )
    brush=CreateBrush(surf)
	stx#=-.5
	sty#=stx
	stp#=Float(1)/Float(segs)
	y#=sty
	For a=0 To segs
		x#=stx
		v#=a/Float(segs)
		For b=0 To segs
			u#=b/Float(segs)
			l_index(a,b)=AddVertex(surf,x,0,y,u,v)
			VertexColor surf,l_index(a,b),255,255,255,0
			x=x+stp
		Next
		y=y+stp
	Next

	For a=0 To segs-1
		For b=0 To segs-1
			v0=a*(segs+1)+b:v1=v0+1
			v2=(a+1)*(segs+1)+b+1:v3=v2-1
			AddTriangle( surf,v0,v2,v1 )
			AddTriangle( surf,v0,v3,v2 )
		Next
	Next
	UpdateNormals mesh
    ScaleEntity mesh,ls#,ls#,ls#
	Return mesh
End Function




Function setalpha(l,llx,llz,al#,splatter=0)
 ;set new alpha value to a vert on a layer
  s=GetSurface(layer(l),1)
  nv=CountVertices(s)
  thisvert=l_index(llx,llz)
 ;if you try to set alpha to a vert outside of the max verts in mesh then
 ;return without error, and without alpha change
 If thisvert<nv Then VertexColor s,thisvert,VertexRed(s,thisvert),VertexGreen(s,thisvert),VertexBlue(s,thisvert),al#
 If splatter Then 
  scalealpha(l,llx+1,llz,(-al/2))
  scalealpha(l,llx-1,llz,(-al/2))
 ; scalealpha(l,llx,llz+lsegments,-al)
 ; scalealpha(l,llx,llz-lsegments,-al)
 EndIf
End Function



 
Function ScaleAlpha(l,llx,llz,al#,splatter=0)
 ;set new alpha value to a vert on a layer
 s=GetSurface(layer(l),1)
 nv=CountVertices(s)
 If llx>lsegments Then llx=lsegments
 If llx<0 Then llx=0
 If llz<0 Then llz=0
 If llz>lsegments Then llz=lsegments

 thisvert=l_index(llx,llz)
 ;if you try to set alpha to a vert outside of the max verts in mesh then
 ;return without error, and without alpha change
 old_al#=VertexAlpha(s,thisvert)
 old_al#=old_al#+al#
 If old_al#<0 Then old_al#=0
 If old_al#>1 Then old_al#=1
 If thisvert<nv Then VertexColor s,thisvert,VertexRed(s,thisvert),VertexGreen(s,thisvert),VertexBlue(s,thisvert),old_al#

End Function




Function loadhmap(texture$,amp#=.002)
 ;THIS FUNCTION WAS BASED OFF OF CODE ARCHIVES FUNCION
 ;CALLED DISPLACEMENT MAPPING .. ORIGINALLY
 ;CREATED BY ZILTCH
 texx=LoadImage(texture$)
 ResizeImage texx,lsegments+1,lsegments+1

 s=GetSurface(layer(1),1)
 nv=CountVertices(s)

 For vx = 0 To lsegments
  For vz = 0 To lsegments
   thisvert=l_index(vx,vz)
      
   bx#  = VertexX(s,thisvert)
   by#  = VertexY(s,thisvert)
   bz#  = VertexZ(s,thisvert)

   bnx#  = VertexNX(s,thisvert)
   bny#  = VertexNY(s,thisvert)
   bnz#  = VertexNZ(s,thisvert)
              
   gettexcol(Texx,vx,vz)
   If numcolB<>0 Then
	   Cr#=numcolR  
		Else
		 cr#=((numcolR*256)+numcolG)/256
	 End If
   If (cr > 0) Then
	   byy#=(cr -128)   * amp
    For i=1 To numlayers
     s=GetSurface(layer(i),1)
     VertexCoords s,thisvert,VertexX(s,thisvert),byy,VertexZ(s,thisvert)
    Next
   End If
  Next
 Next
 For i=1 To numlayers
  UpdateNormals layer(i)
 Next
End Function



Global numcolR#,numcolG,numcolB
Function numcolor(num#)
;convert number to r g b values
  numcolR=num  Shr 16 And %11111111
  numcolG=num Shr 8 And %11111111
  numcolB=num And %11111111
End Function



Function gettexcol(tex,ttx,tty)
; get results from numcolR, numcolG, numcolB
  SetBuffer ImageBuffer(tex)
  LockBuffer ImageBuffer(tex)
  numcolor(ReadPixelFast(ttx,tty))
  UnlockBuffer ImageBuffer(tex)
  SetBuffer BackBuffer()
End Function



;Control
; Camera position, angle values
Global cam_x#,cam_z#,cam_pitch#,cam_yaw#,cam_speed#=.5		; Current
Global dest_cam_x#,dest_cam_z#,dest_cam_pitch#,dest_cam_yaw#	; Destination
Global ent_x#,ent_z#,ent_pitch#,ent_yaw#,ent_speed#=.5		; Current
Global dest_ent_x#,dest_ent_z#,dest_ent_pitch#,dest_ent_yaw#	; Destination



Function mouselook(camera,kbd=False,xs#=0,ys#=0)
	; Mouse look
	; ----------

	; Mouse x and y speed
	If kbd=False Then
	mxs#=MouseXSpeed()
	mys#=MouseYSpeed()
	; Mouse shake (total mouse movement)
	mouse_shake=Abs(((mxs+mys)/2)/1000.0)
	Else
	mxs#=xs#:mys#=ys#
	End If

	; Destination camera angle x and y values
	dest_cam_yaw#=dest_cam_yaw#-mxs#
	dest_cam_pitch#=dest_cam_pitch#+mys#

	; Current camera angle x and y values
	cam_yaw=cam_yaw+((dest_cam_yaw-cam_yaw)/5)
	cam_pitch=cam_pitch+((dest_cam_pitch-cam_pitch)/5)
	
	RotateEntity camera,cam_pitch#,cam_yaw#,0
	;RotateEntity camera,mxs,mys,0
		
	; Rest mouse position to center of screen
	If kbd=False Then MoveMouse 400,300

		; Move camera using movement values
	MoveEntity camera,x#,y#,z#
		
End Function	




Function bringup(area=4,fs#=fadespeed#)
 area2=area
 cp=EntityPick(camera,100000)

 If cp Then 
  s=PickedSurface()
  t=PickedTriangle()
  v=TriangleVertex(s,t,1)

  fxx=-1
  ;lets locate the v vertex in our precaled l_index
  For cvx=0 To lsegments
   For cvz=0 To lsegments
    If l_index(cvx,cvz)=v Then 
     fxx=cvx
     fzz=cvz
     Exit ;This had been remmed out for some reason...
    EndIf
   Next
		If fxx<>-1 Then Exit ;I added this also...
  Next

  For i= numlayers To 1 Step -1
   s=GetSurface(layer(i),1)
   For skanx=-area To area ;Paint brush area
    skx=skanx
    For skanz=-area2 To area2

     skz=skanz

		 lix=fxx+skx:liz=fzz+skz
		 If lix<0 Then lix=0
		 If liz<0 Then liz=0
		 If lix>lsegments Then lix=lsegments
		 If liz>lsegments Then liz=lsegments
     v=l_index(lix,liz); - had bug - skx,skz, I, J.B., fix.

     old_al1#=VertexAlpha(s,v)
     ;fade all layers out except selected
     ;fade selected back to view
     If i>selectedlayer Then  old_al1#=old_al1#-(fs#) 
     If i<selectedlayer Then  old_al1#=old_al1#-(fs#)
     If i=selectedlayer Then  old_al1#=old_al1#+(fs#)
     If old_al1#<0 Then old_al1#=0
     If old_al1#>1 Then old_al1#=1
     VertexColor s,v,VertexRed(s,v),VertexGreen(s,v),VertexBlue(s,v),old_al1#

    Next
   Next
  Next
 
  EndIf
End Function



Function createlayers()
 ;create segments and position them barely away from each other.
 ;layer #6 being the top layer or surface layer

 ;if you have more or less layers than I'm using in this example you
 ;can just add or remove some texture assignments below
 For i=1 To numlayers ;To 1 Step -1
   layer(i)=createlayer(lsegments,0,lscale)
   PositionEntity layer(i),((-lsegmesnts/2)*lscale),(i-1)/(lscale*5),0
   assigntexture(i,use_tex$(i))
 Next 

 ;loadmap just applies a heightmap to the layers
 loadhmap "hmap.tga"

 ;make 5 top layers alpha ready. leave bottom layer
 ;as no blending.. so that we don't have ghosting through
 ;alpha layers into dead space.

 EntityFX layer(6),2+32
 EntityFX layer(5),2+32
 EntityFX layer(4),2+32
 EntityFX layer(3),2+32
 EntityFX layer(2),2+32
 EntityFX layer(1),2

 ;parent them all up .. to top layer.. layer 6
 ;now you can rotate.. scale, etc., layer 6 and it will apply
 ;to all layers

 EntityParent layer(1),layer(2)
 EntityParent layer(2),layer(3)
 EntityParent layer(3),layer(4)
 EntityParent layer(4),layer(5)
 EntityParent layer(5),layer(6)

 EntityPickMode layer(6),2
End Function


;--------------------------------- the following was added by jfk: ----------------------------------
.jfk
Global NewFile=1
Function LoadLandscape()
 sAFilter$ = "Blitz3D (*.B3D)" + Chr(0) + "*.B3D" + Chr(0) + "3D Studio (*.3DS)" + Chr(0) + "*.3DS" + Chr(0) + "DirectX7 (*.X)" + Chr(0) + "*.X" + Chr(0) + "All Files (*.*)" + Chr(0) + "*.*" + Chr(0)
 sFileName$ = DLLGetOpenFileName$("Please select a Mesh File...","",sAFilter$,OFN_HIDEREADONLY Or OFN_FILEMUSTEXIST)
 If sFileName3$<>""
 NewFile=0

 End If
End Function

Function SaveLandscape()
 ; File Requester

 ; (for a loader filerequester use:)
 ;     sAFilter$ = "Blitz3D (*.B3D)" + Chr(0) + "*.B3D" + Chr(0) + "3D Studio (*.3DS)" + Chr(0) + "*.3DS" + Chr(0) + "DirectX7 (*.X)" + Chr(0) + "*.X" + Chr(0) + "All Files (*.*)" + Chr(0) + "*.*" + Chr(0)
 ;     sFileName$ = DLLGetOpenFileName$("Please select a Mesh File...","",sAFilter$,OFN_HIDEREADONLY Or OFN_FILEMUSTEXIST)
 ; for more info about the blitzsys read the blitzsys docs from the original distribution
 ;
 sAFilter$ = "Blitzbasic Mesh (*.B3D)" + Chr(0) + "*.B3D" + Chr(0) + "All Files (*.*)" + Chr(0) + "*.*" + Chr(0)
 sFileName3$ = DLLGetSaveFileName$("Export Landscape Mesh (*.B3D) As...","",sAFilter$,OFN_HIDEREADONLY Or OFN_FILEMUSTEXIST ) ;Or OFN_OVERWRITEPROMPT)

 If sFileName3$<>""
  ; actually we save 2 files, when you enter "test", it will be "test_1.b3d" and "test_2.b3d"
  ; so the Win-Api-built-in overwriting-prompt isn't useful anyway.
  If Upper$(Right$(sFileName3$,4))=".B3D" Then sFileName3$=Left$(sFileName3$,Len(sFileName3$)-4)
;;;;;;If newfile=1 then...
  part1$=sFileName3$+".b3d"
  part2$=sFileName3$+"_2.b3d"
  Locate 0,200
  LandScape1=OptimizeLandscape1()
  LandScape2=OptimizeLandscape2()

OpenBB3D_wo_lightmap(Part1$)
  WriteBB3D_wo_lightmap(LandScape1,LandScape2)
CloseBB3D_wo_lightmap()

  FreeEntity LandScape1
  FreeEntity LandScape2
 EndIf
End Function

Global file
Function OpenBB3D_wo_lightmap( f_name$)

	file=WriteFile( f_name$ )

	b3dSetFile( file )
	
	b3dBeginChunk( "BB3D" )
		b3dWriteInt( 1 )	;version
End Function

Function CloseBB3D_wo_lightmap()
	b3dEndChunk()	;end of BB3D chunk
	CloseFile file
End Function

Function WriteBB3D_wo_lightmap(mesh1,mesh2)
	;(Mesh1 has 1 surface)
	 c_surf(0)= GetSurface(mesh1,1)
	 c_brush(0)=GetSurfaceBrush( c_surf(0) )
	 c_tex(0)=GetBrushTexture( c_brush(0) )
	 c_tex_name$(0)=Lower$(TextureName$( c_tex(0))) ; this returns the full path!
     ; so strip the path
   If c_tex_name$(0)<>""
	  For i2= Len(c_tex_name$(0)) To 1 Step -1
	   If Mid$(c_tex_name$(0),i2,1)="\" Then
	    c_tex_name$(0)=Right$(c_tex_name$(0),Len(c_tex_name$(0))-i2)
	    Exit
	   EndIf
	  Next
	 EndIf
	; track down used textures - (5 surfaces for mesh2)
	c_surfs=CountSurfaces(mesh2)
	For i=1 To c_surfs
	 c_surf(i)= GetSurface(mesh2,i)
	 c_brush(i)=GetSurfaceBrush( c_surf(i) )
	 c_tex(i)=GetBrushTexture( c_brush(i) )
	 c_tex_name$(i)=Lower$(TextureName$( c_tex(i))) ; this returns the full path!

     ; so strip the path
     If c_tex_name$(i)<>""
	  For i2= Len(c_tex_name$(i)) To 1 Step -1
	   If Mid$(c_tex_name$(i),i2,1)="\" Then
	    c_tex_name$(i)=Right$(c_tex_name$(i),Len(c_tex_name$(i))-i2)
	    Exit
	   EndIf
	  Next
	 EndIf
	Next

	For i=0 To c_surfs
	 FreeBrush c_brush(i)
	 FreeTexture c_tex(i)
	Next

    ;------

		b3dBeginChunk( "TEXS" ) ; list all textures used by the mesh
			b3dWriteString( c_tex_name$(0) ) 	;texture file
			b3dWriteInt( 1 )					;flags
			b3dWriteInt( 2 )					;blend 2
			b3dWriteFloat( 0 )					;x in tex 0 (hu?)
			b3dWriteFloat( 0 )					;y in tex 0
			; texturescaling, quick and dirty hack, but seems to do the job:
			b3dWriteFloat( 1.0/(layertexscale#(1)) )					;x scale 1
			b3dWriteFloat( 1.0/(layertexscale#(1)) )					;y scale 1
			b3dWriteFloat( 0 )					;rotation 0

		For i=1 To c_surfs
		
			For i2=1 To numlayers ; find true layer index (surfaces are not in the same order!)
				If Upper$(use_tex$(i2))=Upper$(c_tex_name$(i))
				 gotcha=i2
				 Exit
				EndIf
			Next
			b3dWriteString( c_tex_name$(i) ) 	;texture file
			b3dWriteInt( 1 )					;flags
			b3dWriteInt( 2 )					;blend 2
			b3dWriteFloat( 0 )					;x in tex 0 (hu?)
			b3dWriteFloat( 0 )					;y in tex 0
			; texturescaling, quick and dirty hack, but seems to do the job:
			b3dWriteFloat( 1.0/(layertexscale#(gotcha)) )					;x scale 1
			b3dWriteFloat( 1.0/(layertexscale#(gotcha)) )					;y scale 1
			b3dWriteFloat( 0 )					;rotation 0
			
		Next
		b3dEndChunk()	;end of TEXS chunk

			b3dBeginChunk( "BRUS" ) ; describe all brushes used by the mesh
			b3dWriteInt( 8 )					;number of textures per brush ; (eg 2 with lightmap)
		For i=0 To c_surfs
		
			b3dWriteString( "TerBrsh"+(i) )		;brushname
			b3dWriteFloat( 1 )					;red
			b3dWriteFloat( 1 )					;green
			b3dWriteFloat( 1 )					;blue
			b3dWriteFloat( 1 )					;alpha
			b3dWriteFloat( 0 )					;shininess
			b3dWriteInt( 1 )
			If i=0 Then                 ;0 For MESH1, 34 For MESH2
				b3dWriteInt(0)
			Else
				b3dWriteInt(34)
			End If
			b3dWriteInt( i )					;texture index
;			b3dWriteInt( j )					;additional texture index (eg lightmap), but here we only use 1 (see above)
			For t=1 To 28:b3dWriteByte(255):Next
		Next
		b3dEndChunk()	;end of BRUS chunk

		b3dBeginChunk( "NODE" )
			b3dWriteString("Scene Root") ;This outer node seemed to be required to avoid crash
			b3dWriteFloat( 0 )	;x_pos
			b3dWriteFloat( 0 )	;y_pos
			b3dWriteFloat( 0 )	;z_pos
			b3dWriteFloat( 1 )	;x_scale
			b3dWriteFloat( 1 )	;y_scale
			b3dWriteFloat( 1 )	;z_scale
			b3dWriteFloat( 1 )	;rot_w
			b3dWriteFloat( 0 )	;rot_x
			b3dWriteFloat( 0 )	;rot_y
			b3dWriteFloat( 0 )	;rot_z

		b3dBeginChunk( "NODE" )
			b3dWriteString("Terrain1")
			b3dWriteFloat( 0 )	;x_pos
			b3dWriteFloat( -0.01 )	;y_pos
			b3dWriteFloat( 0 )	;z_pos
			b3dWriteFloat( 1 )	;x_scale
			b3dWriteFloat( 1 )	;y_scale
			b3dWriteFloat( 1 )	;z_scale
			b3dWriteFloat( 1 )	;rot_w
			b3dWriteFloat( 0 )	;rot_x
			b3dWriteFloat( 0 )	;rot_y
			b3dWriteFloat( 0 )	;rot_z
			WriteMESH_wo_lightmap( mesh1,True)
		b3dEndChunk()	;end of NODE chunk

		b3dBeginChunk( "NODE" )
			b3dWriteString("Terrain2")
			b3dWriteFloat( 0 )	;x_pos
			b3dWriteFloat( 0 )	;y_pos
			b3dWriteFloat( 0 )	;z_pos
			b3dWriteFloat( 1 )	;x_scale
			b3dWriteFloat( 1 )	;y_scale
			b3dWriteFloat( 1 )	;z_scale
			b3dWriteFloat( 1 )	;rot_w
			b3dWriteFloat( 0 )	;rot_x
			b3dWriteFloat( 0 )	;rot_y
			b3dWriteFloat( 0 )	;rot_z
			WriteMESH_wo_lightmap( mesh2,False)
		b3dEndChunk()	;end of NODE chunk
	b3dEndChunk()	;end of NODE chunk
End Function

Function WriteMESH_wo_lightmap( mesh,ismesh1=False )

	n_surfs=CountSurfaces( mesh )
	
	b3dBeginChunk( "MESH" )
		b3dWriteInt( -1 )				;no 'entity' brush -1
		
		b3dBeginChunk( "VRTS" )
			b3dWriteInt( 2 )			;flags - 0=nothin, normals=1, rgba=2
			b3dWriteInt( 1 )			;number of tex_coord sets (eg: 2 with lightmap)
			b3dWriteInt( 2 )			;coords per set (u,v,w?) 2 with uv, 3 with uvw
			
			For k=1 To n_surfs
				surf=GetSurface( mesh,k )
				n_verts=CountVertices( surf )-1
				
				For j=0 To n_verts
					b3dWriteFloat( VertexX( surf,j ) )
					b3dWriteFloat( VertexY( surf,j ) )
					b3dWriteFloat( VertexZ( surf,j ) )
					
					b3dWriteFloat( 1.0 ) ; r
					b3dWriteFloat( 1.0 ) ; g
					b3dWriteFloat( 1.0 ) ; b
					b3dWriteFloat( VertexAlpha( surf,j ) )

					
					b3dWriteFloat( VertexU#( surf,j,0 ) )
					b3dWriteFloat( VertexV#( surf,j,0 ) )
;					b3dWriteFloat( VertexW#( surf,j,0 ) )
;;					b3dWriteFloat( VertexU#( surf,j,1 ) ) ; lightmap uv
;;					b3dWriteFloat( VertexV#( surf,j,1 ) ) ; lightmap uv
;					b3dWriteFloat( VertexW#( surf,j,1 ) )
				Next
			Next
		b3dEndChunk()	;end of VRTS chunk
		
		first_vert=0
		For k=1 To n_surfs
			surf=GetSurface( mesh,k )
			n_tris=CountTriangles( surf )-1
			
			b3dBeginChunk( "TRIS" )
				If ismesh1=False Then
					b3dWriteInt( k )		;brush for these triangles (surf -1 !!!)
				Else
					b3dWriteInt(0)
				End If
				For j=0 To n_tris
					b3dWriteInt( first_vert+TriangleVertex( surf,j,0 ) )
					b3dWriteInt( first_vert+TriangleVertex( surf,j,1 ) )
					b3dWriteInt( first_vert+TriangleVertex( surf,j,2 ) )
				Next
				
			b3dEndChunk()	;end of TRIS chunk
			
			first_vert=first_vert+CountVertices( surf )
			
		Next
		
	b3dEndChunk()	;end of MESH chunk
	
End Function








Function OptimizeLandscape1()
 ; this function builds a mesh from layer 1. Alpha will not be used, but is kept here
 ; for compatibility with the (dumb) WriteB3D Function
 EntityParent layer(1),0
 EntityParent layer(2),0
 EntityParent layer(3),0
 EntityParent layer(4),0
 EntityParent layer(5),0


 ; build the bottom mesh
 landscape1=CreateMesh()
 For ch=1 To 1
  tris_count=0
  child=layer(ch)
  For su=1 To CountSurfaces(child)
   surf=GetSurface(child,su)

   brush=GetSurfaceBrush(surf)
   surf2=CreateSurface(landscape1,brush)
   For tri=0 To CountTriangles(surf)-1

    v_x0#=VertexX(surf,TriangleVertex(surf,tri,0))
    v_x1#=VertexX(surf,TriangleVertex(surf,tri,1))
    v_x2#=VertexX(surf,TriangleVertex(surf,tri,2))

    v_y0#=VertexY(surf,TriangleVertex(surf,tri,0))
    v_y1#=VertexY(surf,TriangleVertex(surf,tri,1))
    v_y2#=VertexY(surf,TriangleVertex(surf,tri,2))

    v_z0#=VertexZ(surf,TriangleVertex(surf,tri,0))
    v_z1#=VertexZ(surf,TriangleVertex(surf,tri,1))
    v_z2#=VertexZ(surf,TriangleVertex(surf,tri,2))

    v_u0#=VertexU(surf,TriangleVertex(surf,tri,0))
    v_u1#=VertexU(surf,TriangleVertex(surf,tri,1))
    v_u2#=VertexU(surf,TriangleVertex(surf,tri,2))

    v_v0#=VertexV(surf,TriangleVertex(surf,tri,0))
    v_v1#=VertexV(surf,TriangleVertex(surf,tri,1))
    v_v2#=VertexV(surf,TriangleVertex(surf,tri,2))

    v_a0#=VertexAlpha(surf,TriangleVertex(surf,tri,0))
    v_a1#=VertexAlpha(surf,TriangleVertex(surf,tri,1))
    v_a2#=VertexAlpha(surf,TriangleVertex(surf,tri,2))



    v0=AddVertex(surf2,v_x0,v_y0,v_z0,v_u0,v_v0)
    VertexColor surf2, v0,255,255,255,v_a0
    v1=AddVertex(surf2,v_x1,v_y1,v_z1,v_u1,v_v1)
    VertexColor surf2, v1,255,255,255,v_a1
    v2=AddVertex(surf2,v_x2,v_y2,v_z2,v_u2,v_v2)
    VertexColor surf2, v2,255,255,255,v_a2
    AddTriangle(surf2,v0,v1,v2)
   Next
  Next
 Next

 Print "welding..."
 weld(landscape1)

 ScaleMesh landscape1,lscale,lscale,lscale
 UpdateNormals landscape1
 EntityFX landscape1,0
 Print "oki"
 EntityParent layer(1),layer(2)
 EntityParent layer(2),layer(3)
 EntityParent layer(3),layer(4)
 EntityParent layer(4),layer(5)
 EntityParent layer(5),layer(6)

 Return landscape1
End Function










Function OptimizeLandscape2()
 ; this function creates a mesh with all the remaining layers and ignores all the Tris that are 
 ; fully invisible
 EntityParent layer(1),0
 EntityParent layer(2),0
 EntityParent layer(3),0
 EntityParent layer(4),0
 EntityParent layer(5),0

 ; build the optimized mesh
 landscape2=CreateMesh()
 For ch=numlayers To 2 Step -1
  child=layer(ch)
  For su=1 To CountSurfaces(child)
   surf=GetSurface(child,su)

   brush=GetSurfaceBrush(surf)
   surf2=CreateSurface(landscape2,brush)
   For tri=0 To CountTriangles(surf)-1

    v_x0#=VertexX(surf,TriangleVertex(surf,tri,0))
    v_x1#=VertexX(surf,TriangleVertex(surf,tri,1))
    v_x2#=VertexX(surf,TriangleVertex(surf,tri,2))

    v_y0#=VertexY(surf,TriangleVertex(surf,tri,0))
    v_y1#=VertexY(surf,TriangleVertex(surf,tri,1))
    v_y2#=VertexY(surf,TriangleVertex(surf,tri,2))

    v_z0#=VertexZ(surf,TriangleVertex(surf,tri,0))
    v_z1#=VertexZ(surf,TriangleVertex(surf,tri,1))
    v_z2#=VertexZ(surf,TriangleVertex(surf,tri,2))

    v_u0#=VertexU(surf,TriangleVertex(surf,tri,0))
    v_u1#=VertexU(surf,TriangleVertex(surf,tri,1))
    v_u2#=VertexU(surf,TriangleVertex(surf,tri,2))

    v_v0#=VertexV(surf,TriangleVertex(surf,tri,0))
    v_v1#=VertexV(surf,TriangleVertex(surf,tri,1))
    v_v2#=VertexV(surf,TriangleVertex(surf,tri,2))

    v_a0#=VertexAlpha(surf,TriangleVertex(surf,tri,0))
    v_a1#=VertexAlpha(surf,TriangleVertex(surf,tri,1))
    v_a2#=VertexAlpha(surf,TriangleVertex(surf,tri,2))


    If (v_a0<>0.0) Or (v_a1<>0.0) Or (v_a2<>0.0)
     v0=AddVertex(surf2,v_x0,v_y0,v_z0,v_u0,v_v0)
     VertexColor surf2, v0,255,255,255,v_a0
     v1=AddVertex(surf2,v_x1,v_y1,v_z1,v_u1,v_v1)
     VertexColor surf2, v1,255,255,255,v_a1
     v2=AddVertex(surf2,v_x2,v_y2,v_z2,v_u2,v_v2)
     VertexColor surf2, v2,255,255,255,v_a2
     AddTriangle(surf2,v0,v1,v2)
    EndIf
   Next
  Next
 Next

 Print "welding..."
 weld(landscape2)

 ScaleMesh landscape2,lscale,lscale,lscale
 UpdateNormals landscape2
 EntityFX landscape2,32
 Print "oki"
 EntityParent layer(1),layer(2)
 EntityParent layer(2),layer(3)
 EntityParent layer(3),layer(4)
 EntityParent layer(4),layer(5)
 EntityParent layer(5),layer(6)

 Return landscape2
End Function



Function RockySlope(whatlayer,slope#=.85)
 old_layer=selectedlayer
 selectedlayer=whatlayer
  For z#=-(MeshDepth(layer(1))*lscale/2.0) To MeshDepth(layer(1))*lscale/2.0 Step .1
   For x#=-MeshWidth(layer(1))*lscale/2.0 To MeshWidth(layer(1))*lscale/2.0 Step .1
    bringup_RockySlope(x#,z#,slope#,1)
   Next
  Next
 selectedlayer=old_layer
End Function


Function bringup_RockySlope(x#,z#,slope#=.85,area=4,fs#=.06)
 area2=area
 cp=LinePick(x,200,z,0,-400,0,0.0)


 If cp Then 
  s=PickedSurface()
  t=PickedTriangle()
  v=TriangleVertex(s,t,1)

  fxx=-1
  nx#=PickedNX()
  ny#=PickedNY()
  nz#=PickedNZ()

  If Abs(ny)<slope ;((nx)>slope) Or ((-nx)> slope) Or ((nz)>slope) Or ((-nz)> slope);Or Abs(nz)>slope

  ;lets locate the v vertex in our precaled l_index
  For cvx=0 To lsegments
   For cvz=0 To lsegments
    If l_index(cvx,cvz)=v Then 
     fxx=cvx
     fzz=cvz
     Exit
    EndIf
   Next
		If fxx<>-1 Then Exit
  Next

  ;if we did not find the vertex in our l_index .. return
  If fxx=-1 Then RuntimeError "hm";Return

  For i= numlayers To 1 Step -1
   s=GetSurface(layer(i),1)
   For skanx= 0 To 0 ;-1 To 0 ;-area To area
    skx=skanx
    For skanz= 0 To 0 ;-1 To 0 ;-area2 To area2

     skz=skanz

		 lix=fxx+skx:liz=fzz+skz
		 If lix<0 Then lix=0
		 If liz<0 Then liz=0
		 If lix>lsegments Then lix=lsegments
		 If liz>lsegments Then liz=lsegments
     v=l_index(lix,liz); - had bug - skx,skz, I, J.B., fix.

      old_al1#=VertexAlpha(s,v)
      ;fade all layers out except selected
      ;fade selected back to view
      If i>selectedlayer Then  old_al1#=old_al1#-(fs#) 
      If i<selectedlayer Then  old_al1#=old_al1#-(fs#)
      If i=selectedlayer Then  old_al1#=old_al1#+(fs#)
      If old_al1#<0 Then old_al1#=0
      If old_al1#>1 Then old_al1#=1
     VertexColor s,v,VertexRed(s,v),VertexGreen(s,v),VertexBlue(s,v),old_al1#
    Next
   Next
  Next
 
  EndIf
 EndIf
End Function



Function MapTopology(whatlayer,lowlevel#,highlevel#)
 old_layer=selectedlayer
 selectedlayer=whatlayer
  For z#=-(MeshDepth(layer(1))*lscale/2.0) To MeshDepth(layer(1))*lscale/2.0 Step .1
   For x#=-MeshWidth(layer(1))*lscale/2.0 To MeshWidth(layer(1))*lscale/2.0 Step .1
    bringup_MapTopology(x#,z#,lowlevel#,highlevel#,1)
   Next
  Next
 selectedlayer=old_layer
End Function


Function bringup_MapTopology(x#,z#,lowlevel#,highlevel#,area=4,fs#=.06)
 area2=area
 cp=LinePick(x,200,z,0,-400,0,0.0)


 If cp Then 
  s=PickedSurface()
  t=PickedTriangle()
  v=TriangleVertex(s,t,1)

  fxx=-1
  py#=PickedY()

  If (py >= lowlevel#) And (py# <= highlevel#)

  ;lets locate the v vertex in our precaled l_index
  For cvx=0 To lsegments
   For cvz=0 To lsegments
    If l_index(cvx,cvz)=v Then 
     fxx=cvx
     fzz=cvz
     Exit
    EndIf
   Next
	 If fxx<>-1 Then Exit
  Next

  ;if we did not find the vertex in our l_index .. return
  If fxx=-1 Then RuntimeError "hm";Return

  For i= numlayers To 1 Step -1
   s=GetSurface(layer(i),1)
   For skanx= 0 To 0 ;-1 To 0 ;-area To area
    skx=skanx
    For skanz= 0 To 0 ;-1 To 0 ;-area2 To area2

     skz=skanz

		 lix=fxx+skx:liz=fzz+skz
		 If lix<0 Then lix=0
		 If liz<0 Then liz=0
		 If lix>lsegments Then lix=lsegments
		 If liz>lsegments Then liz=lsegments
     v=l_index(lix,liz); - had bug - skx,skz, I, J.B., fix.

      old_al1#=VertexAlpha(s,v)
      ;fade all layers out except selected
      ;fade selected back to view
      If i>selectedlayer Then  old_al1#=old_al1#-(fs#) 
      If i<selectedlayer Then  old_al1#=old_al1#-(fs#)
      If i=selectedlayer Then  old_al1#=old_al1#+(fs#)
      If old_al1#<0 Then old_al1#=0
      If old_al1#>1 Then old_al1#=1
     VertexColor s,v,VertexRed(s,v),VertexGreen(s,v),VertexBlue(s,v),old_al1#
    Next
   Next
  Next
 
  EndIf
 EndIf
End Function