Weird texture problem!

Blitz3D Forums/Blitz3D Programming/Weird texture problem!

I am working with this decorator exported map since some time. I had some troubles, but finally I was able to export it optimized.

For some time I was using it in Interior Dropper(only reading it, never ever touched it in any writing mode), but then all of a sudden the grass texture chanched.

Here's an image how it looked for some time and how it should look:


And this is how it looked after some time. The flat ground texture chanched from floor5.jpg to floor6.jpg (also scaling) all of a sudden! I didn't chanche anything! Is this weird or what?



After some time I found out the texture is still displayed correctly in the engine. After some tests it seems, in Fullscreen it's displayed correctly, but no longer in windowed mode. what the - I hate it when unlogical things happen. First I thought it probably has something to do with absolute vs relative paths inside the mesh, but then I realized: both apps are using exactly the same mesh file.

Is this a bug? What's going on?!?

***********************************

EDIT I am currently trying to isolate te problem. Not so funny when it takes more time to solve problems than to create the art content, that is btw a heavy job by its own anyway. However.

Right now I have written a little test program that will check all triangles agains one another. One would think every Triangle should be there only once, especially when you have a max of two texture layers (diffuse + lightmap).

Surprisingly my app just told me that of 20573 Tris there are 15207 indentical tris. I can hardly believe this and am curretnly trying to find a bug in my test app, without luck so far, could this really be? Or is there some reason for identic multiple triangles that I don't think of right now?

Well, I'll let you know when I know more.

EDIT 2
Ok I have found a bug in the test app: when a triangle was there mutliple times, it was also counted multiple times, from the point of view of every Triangle. However, now I have edited it in a way that "clones" no more count their clones, the result is now:

3449 double Tris out of a 20573 Tris mesh.
Makes more sense, but is still a desaster if it's true.

EDIT 3 -------------------- (and conclusion so far)
Ok, it seems I am getting closer to the isolation of the problem. I have added some code to the test app that will reassemble the complete mesh with all surfaces, copying every aspect possibe (normals, vertex colors, alpha, uvs etc.), and most important, I did not include the double triangles. Now I got 17124 Triangles only, with the same visual result! a walktrough test was showing me there ain't no Tris missing. This means, the 3449 double Tris are not needed! Of course, there is one problem left no, how do I know what clone should I use when there are multiple Triangles. Right now I was able to reduce the polycount in a remarkable amount, but there where still the wrong textures used.

Basicly, the problem seems to be clear now: Decorator is creating additional Triangles in some cases. The triangles are then not removed by the optimation process. It is also some kind of random game if clone one or two of 2 identical Triangles will be rendered (bysides, a remark my Mark suggests identical Tris may cause massive slowdown), because the zbuffer has to choose from two identical depths. you can reproduce the bug this way: load and export a mesh several times in Decorator, the Number of triangles increases repeatedly, whenever you save it and then restart decorator.

It seems "save optimized" does only weld the vertices, but won't remove double triangles. Lee if you read this: I'd say it's a real bug that may be fixed realtive easily (I can give you my code, but it's pretty slow due to comparing 20k Tris vs 20k Tris in a bruteforce method, takes about a minute or so)

Removing double triangles could also be performed on a Per Surface base, whenever the user alters the painting brush (well in fact only when he is actually painting something with a new brush), this would be faster. Although it's still slow.

I still try to find an elegant solution that would remove the bad triangles and leave the wanted triangles unchanched.
The blues goes on :-°

EDIT 4 ----------

Ok, some good news: For every Triangle there seems to be a max of one possible clone (at least in my mesh). So all I had to do is using the clone instead of the first triangle found . Well this seems to be luck, because there's nothing that defines what's the clone and what's the original. The only thing I could think of is the creation date of the triangles. The ones that have been found later where also "painted" (= created) later.

