view .step file (or .iges) / convert .step to .obj

Dear all,

I made a 3D CAD drawing in Pro/Engineer. I tried to export to IGES, STEP and Wavefront Obj - however, the obj-file was ugly, because I think it only exported the solids (and not the surfaces). The iges/step-file is much better, when I import it into another tool, however I don’t know how to read these (and display them) into a very simple openGL application…

I would like to ask for a really really simple example/tutorial code, so I can view .step file (or .iges) or - maybe convert .step/.iges to .obj (whice I can view)…

I’ve been googling around to find some example code (preferably, that compiles on linux and is public domain/freeware or free for noncommercial/academic use), but the only thing I came across was/is Nate Robins *.obj-reader tool…

Please help, if you can. All suggestions/hints are more than welcome.

Thank you.

assimp is a popular library for dealing with 3D files. It compiles and works in linux. IGES and STEP are not supported but Wavefront Obj is.

hmmm… Is that better than the Nate Robins *.obj-reader tool, if I only read obj-files?

Ideally, I would like to read some other file formats (iges/step) - if not possible, then I’ll try some various ways of converting my files into wavefront obj, and then Nate Robins code would work for me…

BTW: I downloaded assimp and compiled it… Only found a single executable:

assimp/bin$ ./assimp help
assimp <verb> <parameters>

verbs:
info - Quick file stats
listext - List all known file extensions available for import
knowext - Check whether a file extension is recognized by Assimp
export - Export a file to one of the supported output formats
listexport - List all supported export formats
exportinfo - Show basic information on a specific export format
extract - Extract embedded texture images
dump - Convert models to a binary or textual dump (ASSBIN/ASSXML)
cmpdump - Compare dumps created using ‘assimp dump <file> -s …’
version - Display Assimp version

Use ‘assimp <verb> --help’ for detailed help on a command.

hmmm… Don’t understand how that can convert anything… Seems like it should be linked to my own programs… I was expecting some GUI to show up…?

A quick google search “iges opengl” lead to gcad3d Other than that maybe others have a better idea of where/how to read/convert iges/step files into openGL. With the example I was able to load into the gui a block.stp file and save as block.obj. Then I could view it with assimp ie ./samplegl block.obj (see below for how to get samplegl)

I apologize I lead you down a wrong path since assimp does not have iges/step support.

The assimp link shows many more formats than just obj. I have found that Nate Robbins obj reader fails with many valid obj files. Assimp reads dxf format – can you generate dxf instead of iges/step with your CAD tool?

The bin/assimp executable is a basic commandline tool that you can use to get information about some 3D file (provided it is one of its supported formats)

assimp is a library that you link with. There is not a GUI for linux. Some examples of how to use it are in the downloaded "samples" folder.

For example


cd samples/SimpleOpenGL
make
./samplegl ../../test/models/OBJ/spider.obj

will show a rotating spider.obj object read from disk file. Reading the code shows how to use the library.

Ok, thank you…

Regarding: “gcad3d” : I tried it, but it crashed when I tried to load a .step file. I tried approx. 6-8 .iges files and none of them worked… Nothing happend, except error message telling me that it could not rescale… I tried to click rescale, but couldn’t see anything… hmmm.

BTW: I just tried this: http://automagically.de/g3dviewer/#requirements - however, I cannot compile the last program:



G3DViewer/g3dviewer-0.2.99.4/src/model.c:69: undefined reference to `g_type_check_instance_cast’

G3DViewer/g3dviewer-0.2.99.4/src/model.c:69: undefined reference to `gtk_window_set_title’

