Page 1 of 2

Blender to CMOD Exporter

Posted: 05.03.2006, 14:34
by cartrite
Hi All,
I managed to put together a python script to export a blender file to cmod. It's a bit of a hack
but it produces a file that "looks" exactly like a cmod file produced by 3dstocmod. When I use cmodfix
to change it to binary I get an error: Block Name expected at line (x). Error in model file.
Anyone know why I might get this error. Everything "looks" right.

cartrite

Posted: 05.03.2006, 16:11
by selden
I think that improper nesting or too many end statements will generate that error message.

Try it with a very minimal object -- e.g. a cube with vertices only at its corners -- and post the results.

Posted: 05.03.2006, 16:32
by GlobeMaker
Hi Cartrite,

Here is a tiny example of a prism with 5 sides. You can see a picture of
the prism and download the .3ds version here:
http://www.celestiaproject.net/forum/viewtopic ... b7a37c53c7

#celmodel__ascii

material
diffuse 1 1 1
end_material

mesh
vertexdesc
position f3
normal f3
end_vertexdesc

vertices 24
0 0 0 0 -1 0
1000 0 0 0 -1 0
0 0 500 0 -1 0
0 0 500 0 -1 0
1000 0 0 0 -1 0
1000 0 500 0 -1 0
0 0 0 0 0 -1
0 500 0 0 0 -1
1000 500 0 0 0 -1
0 0 0 0 0 -1
1000 500 0 0 0 -1
1000 0 0 0 0 -1
0 0 500 0 0.707107 0.707107
1000 0 500 0 0.707107 0.707107
0 500 0 0 0.707107 0.707107
0 500 0 0 0.707107 0.707107
1000 0 500 0 0.707107 0.707107
1000 500 0 0 0.707107 0.707107
0 0 0 -1 0 0
0 0 500 -1 0 0
0 500 0 -1 0 0
1000 0 0 1 0 0
1000 500 0 1 0 0
1000 0 500 1 0 0

trilist 0 24
0 1 2 3 4 5 6 7 8 9 10 11
12 13 14 15 16 17 18 19 20 21 22 23

end_mesh

Posted: 05.03.2006, 16:41
by cartrite
Hi All,
Well I figured out why I got the error. 1 too many numbers in the trilist.
That got something to appear in Celestia. It also showed me how far I got to go to get this to work properly. What a MESS.:?

cartrite

Posted: 05.03.2006, 19:08
by cartrite
For any cmod guru's out there.

I created a simple model with 16641 vertices. That made 16384 faces.
I exported it as a waveobject > 3ds > cmod. The cmod file had 98304 vertices. I also exported directly to cmod with the script I created.
It had 65536 vertices. The reason is that the script writes a line for 4 verts
per face. So it has 4x vertices and the other method has 6x vertices.
Question is why 6x? And were are they comimg from?

cartrite

Posted: 05.03.2006, 20:17
by GlobeMaker
Hello Cartrite,

Please start small. You can start with the tiny prism I donated
earlier on this list. That will show you all you need to know to
make big objects with thousands of faces. Or make your own tetrahedron
to start small. A minimal object will teach you the basics first.

Each face is a triangle, not a rectangle. Two triangles have 6 vertexes.
Each rectangle is made from two triangles, so there are 6 vertexes in a
rectangle.

Here is one triangle :

0 0 0 0 -1 0
1000 0 0 0 -1 0
0 0 500 0 -1 0

Notice that the normal vector is the same on each line (0 -1 0)
That is because the three text lines are a group of points making one triangle.

Here is a second triangle

0 0 500 0 -1 0
1000 0 0 0 -1 0
1000 0 500 0 -1 0

These two triangles make a rectangle. The normal vectors for both
triangles point in the same direction (0 -1 0) . That shows that the two
triangles are in parallel planes or the same plane.


Conclusion
Starting with a 16000 triangle design is not starting small. A tetrahedron is
made from 4 triangles.

