The page contains code to write the contents of a SubSpace(3,DoubleFloat) structure to a Wavefront .OBJ file. I have also included documentation below.
For documentation about related axiom domains and other structures like SubSpace see this page.
Overview
I would like to be able to output a draw 3D image to a standard 3D file format rather than the x-window. Axiom/Fricas does have the ability to write to 'pixmap','bitmap','postscript' and 'image' files, however most of these formats flattern the 3D geometry information to a 2D image or are quite out-of-date file formats.
I want a format which I can import into a 3D editor like blender. Also it must be able to output any geometry, either produced by draw/plot or makeObject or built from primitives. For instance so that a plot can be exported together with axes and grid.
I am looking at creating two graphics output writers (because they would have complimentary capabilities):
- A Wavefront .OBJ file - Produces a very simple file which is supported by virtually every 3D editor.
- A X3D file - more complicated and can do things like text and colour which are harder for .obj files.
How to Use
We first create an .obj file as follows:
(1) -> )library EXP3D Export3D is now explicitly exposed in frame frame1 Export3D will be automatically loaded when needed from /home/martin/EXP3D.NRLIB/EXP3D (1) -> writeObj(subspace(makeObject(x*x-y*y,x=-1..1,y=-1..1)),"myfile.obj") Compiling function %B with type (DoubleFloat,DoubleFloat) -> DoubleFloat Type: Void |
For information about how to create other plots or geometry built from primitives see this tutorial.
This has created a file called myfile.obj in your active directory, we can now edit it using a 3D editor such as Blender.
We can then start Blender (or your 3D editor of choice) and import the file (in this case myfile.obj). We can then rotate, zoom, change its appearance, combine with other graphs, add scales or any other annotation required. |
|
Write to .OBJ file
This code writes the contents of a SubSpace(3,DoubleFloat) structure to a Wavefront .OBJ file.
)abbrev package EXP3D Export3D ++ Author: Martin Baker ++ Date: June, 2010 ++ Description: ++ This package provides support for exporting SubSpace and ++ ThreeSpace structures to files. EF ==> Expression Float SBF ==> SegmentBinding Float DF ==> DoubleFloat I ==> Integer PI ==> PositiveInteger NNI ==> NonNegativeInteger STR ==> String Export3D(): with writeObj:(SubSpace(3,DoubleFloat),String) -> Void ++ writes 3D SubSpace to a file in Wavefront (.OBJ) format == add -- return list of indexes -- assumes subnodes are leaves containing index faceIndex(subSp: SubSpace(3,DoubleFloat)):List NNI == faceIndexList:List NNI := [] for poly in children(subSp) repeat faceIndexList := cons(extractIndex(poly),faceIndexList) reverse faceIndexList -- called if this component contains a single polygon -- write out face information for Wavefront (.OBJ) 3D file format -- one face per line, represented by list of vertex indexes writePolygon(f1:TextFile,curves: List SubSpace(3,DoubleFloat)):Void == faceIndexList:List NNI := [] for curve in curves repeat faceIndexList := append(faceIndexList,faceIndex(curve)) -- write out face information for Wavefront (.OBJ) 3D file format -- one face per line, represented by list of vertex indexes s:String := "f " for i in faceIndexList repeat s:=concat(s,string(i))$String s:=concat(s," ")$String writeLine!(f1,s) -- called if this component contains a mesh, the mesh will be rendered -- as quad polygons. -- write out face information for Wavefront (.OBJ) 3D file format -- one face per line, represented by list of vertex indexes writeMesh(f1:TextFile,curves: List SubSpace(3,DoubleFloat)):Void == meshIndexArray:List List NNI := [] for curve in curves repeat -- write out face information for Wavefront (.OBJ) 3D file format -- one face per line, represented by list of vertex indexes meshIndexArray := cons(faceIndex(curve),meshIndexArray) meshIndexArray := reverse meshIndexArray rowLength := #meshIndexArray colLength := #(meshIndexArray.1) for i in 1..(rowLength-1) repeat for j in 1..(colLength-1) repeat --s1:String := concat["row ",string(i)," col ",string(j)] --writeLine!(f1,s1) s:String := concat["f ",string((meshIndexArray.i).j)," ",_ string((meshIndexArray.(i+1)).j)," ",_ string((meshIndexArray.(i+1)).(j+1))," ",_ string((meshIndexArray.i).(j+1))] writeLine!(f1,s) -- this writes SubSpace geometry to Wavefront (.OBJ) 3D file format -- reqires SubSpace to contain 3 or 4 dimensional points over DoubleFloat -- to export a function plot try: -- writeObj(subspace(makeObject(x*x-y*y,x=-1..1,y=-1..1)),"myfile.obj") -- colour dimension is ignored -- no normals or texture data is generated writeObj(subSp: SubSpace(3,DoubleFloat), filename:String):Void == f1:TextFile:=open(filename::FileName,"output") writeLine!(f1,"# mesh generated by axiom") -- write vertex data verts := pointData(subSp) for v in verts repeat #v < 3 => error "Can't write OBJ file from 2D points" writeLine!(f1,concat(["v ",unparse(convert(v.1)@InputForm)," ",_ unparse(convert(v.2)@InputForm)," ",_ unparse(convert(v.3)@InputForm)])$String) for component in children(subSp) repeat curves := children(component) if #curves < 2 then sayTeX$Lisp "Can't write point or curve to OBJ file" --writeLine!(f1,"new component") if #curves > 1 then if numberOfChildren(curves.1) = 1 then writePolygon(f1,curves) if numberOfChildren(curves.1) > 1 then writeMesh(f1,curves) close! f1 |
Acknowledgement: I have borrowed elements from gnuDraw here as suggested by Bill Page.
Write to X3D
There seem to be many structures in axiom to implement Open Inventor (IVNodeCategory, RenderTools, IVSimpleInnerNode, IVSeparator, IVGroup, IVCoordinate3, IVQuadMesh, IVIndexedLineSet, IVUtilities).
I can't find any documentation about using Open Inventor structures from Axiom, apart from the discussion on this thread:
http://lists.gnu.org/archive/html/axiom-developer/2002-11/msg00151.html
Other Options
I would like to do this because there are limitations in what can be done with the built-in 3D viewer and dedicated 3D editors (such as 'blender') are much more powerful (does it make sense that a computer algebra program also tries to be a 3D editor?). An additional advantage is that it would work on computers without x-window.
However I can't work out exactly how to do this. The options I am considering are:
- Writing a stand alone file converter to convert the 'data' file generated by 'write' into a standard 3D file format that can be read by 3D editors such as a Wavefront .OBJ file. Although, at first glimpse, the format of this file looks promising I can't work out its exact format.
- Add a modified 'write' function to 'ThreeDimensionalViewport' (in view3D.spad). Unfortunately most of the work of the write function is done by lisp calls like: sendI(VIEW,typeVIEW3D)$Lisp so I would have to write it without help from existing code.
Axiom/Fricas does have the ability to write to 'pixmap','bitmap','postscript' and 'image' files as follows:
(1) -> viewWriteAvailable() (1) ["PIXMAP","BITMAP","POSTSCRIPT","IMAGE"] Type: List(String) (2) -> viewWriteDefault() (2) [] Type: List(String) (3) -> v := draw(x*x-y*y,x = -1..1,y= -1..1) Compiling function %B with type (DoubleFloat,DoubleFloat) -> DoubleFloat Transmitting data... (3) ThreeDimensionalViewport: "-1*y^2+x^2" Type: ThreeDimensionalViewport (4) -> write(v,"testDraw","image") (4) "testDraw" Type: String |
This creates a directory 'testDraw.VIEW' containing 3 files:
data
image.bm
image.xpm
But I want a format which I can inport into blender.
Discussion
See these threads on FriCAS forum
- How do I draw 3D image to a standard 3D file format
- Planning new release (discussion about use of unparse)