Extracting cubic beziers from .OBJ files

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