Posted: 05.03.2006, 21:38
by cartrite
Hi GlobeMaker,
Just to make sure we are on the same page. I'm trying to figure out the format and data layout of the cmod file. What you said though gave me a hint. Blender does have rectangle faces. At least the data is stored that way.
I will need to turn them into triangles somehow with the export code or someway. Here is the code I'm dealing with:

#!BPY

"""
Name: 'CMOD (.cmod)...'
Blender: 240
Group: 'Export'
Tooltip: 'cmod exporter'
"""
import Blender

def write_obj(filepath):
out = file(filepath, 'w')
object = Blender.Object.GetSelected()[0]
mesh = object.getData()
a = 0
b = 0
c = 0
d = 0

# file header need to add real materials vertexdesc is dangerous may not have uvcoords
out.write( '#celmodel__ascii\n\n' 'material\n' 'end_material\n\n' 'mesh\n' 'vertexdesc\n' 'position f3\n' 'normal f3\n' 'texcoord0 f2\n' 'end_vertexdesc\n\n' )

#counter for vertice desc
for face in mesh.faces:
for vert in face.v:
a +=1
out.write( 'vertices %i\n' % (a))

# writes a line for 4 vertices per face somehow I must write 6 vertices here.
for face in mesh.faces:
d = 0
for vert in face.v:
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (vert.co.x, vert.co.y, vert.co.z, vert.no.x, vert.no.y, vert.no.z,))
out.write( ' %.6f %.6f\n' % face.uv[d])
d +=1
out.write( '\n\ntrilist %i %i\n' % (b, a))
#write the triangle list 12 per line
while b < a:
if c <= 12:
if b <= a:
out.write( '%i ' % (b))
b +=1
c +=1
else:
out.write( '\n' )
else:
out.write( '\n' )
c = 0
out.write( '\n\nend_mesh' )
out.close()
Blender.Window.FileSelector(write_obj, "Export")


cartrite

Posted: 05.03.2006, 22:28
by selden
cartrite,

When you generate the list of vertices for a flat square surface, you only need to write the four corners in the vertex list.

In the trilist section is where you have to connect the four vertices as two triangles.

If the square looks like

Code: Select all

0+++1
|...|
|...|
2+++3

then the (abbreviated) vertex list of xyz coordinates might look something like

Code: Select all

0 1 0
1 1 0
0 0 0
1 0 0

and the trilist that draws it can be

Code: Select all

0 1 3   3 2 0

Which draws something like this:

Code: Select all

0+++1
\...|
.\..|
..\.|
....3

0
|\
| \
|  \
2+++3


(although the triangles are adjacent to one another, not separated as I've shown them.)

One discussion of such things can be found at
http://www.lepp.cornell.edu/~seb/celest ... .html#14.2
and the section above it documents the ASCII CMOD format.

Does this help at all?

Posted: 05.03.2006, 22:29
by GlobeMaker
Hi Cartrite,
Your program did not have enough comments for me to understand it. So
let's get to the preliminaries :

You can get a spec on CMOD format.

You can get a spec on Blender format

You can post one rectangle data in Blender format.

I never used Python, but it seems you left out parentheses. I guess there are
syntax errors, but I never used Python. Are you already skilled at Python?
_____________________________________________

But we do not need all that to make progress on a hack :

Assume four vertexes e f g h are in a square and the normal is known
for the square (ijk is the normal vector):

e f
g h

Make two triangles using clockwise direction:

efg
gfh

Make .cmod vertexes for each of 6 points in two triangles

e : xe ye ze i j k
f : xf yf gf i j k
g : xg yg zg i j k

g : xg yg zg i j k
f : xf yf zf i j k
h : xh yh zh i j k

These two triangles in .cmod format have the same shape as the original square. (Maybe counter clockwise is the right direction for vector math).

Posted: 06.03.2006, 01:08
by cartrite
Getting there. Everything is backwards. The normals seem to be flipped. The UV's seem to be mirrored. And still missing a lot of triangles. But Getting there.

Image

cartrite

Posted: 06.03.2006, 02:00
by cartrite
Selden, GlobeMaker,
Thank You This is what I'm trying. When Anim8or or any other program converts to 3ds, thats when this conversion to triangles must take place.

Python is very new to me. First time. It's hard to find the code (and syntax) that I need to use. What I posted above runs. The syntax is correct. (if that is the right version that is). This time, nothing fancy, just wrote the lines for verts 0,1,2 then 1,2,3. Now to figure out why all the negitive numbers became positive and vise versa. Well, getting there.

I was going to post this earlier but the internet is very slow tonight.

cartrite

Posted: 06.03.2006, 03:39
by cartrite
Selden, GlobeMaker,
I used your suggestion about triangles and it worked. Thank You Both. I used the verts in the order 012230. I also had to swap the y and z coords.
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[0].co.x, face.v[0].co.z, face.v[0].co.y, face.v[0].no.x, face.v[0].no.z, face.v[0].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[0])
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[1].co.x, face.v[1].co.z, face.v[1].co.y, face.v[1].no.x, face.v[1].no.z, face.v[1].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[1])
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[2].co.x, face.v[2].co.z, face.v[2].co.y, face.v[2].no.x, face.v[2].no.z, face.v[2].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[2])
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[2].co.x, face.v[2].co.z, face.v[2].co.y, face.v[2].no.x, face.v[2].no.z, face.v[2].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[2])
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[3].co.x, face.v[3].co.z, face.v[3].co.y, face.v[3].no.x, face.v[3].no.z, face.v[3].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[3])
out.write( '%.6f %.6f %.6f %.6f %.6f %.6f' % (face.v[0].co.x, face.v[0].co.z, face.v[0].co.y, face.v[0].no.x, face.v[0].no.z, face.v[0].no.y,))
out.write( ' %.6f %.6f\n' % face.uv[0])

