Raiders of the lost Brushes

Blitz3D Forums/Blitz3D Programming/Raiders of the lost Brushes

I still have a problem with saving meshes using correct texture flags, blendmode etc.

The basement for this is a modified version of the SaveB3D code form the archives. In my app I need to AddMesh multiple meshes together to one mesh, then I save this multisurface mesh.

While it is possible to store each loaded meshes properties (flags etc.) by using the DumpB3D example from the b3dfile_utils.zip, I am losing control over them when I use commands like GetSurfaceBrush. This will create a new brush that is identic with the original brush, but actually is a new brush with a new handle.

Now, the only way to identify a brush would be to track down its texture filename(s). Problem: there may be brushes that are using the same texture, but diffrent flags etc. So this method is not save.

Do you have an idea on how to identify a brush, to compare it against a list of brushes, where in the list I got all properties stored, but Blitz's native commands to determine Brush properties (of a brush created using GetSurfaceBrush) are very limited (see online manual, search for "brush"). Any ideas? thanks!

EDIT
Ok right now the best solution I got is: With every loaded mesh its properties are collected in a global brushes list, using the DumpB3D example. Then when I have to identify un unknown, cloned brush, I will use the Properties hack functions, as good as it gets: I can't use them to determine the texture flags (that's the reason why I don't use them in the first place). So I wil compare texture names, fx, blendmode, tex scale, position and rotation, basicly everything but the flags that cannot be determined by the hack method (peeking offset etc)

If all these things are the same then I may assume we have identified the brush in the list of known loaded brushes. Unfort. this also means if only the flags are diffrent (and if the texture name is the same, then the flags may indeed be the only thing that is diffrent) then it won't be recognized correctly.
So, basicly I'm still searching for a real solution.

Good luck jfk - this would be very useful !

ouch - you were my last hope...

Currently I have no plan. The comparing method is not safe and complex, probably bug-sensitive and especially not future compilerversions proof.

I just can't believe there's no way, no trick or cheat.

jfk - I'm sure its possible - I would use the global list of all loaded brushes method and compare texture filenames.
Using a naming convention for the texture filenames when they are used with different flags may help ?

I'm trying to find a solution for this since a few days now. I want to cover most possible situations. It's easly possible that a mesh is using the same texture twice, with diffrent flags. Using a naming converntion in the filenames woud force me/the user to have the texture twice on the harddrive. I'd also like to make it in a way that allows to use any mesh file as long as blitz can load it.

The problem with the flags hack is: when the bit value 4 is set, the bits 1 and 2 are ignored:

set / peeked
0:0
1:1
2:2
3:3
4:7
5:7
6:7
7:7
8:8
9:9
10:10
11:11
12:15
13:15
14:15
15:15

and so on.

There is some action at other adresses too, in relation to the tex flags, but none of them allows to see if %111 is 4,5,6 or 7.

I am currently thinking about to use Tomspeeds MemoryLib.dll that has a GetTextureFlags decls function. Not sure tho if this still works. I would also like a solution that works without external dlls. Noless, this may be the only way out:
http://tomspeed.com/memorylib/

well, basicly I am fighting against two bits here :) Programming games can be martial sometimes :/

Update:
I just tried the latest version of TomSpeeds MemoryLib.dll. It seems he had to release a new version for Blitz3d 1.98. Actually, this one works, there's only one problem left:

Unlike in the old version you have to use the texturebuffer handle as the parameter of GetTextureFlags(). This means, since DDS textures will return zero with TextureBuffer(), they cannot be used with Tomspeeds DLL.
Funny, since it was Toms DDS code that was added to Blitz3D.
Not funny: I really wanted to use DDS a lot! Yes, I could use other masked textures, and then patch the final b3d files. But I hate this patchwork artpath where you have to remember to do this and that and that...

Some more infos:

When a texture is created using CreateTexture then the flag 1 is set automaticly. When a texture is loaded then the flag 9 (8+1) is set automaticly. This discrepancy doesn't make sense to me, however. When ClearTexture is used before then the bit value 8 is not set automaticly.

When the mask flag 4 is used, the bit value 1 is forced automaticly. This means, setting the flag to 4 will automaticly turn it into 5. For some reason it also seems you cannot set bit value 2 and 4 at the same time, or, bit value 2 (alpha) is ignored automaticly as soon as value 4 (mask) is set. So the mask flag priority rules over the alpha flag.

Ah, no DDS. What an odysse!

