B3D File Format

Blitz3D Forums/Blitz3D Programming/B3D File Format

carpman(Posted 2006) [#1]
Hi,hope someone can help, I'm a bit of a clutz with file formats.
I'm trying to bring together JFK's SaveB3D code and sswift's Calculate Normals code from the code archives.
I've been using both of these seperatly for quite a while and they work very well...many thanks to you both for the code.
However, what I would like to do is to load the mesh...calculate the normals then save the mesh with the new normals. Mainly so I can then load the meshes into other Blitz related apps with the correct normals.

I've got things working OK but when the file is saved, the new normals aren't.
I've checked out the B3D specs and think I need to be looking at the Verts part of the code in the
WriteMesh(Mesh) function.
Seems I need to set the first Int flag to 1 ...but when I do this my system just hangs after I reach the
"Press any key to save this mesh" part.
I've also tried adding this to the to the WriteMesh(Mesh) function...do I need to do this?

b3dWriteFloat( VertexNX( surf,j ) )
b3dWriteFloat( VertexNY( surf,j ) )
b3dWriteFloat( VertexNZ( surf,j ) )

However my system still just hangs.
I've found an endless number of ways of how NOT to do it but can't seem to find the right way. LOL
If some kind person could point me in the right direction it would be much appreciated....Thanks.

;Thanks to JFK and sswift for all the code

Graphics3D 640,480,32,2

SetBuffer BackBuffer()

Include "b3dfile.bb"
; this file can be found here: http://www.blitzbasic.com/sdkspecs/sdkspecs/b3dfile_utils.zip


Dim Face_NX#(32768)
Dim Face_NY#(32768)
Dim Face_NZ#(32768)
		
Dim Vertex_ConnectedTris(32768)
Dim Vertex_TriList(32768, 32)


Global Mesh
Global MeshName$

cam=CreateCamera()
TranslateEntity cam,0,0,-10

light=CreateLight(2)       
                          
AmbientLight 0,0,0     


MeshName$="Whatever.x" ; Mesh to save... (one that is using textures)

Mesh=LoadMesh(MeshName$)  ;Load the mesh

Calculate_Normals(Mesh)   ;Calculate the new normals


For n=1 To Len(meshname$)  ;Drop the file extension
mi$=Mid$(meshname$,n,1) 
If mi$="."  Then length=n-1
Next 
For n=1 To length          ;Load new mesh name into a$
a$ = a$ + Mid(meshname$,n,1)
Next

; BTW: you should run this from inside the folder where the Mesh resides, unless you edit the
; Part labeled by "<<<<<<<<<<<<"

Global c_surfs=CountSurfaces(mesh)
Global name$

Print "Mesh "+Meshname$+" has "+ c_surfs+" Surfaces, using the following textures:"

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

; track down used textures (thanks Mark!)
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))) ; Full (!) Texture Path
 curdir$=Lower$(CurrentDir$()) 
 c_tex_name$(i)= Replace$(c_tex_name$(i),curdir$,"") ;<<<<<<<<<<<<<<<<<<<
 
 
 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

Print "Press any key to save this Mesh as a .b3d File"


WaitKey()

; end

WriteBB3D( a$+".b3d",mesh )

For i=1 To c_surfs
 FreeBrush c_brush(i); release memory
 FreeTexture c_tex(i)
Next


; test if it worked...
FreeEntity mesh
mesh2=LoadMesh(a$+".b3d")
While Not KeyDown(1)
 TurnEntity mesh2,1,2,3
 RenderWorld()
 Text 0,0,a$+".b3d"
 Flip
