|
SCM Specifications
Introduction
The SCM file format is a 3D object model
file very easy to load and use, it's a little like a 3DS file format (.3ds),
sure less rich, but very efficient. Its roots are from the ASE file format
(.ase). The ASE file format is a 3D object model ASCII file format, it means
that all the information containing vertices, polygons, texture coordinates or
vertices normal vectors are written directly in a human-understanding language.
This way is easy to understand and load, but the problem is that its very slow
to load and very heavy to stock. The SCM file format is a kind of "compiled" ASE
file format, you retrieve each ASE information in the SCM, but packed in a
binary way, and this way, data are compressed and more easily recoverable.
To give you a little example, the SCM file
format is loaded between 50 and 100 times faster than the ASE file format, and
SCM file size is about 10 times compressed compared to ASE. Imagine you want to
create a game with a lot of object models, it'll be impossible for you to hold
them all in ASE file format, and players won't enjoy to wait while long
loadings.
Explanations
SCM, what it means, hehe, you'll probably
laugh, it means Sebseb Croco Model. It's a multi-object file format, like 3DS
file format, it's not a key-frames model like MD2. It contains every 3D
information of each objects, and everything you need to draw your object form,
and it also contains the texture coordinates and vertex/polygons normal vectors,
it also contains the objects names and color, like that, you can still render
good looking objects even if they aren't textured.
This format has been created to be very
easy to load, and you'll see how to do, this is a free file format and every
tools I made are free and OpenSource, like that, you won't be able to say you
can't load SCM object model, you'll have all the information needed to do it, in
the documentation and in tools source codes.
Specifications
Ahh, enfin. Well, there is the technical
specifications of the SCM file format, first, you'll have to declare some
specifics little structures to store data. The SCM file contains information
about each sub-objects in a "chunk" way.
Structures
There is two types of data structures used
by SCM, they are t_vertex which will contains vertex information and t_polygon
which will contains vertices indices. Look at what they look like.
typedef struct s_vertex
{
float v1;
float v2;
float v3;
}
t_vertex; |
This structure will hold every floating
point, and it'll be used for vertices, texture coordinates and both vertices and
polygons normal vectors.
Now, the polygons data structures, they
won't hold any floating points, but only indices to vertices data structures, to
represent a 3D polygon, we use 3x3 floating points, but not directly because
some points (vertices) can be re-used, so we stock every points into floating
points, and then to draw a polygon (three points), we only use 3 indices which
points to a floating point vertex, do you get it ?

