Hi,
I’m trying to design my own 3d engine with cubic beziers patches being the primary primative (as opposed to dealing directly with triangles) and I’m pretty familiar with techniques like forward dfferencing to render the patches. My problem is importing geometry from standard file formats like .OBJ (.OBJ is one of a number of formats, but it seemed like the easiest and most common to start with). I’ve read the file format specifications that are posted on www.wotsit.org but most modelling programs store all curves as complex curves rather than nicely defined control points. What I would like to know is how to take that info out of the file and derive the control points from it and, in the case of more complex curved surfaces, how to break the curves up into smaller cubic patches. If anyone knows how to do this, knows of an example/tutorial, or knows of another file format that is easier for this please let me know. Any help would be greatly appreciated.
-Daedalus
Daedalus
The best way to go about implemetning parametric surfaces into your program, would be to actually use the vertex information as the control points. This method would work best if implemented with bezier equations, I am unfamilier with how nurbs operate.
If you want to break up the curved surface into smaller groups, the .obj format breaks up all its triangle information into groups. Just seperate the groups into a smaller chunks.
I am unaware of any programs which export control point data, however their might be some.
I modified a 3DSMax (2.5 or 3.0) script that my friend wrote for gamasutra.com and made it export NURBs data. You can probably look at the MaxScript language and figure out how to get bezier patch data. The target format is called RTG and it’s pretty easy to parse.
-Joseph
here’s the MaxScript:
– RTGExport.ms
– This utility exports geometry from Max 2.5 into the Alias text-based RTG v1.8 format.
– The RTG (RealTimeGames) format is not very well supported, but it is so simple that
– writing support for it is easy.
– Original script was written by Wyeth Ridgway
– Modified by Joseph Laurino: added support for NURBS
utility RTGExport “Alias RTG v1.8 Export”
(
– Define variables that are visible to all functions in the utility
local ostream, tabs = “”
-- Define the GUI interface
group "Options"
(
checkbox cb_exportSelOnly "Export Selected Only"
checkbox cb_outputVertNorms "Output Vertex Normals" checked:true
checkbox cb_outputVertColors "Output Vertex Colors" checked:true
checkbox cb_outputPolyNorms "Output Polygon Normals" checked:true
checkbox cb_outputTexCoords "Output Texture Coords" checked:true
checkbox cb_outputHierarchy "Output Hierarchy" checked:true
)
button btn_export "Save As..." width:100
-- Now define the functions that the utility uses
-------------------------------------------------------------------------------------
-- This function exports a NURBSCVSurface object to the RTG file.
-- <nurbscvsurface>.uOrder : integer
-- <nurbscvsurface>.vOrder : integer
-- <nurbscvsurface>.numUKnots : integer
-- <nurbscvsurface>.numVKnots : integer
-- <nurbscvsurface>.numCVs : point2
function ExportNURBSCVSurface nurbsObj name =
(
-- Output the nurbs header
Format "
NURBS_START % uOrder% vOrder% numUKnots% numVKnots% numCVsU% numCVsV%
" name nurbsObj.uOrder nurbsObj.vOrder nurbsObj.numUKnots nurbsObj.numVKnots (nurbsObj.numCVs.x as Integer) (nurbsObj.numCVs.y as Integer) to stream
Format "
" to stream
for i = 1 to nurbsObj.numUKnots do
(
Format "UKNOT %
" (getUKnot nurbsObj i) to stream
)
Format "
" to stream
for i = 1 to nurbsObj.numVKnots do
(
Format "VKNOT %
" (getVKnot nurbsObj i) to stream
)
Format "
" to stream
for i = 1 to (nurbsObj.numCVs.x as Integer) do
(
for j = 1 to (nurbsObj.numCVs.y as Integer) do
(
controlVertex = (getCV nurbsObj i j )
Format "CV x % y % z % w %
" controlVertex.x controlVertex.y controlVertex.z controlVertex.weight to stream
)
)
Format "
NURBS_END %" name to stream
)
-------------------------------------------------------------------------------------
-- This function exports a geometry object to the RTG file.
function ExportMesh meshObj name =
(
-- Output the object header
Format "
OBJECT_START % v%" name meshObj.numVerts to stream
if cb_outputVertNorms.checked then
Format " n%" meshObj.numVerts to stream
if cb_outputTexCoords.checked then
Format " t%" meshObj.numtverts to stream
Format " p%
" meshObj.numFaces to stream
-- Vertex output is in local space
Format "VERTEX local
" to stream
if meshObj.numVerts > 0 then
(
for i = 1 to meshObj.numVerts do
(
vert = ((GetVert meshObj i)-meshObj.pos)
Format "% % % %
" (i-1) vert.x vert.y vert.z to stream
)
)
-- Vertex normals
if meshObj.numVerts > 0 and cb_outputVertNorms.checked then
(
Format "
NORMAL
" to stream
for i = 1 to meshObj.numVerts do
(
normal = GetNormal meshObj i
Format "% % % %
" (i-1) normal.x normal.y normal.z to stream
)
)
-- Texture coords
if meshObj.numTVerts > 0 and cb_outputTexCoords.checked then
(
Format "
TEXCOORD
" to stream
for i = 1 to meshObj.numTVerts do
(
uvw = GetTVert meshObj i
Format "% % % %
" (i-1) uvw.x uvw.y uvw.z to stream
)
)
-- Polygon
Format "
POLYGON
" to stream
if meshObj.NumFaces > 0 then
(
for i = 1 to meshObj.numFaces do
(
poly = GetFace meshObj i
Format “% 3 v % % %” (i-1) (poly.x as integer -1) (poly.y as integer -1) (poly.z as integer -1) to stream
– Specify vert normals
if cb_outputVertNorms.checked then
Format " n % % %" (poly.x as integer -1) (poly.y as integer -1) (poly.z as integer -1) to stream
– Tex coord
if cb_outputTexCoords.checked and meshObj.numTverts > 0 then
(
tvert = GetTVFace meshObj i
Format " t % % %" (tvert.x as integer -1) (tvert.y as integer -1) (tvert.z as integer -1) to stream
)
– Face normal
if cb_outputPolyNorms.checked then
(
normal = GetFaceNormal meshObj i
Format " N % % %" normal.x normal.y normal.z to stream
)
– Texture ref
Format " T 0
" to stream
)
)
Format "
OBJECT_END %
" name to stream
)
-------------------------------------------------------------------------------------
-- This function is called once per node in the scene.
-- A node in Max may be all sorts of things. We are only interested in geometry.
--
-- jyl: figuring out if a node is a NURBS surface is pretty tricky, I had to resort to an
-- ugly string in string search for "Surface"
-- hopefully in Max 3.0, I can determine the class type better!
--
function ExportNode node =
(
-- Create node and export class specific data
local surfaceString = findString node.name "Surface"
if SuperClassOf node == GeometryClass and surfaceString != undefined then
(
nurbSet = getNurbsSet node
if nurbSet.numObjects > 0 then
(
local nurbsObj = getObject nurbSet 1
if classOf nurbsObj == NURBSCVSurface then
(
ExportNURBSCVSurface nurbsObj node.name
)
)
)
else if SuperClassOf node == GeometryClass and ClassOf node == Editable_mesh then
ExportMesh node node.name
else if SuperClassOf node == GeometryClass then
(
-- Build a mesh out of this object and save it
local temp = copy node
convertToMesh temp
ExportMesh temp node.name
delete temp
)
else -- Not geometry.. could be a camera, light, etc.
return false
return true
)
-------------------------------------------------------------------------------------
-- This function recurses down the node hierarchy calling ExportNode for each node.
function RecursiveExportNode node =
(
if (ExportNode node) == false then
return false
-- Recurse children before writing this node
for child in node.children do
RecursiveExportNode child
)
-------------------------------------------------------------------------------------
-- Output the RTG file header.
function OutputHeader =
(
Format "OUTPUT_VERT_NORMS " to [img]http://www.opengl.org/discussion_boards/ubb/redface.gif[/img]stream
if cb_outputVertNorms.checked then ; Format "on
" to stream
else Format "off
" to stream
Format "OUTPUT_VERT_COLORS " to stream
if cb_outputVertColors.checked then ; Format "on
" to stream
else Format "off
" to stream
Format "OUTPUT_POLY_NORMS " to stream
if cb_outputPolyNorms.checked then ; Format "on
" to stream
else Format "off
" to stream
Format "OUTPUT_TEX_COORDS " to stream
if cb_outputTexCoords.checked then ; Format "on
" to stream
else Format "off
" to stream
Format "OUTPUT_HIERARCHY " to stream
if cb_outputHierarchy.checked then ; Format "on
" to stream
else Format "off
" to stream
Format "SHOW_INDEX_COUNTERS on
" to stream
Format "TEXTURE_MODE per_shader
" to stream
)
-------------------------------------------------------------------------------------
– A helper function to write indentation to the file.
function doTabs depth =
(
for x = 1 to depth do
(
Format " " to stream
)
)
-------------------------------------------------------------------------------------
– A function to output the hierarchical relationship of geometric objects.
function RecursiveWriteHierarchy node depth =
(
– Write this node
if SuperClassOf node == GeometryClass then
(
– Geometry info
doTabs (depth as integer -1)
Format "% G %
" depth node.name to stream
doTabs (depth+1)
Format "tran: % % %
" node.pos.x node.pos.y node.pos.z to stream
doTabs (depth+1)
rot = node.rotation as eulerangles
Format "rot: % % %
" rot.x rot.y rot.z to stream
doTabs (depth+1)
Format "scal: % % %
" node.scale.x node.scale.y node.scale.z to stream
doTabs (depth+1)
Format "sPiv: % % %
" node.pivot.x node.pivot.y node.pivot.z to stream
doTabs (depth+1)
Format "rPiv: % % %
" node.pivot.x node.pivot.y node.pivot.z to stream
)
– Pivot info
– Recurse to children
– for children in node.children do
– RecursiveWriteHierarchy children
)
-------------------------------------------------------------------------------------
– The highest level function called to export an RTG file.
function ExportRTG =
(
– Write the RTG header
Format "HEADER_TITLE Max 2.5 Real Time Game Output
" to stream
Format "HEADER_VERSION v1.8
" to stream
Format ("HEADER_DATE " + localtime + "
") to stream
Format "
" to stream
if cb_exportSelOnly.checked then
(
Format "NUMBER_OF_OBJECTS = %
" selection.count to stream
OutputHeader()
for node in selection do
ExportNode node
)
else
(
Format "NUMBER_OF_OBJECTS = %
" rootnode.children.count to stream
OutputHeader()
if cb_outputHierarchy.checked then
(
Format "
HIERARCHY_LIST HXP 1 top_level
" to stream
for node in rootnode.children do
RecursiveWriteHierarchy node 0
Format "END_HIERARCHY_LIST
" to stream
)
for node in rootnode.children do
RecursiveExportNode node
)
)
-------------------------------------------------------------------------------------
-- Open an prepare a file handle for writing.
function GetSaveFileStream =
(
fname = GetSaveFileName types:"Alias RTG (*.rtg)|*.rtg|All Files(*.*)|*.*|"
if fname == undefined then
return undefined
ostream = CreateFile fname
if ostream == undefined then
(
MessageBox "Couldn't open file for writing !"
return undefined
)
return ostream
)
-------------------------------------------------------------------------------------
-- This is the function called when the user activates the utility by pressing on
-- the export button. It opens the file and calls the export routine.
on btn_export pressed do
(
ostream = GetSaveFileStream()
if ostream != undefined then
(
ExportRTG()
close ostream
)
)
) – End RTGExport
opps, please convert the wierd smiley ; graphics to semi-colons!
-joseph
make that colons!
: to colons (i’m really sorry about this)
Wow, thanks for the code Joseph. I changed all the faces into ‘:’ and saves the file with a .ms extension. The only problem is that when I load up 3dsmax 3 and try to load the script nothing happens. I don’t get any errors, but I also don’t get any extra options under “save as” or “export”. I have no experience with Max Script, so I have no idea what could be wrong. You wouldn’t, by any chance, have any pointer on what I might have done wrong would you?
-Daedalus
well, I think I might have found the problem. It looks as thought the face art erased more than just a ‘:’
test:
:-o
I’m trying to figure out exactly what set of characters is missing.
-Daedalus
I made a link to the original script with instructions on how to intall it on my web site.
http://209.181.143.70/docs/NURBs.htm
-joseph
Thanks a lot Joseph. I finally managed to get the script working. You were right about the .RTG file format too. It is easy to understand. I spent a while, before I got the script working, looking for the file format documentation on the net and couldn’t find it anywhere. Luckily I didn’t need it.
-Daedalus
This is off topic but you should read this about ubb…
http://www.opengl.org/discussion_boards/ubb/faq.html