Wend
ClearWorld
End


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
		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
		DropType$=Right$(c_tex_name$(i),4) 
		c_tex_name$(i)=Replace$(c_tex_name$(i),DropType$,"") ;Drop the file type for materials
		
			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
			b3dWriteString(c_tex_name$(i)  )	
			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( ? )					;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 )	;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( 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( 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( 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


Function getextension$(filename$) ; Returns the extension minus the .
	lastdir = 1
	For i=1 To Len(filename$)
		If Mid$(filename$,i,1) = "." Then Lastdir = i
	Next
	If Lastdir > 1 Then Lastdir = Lastdir + 1
	For i=Lastdir To Len(filename$)
		a$ = a$ + Mid(filename$,i,1)
	Next
	Return a$
End Function


; -------------------------------------------------------------------------------------------------------------------
; This function calculates and sets the normals for a mesh.
;
; Should probably update this so that it can recursively loop through all of an entities children as well.
; -------------------------------------------------------------------------------------------------------------------
Function Calculate_Normals(Mesh)
	
	; Loop through all surfaces of the mesh.
	Surfaces = CountSurfaces(Mesh)
	For LOOP_Surface = 1 To Surfaces

		Surface_Handle = GetSurface(Mesh, LOOP_Surface)

		; Reset the number of connected polygons for each vertex.
		For LoopV = 0 To 32767	
			Vertex_ConnectedTris(LoopV) = 0
		Next	
	
		; Loop through all triangles in this surface of the mesh.
		Tris = CountTriangles(Surface_Handle)
		For LOOP_Tris = 0 To Tris-1

			; Get the vertices that make up this triangle.
				Vertex_0 = TriangleVertex(Surface_Handle, LOOP_Tris, 0)
				Vertex_1 = TriangleVertex(Surface_Handle, LOOP_Tris, 1)
				Vertex_2 = TriangleVertex(Surface_Handle, LOOP_Tris, 2)
	
			; Adjust the number of triangles each vertex is connected to and
			; store this triangle in each vertex's list of triangles it is connected to.
				ConnectedTris = Vertex_ConnectedTris(Vertex_0)
				Vertex_TriList(Vertex_0, ConnectedTris) = LOOP_Tris
				Vertex_ConnectedTris(Vertex_0) = ConnectedTris + 1

				ConnectedTris = Vertex_ConnectedTris(Vertex_1)
				Vertex_TriList(Vertex_1, ConnectedTris) = LOOP_Tris
				Vertex_ConnectedTris(Vertex_1) = ConnectedTris + 1

				ConnectedTris = Vertex_ConnectedTris(Vertex_2)
				Vertex_TriList(Vertex_2, ConnectedTris) = LOOP_Tris
				Vertex_ConnectedTris(Vertex_2) = ConnectedTris + 1

			; Calculate the normal for this face.

				; Get the corners of this face:
				Ax# = VertexX#(Surface_Handle, Vertex_0)
				Ay# = VertexY#(Surface_Handle, Vertex_0)
				Az# = VertexZ#(Surface_Handle, Vertex_0)

				Bx# = VertexX#(Surface_Handle, Vertex_1)
				By# = VertexY#(Surface_Handle, Vertex_1)
				Bz# = VertexZ#(Surface_Handle, Vertex_1)

				Cx# = VertexX#(Surface_Handle, Vertex_2)
				Cy# = VertexY#(Surface_Handle, Vertex_2)
				Cz# = VertexZ#(Surface_Handle, Vertex_2)

				; Triangle 1
				; Get the vectors for two edges of the triangle.
				Px# = Ax#-Bx#
				Py# = Ay#-By#
				Pz# = Az#-Bz#

				Qx# = Bx#-Cx#
				Qy# = By#-Cy#
				Qz# = Bz#-Cz#

				; Compute their cross product.
				Nx# = Py#*Qz# - Pz#*Qy#
				Ny# = Pz#*Qx# - Px#*Qz#
				Nz# = Px#*Qy# - Py#*Qx#

				; Store the face normal.
				Face_NX#(LOOP_Tris) = Nx#
				Face_NY#(LOOP_Tris) = Ny#
				Face_NZ#(LOOP_Tris) = Nz#

		Next

		; Now that all the face normals for this surface have been calculated, calculate the vertex normals.
		Vertices = CountVertices(Surface_Handle)
		For LOOP_Vertices = 0 To Vertices-1

			; Reset this normal.
			Nx# = 0
			Ny# = 0
			Nz# = 0

			; Add the normals of all polygons which are connected to this vertex.
			Polys = Vertex_ConnectedTris(LOOP_Vertices)
				
			For LOOP_Polys = 0 To Polys-1

				ThisPoly = Vertex_TriList(LOOP_Vertices, LOOP_Polys)

				Nx# = Nx# + Face_NX#(ThisPoly)
				Ny# = Ny# + Face_NY#(ThisPoly)
				Nz# = Nz# + Face_NZ#(ThisPoly)			
				
			Next	
				
			; Normalize the new vertex normal.
			; (Normalizing is scaling the vertex normal down so that it's length = 1)

				Nl# = Sqr(Nx#^2 + Ny#^2 + Nz#^2)

				; Avoid a divide by zero error if by some freak accident, the vectors add up to 0.
				; If Nl# = 0 Then Nl# = 0.1

				Nx# = Nx# / Nl#
				Ny# = Ny# / Nl#
				Nz# = Nz# / Nl#

			; Set the vertex normal.
			
				VertexNormal Surface_Handle, LOOP_Vertices, Nx#, Ny#, Nz#
				;VertexColor Surface_Handle, LOOP_Vertices, polys*127, polys*127, polys*127
		
		Next

	Next

End Function



carpman(Posted 2006) [#2]
As it is this code works but doesn't save the new normals.


Beaker(Posted 2006) [#3]
You might want a look at this:
http://www.zen54709.zen.co.uk/b3dformat.htm


Ricky Smith(Posted 2006) [#4]

I've also tried adding this to the to the WriteMesh(Mesh) function...do I need to do this?

b3dWriteFloat( VertexNX( surf,j ) )
b3dWriteFloat( VertexNY( surf,j ) )
b3dWriteFloat( VertexNZ( surf,j ) )



Yes you need to write the new normal values to the file as well. First of set the Vertex Flag to the value of 1.
The value 0 that you are setting means that you are storing vertex positions only. You need to add 1 to this value for vertex normals and add another 2 for vertex color.
e.g 0=vertex positions only
1=positions+normals
2=positions+colour
3=positions+normals+colour

You need to set the vertex flag value to 1 and then write the vertex normal values after the position values.
b3dBeginChunk( "VRTS" )
			b3dWriteInt( 1 )			;flags - 0=position only, 1= position+normals
			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( VertexNX( surf,j ) )
					b3dWriteFloat( VertexNY( surf,j ) )
					b3dWriteFloat( VertexNZ( 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



carpman(Posted 2006) [#5]
Beaker....many thanks for the link. I haven't seen this before, looks very interesting, should learn a bit more with this.

Smiff...great stuff...that works just fine, brilliant. I can move forward a bit now, many thanks.
It's strange you should answere this post, I just downloaded the demo for Pacemaker a couple of days ago. :)