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 |
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.
| _object_number; _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.