s3o file specification

s3o file specification

Discuss the source code and development of Spring Engine in general from a technical point of view. Patches go here too.

Moderator: Moderators

Post Reply
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

s3o file specification

Post by Das Bruce »

Does anyone have a link to it? I've been trying to reverse engineer the spring s3o parser but I'm struggling.
User avatar
MidKnight
Posts: 2652
Joined: 10 Sep 2008, 03:11

Re: s3o file specification

Post by MidKnight »

I applaud your efforts. I, however, have no links. Sorry! :(

...Perhaps thesleepless' import/export tools can help you? Their Python source is likely more readable than spring's s3o code.
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: s3o file specification

Post by Beherith »

Its actually super simple. I have the python code ready (although it looks like SHIT) but the general concept is simple enough:

Use the pack and unpack methods to generate binary data from strings. and these are the C structs you need to parse:

Code: Select all

struct S3OHeader{
	char magic[12];		///< "Spring unit\0"
	int version;		///< 0 for this version
	float radius;		///< radius of collision sphere
	float height;		///< height of whole object
	float midx;		///< these give the offset from origin(which is supposed to lay in the ground plane) to the middle of the unit collision sphere
	float midy;
	float midz;
	int rootPiece;		///< offset in file to root piece
	int collisionData;	///< offset in file to collision data, must be 0 for now (no collision data)
	int texture1;		///< offset in file to char* filename of first texture
	int texture2;		///< offset in file to char* filename of second texture

Code: Select all

struct Piece{
	int name;		///< offset in file to char* name of this piece
	int numChilds;		///< number of sub pieces this piece has
	int childs;		///< file offset to table of dwords containing offsets to child pieces
	int numVertices;	///< number of vertices in this piece
	int vertices;		///< file offset to vertices in this piece
	int vertexType;	///< 0 for now
	int primitiveType;	///< type of primitives for this piece, 0=triangles,1 triangle strips,2=quads
	int vertexTableSize;	///< number of indexes in vertice table
	int vertexTable;	///< file offset to vertice table, vertice table is made up of dwords indicating vertices for this piece, to indicate end of a triangle strip use 0xffffffff
	int collisionData;	///< offset in file to collision data, must be 0 for now (no collision data)
	float xoffset;		///< offset from parent piece
	float yoffset;
	float zoffset;

Code: Select all

struct Vertex{
	float xpos;		///< position of vertex relative piece origin
	float ypos;
	float zpos;
	float xnormal;		///< normal of vertex relative piece rotation
	float ynormal;
	float znormal;
	float texu;		///< texture offset for vertex
	float texv;
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: s3o file specification

Post by Beherith »

Dont do the same work I did twice!

So here is the python code to fully load an s3o, and to save it as well:
Attachments
s3olib.zip
(3.07 KiB) Downloaded 22 times
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Thanks.

The variable S3oPiece.primitives doesn't appear to get used, how do I know how to create the triangles from the vertices?

[Edit] Never mind, it appears you've stored them temporarily in a list called prims without copying it to primitives. [/Edit]
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: s3o file specification

Post by Beherith »

Yeah may be buggy, feel free to pester me here or in lobby. Primitives is not part of spec, was only left in there cause 3do's had them.
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Hurrah, I managed to cause an Access Violation with my s3o.
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: s3o file specification

Post by Beherith »

Access violation in upspring or engine? Its easy to screw them up, please post your code if you need help. I also recall that if you reload the s3o that you made with my code, you can easily verify it.
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Access violation in Engine, instacrash in UpSpring.

The file also swells to ~10x the size.
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Ok, I think the problem I'm having is with vertexTable. I'll have a look again in the morning.
abma
Spring Developer
Posts: 3798
Joined: 01 Jun 2009, 00:08

Re: s3o file specification

Post by abma »

@Das Bruce:

if you try with latest spring + paste infolog.txt, the crash can maybe fixed...
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Abma: I'll give that a go when I get a chance.

Beherith: Quads arn't working right for me, the vertices are coming up right but I can't seem to get it to create the faces properly. Faces(triangles) contain three indexes to their vertices and a reference to a list of all vertices in the piece.

The only idea I have is that I've misunderstood Python.

Code: Select all

elif sp.primitivetype == 2:		# Quads
	for x in range(len(sp.vertices)/4):
		a = 4*x
		b = 4*x+1
		c = 4*x+2
		d = 4*x+3
		print 'Quad vertices:'
		print sp.vertices[a].v.x, sp.vertices[a].v.y, sp.vertices[a].v.z
		print sp.vertices[b].v.x, sp.vertices[b].v.y, sp.vertices[b].v.z
		print sp.vertices[c].v.x, sp.vertices[c].v.y, sp.vertices[c].v.z
		print sp.vertices[d].v.x, sp.vertices[d].v.y, sp.vertices[d].v.z
		
		self.vertices.append(Vertex(sp.vertices[a].v.x, sp.vertices[a].v.y, sp.vertices[a].v.z))
		self.vertices.append(Vertex(sp.vertices[b].v.x, sp.vertices[b].v.y, sp.vertices[b].v.z))
		self.vertices.append(Vertex(sp.vertices[c].v.x, sp.vertices[c].v.y, sp.vertices[c].v.z))
		self.faces.append(Face(a, b, c, self.vertices))
		print 'First Tri vertices:'
		self.faces[-1].Debug()
		
		self.vertices.append(Vertex(sp.vertices[a].v.x, sp.vertices[a].v.y, sp.vertices[a].v.z))
		self.vertices.append(Vertex(sp.vertices[c].v.x, sp.vertices[c].v.y, sp.vertices[c].v.z))
		self.vertices.append(Vertex(sp.vertices[d].v.x, sp.vertices[d].v.y, sp.vertices[d].v.z))
		self.faces.append(Face(a, c, d, self.vertices))				
		print 'Second Tri vertices:'
		self.faces[-1].Debug()
Gives output:

Code: Select all

Quad vertices:
3.0 -1.0 1.0
1.0 -1.0 1.0
1.0 1.0 1.0
3.0 1.0 1.0
First Tri vertices:
[ 3.0 , -1.0 , 1.0 ]
[ 1.0 , -1.0 , 1.0 ]
[ 1.0 , 1.0 , 1.0 ]
Second Tri vertices:
[ 3.0 , -1.0 , 1.0 ]
[ 1.0 , 1.0 , 1.0 ]
[ 3.0 , -1.0 , 1.0 ]
For the first quad. What have I overlooked?
Complete source
Andrej
Posts: 176
Joined: 13 Aug 2006, 18:55

Re: s3o file specification

Post by Andrej »

Looked at the code, didn't run it.

It looks like you are not using the 'vertexTable' member of Piece, instead just printing the 'vertex' members cosecutively.

I will attempt to quote the relevant sections.

You ARE reading the vertexTable (piece[8]) properly as far as I can tell, storing in self.primitives:

Code: Select all

#From s3olib.py
def recursereads3o
...
			f.seek(piece[8])
			vtable=f.read(piece[7]*4)			
	
	
			unpackstr=''
			for i in range(0,piece[7]):
				unpackstr+='l'
			# print 'vtable length:',len(vtable)
			self.primitives=unpack(unpackstr,vtable)
			for p2 in self.primitives:
				self.vertexTable.append(p2)
But in:

Code: Select all

#File Model.py
def Loads3o
...
			for x in range(len(sp.vertices)/4):
			  a = 4*x
			  sp.vertices[a].v.x ...
You just iterate through the vertices in groups of four.

Instead you'd want something like

Code: Select all

for x in range(len(sp.primitives)/4):
  a = 4*x
  sp.vertices[ sp.primitives[a] ].v.x ...
Image

I suppose this could be useful:
http://www.songho.ca/opengl/gl_vertexarray.html
Talks about indexed geometry in OpenGL, reputable site afaik.

Also during my first attempt at writing I accidentally pressed the Emacs shortcut for copying text (Ctrl+W) which is conveniently the same as the close tab shortcut in Firefox, you'd expect their devs to be Emacs users and get rid of of it but no they troll us :s
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

I don't understand. Why does

Code: Select all

#File Model.py
def Loads3o
...
				print 'Quad vertices:'
				print sp.vertices[a].v.x, sp.vertices[a].v.y, sp.vertices[a].v.z
				print sp.vertices[b].v.x, sp.vertices[b].v.y, sp.vertices[b].v.z
				print sp.vertices[c].v.x, sp.vertices[c].v.y, sp.vertices[c].v.z
				print sp.vertices[d].v.x, sp.vertices[d].v.y, sp.vertices[d].v.z
Print the correct values every time, yet the code just below, which should use those values, decides not to?
Andrej
Posts: 176
Joined: 13 Aug 2006, 18:55

Re: s3o file specification

Post by Andrej »

For that in particular:

I believe you do:
1. self.vertices starts as empty
2. Append 3 vertices to self.vertices, it now contains: a b c
3. Make a Face with indexes 0-1-2, which is a b c
4. Append another 3 vertices to self.vertices on top of the previous ones: it now contains: a b c a c d
5. Make a Face with indexes 0-2-3 which is a c a
This is why the second Face is degenerate, two points are the same.
sp.vertices is a b c d e f g h i etc..
self.vertices is a b c a c d etc.

I'm not entirely sure what the intent/algorithm you following is, seems just broken though?

Just noticed some other things, might want to doublecheck:

Code: Select all

#File s3olib.py
def normalize(self):
	length=sqrt(self.x*self.x,self.y*self.y+self.z*self.z)
Should maybe be?
length=math.sqrt(self.x*self.x+self.y*self.y+self.z*self.z)
(+ instead of comma)

And:

Code: Select all

#Structure definition in Spring source:
struct S3OHeader{
	char magic[12];		///< "Spring unit\0"
	int version;		///< 0 for this version
	float radius;		///< radius of collision sphere
	float height;		///< height of whole object
	float midx;		///< these give the offset from origin(which is supposed to lay in the ground plane) to the middle of the unit collision sphere
	float midy;
	float midz;

Code: Select all

#File s3olib.py at reads3o function
#The way it is read in:
#Notice how the member after radius is supposed to be height
#but it continues to index midx,midy,midz
		self.version=header[0]
		self.radius=header[1]
		self.midx=header[2]
		self.midy=header[3]
		self.midz=header[4]
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

Andrej wrote:For that in particular:

I believe you do:
1. self.vertices starts as empty
2. Append 3 vertices to self.vertices, it now contains: a b c
3. Make a Face with indexes 0-1-2, which is a b c
4. Append another 3 vertices to self.vertices on top of the previous ones: it now contains: a b c a c d
5. Make a Face with indexes 0-2-3 which is a c a
This is why the second Face is degenerate, two points are the same.
sp.vertices is a b c d e f g h i etc..
self.vertices is a b c a c d etc.

I'm not entirely sure what the intent/algorithm you following is, seems just broken though?
Hah, brilliant, that was the problem all along. I can't really comment on s3olib, that's purely Beheriths work.
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

I'm not sure what's happening here. Is it possible that some pieces are being saved as tristrips without being marked as such? I find it extra odd because the infantry models hat and head are part of the same piece but the head renders fine.

Image
odd.png
(32.8 KiB) Downloaded 2 times
User avatar
Beherith
Posts: 5145
Joined: 26 Oct 2007, 16:21

Re: s3o file specification

Post by Beherith »

Found the bug:

Code: Select all

		if sp.primitivetype == 0:		# Triangles
			for x in range(len(sp.vertices)/3):
in Model.py is wrong, you are not taking into account the fact that vertices can be reused. Dont iterate x over vertices, but over vertex table size.

Same goes for quads.
User avatar
Das Bruce
Posts: 3544
Joined: 23 Nov 2005, 06:16

Re: s3o file specification

Post by Das Bruce »

so just iterate over vertexindextable
and not vertextable
Post Reply

Return to “Engine”