thanks for the report, really valuable info in there.

Hi JFK, i dont know if it can be adapted for your needs, its set up for a group of terrain blocks as one mesh, but that could be adapted to recursively store any mesh (in theory).

There is a surface recording system and if a texture has been used once already it cross references its ID. Or at least should do :P

in the record brushes bit, you would still need to find a way of reading the getsurfacebrushes FX (at the moment im setting it manually from TEDs internal knowlage of what i want, but if you store it there in \bfx it should still be able to cross reference the same texs

Mabe the system i use for recording brushes may help you, or not...


Type brushdata
	Field brushname$, surfnumber,mesh, texindex ,bfx, lmindex,brush
End Type

Type texdata
Field texname$,texindex,lmindex,lmname,islightmap,mesh,surfnumber
End Type

Global b3d_numtexs,b3d_numbrushes,b3d_currentlm

Function writeb3d_terrainAsonemesh(e_filename$)


	;reset texture list
	Delete Each brushdata
	Delete Each texdata

	;open output file
	;If trn_quadnumbery>1 Or trn_quadnumberx>1 numb$="("+a+","+b+")"

	filenameout$=e_filename$+".b3d"

	b3d_numtexs=0
	b3d_numbrushes=0
	For b=1 To trn_quadnumbery
	For a=1 To trn_quadnumberx
		quad=quad+1
		lmname$=e_filename$+"("+a+","+b+")"+"_LM.bmp"

		b3d_recordtextures_terrainAsonemesh(meshout(a,b),"",lmname,quad)
		
		;lm
		td.texdata=New texdata
		td\texindex=b3d_numtexs
		td\texname$=lmname$
		td\islightmap=True
		b3d_numtexs=b3d_numtexs+1
		
		b3d_recordbrushes_terrainAsonemesh(meshout(a,b),0,quad)
		
	Next
	Next


	file=WriteFile( filenameout )
	b3dSetFile( file )
	
	b3dBeginChunk( "BB3D" )
		b3dWriteInt( 1 )	;version

		writetextures()
		writebrushes()

		b3dBeginChunk( "NODE" )
			b3dWriteString( "TERRAIN" )
			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
			
			For b=1 To trn_quadnumbery
			For a=1 To trn_quadnumberx
				writemesh(meshout(a,b))
			Next
			Next
	
		b3dendchunk()
	b3dendchunk()
	CloseFile (file)
 
End Function

Function b3d_recordtextures_terrainAsonemesh(mesh,e_directory$,lmname$,quad)

	If EntityClass (mesh)="Mesh"
		For sc=1 To CountSurfaces (mesh)
			;analyse mesh
			surf=GetSurface (mesh,sc)
			brush=GetSurfaceBrush (surf)
			tex1=GetBrushTexture (brush,0)
			name$=TextureName (tex1)
			nonew=0
			
			;get rid of tex path & copy textures
			from$=name$
			XE_XFilen$=strip_path(from)
			CopyFile from,e_directory+XE_XFilen$
			DebugLog "Copying tex to "+e_directory+XE_XFilen$
				
			;if not in data create a new texture
			If nonew=0 
				td.texdata=New texdata
				td\texindex=b3d_numtexs
				b3d_numtexs=b3d_numtexs+1
				td\texname$=name$
				td\islightmap=0
				td\mesh=mesh
				td\surfnumber=sc
			EndIf 

			;cleanup
			If brush FreeBrush brush :brush=0
			If tex2 FreeTexture tex2 :tex2=0
			If tex1 FreeTexture tex1 :tex1=0
			
			
		Next
	EndIf

	;recurse all
	For cc=1 To CountChildren(mesh)
		chi=GetChild(mesh,cc)
		b3d_recordtextures_terrainasonemesh(chi,e_directory$,lmname,quad)
	Next
	
		
End Function

Function b3d_recordbrushes_terrainAsonemesh(mesh,fx,quad)

	If EntityClass (mesh)="Mesh"
		For sc=1 To CountSurfaces (mesh)
			;analyse mesh
			surf=GetSurface (mesh,sc)
			brush=GetSurfaceBrush (surf)
			tex1=GetBrushTexture (brush,0)
			name$=TextureName (tex1)

			;Create new brush
			br.brushdata=New brushdata
			br\mesh=mesh
			br\surfnumber=sc
			br\bfx=fx
			br\brush=b3d_numbrushes
			b3d_numbrushes=b3d_numbrushes+1

			;specials for forcing vertex alpha in terrains
			If Instr(EntityName(mesh),"TRN_BASE") Then br\bfx=2+FX
			If Instr(EntityName(mesh),"TRN_ALPHA") Then br\bfx=2+32+FX
			
			
			;look up index for texture used
			For t.texdata=Each texdata
				If t\texname$=name$ And t\mesh=mesh And t\surfnumber=sc Then
					br\texindex=t\texindex
					br\lmindex=b3d_numtexs-1
					DebugLog "Brush lightmapinex="+mesh+" "+sc+","+br\texindex+","+(b3d_numtexs-1)
				EndIf 
			Next
			br\brushname$="brush_"+Int(b3d_numbrushes-1)+"_"+name$
		
			;cleanup
			If brush FreeBrush brush :brush=0
			If tex2 FreeTexture tex2 :tex2=0
			If tex1 FreeTexture tex1 :tex1=0
			
		Next
	EndIf

	;recurse all
	For cc=1 To CountChildren(mesh)
		chi=GetChild(mesh,cc)
		b3d_recordbrushes_terrainAsonemesh(chi,fx,quad)
	Next
	
		
End Function

Function writetextures()

		b3dBeginChunk( "TEXS" ) ; list all textures used by the mesh
		For td.texdata=Each texdata
			If td\islightmap=False
				b3dWriteString(strip_path(td\texname)) 	;texture file
				b3dWriteInt( 1+8 )					;flags
				b3dWriteInt( 2 )			  		;blend
				b3dWriteFloat( 0 )					;x in tex 0 (hu?)
				b3dWriteFloat( 0 )					;y in tex 0
				b3dWriteFloat(1.000);tu );tex_uvscale(7-i) )					;x scale 1
				b3dWriteFloat(1.000);tex_uvscale(7-i) )					;y scale 1
				b3dWriteFloat(0 )					;rotation 0			
				typ$="tex"
			Else		
				b3dWriteString(strip_path(td\texname)) 	;texture file
				b3dWriteInt( 1+ 8 + 16 + 32 + 65536)					;flags
				b3dWriteInt( 2 )					;blend
				b3dWriteFloat( 0 )					;x in tex 0 (hu?)
				b3dWriteFloat( 0 )					;y in tex 0
				b3dWriteFloat(1.000);tu );tex_uvscale(7-i) )					;x scale 1
				b3dWriteFloat(1.000);tex_uvscale(7-i) )					;y scale 1
				b3dWriteFloat(0 )					;rotation 0
				typ$="lm"
		   EndIf
		   DebugLog strip_path(td\texname)+" "+typ+" "+indx
		   indx=indx+1
		Next
		b3dEndChunk()