Everything is still flipped but this is major progress. Here is a thumb. The normals are flipped. The texture was also flipped to account for the errors.

Image

Strange thing is that this same model, when exported as a waveobject > 3ds > cmod, well it has no problems and sits on the planet right were it belongs.
cartrite

Posted: 06.03.2006, 03:42
by Chuft-Captain
Hi,

Sorry to dive in with beginners questions, but I can't understand much of this thread at all. I was wondering if someone could explain to me the differences between 3DS and CMOD forms.

What are the pros and cons of the 2 file-formats?
If celestia is able to display both formats, then why bother converting to CMOD at all. This seems like a lot of fiddly work.
What's the big advantage of CMOD?

Is CMOD format an industry standard, or proprietary to Celestia?

Cheers

Posted: 06.03.2006, 04:54
by cartrite
I may be wrong, but I believe that cmod is Celestia's native model format.
I think that I read somewhere that cmod stands for Celestia Model.
As I come to realize today, cmod is big on triangles. So is 3ds.

The programs that I have can only export small 3ds files. That is why I am trying to write a export script to cmod format because cmod files can be very large.

I created this model with Blender and exported it to cmod with Blender and and had it running in Celestia in about 10 minutes. It has 131072 faces. It would have taken me more then a day to chop it up and export it the old way of .obj > 3ds > cmod > text editor > modfix.

Image

cartrite

Posted: 06.03.2006, 09:24
by Chuft-Captain
cartrite wrote:I may be wrong, but I believe that cmod is Celestia's native model format.
I think that I read somewhere that cmod stands for Celestia Model.
I read somewhere since posting my question that Chris created the CMOD format, so you're not wrong. :)
cartrite wrote:The programs that I have can only export small 3ds files. That is why I am trying to write a export script to cmod format because cmod files can be very large.
I've exported models with 60,000 or so faces to 3DS format from Anim8tor. Is there some limit that you have discovered in Anim8tor that I perhaps haven't reached yet? If so, what's this limit? Best to know before I reach it I guess!

I don't quite understand the process you're using. I assume you've started with a 131,000 face model in Anim8tor or Blender and you've found you cannot export it to 3DS because it's too large. I gather that in the past you've exported larger models in several smaller parts to 3DS form and then stitched them together somehow.

So if it can't be exported, how does your script get at it in order to convert it? Does it access an .an8 (or blender) project file directly?

BTW: What's a waveobject ?