So all I have to do now it adding a welding and a SaveB3D routine that can save a lightmapped level properly. I'll release this utility ASAP, but I'm not sure if it works for all similar problems, for the named reasons (no way to find out what triangles are unwanted clones and what's not).

Ok, here's the test app.
Graphics3D 640,480,32,2
SetBuffer BackBuffer()
; Test APP: testing a mesh for Clone Triangles.
; Use a decorator mesh with about 10k Tris to test. Also it needs to be one that was 
; edited in about 10 or more Decorator sessions (save, then restart Decorator) and has multiple surfaces/textures.

mesh_name$="camp2_dec18weld2.b3d"

mesh=LoadMesh(mesh_name$)

;Goto test_walk


surfs=CountSurfaces(mesh)

Dim s(surfs),s2(surfs),tris(surfs),brush(surfs),texturepath$(surfs,1)
Dim same(100000,3)
same_count=0

b=1000

seenflag=990

Print "surfaces: "+ surfs

; --------------------- first read all surfaces and triangle data to some arrays and banks ---------
For i=1 To surfs

 s(i)=GetSurface(mesh,i)
 bru=GetSurfaceBrush(s(i))
 brush(i)=bru

 texture=GetBrushTexture( bru,0)
 name$=TextureName$(texture)
 texturepath$(i,0)=name$
 FreeTexture texture

 texture=GetBrushTexture( bru,1)
 name$=TextureName$(texture)
 texturepath$(i,1)=name$
 FreeTexture texture

 Print texturepath$(i,0)
 Print texturepath$(i,1)

 Print "surface "+i+" has "+CountTriangles(s(i))+" tris"
 tris(i)=CreateBank(CountTriangles(s(i))*b)
 total_tris=total_tris+CountTriangles(s(i))
 For j=0 To CountTriangles(s(i))-1

  PokeFloat tris(i),(j*b)+  0,VertexX		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+  4,VertexY		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+  8,VertexZ		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 12,VertexU		(s(i),TriangleVertex(s(i),j,0),0)
  PokeFloat tris(i),(j*b)+ 16,VertexV		(s(i),TriangleVertex(s(i),j,0),0)
  PokeFloat tris(i),(j*b)+ 20,VertexU		(s(i),TriangleVertex(s(i),j,0),1)
  PokeFloat tris(i),(j*b)+ 24,VertexV		(s(i),TriangleVertex(s(i),j,0),1)
  PokeFloat tris(i),(j*b)+ 28,VertexNX		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 32,VertexNY		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 36,VertexNZ		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 40,VertexRed		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 44,VertexGreen	(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 48,VertexBlue	(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 52,VertexAlpha	(s(i),TriangleVertex(s(i),j,0))

  PokeFloat tris(i),(j*b)+100,VertexX		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+104,VertexY		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+108,VertexZ		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+112,VertexU		(s(i),TriangleVertex(s(i),j,1),0)
  PokeFloat tris(i),(j*b)+116,VertexV		(s(i),TriangleVertex(s(i),j,1),0)
  PokeFloat tris(i),(j*b)+120,VertexU		(s(i),TriangleVertex(s(i),j,1),1)
  PokeFloat tris(i),(j*b)+124,VertexV		(s(i),TriangleVertex(s(i),j,1),1)
  PokeFloat tris(i),(j*b)+128,VertexNX		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+132,VertexNY		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+136,VertexNZ		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+140,VertexRed		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+144,VertexGreen	(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+148,VertexBlue	(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+152,VertexAlpha	(s(i),TriangleVertex(s(i),j,1))

  PokeFloat tris(i),(j*b)+200,VertexX		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+204,VertexY		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+208,VertexZ		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+212,VertexU		(s(i),TriangleVertex(s(i),j,2),0)
  PokeFloat tris(i),(j*b)+216,VertexV		(s(i),TriangleVertex(s(i),j,2),0)
  PokeFloat tris(i),(j*b)+220,VertexU		(s(i),TriangleVertex(s(i),j,2),1)
  PokeFloat tris(i),(j*b)+224,VertexV		(s(i),TriangleVertex(s(i),j,2),1)
  PokeFloat tris(i),(j*b)+228,VertexNX		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+232,VertexNY		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+236,VertexNZ		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+240,VertexRed		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+244,VertexGreen	(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+248,VertexBlue	(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+252,VertexAlpha	(s(i),TriangleVertex(s(i),j,2))

 Next
Next

;Goto quicky

Print "-------------------------"


; --------- then compare every single triangle to all other triangles if they are identical --------
; this is a brute force thing, may take a minute or so. You can abort the app with ESC
For i=1 To surfs
 For j=0 To CountTriangles(s(i))-1

  If PeekFloat(tris(i),(j*b)+seenflag)=0
   x0#=PeekFloat(tris(i),(j*b)+ 0)
   y0#=PeekFloat(tris(i),(j*b)+ 4)
   z0#=PeekFloat(tris(i),(j*b)+ 8)
   x1#=PeekFloat(tris(i),(j*b)+100)
   y1#=PeekFloat(tris(i),(j*b)+104)
   z1#=PeekFloat(tris(i),(j*b)+108)
   x2#=PeekFloat(tris(i),(j*b)+200)
   y2#=PeekFloat(tris(i),(j*b)+204)
   z2#=PeekFloat(tris(i),(j*b)+208)
   For ii=1 To surfs
    For jj=0 To CountTriangles(s(ii))-1
     If (ii<>i) Or (jj<>j); don't compare with myself
      xx0#=PeekFloat(tris(ii),(jj*b)+ 0)
      yy0#=PeekFloat(tris(ii),(jj*b)+ 4)
      zz0#=PeekFloat(tris(ii),(jj*b)+ 8)
      xx1#=PeekFloat(tris(ii),(jj*b)+100)
      yy1#=PeekFloat(tris(ii),(jj*b)+104)
      zz1#=PeekFloat(tris(ii),(jj*b)+108)
      xx2#=PeekFloat(tris(ii),(jj*b)+200)
      yy2#=PeekFloat(tris(ii),(jj*b)+204)
      zz2#=PeekFloat(tris(ii),(jj*b)+208)
      If (x0=xx0) And (y0=yy0) And (z0=zz0) And (x1=xx1) And (y1=yy1) And (z1=zz1) And (x2=xx2) And (y2=yy2) And (z2=zz2)
       Print "In surface:"+i+" of "+surfs
       Print "Found identic Triangle: surf:"+i+" Tri:"+j+" is the same as surf:"+ii+" Tri:"+jj
       PokeFloat tris(ii),(jj*b)+seenflag,1 ; set this triangle to "seen"
       same(same_count,0)=i
       same(same_count,1)=j
       same(same_count,2)=ii
       same(same_count,3)=jj
       same_count=same_count+1
      EndIf
     EndIf
    Next
   Next
  EndIf
  If KeyDown(1)<>0 Then End
 Next
Next

Print "double tris: "+same_count
Print "total tris in mesh: "+total_tris
Print "And I really didn't take any drugs o_O"
Print "Press Space for a wallktrough test"
WaitKey()


; ----------------------------- recreate the mesh without the clone tris! ---------------------
.quicky


mesh2=CreateMesh()


For i=1 To surfs
 s2(i)=CreateSurface(mesh2,brush(i))
Next


For i=1 To surfs
 For j=0 To CountTriangles(s(i))-1
  If PeekFloat(tris(i),(j*b)+seenflag)=0

   x0#=		PeekFloat(tris(i),(j*b)+0)
   y0#=		PeekFloat(tris(i),(j*b)+4)
   z0#=		PeekFloat(tris(i),(j*b)+8)
   ua0#=	PeekFloat(tris(i),(j*b)+12)
   va0#=	PeekFloat(tris(i),(j*b)+16)
   ub0#=	PeekFloat(tris(i),(j*b)+20)
   vb0#=	PeekFloat(tris(i),(j*b)+24)
   nx0#=	PeekFloat(tris(i),(j*b)+28)
   ny0#=	PeekFloat(tris(i),(j*b)+32)
   nz0#=	PeekFloat(tris(i),(j*b)+36)
   red0#=	PeekFloat(tris(i),(j*b)+40)
   gre0#=	PeekFloat(tris(i),(j*b)+44)
   blu0#=	PeekFloat(tris(i),(j*b)+48)
   alp0#=	PeekFloat(tris(i),(j*b)+52)

   x1#=		PeekFloat(tris(i),(j*b)+100)
   y1#=		PeekFloat(tris(i),(j*b)+104)
   z1#=		PeekFloat(tris(i),(j*b)+108)
   ua1#=	PeekFloat(tris(i),(j*b)+112)
   va1#=	PeekFloat(tris(i),(j*b)+116)
   ub1#=	PeekFloat(tris(i),(j*b)+120)
   vb1#=	PeekFloat(tris(i),(j*b)+124)
   nx1#=	PeekFloat(tris(i),(j*b)+128)
   ny1#=	PeekFloat(tris(i),(j*b)+132)
   nz1#=	PeekFloat(tris(i),(j*b)+136)
   red1#=	PeekFloat(tris(i),(j*b)+140)
   gre1#=	PeekFloat(tris(i),(j*b)+144)
   blu1#=	PeekFloat(tris(i),(j*b)+148)
   alp1#=	PeekFloat(tris(i),(j*b)+152)

   x2#=		PeekFloat(tris(i),(j*b)+200)
   y2#=		PeekFloat(tris(i),(j*b)+204)
   z2#=		PeekFloat(tris(i),(j*b)+208)
   ua2#=	PeekFloat(tris(i),(j*b)+212)
   va2#=	PeekFloat(tris(i),(j*b)+216)
   ub2#=	PeekFloat(tris(i),(j*b)+220)
   vb2#=	PeekFloat(tris(i),(j*b)+224)
   nx2#=	PeekFloat(tris(i),(j*b)+228)
   ny2#=	PeekFloat(tris(i),(j*b)+232)
   nz2#=	PeekFloat(tris(i),(j*b)+236)
   red2#=	PeekFloat(tris(i),(j*b)+240)
   gre2#=	PeekFloat(tris(i),(j*b)+244)
   blu2#=	PeekFloat(tris(i),(j*b)+248)
   alp2#=	PeekFloat(tris(i),(j*b)+252)

    vr0=AddVertex(s2(i),x0,y0,z0)
   VertexTexCoords s2(i),vr0,ua0,va0,0,0 
   VertexTexCoords s2(i),vr0,ub0,vb0,0,1 
   VertexNormal s2(i),vr0,nx0,ny0,nz0
   VertexColor  s2(i),vr0,red0,gre0,blu0,alp0

   vr1=AddVertex(s2(i),x1,y1,z1)
   VertexTexCoords s2(i),vr1,ua1,va1,0,0 
   VertexTexCoords s2(i),vr1,ub1,vb1,0,1 
   VertexNormal s2(i),vr1,nx1,ny1,nz1
   VertexColor  s2(i),vr1,red1,gre1,blu1,alp1

   vr2=AddVertex(s2(i),x2,y2,z2)
   VertexTexCoords s2(i),vr2,ua2,va2,0,0 
   VertexTexCoords s2(i),vr2,ub2,vb2,0,1 
   VertexNormal s2(i),vr2,nx2,ny2,nz2
   VertexColor  s2(i),vr2,red2,gre2,blu2,alp2

   AddTriangle (s2(i),vr0,vr1,vr2)

  EndIf
  If KeyDown(1)<>0 Then End
 Next
Next


FreeEntity mesh ; original Mesh not needed anymore

mesh=mesh2

; ---------------------------------- a walktrough test code -------------------------------
.test_walk

Collisions 1,2,2,2

player=CreatePivot()
PositionEntity player,0,100,-1
EntityRadius player,0.2,0.9
EntityType player,1

camera=CreateCamera( player )
CameraRange camera,.1,200
TranslateEntity camera,0,1.8,0
CameraClsColor camera,180,200,255

light=CreateLight()
LightColor light,32,32,32
TurnEntity light,45,45,0


EntityFX mesh,1
EntityType mesh,2

sp#=.5
ey#=EntityY(player)

; walktrough loop
While Not KeyHit(1)
	If KeyHit(17)
	 wire=1-wire
	 WireFrame wire
	EndIf
	yv#=EntityY(player)-ey
	ey=EntityY(player)
	If KeyHit(57) Then jump#=1.0
	MoveEntity player,0,-Abs(yv-.005),0
	If jump>0 Then
	 jump=jump-.1
 	 If jump<0 Then jump=0
	 MoveEntity player,0,Sin(jump)*30.0,0
	EndIf
	mxa#=-MouseXSpeed()/4.0
	mya#=MouseYSpeed()/4.0
	MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
	TurnEntity player,0,mxa,0
	TurnEntity camera,mya,0,0
	If KeyDown(205) MoveEntity player,sp,0,0
	If KeyDown(203) MoveEntity player,-sp,0,0
	If KeyDown(200) MoveEntity player,0,0,sp
	If KeyDown(208) MoveEntity player,0,0,-sp
	UpdateWorld
	RenderWorld
	Text 0,0,TrisRendered()
	Flip
Wend
End


Ya gotta love jfk...

... Find problem. Post problem. Tackle problem. Re-tackle problem. Re-re-tackle problem. Solve problem. Share solution.

... and it's not even Xmas yet.

Cheers for this. Hopefully Lee can find time to take this on board.

yeah, thanks. I just tested the exporter, seems to work nicely. Gimme a couple of minutes.

EDIT
Outch new problem. Loading order of the surfaces is diffrent every time, so it's not so easy to say if you shoud save the first tris or the second tris of the clones.

Ok, here's this little emergency tool, very custom and inhouseish:
http://www.melog.ch/dl/kill_decorator18_clonetris_src.zip

or copy paste from here:
Graphics3D 640,480,32,2
SetBuffer BackBuffer()

; Sometimes Decorator 1.8 created clone Triangles which have the potential to make a lot
; of troubles. This Program may be used to remove the clone Triangles from a Mesh and
; export the result as a B3D Mesh. Cloned Triangles can be removed easily, but since they use
; individual surfaces, you may have to experiment with the "use_clone" variable to make it
; use the right material/surface!

; Use an optimated Decorator Export as mesh source. alpha, vertex normals and color are 
; not supported by now.

; The program also includes Functions to export a lightmapped Mesh, that may be useful 
; for some people.

; Version 0.1, released dec. 2005 by CSP GAMES

; init savemesh
Include "b3dfile.bb"
Global c_surfs
Dim c_surf(0)
Dim c_brush(0)
Dim c_tex(0)
Dim c_tex_name$(0)
; eo init stuff

Global do_weld=1 ; set this to one if you wish to weld the mesh
Global use_clone=1 ; set this to one to use the secondary clones, zero to use the first ones
;(try both if the clone tris are not textured as you wish)

mesh_name$="camp2_dec18weld2.b3d" ; load this mesh
export_path$="diditest.b3d" ; save this optimized mesh



mesh=LoadMesh(mesh_name$)


;Goto test_walk ; use this to skip optimation and save a 1:1 copy of the mesh (for tests only)






; --------------------- first read all surfaces and triangle data to some arrays and banks ---------
surfs=CountSurfaces(mesh)
Dim s(surfs),s2(surfs),tris(surfs),brush(surfs),texturepath$(surfs,1)
Dim same(100000,4)
same_count=0
b=1000
seenflag=990
Print "surfaces: "+ surfs

For i=1 To surfs

 s(i)=GetSurface(mesh,i)
 bru=GetSurfaceBrush(s(i))
 brush(i)=bru

 texture=GetBrushTexture( bru,0)
 name$=TextureName$(texture)
 texturepath$(i,0)=name$
 FreeTexture texture

 texture=GetBrushTexture( bru,1)
 name$=TextureName$(texture)
 texturepath$(i,1)=name$
 FreeTexture texture

 Print texturepath$(i,0)
 Print texturepath$(i,1)

 Print "surface "+i+" has "+CountTriangles(s(i))+" tris"
 tris(i)=CreateBank(CountTriangles(s(i))*b)
 total_tris=total_tris+CountTriangles(s(i))
 For j=0 To CountTriangles(s(i))-1

  PokeFloat tris(i),(j*b)+  0,VertexX		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+  4,VertexY		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+  8,VertexZ		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 12,VertexU		(s(i),TriangleVertex(s(i),j,0),0)
  PokeFloat tris(i),(j*b)+ 16,VertexV		(s(i),TriangleVertex(s(i),j,0),0)
  PokeFloat tris(i),(j*b)+ 20,VertexU		(s(i),TriangleVertex(s(i),j,0),1)
  PokeFloat tris(i),(j*b)+ 24,VertexV		(s(i),TriangleVertex(s(i),j,0),1)
  PokeFloat tris(i),(j*b)+ 28,VertexNX		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 32,VertexNY		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 36,VertexNZ		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 40,VertexRed		(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 44,VertexGreen	(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 48,VertexBlue	(s(i),TriangleVertex(s(i),j,0))
  PokeFloat tris(i),(j*b)+ 52,VertexAlpha	(s(i),TriangleVertex(s(i),j,0))

  PokeFloat tris(i),(j*b)+100,VertexX		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+104,VertexY		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+108,VertexZ		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+112,VertexU		(s(i),TriangleVertex(s(i),j,1),0)
  PokeFloat tris(i),(j*b)+116,VertexV		(s(i),TriangleVertex(s(i),j,1),0)
  PokeFloat tris(i),(j*b)+120,VertexU		(s(i),TriangleVertex(s(i),j,1),1)
  PokeFloat tris(i),(j*b)+124,VertexV		(s(i),TriangleVertex(s(i),j,1),1)
  PokeFloat tris(i),(j*b)+128,VertexNX		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+132,VertexNY		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+136,VertexNZ		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+140,VertexRed		(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+144,VertexGreen	(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+148,VertexBlue	(s(i),TriangleVertex(s(i),j,1))
  PokeFloat tris(i),(j*b)+152,VertexAlpha	(s(i),TriangleVertex(s(i),j,1))

  PokeFloat tris(i),(j*b)+200,VertexX		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+204,VertexY		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+208,VertexZ		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+212,VertexU		(s(i),TriangleVertex(s(i),j,2),0)
  PokeFloat tris(i),(j*b)+216,VertexV		(s(i),TriangleVertex(s(i),j,2),0)
  PokeFloat tris(i),(j*b)+220,VertexU		(s(i),TriangleVertex(s(i),j,2),1)
  PokeFloat tris(i),(j*b)+224,VertexV		(s(i),TriangleVertex(s(i),j,2),1)
  PokeFloat tris(i),(j*b)+228,VertexNX		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+232,VertexNY		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+236,VertexNZ		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+240,VertexRed		(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+244,VertexGreen	(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+248,VertexBlue	(s(i),TriangleVertex(s(i),j,2))
  PokeFloat tris(i),(j*b)+252,VertexAlpha	(s(i),TriangleVertex(s(i),j,2))

 Next
Next

;Goto quicky

Print "-------------------------"


; --------- then compare every single triangle to all other triangles if they are identical --------
; this is a brute force thing, may take a minute or so. You can abort end the app with ESC here.
AppTitle "Please wait, seeking for Clones..."
For i=1 To surfs
 For j=0 To CountTriangles(s(i))-1

  If PeekFloat(tris(i),(j*b)+seenflag)<>-1
   x0#=PeekFloat(tris(i),(j*b)+ 0)
   y0#=PeekFloat(tris(i),(j*b)+ 4)
   z0#=PeekFloat(tris(i),(j*b)+ 8)
   x1#=PeekFloat(tris(i),(j*b)+100)
   y1#=PeekFloat(tris(i),(j*b)+104)
   z1#=PeekFloat(tris(i),(j*b)+108)
   x2#=PeekFloat(tris(i),(j*b)+200)
   y2#=PeekFloat(tris(i),(j*b)+204)
   z2#=PeekFloat(tris(i),(j*b)+208)
   For ii=1 To surfs
    For jj=0 To CountTriangles(s(ii))-1
     If (ii<>i) Or (jj<>j); don't compare with myself
      xx0#=PeekFloat(tris(ii),(jj*b)+ 0)
      yy0#=PeekFloat(tris(ii),(jj*b)+ 4)
      zz0#=PeekFloat(tris(ii),(jj*b)+ 8)
      xx1#=PeekFloat(tris(ii),(jj*b)+100)
      yy1#=PeekFloat(tris(ii),(jj*b)+104)
      zz1#=PeekFloat(tris(ii),(jj*b)+108)
      xx2#=PeekFloat(tris(ii),(jj*b)+200)
      yy2#=PeekFloat(tris(ii),(jj*b)+204)
      zz2#=PeekFloat(tris(ii),(jj*b)+208)
      If (x0=xx0) And (y0=yy0) And (z0=zz0) And (x1=xx1) And (y1=yy1) And (z1=zz1) And (x2=xx2) And (y2=yy2) And (z2=zz2)
       Print "In surface:"+i+" of "+surfs
       Print "Found identic Triangle: surf:"+i+" Tri:"+j+" is the same as surf:"+ii+" Tri:"+jj
       PokeFloat tris(ii),(jj*b)+seenflag,-1 ; set this triangle to "seen"
       PokeFloat tris(i),(j*b)+seenflag,2 ; set this triangle to "seen"
       same(same_count,0)=i
       same(same_count,1)=j
       same(same_count,2)=ii
       same(same_count,3)=jj
       same(same_count,4)=same(same_count,4)+1 ; count number of clones per triangle
       same_count=same_count+1
      EndIf
     EndIf
    Next
   Next
  EndIf
  If KeyDown(1)<>0 Then End
 Next
Next

AppTitle "Ready"

max_clone=0
For i=0 To same_count-1
 If same(i,4) > max_clone Then
  max_clone=same(i,4)
 EndIf
Next

Print "double tris: "+same_count
Print "highest number of clones of one Triangle: "+max_clone ; should not be higher than 1...
Print "total tris in mesh: "+total_tris
Print "And I really didn't take any drugs o_O"
Print "Press Space for a walktrough test"
WaitKey()


; ----------------------------- recreate the mesh without the clone tris! ---------------------
.quicky


mesh2=CreateMesh()


For i=1 To surfs
 s2(i)=CreateSurface(mesh2,brush(i))
Next


For i=1 To surfs
 For j=0 To CountTriangles(s(i))-1
  If PeekFloat(tris(i),(j*b)+seenflag)<>-1

  If PeekFloat(tris(i),(j*b)+seenflag)=2 ; has clone(s)

   i2=i
   j2=j

   If use_clone=1
    For iii=0 To same_count-1 ; use latest clone that was found
     If (same(iii,0)=i) And (same(iii,1)=j)
      i2=same(iii,2)
      j2=same(iii,3)
     EndIf
    Next
   EndIf

   x0#=		PeekFloat(tris(i2),(j2*b)+0)
   y0#=		PeekFloat(tris(i2),(j2*b)+4)
   z0#=		PeekFloat(tris(i2),(j2*b)+8)
   ua0#=	PeekFloat(tris(i2),(j2*b)+12)
   va0#=	PeekFloat(tris(i2),(j2*b)+16)
   ub0#=	PeekFloat(tris(i2),(j2*b)+20)
   vb0#=	PeekFloat(tris(i2),(j2*b)+24)
   nx0#=	PeekFloat(tris(i2),(j2*b)+28)
   ny0#=	PeekFloat(tris(i2),(j2*b)+32)
   nz0#=	PeekFloat(tris(i2),(j2*b)+36)
   red0#=	PeekFloat(tris(i2),(j2*b)+40)
   gre0#=	PeekFloat(tris(i2),(j2*b)+44)
   blu0#=	PeekFloat(tris(i2),(j2*b)+48)
   alp0#=	PeekFloat(tris(i2),(j2*b)+52)

   x1#=		PeekFloat(tris(i2),(j2*b)+100)
   y1#=		PeekFloat(tris(i2),(j2*b)+104)
   z1#=		PeekFloat(tris(i2),(j2*b)+108)
   ua1#=	PeekFloat(tris(i2),(j2*b)+112)
   va1#=	PeekFloat(tris(i2),(j2*b)+116)
   ub1#=	PeekFloat(tris(i2),(j2*b)+120)
   vb1#=	PeekFloat(tris(i2),(j2*b)+124)
   nx1#=	PeekFloat(tris(i2),(j2*b)+128)
   ny1#=	PeekFloat(tris(i2),(j2*b)+132)
   nz1#=	PeekFloat(tris(i2),(j2*b)+136)
   red1#=	PeekFloat(tris(i2),(j2*b)+140)
   gre1#=	PeekFloat(tris(i2),(j2*b)+144)
   blu1#=	PeekFloat(tris(i2),(j2*b)+148)
   alp1#=	PeekFloat(tris(i2),(j2*b)+152)

   x2#=		PeekFloat(tris(i2),(j2*b)+200)
   y2#=		PeekFloat(tris(i2),(j2*b)+204)
   z2#=		PeekFloat(tris(i2),(j2*b)+208)
   ua2#=	PeekFloat(tris(i2),(j2*b)+212)
   va2#=	PeekFloat(tris(i2),(j2*b)+216)
   ub2#=	PeekFloat(tris(i2),(j2*b)+220)
   vb2#=	PeekFloat(tris(i2),(j2*b)+224)
   nx2#=	PeekFloat(tris(i2),(j2*b)+228)
   ny2#=	PeekFloat(tris(i2),(j2*b)+232)
   nz2#=	PeekFloat(tris(i2),(j2*b)+236)
   red2#=	PeekFloat(tris(i2),(j2*b)+240)
   gre2#=	PeekFloat(tris(i2),(j2*b)+244)
   blu2#=	PeekFloat(tris(i2),(j2*b)+248)
   alp2#=	PeekFloat(tris(i2),(j2*b)+252)

   vr0=AddVertex	(s2(i2),x0,y0,z0)
   VertexTexCoords 	s2(i2),vr0,ua0,va0,0,0 
   VertexTexCoords 	s2(i2),vr0,ub0,vb0,0,1 
   VertexNormal 	s2(i2),vr0,nx0,ny0,nz0
   VertexColor  	s2(i2),vr0,red0,gre0,blu0,alp0

   vr1=AddVertex	(s2(i2),x1,y1,z1)
   VertexTexCoords 	s2(i2),vr1,ua1,va1,0,0 
   VertexTexCoords 	s2(i2),vr1,ub1,vb1,0,1 
   VertexNormal 	s2(i2),vr1,nx1,ny1,nz1
   VertexColor  	s2(i2),vr1,red1,gre1,blu1,alp1

   vr2=AddVertex	(s2(i2),x2,y2,z2)
   VertexTexCoords 	s2(i2),vr2,ua2,va2,0,0 
   VertexTexCoords 	s2(i2),vr2,ub2,vb2,0,1 
   VertexNormal 	s2(i2),vr2,nx2,ny2,nz2
   VertexColor  	s2(i2),vr2,red2,gre2,blu2,alp2

   AddTriangle 		(s2(i2),vr0,vr1,vr2)



Else ; has no clone

   x0#=		PeekFloat(tris(i),(j*b)+0)
   y0#=		PeekFloat(tris(i),(j*b)+4)
   z0#=		PeekFloat(tris(i),(j*b)+8)
   ua0#=	PeekFloat(tris(i),(j*b)+12)
   va0#=	PeekFloat(tris(i),(j*b)+16)
   ub0#=	PeekFloat(tris(i),(j*b)+20)
   vb0#=	PeekFloat(tris(i),(j*b)+24)
   nx0#=	PeekFloat(tris(i),(j*b)+28)
   ny0#=	PeekFloat(tris(i),(j*b)+32)
   nz0#=	PeekFloat(tris(i),(j*b)+36)
   red0#=	PeekFloat(tris(i),(j*b)+40)
   gre0#=	PeekFloat(tris(i),(j*b)+44)
   blu0#=	PeekFloat(tris(i),(j*b)+48)
   alp0#=	PeekFloat(tris(i),(j*b)+52)

   x1#=		PeekFloat(tris(i),(j*b)+100)
   y1#=		PeekFloat(tris(i),(j*b)+104)
   z1#=		PeekFloat(tris(i),(j*b)+108)
   ua1#=	PeekFloat(tris(i),(j*b)+112)
   va1#=	PeekFloat(tris(i),(j*b)+116)
   ub1#=	PeekFloat(tris(i),(j*b)+120)
   vb1#=	PeekFloat(tris(i),(j*b)+124)
   nx1#=	PeekFloat(tris(i),(j*b)+128)
   ny1#=	PeekFloat(tris(i),(j*b)+132)
   nz1#=	PeekFloat(tris(i),(j*b)+136)
   red1#=	PeekFloat(tris(i),(j*b)+140)
   gre1#=	PeekFloat(tris(i),(j*b)+144)
   blu1#=	PeekFloat(tris(i),(j*b)+148)
   alp1#=	PeekFloat(tris(i),(j*b)+152)

   x2#=		PeekFloat(tris(i),(j*b)+200)
   y2#=		PeekFloat(tris(i),(j*b)+204)
   z2#=		PeekFloat(tris(i),(j*b)+208)
   ua2#=	PeekFloat(tris(i),(j*b)+212)
   va2#=	PeekFloat(tris(i),(j*b)+216)
   ub2#=	PeekFloat(tris(i),(j*b)+220)
   vb2#=	PeekFloat(tris(i),(j*b)+224)
   nx2#=	PeekFloat(tris(i),(j*b)+228)
   ny2#=	PeekFloat(tris(i),(j*b)+232)
   nz2#=	PeekFloat(tris(i),(j*b)+236)
   red2#=	PeekFloat(tris(i),(j*b)+240)
   gre2#=	PeekFloat(tris(i),(j*b)+244)
   blu2#=	PeekFloat(tris(i),(j*b)+248)
   alp2#=	PeekFloat(tris(i),(j*b)+252)

   vr0=AddVertex	(s2(i),x0,y0,z0)
   VertexTexCoords 	s2(i),vr0,ua0,va0,0,0 
   VertexTexCoords 	s2(i),vr0,ub0,vb0,0,1 
   VertexNormal 	s2(i),vr0,nx0,ny0,nz0
   VertexColor  	s2(i),vr0,red0,gre0,blu0,alp0

   vr1=AddVertex	(s2(i),x1,y1,z1)
   VertexTexCoords 	s2(i),vr1,ua1,va1,0,0 
   VertexTexCoords 	s2(i),vr1,ub1,vb1,0,1 
   VertexNormal 	s2(i),vr1,nx1,ny1,nz1
   VertexColor  	s2(i),vr1,red1,gre1,blu1,alp1

   vr2=AddVertex	(s2(i),x2,y2,z2)
   VertexTexCoords 	s2(i),vr2,ua2,va2,0,0 
   VertexTexCoords 	s2(i),vr2,ub2,vb2,0,1 
   VertexNormal 	s2(i),vr2,nx2,ny2,nz2
   VertexColor  	s2(i),vr2,red2,gre2,blu2,alp2

   AddTriangle 		(s2(i),vr0,vr1,vr2)

EndIf

  EndIf
  If KeyDown(1)<>0 Then End
 Next
Next


FreeEntity mesh ; original Mesh not needed anymore

mesh=mesh2

; ---------------------------------- a walktrough test code -------------------------------
.test_walk

Collisions 1,2,2,2

player=CreatePivot()
PositionEntity player,0,100,-1
EntityRadius player,0.2,0.9
EntityType player,1

camera=CreateCamera( player )
CameraRange camera,.1,200
TranslateEntity camera,0,1.8,0
CameraClsColor camera,180,200,255

light=CreateLight()
LightColor light,32,32,32
TurnEntity light,45,45,0


EntityFX mesh,1
EntityType mesh,2

sp#=.5
ey#=EntityY(player)

; walktrough loop
While Not KeyHit(1)
	If KeyHit(17)
	 wire=1-wire
	 WireFrame wire
	EndIf
	yv#=EntityY(player)-ey
	ey=EntityY(player)
	If KeyHit(57) Then jump#=1.0
	MoveEntity player,0,-Abs(yv-.005),0
	If jump>0 Then
	 jump=jump-.1
 	 If jump<0 Then jump=0
	 MoveEntity player,0,Sin(jump)*30.0,0
	EndIf
	mxa#=-MouseXSpeed()/4.0
	mya#=MouseYSpeed()/4.0
	MoveMouse GraphicsWidth()/2,GraphicsHeight()/2
	TurnEntity player,0,mxa,0
	TurnEntity camera,mya,0,0
	If KeyDown(205) MoveEntity player,sp,0,0
	If KeyDown(203) MoveEntity player,-sp,0,0
	If KeyDown(200) MoveEntity player,0,0,sp
	If KeyDown(208) MoveEntity player,0,0,-sp
	UpdateWorld
	RenderWorld
	Text 0,0,"Tris rendered: "+TrisRendered()
	Text 0,16,"Press ESC to save and exit"
	Flip
Wend

ExportMesh(export_path$,mesh)

End
























; ----------------------------------------------------
; Mesh Saving Functions


Function ExportMesh(what_to_save$,mesh)
  Print
  If do_weld=1
   Print "Welding Mesh"
   weld(mesh)
  EndIf

  c_surfs=CountSurfaces(mesh)


  Dim c_surf(c_surfs+1)
  Dim c_brush(c_surfs+1)
  Dim c_tex(c_surfs+1)
  Dim c_tex_name$(c_surfs+1)

  Print "Saving Mesh"
	For i=1 To c_surfs
	 c_surf(i)= GetSurface(mesh,i)
	 c_brush(i)=GetSurfaceBrush( c_surf(i) )
	 c_tex(i)=GetBrushTexture( c_brush(i) )
	 c_tex_name$(i)=Lower$(TextureName$( c_tex(i)))
	 If c_tex_name$(i)="" Then Print "Warning: Surface No."+i+" has no Texture. This may produce non-working Meshes!"
     c_tex_name$(i)=strippath$(c_tex_name$(i))
	 If FileType(c_tex_name$(i))<>1 Then Print "Warning: Surface No."+i+" uses nonexistant Texture ("+c_tex_name$(i)+")."
	Next
  ; get lightmap details
  c_tex(0)=GetBrushTexture( c_brush(1),1)
  c_tex_name$(0)=Lower$(TextureName$( c_tex(0)))
  c_tex_name$(0)=StripPath$(c_tex_name$(0))


	WriteBB3D( what_to_save$,mesh )

	For i=1 To c_surfs
	 FreeBrush c_brush(i)
	 FreeTexture c_tex(i)
	Next
End Function


Function ExportMesh_wo_lightmap(what_to_save$,mesh)
  If do_weld=1
   Print "Welding Mesh"
   ;mesh=unweld(mesh)
  EndIf
;    UpdateNormals mesh
;    weld(mesh)
;    UpdateNormals mesh
	c_surfs=CountSurfaces(mesh)

  Dim c_surf(c_surfs+1)
  Dim c_brush(c_surfs+1)
  Dim c_tex(c_surfs+1)
  Dim c_tex_name$(c_surfs+1)


  Print "Saving Mesh"

	For i=1 To c_surfs
	 c_surf(i)= GetSurface(mesh,i)
	 c_brush(i)=GetSurfaceBrush( c_surf(i) )
	 c_tex(i)=GetBrushTexture( c_brush(i) )
	 c_tex_name$(i)=Lower$(TextureName$( c_tex(i)))
     c_tex_name$(i)=strippath$(c_tex_name$(i))
	 Print c_tex_name$(i)
	 If c_tex_name$(i)="" Then Print "Error: Surface No."+i+" has no Texture"
	 If FileType(c_tex_name$(i))<>1 Then Print "Warning: Surface No."+i+" uses nonexistant Texture ("+c_tex_name$(i)+")."
	Next

	WriteBB3D_wo_lightmap( what_to_save$,mesh )

	For i=1 To c_surfs
	 FreeBrush c_brush(i)
	 FreeTexture c_tex(i)
	Next
End Function


Function WriteBB3D_wo_lightmap( f_name$,mesh )

	file=WriteFile( f_name$ )

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

		b3dBeginChunk( "TEXS" ) ; list all textures used by the mesh
		For i=1 To c_surfs
			b3dWriteString( c_tex_name$(i) ) 	;texture file
			b3dWriteInt( 1 )					;flags
			b3dWriteInt( 2 )					;blend
			b3dWriteFloat( 0 )					;x in tex 0 (hu?)
			b3dWriteFloat( 0 )					;y in tex 0
			b3dWriteFloat( 1 )					;x scale 1
			b3dWriteFloat( 1 )					;y scale 1
			b3dWriteFloat( 0 )					;rotation 0
			
		Next
		b3dEndChunk()	;end of TEXS chunk

		
		For i=1 To c_surfs
			b3dBeginChunk( "BRUS" ) ; describe all brushes used by the mesh
		
			b3dWriteInt( 1 )					;number of textures per brush ; (eg 2 with lightmap)
			b3dWriteString( "brush"+(i-1) )		;brushname
			b3dWriteFloat( 1 )					;red
			b3dWriteFloat( 1 )					;green
			b3dWriteFloat( 1 )					;blue
			b3dWriteFloat( 1 )					;alpha
			b3dWriteFloat( 0 )					;shininess
			b3dWriteInt( 1 )					;blendmode
			b3dWriteInt( 0 )					;FX
			b3dWriteInt( i-1 )					;used texture index ?
;			b3dWriteInt( i-1 )					;additional texture index (eg lightmap), but here we only use 1 (see above)

			b3dEndChunk()	;end of BRUS chunk
		Next
		
		b3dBeginChunk( "NODE" )
			b3dWriteString( "entity_name_here!" )
			b3dWriteFloat( 0 )	;x_pos
			b3dWriteFloat( 0 )	;y_pos
			b3dWriteFloat( 0 )	;y_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( mesh )
		b3dEndChunk()	;end of NODE chunk
		
	b3dEndChunk()	;end of BB3D chunk
	
	CloseFile file
End Function

Function WriteMESH_wo_lightmap( mesh )

	n_surfs=CountSurfaces( mesh )
	
	b3dBeginChunk( "MESH" )
		b3dWriteInt( -1 )				;no 'entity' brush -1
		
		b3dBeginChunk( "VRTS" )
			b3dWriteInt( 0 )			;flags - 0=no normal/color
			b3dWriteInt( 1 )			;number of tex_coord sets (z.B: 2 bei lightmap)
			b3dWriteInt( 2 )			;coords per set (u,v,w?) 2 beu uv, 3 bei 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( 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" )
				b3dWriteInt( k-1 )		;brush for these triangles (surf -1 !!!)
				
				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

; save mesh WITH lightmap funcs: --------------------------------------------------------

Function WriteBB3D( f_name$,mesh )

	file=WriteFile( f_name$ )

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

		b3dBeginChunk( "TEXS" ) ; list all textures used by the mesh
		; write lightmap tex
		limaname$=Left$(f_name$,Len(f_name$)-4)+"_lm.bmp"
;		If Upper$(Left$(limaname$,Len(runfromhere$)))=Upper$(runfromhere$)
;		 limaname$=Right$(limaname$,Len(limaname$)-Len(runfromhere$))
;		EndIf
For i=Len(limaname$) To 1 Step -1
 If Mid$(limaname$,i,1)="" Then Exit
Next
If i<1 Then i=1
limaname$=Right$(limaname$,Len(limaname$)-i)

		CopyFile "ssl_temp_lm.bmp", limaname$
		
		b3dWriteString( c_tex_name$(0) ) 	;texture file
		b3dWriteInt( 1 Or $10000 )			;flags
		b3dWriteInt( 2 )					;blend - 2
		b3dWriteFloat( 0 )					;x in tex 0 (hu?)
		b3dWriteFloat( 0 )					;y in tex 0
		b3dWriteFloat( 1 )					;x scale 1
		b3dWriteFloat( 1 )					;y scale 1
		b3dWriteFloat( 0 )					;rotation 0
			
		For i=1 To c_surfs
			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
			b3dWriteFloat( 1 )					;x scale 1
			b3dWriteFloat( 1 )					;y scale 1
			b3dWriteFloat( 0 )					;rotation 0
			
		Next
		b3dEndChunk()	;end of TEXS chunk

		
		For i=1 To c_surfs
			b3dBeginChunk( "BRUS" ) ; describe all brushes used by the mesh
		
			b3dWriteInt( 2 )					;number of textures per brush ; (eg 2 with lightmap)
			b3dWriteString( "brush"+(i-1) )		;brushname
			b3dWriteFloat( 1 )					;red
			b3dWriteFloat( 1 )					;green
			b3dWriteFloat( 1 )					;blue
			b3dWriteFloat( 1 )					;alpha
			b3dWriteFloat( 0 )					;shininess
			b3dWriteInt( 1 )					;blendmode - once 2, mow 1!
			b3dWriteInt( 1 )					;FX
			b3dWriteInt( i )					;used texture index ?
			b3dWriteInt( 0 )					;additional texture index (eg lightmap), but here we only use 1 (see above)

			b3dEndChunk()	;end of BRUS chunk
		Next
		
		b3dBeginChunk( "NODE" )
			b3dWriteString( "whatever" )
			b3dWriteFloat( 0 )	;x_pos
			b3dWriteFloat( 0 )	;y_pos
			b3dWriteFloat( 0 )	;y_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( mesh )
		b3dEndChunk()	;end of NODE chunk
		
	b3dEndChunk()	;end of BB3D chunk
	
	CloseFile file
End Function

Function WriteMESH( mesh )

	n_surfs=CountSurfaces( mesh )
	
	b3dBeginChunk( "MESH" )
		b3dWriteInt( -1 )				;no 'entity' brush -1
		
		b3dBeginChunk( "VRTS" )
			b3dWriteInt( 0 )			;flags - 0=no normal/color
			b3dWriteInt( 2 )			;number of tex_coord sets (z.B: 2 bei lightmap)
			b3dWriteInt( 2 )			;coords per set (u,v,w?) 2 beu uv, 3 bei 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( 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" )
				b3dWriteInt( k-1 )		;brush for these triangles (surf -1 !!!)
				
				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


;---------------------------------- eo saving mesh functions ------------------------------------

;-------------------------------------- welding functions ---------------------------------------

; init weld
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#)
					loc_u#=(vq\u20#)
					loc_v#=(vq\v20#)
;					If loc_u> 1.0 Then loc_u=loc_u-(loc_u-1.0)
;					If loc_u<-1.0 Then loc_u=loc_u+(Abs(loc_u)-1.0)
;					If loc_v> 1.0 Then loc_v=loc_v-(loc_v-1.0)
;					If loc_v<-1.0 Then loc_v=loc_v+(Abs(loc_v)-1.0)

					VertexTexCoords su,mycount,loc_u,loc_v,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#)
					loc_u#=(vq\u21#)
					loc_v#=(vq\v21#)
;					If loc_u> 1.0 Then loc_u=loc_u-(loc_u-1.0)
;					If loc_u<-1.0 Then loc_u=loc_u+(Abs(loc_u)-1.0)
;					If loc_v> 1.0 Then loc_v=loc_v-(loc_v-1.0)
;					If loc_v<-1.0 Then loc_v=loc_v+(Abs(loc_v)-1.0)
					VertexTexCoords su,mycount,loc_u,loc_v,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#)
					loc_u#=(vq\u22#)
					loc_v#=(vq\v22#)
;					If loc_u> 1.0 Then loc_u=loc_u-(loc_u-1.0)
;					If loc_u<-1.0 Then loc_u=loc_u+(Abs(loc_u)-1.0)
;					If loc_v> 1.0 Then loc_v=loc_v-(loc_v-1.0)
;					If loc_v<-1.0 Then loc_v=loc_v+(Abs(loc_v)-1.0)
					VertexTexCoords su,mycount,loc_u,loc_v,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


;----------------------------------- eo welding functions ---------------------------------------








Function StripPath$(file$)
 If Len(file$)>0 
  For i=Len(file$) To 1 Step -1 
   mi$=Mid$(file$,i,1) 
   If mi$="" Or mi$="/" Then Return name$ Else name$=mi$+name$ 
  Next 
 EndIf 
 Return name$ 
End Function




the required b3dfile.bb include file:

;
;b3d file utils to be included
;

Dim b3d_stack(100)
Global b3d_file,b3d_tos

Function b3dSetFile( file )
	b3d_tos=0
	b3d_file=file
End Function

;***** functions for reading from B3D files *****

Function b3dReadByte()
	Return ReadByte( b3d_file )
End Function

Function b3dReadInt()
	Return ReadInt( b3d_file )
End Function

Function b3dReadFloat#()
	Return ReadFloat( b3d_file )
End Function

Function b3dReadString$()
	Repeat
		ch=b3dReadByte()
		If ch=0 Return t$
		t$=t$+Chr$(ch)
	Forever
End Function

Function b3dReadChunk$()
	For k=1 To 4
		tag$=tag$+Chr$(b3dReadByte())
	Next
	sz=ReadInt( b3d_file )
	b3d_tos=b3d_tos+1
	b3d_stack(b3d_tos)=FilePos( b3d_file )+sz
	Return tag$
End Function

Function b3dExitChunk()
	SeekFile b3d_file,b3d_stack(b3d_tos)
	b3d_tos=b3d_tos-1
End Function

Function b3dChunkSize()
	Return b3d_stack(b3d_tos)-FilePos( b3d_file )
End Function

;***** Functions for writing to B3D files *****

Function b3dWriteByte( n )
	WriteByte( b3d_file,n )
End Function

Function b3dWriteInt( n )
	WriteInt( b3d_file,n )
End Function

Function b3dWriteFloat( n# )
	WriteFloat( b3d_file,n )
End Function

Function b3dWriteString( t$ )
	For k=1 To Len( t$ )
		ch=Asc(Mid$(t$,k,1))
		b3dWriteByte(ch)
		If ch=0 Return
	Next
	b3dWriteByte( 0 )
End Function

Function b3dBeginChunk( tag$ )
	b3d_tos=b3d_tos+1
	For k=1 To 4
		b3dWriteByte(Asc(Mid$( tag$,k,1 )))
	Next
	b3dWriteInt( 0 )
	b3d_stack(b3d_tos)=FilePos( b3d_file )
End Function

Function b3dEndChunk()
	n=FilePos( b3d_file )
	SeekFile b3d_file,b3d_stack(b3d_tos)-4
	b3dWriteInt( n-b3d_stack(b3d_tos) )
	SeekFile b3d_file,n
	b3d_tos=b3d_tos-1
End Function


Wonderful work there jfk, that was a pleasure to read!

What format are you exporting this in? If its B3D, you could always write your own in-house loader and optimizer (and even exporter). Its a bit of a "why should I do that?" resolution, however it seems that your editor really has issues with saving out more than needed geometry.

Just a thought

Thanks for the suggestion. Probably there was a misunderstanding, caused by my broken english. It's not my editor, it's TeraBit's. The bug occurs rarely, so it wasn't reported until now. It is also something that is hidden very well.

My Optimizer Loads a b3d, then removes unneeded Triangles (clones) and welds the result, then saves it as b3d. It's an acceptable solution. Of course I hope this won't be neccessary anymore as soon as Terabit has fixed this in the original content creation tool: decorator, which is BTW. a pleasure to work with.

EDIT
Terabit just released version 1.81 with a bug fix for this and for a problem some other people had with the bitzsys.dll. So go and get it from from the toolbox.

Yeah, I hear ya