End Function
Function Writebrushes()
		b3dBeginChunk( "BRUS" ) ; describe all brushes used by the mesh
		b3dWriteInt(2)					;number of textures per brush ; (eg 2 with lightmap)
		For br.brushdata=Each brushdata
			b3dWriteString( br\brushname$ )		;brushname
			b3dWriteFloat( 1 )					;red
			b3dWriteFloat( 1 )					;green
			b3dWriteFloat( 1 )					;blue
			b3dWriteFloat( 1 )					;alpha
			b3dWriteFloat( 0 )					;shininess
			b3dWriteInt( 1 )					;blendmode
			b3dWriteInt( br\bfx)						;brushfx
			b3dWriteInt( br\texindex )					;used texture index 
			b3dWriteInt( br\lmindex )					;additional texture index (eg lightmap), but here we only use 1 (see above)
			DebugLog br\texindex +" "+br\lmindex
		Next
		b3dEndChunk()	;end of BRUS chunk
End Function 
Function WriteMESH( mesh )

	b3dBeginChunk( "NODE" )
		name$=EntityName(mesh):If name="" name="noname"
		b3dWriteString( name$ )
		
		b3dWriteFloat( EntityX(MESH) )	;x_pos
		b3dWriteFloat( EntityY(MESH) )	;y_pos
		b3dWriteFloat( EntityZ(MESH) )	;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

		If EntityClass(mesh)="Mesh"
			b3dBeginChunk( "MESH" )
				b3dWriteInt( -1 )				;no 'entity' brush -1
				
				b3dBeginChunk( "VRTS" )
					b3dWriteInt( 3 )			;flags - 0=no normal/color
					b3dWriteInt( 2)             ;number of tex_coord sets (eg: 2 with lightmap)
					b3dWriteInt( 2 )			;coords per set (u,v,w?) 2 with uv, 3 with uvw
			
					n_surfs=CountSurfaces( mesh )
					For k=n_surfs To 1 Step -1
						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( VertexNX( surf,j ) )
							b3dWriteFloat( VertexNY( surf,j ) )
							b3dWriteFloat( VertexNZ( surf,j ) )
							b3dWritefloat( VertexRed( surf,j )/255 )
							b3dWritefloat( VertexGreen( surf,j )/255 )
							b3dWritefloat( VertexBlue( surf,j )/255 )
							b3dWriteFloat( VertexAlpha( surf,j ) )
							b3dWriteFloat( VertexU#( surf,j,0 ) )
							b3dWriteFloat( VertexV#( surf,j,0 ) )
		;					b3dWriteFloat( VertexW#( surf,j,0 ) )
;							If lightmap_onoff
								uaj#=VertexU#( surf,j,1 );VertexX( surf,j )/trn_size;
								vaj#=VertexV#( surf,j,1 );VertexY( surf,j )/trn_size;
								b3dWriteFloat( uaj) ; lightmap uv
								b3dWriteFloat( vaj) ; lightmap uv
		;						b3dWriteFloat( VertexW#( surf,j,1 ) )
;							EndIf 
						Next
					Next
				b3dEndChunk()	;end of VRTS chunk
		
				first_vert=0
				For k=n_surfs To 1 Step -1
					surf=GetSurface( mesh,k )
										
					n_tris=CountTriangles( surf )-1
					
					b3dBeginChunk( "TRIS" )

						b3dWriteInt( lookuptexidfromsurf(mesh,k,0) )			;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
		EndIf
		For cc=CountChildren(mesh)To 1 Step -1
			chi=GetChild(mesh,cc)
			WriteMESH( chi )
		Next 
		b3dEndChunk()	;end of node chunk

End Function
Function lookuptexidfromsurf(mesh,surfnumber,fromlayer)
DebugLog "From Mesh:"+mesh+"   surf:"+Surfnumber
For td.brushdata=Each brushdata
	If  td\mesh=mesh And td\surfnumber=surfnumber
		DebugLog"yes to :"+td\mesh+"  Surf:"+surfnumber +"  Tex:"+td\texindex+"  lm:"+td\lmindex +" brush:"+td\brush
		Return td\brush
	EndIf
Next

End Function 





I'll try this, thank you!

Ok, thanks for all your help. For all those who need a solution for the same problem, here's how I fixed it for now:

All properties can be determined using tomspeeds MemoryLib.DLL (mentioned above), with the exception of:

Blitz's internal functions
GetSurfaceBrush
GetBrushTexture
TextureName

as well as

To obtain texture x scaling and y scaling and the coordSet flag. For these 3 functions use the old hacks published here:
http://www.blitzbasic.com/Community/posts.php?topic=66397
GetBrushTextureCoords
GetBrushTextureScaleU
GetBrushTextureScaleV


Note: although Tomspeed's dll will return the TexFlags, the bit $10000 (65536) does not reflect the CoordsSet as it is stored in the B3D file! Therefor you need to use the hack GetBrushTextureCoords(tex)

Now, with this patchwork of internal functions, external functions and quickndirty hacks, all informations to save a (static) mesh with all visual settings (excluding cubemapping) can be determined. Future compiler version compatibility not guaranted.

Unfort. there may be a memory leak caused by lost brush handles. Noless, a tool can be powerfull, even if it has to be restarted from time to time.

EDIT Additional Note:

when using GetBrushTexture for the 8 possible textures of a brush, it will return numbers other than zero, even if there is no texture on a certain layer. Currently the only way to determine if a returned number is a valid texture handle is to use:

if TextureName$(tex(i))<>"" then

This does of course exclude the usage of Textures that have been created using CreateTexture (I have no idea on how to handle them right now)

Note, it seems TextureName will return the texture name as it was used in the LoadMesh function, regardless if the file was found. So if the file wasn't found, it will still return a TextureName() other than "", but the texture handle will be invalid! To check this out you need to use

Is there a TextureName? yes, layer seems to be used.
Does a file named Name exist? Yes, assume the texture was loaded.
Now you may access TextureBuffer() without to worry too much. If it returns zero, it seems to be a DDS texture, this can be doublechecked by testing the filename extension (".DDS").

After all:
GetBrushTexture(brush,index) that won't return zero for unused layers may be considered a bug.