Posted: 06.03.2006, 10:38
by cartrite
Hi Chuft-Captain,
It's been my experience that Blender, although it exports 3ds, produces files that can't be used in Celestia because the uvcoords are lost. Anim8tor will not export a 3ds that has more then 65,000 triangles. Wings has problems too.

This script exports a .blend file to cmod. You can say it's in it's development phase. It was written yesterday, has a lot of problems, and needs some improvement. But here is a working copy in case anyone wants to play with it.. Beware that the word formatting is lost when it is pasted into this thread and I think that python requires indenting under if, for, and while statements. This may be unusable without proper formatting.Sorry.


Edit: Keep in mind that there is NO texcoord check. If you try to export a model without UV Face coordinates the script fails. Also this exporter was created to output the data for faces with 4 vertices. If your model has more or less it may not work properly or may fail to run at all. I guess you could say it's in a pre alpha stage.



EDIT2:I found a dangerous bug in this code. If one forgets to change the name of the file before exporting, the model file will be OVERWRITTEN without warning.
I changed the code to add the ext cmod automaticly. I'm working on a warning if the file names are the same. For now at least the original file will not be changed.



Code: Select all

#!BPY

"""
Name: 'CMOD (.cmod)...'
Blender: 240
Group: 'Export'
Tooltip: 'cmod exporter'
"""
import Blender
import sys

def write_obj(filepath):
        out = file(filepath, 'w')
        object = Blender.Object.GetSelected()[0]
        mesh = object.getData()
        a = 0
        b = 0
        c = 0
        d = 0

        # file header need to add real materials vertexdesc is dangerous may not have uvcoords
        out.write( '#celmodel__ascii\n\n' 'material\n' 'end_material\n\n' 'mesh\n' 'vertexdesc\n' 'position f3\n' 'normal f3\n' 'texcoord0 f2\n' 'end_vertexdesc\n\n' )

        #counter for vertice desc
        #This part goes thru the list twice counting for a the verts 1,2,3 and 0,1,2
        for face in mesh.faces:
            d = 0
            for vert in face.v:
                if d <= 0:
                   d +=1
                else:
                   a +=1
                   d +=1
            d = 0
        for face in mesh.faces:
            for vert in face.v:
                if d >= 3:
                   d +=1
                else:
                   a +=1
                   d +=1
            d = 0   
        out.write( 'vertices %i\n' % (a))

        # writes a line for verts 0,1,2 and 1,2,3.
        for face in mesh.faces:
                   out.write( '%f %f %f %f %f %f' % (face.v[0].co.x, face.v[0].co.y, face.v[0].co.z, face.v[0].no.x, face.v[0].no.y, face.v[0].no.z,))
                   out.write( ' %f %f\n' % face.uv[0])
                   out.write( '%f %f %f %f %f %f' % (face.v[1].co.x, face.v[1].co.y, face.v[1].co.z, face.v[1].no.x, face.v[1].no.y, face.v[1].no.z,))
                   out.write( ' %f %f\n' % face.uv[1])
                   out.write( '%f %f %f %f %f %f' % (face.v[2].co.x, face.v[2].co.y, face.v[2].co.z, face.v[2].no.x, face.v[2].no.y, face.v[2].no.z,))
                   out.write( ' %f %f\n' % face.uv[2])
                   out.write( '%f %f %f %f %f %f' % (face.v[2].co.x, face.v[2].co.y, face.v[2].co.z, face.v[2].no.x, face.v[2].no.y, face.v[2].no.z,))
                   out.write( ' %f %f\n' % face.uv[2])
                   out.write( '%f %f %f %f %f %f' % (face.v[3].co.x, face.v[3].co.y, face.v[3].co.z, face.v[3].no.x, face.v[3].no.y, face.v[3].no.z,))
                   out.write( ' %f %f\n' % face.uv[3])
                   out.write( '%f %f %f %f %f %f' % (face.v[0].co.x, face.v[0].co.y, face.v[0].co.z, face.v[0].no.x, face.v[0].no.y, face.v[0].no.z,))
                   out.write( ' %f %f\n' % face.uv[0])
        out.write( '\n\ntrilist %i %i\n' % (b, a))
        #write the triangle list 12 per line
        while b < a:
            if c <= 12:
               if b <= a:
                  out.write( '%i ' % (b))
                  b +=1
                  c +=1
               else:
                  out.write( '\n' )
            else:
                out.write( '\n' )
                c = 0
        out.write( '\n\nend_mesh' )               
        out.close()