G3DViewer/g3dviewer-0.2.99.4/src/model.c:70: undefined reference to `g_free’
… etc…

That part of the program is from 2003… I don’t know if anyone has any ideas about that…

I then followed your fine suggestion and installed assimp - first the svn-version… The sample didn’t work… Then I installed latest release, 2.0 I think - the sample (samples/SimpleOpenGL) wouldn’t compile by itself. I then added -lGLU compiler flag and it worked (took quite some time to compile, I didn’t expect so much, anyway…)!

So, now I can see the rotating spider… Great…

I then tried to type:

./samplegl …/…/…/…(…blabla)…/myTestfile.stl

And that seemed to work nicely… I then tried another .obj-file - it crashed with a segmentation fault… Then I tried the step-file: “No suitable reader found for the file format of file”, but that’s also what you wrote… Same for an iges-file…

I then tried a .dxf-file - it seemed to work, but the imported object was turned upside-down…

Now I would like to get very close to my objects, using mouse/keyboard - is that relatively easy, so I can check if the details are ok?

Thank you very much for your hints/help so far!

Are you on ubuntu? I was able to get the g3dviewer you mentioned to install with


sudo apt-get install g3dviewer

To get zoom, pan, rotate for example download gltZpr0.4.tgz and copy zpr.c and zpr.h into samples/SimpleOpenGL/gltZpr/

and compile following modified Sample_SimpleOpenGL_zpr.c


gcc -o samplegl -I../../include  -s Sample_SimpleOpenGL_zpr.c gltZpr/zpr.c -L../../bin/gcc -Wl,-Bstatic -lassimp -Wl,-Bdynamic -lstdc++ -lglut -lGL -lGLU
./samplegl <your 3d file>
hold middle mouse button and move up/down to zoom (may appear blank until do this)


// gcc -o samplegl -I../../include  -s Sample_SimpleOpenGL_zpr.c gltZpr/zpr.c -L../../bin/gcc -Wl,-Bstatic -lassimp -Wl,-Bdynamic -lstdc++ -lglut -lGL -lGLU
// ----------------------------------------------------------------------------
// Simple sample to prove that Assimp is easy to use with OpenGL.
// It takes a file name as command line parameter, loads it using standard
// settings and displays it.
//
// If you intend to _use_ this code sample in your app, do yourself a favour 
// and replace immediate mode calls with VBOs ...
//
// The vc8 solution links against assimp-release-dll_win32 - be sure to
// have this configuration built.
// ----------------------------------------------------------------------------

//#include "GL/glut.h"
#include "gltZpr/zpr.h"

// assimp include files. These three are usually needed.
#include "assimp.h"
#include "aiPostProcess.h"
#include "aiScene.h"

// the global Assimp scene object
const struct aiScene* scene = NULL;
GLuint scene_list = 0;
struct aiVector3D scene_min, scene_max, scene_center;

// current rotation angle
static float angle = 0.f;

#define aisgl_min(x,y) (x<y?x:y)
#define aisgl_max(x,y) (y>x?y:x)

// ----------------------------------------------------------------------------
void get_bounding_box_for_node (const struct aiNode* nd, 
	struct aiVector3D* min, 
	struct aiVector3D* max, 
	struct aiMatrix4x4* trafo
){
	struct aiMatrix4x4 prev;
	unsigned int n = 0, t;

	prev = *trafo;
	aiMultiplyMatrix4(trafo,&nd->mTransformation);

	for (; n < nd->mNumMeshes; ++n) {
		const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];
		for (t = 0; t < mesh->mNumVertices; ++t) {

			struct aiVector3D tmp = mesh->mVertices[t];
			aiTransformVecByMatrix4(&tmp,trafo);

			min->x = aisgl_min(min->x,tmp.x);
			min->y = aisgl_min(min->y,tmp.y);
			min->z = aisgl_min(min->z,tmp.z);

			max->x = aisgl_max(max->x,tmp.x);
			max->y = aisgl_max(max->y,tmp.y);
			max->z = aisgl_max(max->z,tmp.z);
		}
	}

	for (n = 0; n < nd->mNumChildren; ++n) {
		get_bounding_box_for_node(nd->mChildren[n],min,max,trafo);
	}
	*trafo = prev;
}

// ----------------------------------------------------------------------------

void get_bounding_box (struct aiVector3D* min, struct aiVector3D* max)
{
	struct aiMatrix4x4 trafo;
	aiIdentityMatrix4(&trafo);

	min->x = min->y = min->z =  1e10f;
	max->x = max->y = max->z = -1e10f;
	get_bounding_box_for_node(scene->mRootNode,min,max,&trafo);
}

// ----------------------------------------------------------------------------

void color4_to_float4(const struct aiColor4D *c, float f[4])
{
	f[0] = c->r;
	f[1] = c->g;
	f[2] = c->b;
	f[3] = c->a;
}

// ----------------------------------------------------------------------------

void set_float4(float f[4], float a, float b, float c, float d)
{
	f[0] = a;
	f[1] = b;
	f[2] = c;
	f[3] = d;
}

// ----------------------------------------------------------------------------
void apply_material(const struct aiMaterial *mtl)
{
	float c[4];

	GLenum fill_mode;
	int ret1, ret2;
	struct aiColor4D diffuse;
	struct aiColor4D specular;
	struct aiColor4D ambient;
	struct aiColor4D emission;
	float shininess, strength;
	int two_sided;
	int wireframe;
	int max;

	set_float4(c, 0.8f, 0.8f, 0.8f, 1.0f);
	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_DIFFUSE, &diffuse))
		color4_to_float4(&diffuse, c);
	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, c);

	set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_SPECULAR, &specular))
		color4_to_float4(&specular, c);
	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);

	set_float4(c, 0.2f, 0.2f, 0.2f, 1.0f);
	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_AMBIENT, &ambient))
		color4_to_float4(&ambient, c);
	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, c);

	set_float4(c, 0.0f, 0.0f, 0.0f, 1.0f);
	if(AI_SUCCESS == aiGetMaterialColor(mtl, AI_MATKEY_COLOR_EMISSIVE, &emission))
		color4_to_float4(&emission, c);
	glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, c);

	max = 1;
	ret1 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS, &shininess, &max);
	max = 1;
	ret2 = aiGetMaterialFloatArray(mtl, AI_MATKEY_SHININESS_STRENGTH, &strength, &max);
	if((ret1 == AI_SUCCESS) && (ret2 == AI_SUCCESS))
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, shininess * strength);
	else {
		glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 0.0f);
		set_float4(c, 0.0f, 0.0f, 0.0f, 0.0f);
		glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, c);
	}

	max = 1;
	if(AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_ENABLE_WIREFRAME, &wireframe, &max))
		fill_mode = wireframe ? GL_LINE : GL_FILL;
	else
		fill_mode = GL_FILL;
	glPolygonMode(GL_FRONT_AND_BACK, fill_mode);

	max = 1;
	if((AI_SUCCESS == aiGetMaterialIntegerArray(mtl, AI_MATKEY_TWOSIDED, &two_sided, &max)) && two_sided)
		glEnable(GL_CULL_FACE);
	else 
		glDisable(GL_CULL_FACE);
}

// ----------------------------------------------------------------------------

// Can't send color down as a pointer to aiColor4D because AI colors are ABGR.
void Color4f(const struct aiColor4D *color)
{
	glColor4f(color->r, color->g, color->b, color->a);
}

// ----------------------------------------------------------------------------
void recursive_render (const struct aiScene *sc, const struct aiNode* nd)
{
	int i;
	unsigned int n = 0, t;
	struct aiMatrix4x4 m = nd->mTransformation;

	// update transform
	aiTransposeMatrix4(&m);
	glPushMatrix();
	glMultMatrixf((float*)&m);

	// draw all meshes assigned to this node
	for (; n < nd->mNumMeshes; ++n) {
		const struct aiMesh* mesh = scene->mMeshes[nd->mMeshes[n]];

		apply_material(sc->mMaterials[mesh->mMaterialIndex]);

		if(mesh->mNormals == NULL) {
			glDisable(GL_LIGHTING);
		} else {
			glEnable(GL_LIGHTING);
		}

		if(mesh->mColors[0] != NULL) {
			glEnable(GL_COLOR_MATERIAL);
		} else {
			glDisable(GL_COLOR_MATERIAL);
		}

		for (t = 0; t < mesh->mNumFaces; ++t) {
			const struct aiFace* face = &mesh->mFaces[t];
			GLenum face_mode;

			switch(face->mNumIndices) {
				case 1: face_mode = GL_POINTS; break;
				case 2: face_mode = GL_LINES; break;
				case 3: face_mode = GL_TRIANGLES; break;
				default: face_mode = GL_POLYGON; break;
			}

			glBegin(face_mode);

			for(i = 0; i < face->mNumIndices; i++) {
				int index = face->mIndices[i];
				if(mesh->mColors[0] != NULL)
					Color4f(&mesh->mColors[0][index]);
				if(mesh->mNormals != NULL) 
					glNormal3fv(&mesh->mNormals[index].x);
				glVertex3fv(&mesh->mVertices[index].x);
			}

			glEnd();
		}

	}

	// draw all children
	for (n = 0; n < nd->mNumChildren; ++n) {
		recursive_render(sc, nd->mChildren[n]);
	}

	glPopMatrix();
}

// ----------------------------------------------------------------------------
void display(void)
{
	float tmp;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        // if the display list has not been made yet, create a new one and
        // fill it with scene contents
	if(scene_list == 0) {
	    scene_list = glGenLists(1);
	    glNewList(scene_list, GL_COMPILE);
            // now begin at the root node of the imported data and traverse
            // the scenegraph by multiplying subsequent local transforms
            // together on GL's matrix stack.
	    recursive_render(scene, scene->mRootNode);
	    glEndList();
	}

	glCallList(scene_list);

	glutSwapBuffers();
}

// ----------------------------------------------------------------------------
int loadasset (const char* path)
{
	// we are taking one of the postprocessing presets to avoid
	// writing 20 single postprocessing flags here.
	scene = aiImportFile(path,aiProcessPreset_TargetRealtime_Quality);

	if (scene) {
		get_bounding_box(&scene_min,&scene_max);
		scene_center.x = (scene_min.x + scene_max.x) / 2.0f;
		scene_center.y = (scene_min.y + scene_max.y) / 2.0f;
		scene_center.z = (scene_min.z + scene_max.z) / 2.0f;
		return 0;
	}
	return 1;
}

// ----------------------------------------------------------------------------
int main(int argc, char **argv)
{
	struct aiLogStream stream;

	glutInitWindowSize(900,600);
	glutInitWindowPosition(100,100);
	glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
	glutInit(&argc, argv);

	glutCreateWindow("Assimp - Very simple OpenGL sample");
	glutDisplayFunc(display);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
	gluLookAt(0.f,0.f,3.f,0.f,0.f,-5.f,0.f,1.f,0.f);

	glMatrixMode(GL_PROJECTION);
	glLoadIdentity();

  zprInit();

	// get a handle to the predefined STDOUT log stream and attach
	// it to the logging system. It will be active for all further
	// calls to aiImportFile(Ex) and aiApplyPostProcessing.
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_STDOUT,NULL);
	aiAttachLogStream(&stream);

	// ... exactly the same, but this stream will now write the
	// log file to assimp_log.txt
	stream = aiGetPredefinedLogStream(aiDefaultLogStream_FILE,"assimp_log.txt");
	aiAttachLogStream(&stream);

	// the model name can be specified on the command line. we try to locate
	// one of the more expressive test models from the repository.
	if( 0 != loadasset( argc >= 2 ? argv[1] : "../../test/models-nonbsd/X/dwarf.x")) {
		if( argc != 1 || 0 != loadasset( "../../../../test/models-nonbsd/X/dwarf.x") && 0 != loadasset( "../../test/models/X/Testwuson.X")) { 
			return -1;
		}
	}

	glClearColor(0.1f,0.1f,0.1f,1.f);

	glEnable(GL_LIGHTING);
	glEnable(GL_LIGHT0);    // Uses default lighting parameters

	glEnable(GL_DEPTH_TEST);

	glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
	glEnable(GL_NORMALIZE);

	// XXX docs say all polygons are emitted CCW, but tests show that some aren't.
	if(getenv("MODEL_IS_BROKEN"))  
		glFrontFace(GL_CW);

	glColorMaterial(GL_FRONT_AND_BACK, GL_DIFFUSE);

	// rotate it around the y axis
	//glRotatef(angle,0.f,1.f,0.f);
	get_bounding_box(&scene_min,&scene_max);
	scene_center.x = (scene_min.x + scene_max.x) / 2.0f;
	scene_center.y = (scene_min.y + scene_max.y) / 2.0f;
	scene_center.z = (scene_min.z + scene_max.z) / 2.0f;

	// scale the whole asset to fit into our view frustum 
	float tmp;
	tmp = scene_max.x-scene_min.x;
	tmp = aisgl_max(scene_max.y - scene_min.y,tmp);
	tmp = aisgl_max(scene_max.z - scene_min.z,tmp);
	tmp = 1.f / tmp;
	glScalef(tmp, tmp, tmp);

  // center the model
	glTranslatef( -scene_center.x, -scene_center.y, -scene_center.z );

	glutGet(GLUT_ELAPSED_TIME);
	glutMainLoop();

	// cleanup - calling 'aiReleaseImport' is important, as the library 
	// keeps internal resources until the scene is freed again. Not 
	// doing so can cause severe resource leaking.
	aiReleaseImport(scene);

	// We added a log stream to the library, it's our job to disable it
	// again. This will definitely release the last resources allocated
	// by Assimp.
	aiDetachAllLogStreams();
	return 0;
}

Wow, marshats:

(1) Yep, I’m on ubuntu and tried: “sudo apt-get install g3dviewer” - unfortunately, it didn’t read my files very well and it seemed like I couldn’t even export my files into the formats I hoped for…

(2) Assimp + gltZpr: Wow! That is EXTREMELY NICE! :slight_smile:

It now works on linux (though I haven’t tried it on Windows - will do some more testing in the coming days), exactly like you wrote…

I successfully made it read a proper/decent .stl-file + .obj-file + .dxf-file… It’s great - I’m very grateful for your help/hints…

Just a quick last question: Suppose I want a clipping plane, so I can look into an arbitrary cross-section of my graphics-file/object/3D CAD-drawing… How would I do that? suppose I provide two points:


                    (x2,y2)
   +---------------+
   |               |
   |               |
   +---------------+
(x1,y1)

And I want everything behind/in front of the rectangle (x1,y1)->(x2,y2) to be cleared/removed so I can “look into” the objects… Is there an easy solution for this “clipping
box/plane”-problem? That would be really great, if it exists…

Thank you!

Ooh, and then some quick questions (in addition to the clipping plane/cut box-thing above):

(3) I suppose that with Assimp, I could also load more “scenes”, i.e. different files and show them… Though, I think I would have to modify the code for finding the bounding box…?

(4) Anyway: Here’s what I want: I want to make a rotating wind turbine and I already drawed the wind turbine tower+nacelle+blades etc. in a 3D CAD program. I suppose that if I have 2 files:

—a--- the tower+nacelle in a separate CAD file (suppose it’s called: “WindTurb.dxf”)

—b--- the rotor+hub (incl. the 3 blades) as a separate file (suppose it’s called: “WT_rotor.dxf”),

… then I can perhaps load two (or more) files with assimp (and make the rotor rotate), maybe something like:


// the global Assimp scene object
const struct aiScene* sceneWindTurbine = NULL;

// the global Assimp scene object
const struct aiScene* sceneRotor = NULL;

(or I could perhaps change the “loadasset”-function in Sample_SimpleOpenGL.c, so it can load more than 1 file?)


sceneWindTurbine = iImportFile("WindTurb.dxf",aiProcessPreset_TargetRealtime_Quality);

sceneRotor = iImportFile("WT_rotor.dxf",aiProcessPreset_TargetRealtime_Quality);

And then in my glutDisplayFunc(display), perhaps I could have something that only rotates the rotor, with time:


GLfloat rotationAboutCenterAxis = 0.0; // global - updated below, something like (probably not correct, just some quick pseudo-code):

static void timerCallback (int value) // 
{ 
      // NB!!! USED TO ROTATE ROTOR IN DISPLAY(), SO IT TURNS:
      rotationAboutCenterAxis = rotationAboutCenterAxis + someth;
      glutPostRedisplay();
      glutTimerFunc ( blabla something ); // cannot remember syntax
}
 

void display(void)
{
	float tmp;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	glMatrixMode(GL_MODELVIEW);
	glLoadIdentity();
        ....
	if(scene_list == 0) {
	    scene_list = glGenLists(1);
	    glNewList(scene_list, GL_COMPILE);
            // now begin at the root node of the imported data and traverse
            // the scenegraph by multiplying subsequent local transforms
            // together on GL's matrix stack.

            // here's my suggestion: change code, NON-ROTATING DXF/STL/OBJ:
	    recursive_render(sceneWindTurbine, scene->mRootNode);

            // rotor is rotating e.g. about the y-axis - ROTATING DXF/STL/OBJ:
            glRotate( rotationAboutCenterAxis, 0.0, 1.0, 0.0);
	    recursive_render(sceneRotor, scene->mRootNode);
	    glEndList();
	}
        ....
}

Is it something like that, I should do to make a spinning wind turbine?

I guess it would be something like this (using 2 cad files, one that doesn’t rotate and one that does rotate)…

Glad that helped.

As for slicing the model. I don’t have a good answer — you may want to open a new thread to ask about that.

It helped a lot, thank you!

Does it sound reasonable, about my thoughts for making the rotor of the wind turbine to spin?

I’ll also try a few things myself, if nobody answers…

hmm. A little problem with your code, which I don’t understand…

The dwarf seems to be loading correctly, but it is not shown on my pc if I run the code you suggested/posted… I don’t understand why the dwarf doesn’t appear…

Also looks like your:


glClearColor(0.1f,0.1f,0.1f,1.f);

Has absolutely no effect at all… Even:


glClearColor(0.5f,0.5f,0.5f,1.f);

Makes the background completely dark/black, at least when I try loading an stl-file… Wonder what the reason is…

Try zooming out (middle mouse + upward movement) after loading the dwarf. I think it will come into view … the gltzpr code forces the initial viewing volume to be smaller than I think you like – the code expects everything to be in a unit cube to begin with. Remeber all this code is a starting point / template and should be modified to suit your actual needs. One approach is to make multiple display lists and compile for each of your seperate 3D objects.

Try zooming in and out … when I set the glClearColor it does have an effect but if the object is completely blocking the view then it will appear to not be working.

As for the wind turbine … yes you are on the right track. assimp has no problem to load multiple objects. Just generalized the display function to draw more than just one.

marshats:

Ah, thank you very much - you’re completely right about everything… I think I can figure out the rest from here - if not I’ll make a new thread and ask again… Now I need some time to test a few things (and look a bit into displaylists, which is a new thing for me)…

Thank you very much!