.: SCM :.

 
 

 

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.

#define SIZEOF_NAME     16

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.

 

 
 

webmaster : sebseb@securesphere.net

.: HOME :.

 - Home

 - Old News

 

.: PROGRAMS :.

 - Graphic (10)

 - Network (7)

 - Games (2)

 - System (13)

 - Crypto (3)

 - Misc (3)

 - Game Programming Section

 - SCM Section

 

.: TUTORIALS :.

 - No tutorials yet

 

.: MISC :.


CrocoLib

CrocoSkinner
 

.: AUTHOR :.

 - My Curriculum-Vitae
 

.: LINKS :.

 - OpenGL

 - GameDev

 - SecureSphere

 - Warcraft3 FuckerFou

 - CaraibesSunshine

 - Ambria