Blender.Window.FileSelector(write_obj, "Export", Blender.sys.makename(ext='.cmod' ))



Good Luck,

cartrite

Posted: 06.03.2006, 11:31
by Chuft-Captain
cartrite wrote:Beware that the word formatting is lost when it is pasted into this thread and I think that python requires indenting under if, for, and while statements.[/color] This may be unusable without proper formatting.Sorry.

Cartrite,
FYI: You can preserve formatting when posting your code by selecting it, and then pressing the <Code> button (next to the <Quote> button when creating the post. (or alt+C)

You may like to edit your original post, although it doesn't actually matter because, even though not displayed, your formatting characters are still in the post, and anyone can get at it by just pressing the <quote> button and then cut and paste.

Actually, why don't I just do it for you..

Code: Select all

#!BPY

"""
Name: 'CMOD (.cmod)...'
Blender: 240
Group: 'Export'
Tooltip: 'cmod exporter'
"""
import Blender

def write_obj(filepath):
        out = file(filepath, 'w')
        object = Blender.Object.GetSelected()[0]
        mesh = object.getData()
        a = 0
        b = 0
        c = 0
        d = 0

        # file header. I need to add real materials data here, texcoord switch, vertexdesc is dangerous, may not have uvcoords
        out.write( '#celmodel__ascii\n\n' 'material\n' 'end_material\n\n' 'mesh\n' 'vertexdesc\n' 'position f3\n' 'normal f3\n' 'texcoord0 f2\n' 'end_vertexdesc\n\n' )

        #counter for vertice desc
        #This part goes thru the list twice counting for a the verts 1,2,3 and 0,1,2
        for face in mesh.faces:
            d = 0
            for vert in face.v:
                if d <= 0:
                   d +=1
                else:
                   a +=1
                   d +=1
            d = 0
        for face in mesh.faces:
            for vert in face.v:
                if d >= 3:
                   d +=1
                else:
                   a +=1
                   d +=1
            d = 0   
        out.write( 'vertices %i\n' % (a))

        # for each face, writes a line for verts 0,1,2 and 2,3,0. Decided to keep it simple with hard coded indexes
        for face in mesh.faces:
                   out.write( '%f %f %f %f %f %f' % (face.v[0].co.x, face.v[0].co.y, face.v[0].co.z, face.v[0].no.x, face.v[0].no.y, face.v[0].no.z,))
                   out.write( ' %f %f\n' % face.uv[0])
                   out.write( '%f %f %f %f %f %f' % (face.v[1].co.x, face.v[1].co.y, face.v[1].co.z, face.v[1].no.x, face.v[1].no.y, face.v[1].no.z,))
                   out.write( ' %f %f\n' % face.uv[1])
                   out.write( '%f %f %f %f %f %f' % (face.v[2].co.x, face.v[2].co.y, face.v[2].co.z, face.v[2].no.x, face.v[2].no.y, face.v[2].no.z,))
                   out.write( ' %f %f\n' % face.uv[2])
                   out.write( '%f %f %f %f %f %f' % (face.v[2].co.x, face.v[2].co.y, face.v[2].co.z, face.v[2].no.x, face.v[2].no.y, face.v[2].no.z,))
                   out.write( ' %f %f\n' % face.uv[2])
                   out.write( '%f %f %f %f %f %f' % (face.v[3].co.x, face.v[3].co.y, face.v[3].co.z, face.v[3].no.x, face.v[3].no.y, face.v[3].no.z,))
                   out.write( ' %f %f\n' % face.uv[3])
                   out.write( '%f %f %f %f %f %f' % (face.v[0].co.x, face.v[0].co.y, face.v[0].co.z, face.v[0].no.x, face.v[0].no.y, face.v[0].no.z,))
                   out.write( ' %f %f\n' % face.uv[0])
        out.write( '\n\ntrilist %i %i\n' % (b, a))
        #writes the triangle list 12 per line
        while b < a:
            if c <= 12:
               if b <= a:
                  out.write( '%i ' % (b))
                  b +=1
                  c +=1
               else:
                  out.write( '\n' )
            else:
                out.write( '\n' )
                c = 0
        out.write( '\n\nend_mesh' )               
        out.close()
Blender.Window.FileSelector(write_obj, "Export")


Cheers
CC

Posted: 06.03.2006, 12:20
by GlobeMaker
Congratulation Cartrite!
You are doing valuable work. CMOD format has the advantage over .3ds
format because it gives smoother models without the seams from .3ds.
When I convert .stl files to .3ds files with Accutrans3D, the model is broken up into
several sections, leaving ugly seams between model sections. When the .3ds is
converted to .cmod, the seams go away.

I want to applaud your efforts to write that program to output .cmod files of any size.
This is a technical utility which Celestians can use and expand upon for a
long time. Your pictures are looking very good. I suggest that you also look
at Mars images without textures, so you can check the reasonableness of
the normals.

Please tell me if your pictures are from MGS MOLA 16 points per degree.
Do you cut the model with Blender so only a part of Mars is used?
Is this cutting done with precision or approximately? Precision cutting would
allow tiles of Mars to fit together with exact matching of edge points on
adjacent tiles.
How big is the file size of the model shown? What frame rate exists for the
images you are posting?

Conclusion
Good work. This is a useful program you have written.

Posted: 06.03.2006, 19:48
by cartrite
Chuft-Captain,
So thats how thats done. Thanks I'll try that next time. I never picked up on a lot of things that this forum can do.

Hi GlobeMaker,
Thanks again for the help yeterday, I was stuck for a while.
The file I used for noise was megt0n270hb.img and 128 pixels from megt44s270hb.img. They are 128 points per degree
However I only had 131072 Square faces. The image is 11520x5760.
So I don't have a face for every point. What I do is use another python script written by Toti called Z-Fold. It changes a plane into a sphere after adjusting the vertice hieght according to the radius and max height. Then I map the UV's. Cutting. No. Not this time. What I do here is add a plane and subdivide to 16 faces. Each face covers an area of 90 degrees by 45 degrees. I select the square I want and delete the rest. Then I subdivide to get 4 squares from that. I chose 1 square that had coordinates 270w 0n 22.5s 315e. Subdivided again. 4 faces. erease the middle vertices and create 2 rectangle faces 1x2.
Then subdivided to 131072 faces. The image was loaded. And in the toolbar were the texture was added there is a section to map input. Here I offset the x and y by .25 and size the x and y by 4. This allows me to use the section of the Mars height map that cooresponds with the coordinates I'm using. Apply noise. Then run Z-Fold in Blender 2.36. I use this version because it runs 10 times faster then 2.40. When its finished I map the UV's. This sounds like a lot but it goes very quickly. Alot of things can be done with a keystroke.

The file size is 2048x1024. Texture that is. I used to cut the finished model into many peices. No precision. I overlapped everything. As long as
the model was done at once, No seems would show. I did this so I can export it. But this was a PAIN. That's why I wrote this script.

There are still some problems. I'm not sure what the causes are. I did find that the normals are no longer reversed with a model done right side up. But I also changed the code ( %.6f was replaced with %f ). So mabey that was it.

Frame rates are slow. About 7 onthe highend and less then 1 on the low.

When I tried to do the models seperatly, I found it impossible for me get anything to match up. I gave up on that one.

cartrite

Posted: 07.03.2006, 02:15
by cartrite
I added a patch to the exporter script that I thought was important. It was the last line. The following explains:

EDIT2:I found a dangerous bug in this code. If one forgets to change the name of the file before exporting, the model file will be OVERWRITTEN without warning.
I changed the code to add the ext cmod automaticly. I'm working on a warning if the file names are the same. For now at least the original file will not be changed.

If you want to use this make sure you copy the one from my post. Not ChuftCaptains.

cartrite