Let's look what the t_polygon
structure look like :
typedef struct s_polygon
{
int p1;
int
p2;
int
p3;
}
t_polygon; |
You'll notice that both t_vertex and
t_polygon structures are 12 bytes sizeof(t_vertex) and sizeof(t_polygon) will
both give 12 on x86 architectures. You could use a #define SIZEOF_STRUCT 12, but
you shouldn't, it's not proper to do it because in case of data type length
change, this will make our SCM implementation wrong, so I advice you to declare
these structures and use the sizeof() macros with.
Macros
Well, I think I said enough for
structures, now let's see the defined values, there is two #define you have to
know. First, when you load a SCM file, you have to know if you're currently
working on a good SCM file or not, so for that, you have to check the file ID.
Then for the sub-objects name, I also told you they were contained into the SCM
file, so to read it properly, you have to know how many bytes are used to store
them.
To check the SCM file ID, you have this
define
#define SCM_ID
0x464D4353 |
This value is the hexadecimal equivalence
of "SCMF" for "SCM File", so reading the first int of the SCM file (the first 4
bytes) will tell you if they are equal to the ID. See this little sample code (I
admit the file has just opened) :
int
id;
FILE *fd;
...
fread(&id, sizeof(int), 1, fd);
if (id == SCM_ID)
valid_scm_file(fd);
else
fprintf(stderr,
"error: this file is not a valid SCM file\n");
... |
Now let's see for the objects name, there
is a #define to set a fixed size of bytes, for the moment, this value is 16,
it's not obligatory a multiple of 4 (sizeof(int)) but it's much better if it is.
Well, you have to know that the name of the file is always written with a
terminating null character, so if you set a pointer on the start of the string,
you can give it to any strings treatment functions, but in case you need to know
precisely the size to directly jump, the #define will help you.
So, for an example, if you just opened
your SCM file, and you want to jump directly to the vertices information, this
code you'll do it :
char *ptr;
...
ptr = buff_scm;
ptr += 4 * sizeof(int) + SIZEOF_NAME;
... |
Because there is 4 integers and the name
of the first object before the first vertices information of the SCM file. Well,
now we saw structures and macros, and that you start to have a little idea of
what is the SCM file format, we'll be able to go on to a more interesting part
of the specification.
Datagram
There is the more interesting part of the
specification I think, and also the one will make you understand more things,
and much better :)
The array is cut into two parts
4 bytes |
4 bytes |
SIZEOF_NAME bytes |
4 bytes |
4 bytes |
SCMF (ID) |
object number |
object name |
object id |
object color |
The following the the suit of the
previous array
4 bytes |
nvert
bytes |
4 bytes |
npoly
bytes |
4 bytes |
ntvert
bytes |
4 bytes |
ntpoly
bytes |
4 bytes |
nnormvert
bytes |
4 bytes |
nnormpoly
bytes |
nvert |
vertex
list
(t_vertex) |
npoly |
polygon
list
(t_polygon) |
ntvert |
texture
coordinates list
(t_vertex) |
ntpoly |
texture
coordinates polygon list
(t_polygon) |
nnormvert |
vertex
normal vector list
(t_vertex) |
nnormpoly |
polygon
normal vector list
(t_vertex) |
The orange part is the "header" part of
the SCM file, these information has to be read only one time, SCMF permit you to
know if the file is valid, and object number give you the number of objects there is
in your SCM file, like that you know how many memory to allocate.
The green part contains a blue and a
purple one, representing an object, and
is probably repeated in the file (in case you have a multiple object file). So in each
green part (object) you have a little object "header" that contains name, id and
color of the current object (blue part), every id are generated from 0, by incrementing, and
the color is originally set to 0xFFFFFFFF (white) but there is very good tools
to change it, we'll see that later.
Well, now see the lists (purple part), the nvert integer
give you the number of vertices there is in the vertex list, like that, you know
how much memory to allocate, but I warn you, it's nvert * sizeof(t_vertex)
bytes. Just repeat the same operation for the other lists. Be careful, if you
didn't generated some lists, for example, if you decided to not generate the
normal vectors, there will still be the integers to indicate the numbers, but
will be set to zero, so you'll have to test it before allocating memory.
Code
There is a little sample code to load the
data into memory, but you have the complete source code of the CScm class object
in the download section. You have to have two variable for that (class member
variables)
#define
SCM_ID 0x464D4353
#define SIZEOF_NAME 16
typedef struct s_vertex
{
float v1;
float v2;
float v3;
}
t_vertex;
typedef struct s_polygon
{
int p1;
int p2;
int p3;
}
t_polygon;
typedef struct s_object
{
char *name;
int id;
uchar color[4];
int
vertex_number;
t_vertex *vertex_list;
int
polygons_number;
t_polygon *polygons_list;
int
texvertex_number;
t_vertex *texvertex_list;
int
texpolygons_number;
t_polygon *texpolygons_list;
int
normals_vertex_number;
t_vertex *normals_vertex_list;
int
normals_polygons_number;
t_vertex *normals_polygons_list;
}
t_object; |
Above are the structures and defines to
use, I wrote you the t_object structure, but you can write it as you wish, it's
a data container and it's up to you to arrange it how it's better for yourself.
int _object_number;
t_object *_object_list; |
Just above are two variables that you'll
need to load your object file, I used them as a class member variables. And
then, two little and easy to understand functions.
int _read_object(t_object *obj, FILE *fd)
{
char buff_name[SIZEOF_NAME];
fread(buff_name, sizeof(char), SIZEOF_NAME, fd);
obj->name = strdup(buff_name);
fread(&(obj->id), sizeof(int), 1, fd);
fread(obj->color, sizeof(int), 1, fd);
fread(&(obj->vertex_number), sizeof(int), 1, fd);
if (obj->vertex_number > 0)
{
obj->vertex_list = (t_vertex*)malloc(obj->vertex_number
* sizeof(*(obj->vertex_list)));
if (obj->vertex_list == NULL)
return (0);
fread(obj->vertex_list, sizeof(t_vertex), obj->vertex_number, fd);
}
fread(&(obj->polygons_number), sizeof(int), 1, fd);
if (obj->polygons_number > 0)
{
obj->polygons_list = (t_polygon*)malloc(obj->polygons_number
* sizeof(*(obj->polygons_list)));
if (obj->polygons_list == NULL)
return (0);
fread(obj->polygons_list, sizeof(t_polygon), obj->polygons_number, fd);
}
fread(&(obj->texvertex_number), sizeof(int), 1, fd);
if (obj->texvertex_number > 0)
{
obj->texvertex_list = (t_vertex*)malloc(obj->texvertex_number
* sizeof(*(obj->texvertex_list)));
if (obj->texvertex_list == NULL)
return (0);
fread(obj->texvertex_list, sizeof(t_vertex), obj->texvertex_number, fd);
}
fread(&(obj->texpolygons_number), sizeof(int), 1, fd);
if (obj->texpolygons_number > 0)
{
obj->texpolygons_list = (t_polygon*)malloc(obj->texpolygons_number
* sizeof(*(obj->texpolygons_list)));
if (obj->texpolygons_list == NULL)
return (0);
fread(obj->texpolygons_list, sizeof(t_polygon), obj->texpolygons_number, fd);
}
fread(&(obj->normals_vertex_number), sizeof(int), 1, fd);
if (obj->normals_vertex_number > 0)
{
obj->normals_vertex_list =
(t_vertex*)malloc(obj->normals_vertex_number *
sizeof(*(obj->normals_vertex_list)));
if (obj->normals_vertex_list == NULL)
return (0);
fread(obj->normals_vertex_list, sizeof(t_vertex), obj->normals_vertex_number, fd);
}
fread(&(obj->normals_polygons_number), sizeof(int), 1, fd);
if (obj->normals_polygons_number > 0)
{
obj->normals_polygons_list =
(t_vertex*)malloc(obj->normals_polygons_number *
sizeof(*(obj->normals_polygons_list)));
if (obj->normals_polygons_list == NULL)
return (0);
fread(obj->normals_polygons_list, sizeof(t_polygon), obj->normals_polygons_number, fd);
}
return (1);
} |
Here is a function to read only one
object, it read it completely and store the data into the good buffers. It fills
the current t_object structure.
int LoadObject(char *file)
{
int i;
int id;
FILE *fd;
fd = fopen(file, "rb");
if (fd == NULL)
return (0);
fread(&id, sizeof(int), 1, fd);
if (id != SCM_ID)
{
fclose(fd);
return (0);
}
fread(&_object_number, sizeof(int), 1, fd);
if (_object_number <= 0)
{
fclose(fd);
return (0);
}
_object_list = (t_object*)malloc(_object_number * sizeof(*_object_list));
if (_object_list == NULL)
{
fclose(fd);
return (0);
}
for (i = 0; i < _object_number; i++)
{
if (_read_object(&_object_list[i], fd)
== 0)
return (0);
}
_current_polygon = 0;
_current_object = 0;
fclose(fd);
return (1);
} |
And this last function first read and
check the SCM file ID, read the number of object, allocate memory and then loop
on all objects and use for each one the previous function.
Sorry, I didn't colored the code, it
would be too long to do so... This code only load SCM object file into memory,
pretty easy right ? Sorry for the long code, and more it's a little useless
because it won't really help you to see how to do but I let it anyway. I don't put the
code to draw the object, but I told you the source code is available in the
download section, and it's the source code of
the SCM Viewer, so you'll find everything you need :) The SCM Viewer use
OpenGL as a graphic render support.
I told you about tools, you'll see on this
site some little, powerful and useful tools to modify objects color, objects
name, export or import objects color pattern, and most important, you'll have
the tool to generate SCM files from ASE files.
|
|