Importing object from 3DS Max into Opengl.

I have a few very basic models I want to import, I was just wondering if this is at all possible to do? And if so how to go about doing this?

Any help would be greatly appreciated, thank you :slight_smile:

You could try https://code.google.com/p/lib3ds/. The examples for the Graphics Gems 2 book (free online) use lib3ds, so they would provide example code. I don’t have any experience with it myself. 3ds is getting quite old, so you may want to look into newer formats. The Unreal4 engine might have 3DS format loading.

If you are looking for simplicity the PowerVR SDK loads its own format right out of the box and comes with texture/model/text viewing/converting tools. My first day with PVR I had working models and onscreen text. It has good examples and it’s free.

As an example: (it has a little objective-C mixed in, so don’t hate me.)


// A quick model class I whipped up a few weeks ago for some demos.
class BBSimpleModel {
public:
    BBSimpleModel() = delete;
    
    /**
     * Loads a model and builds the VBOs for static drawing.
     *
     * \param fileName_ location to tell PVR to find the model
     * 	hrow Exception if model cannot be loaded or if model data is not interleaved
     */
    explicit BBSimpleModel ( const char * fileName_ ) {
        assert( fileName_ != nullptr );
        
        CPVRTModelPOD   scene;
        SPODMesh *      mesh;
        
        // Get and set the read path for content files
        NSString * resourcePath = [NSString stringWithFormat:@"%@/",[[NSBundle mainBundle] resourcePath] ];
        CPVRTResourceFile::SetReadPath([resourcePath UTF8String]);
        
        // Get and set the load/release functions for loading external files.
        // In the majority of cases the PVRShell will return NULL function pointers implying that
        // nothing special is required to load external files.
        CPVRTResourceFile::SetLoadReleaseFunctions(NULL, NULL);

        if( scene.ReadFromFile( fileName_ ) != PVR_SUCCESS ) {
            std::cerr << "Unable to load model from " << fileName_ << std::endl;
            throw "Unable to load model.";
        }else {
            // Grab the mesh from the scene
            mesh = & scene.pMesh[0];
            
            // Generate vertex VBO and indexed VBO
            glGenBuffers( 1, & m_vertexVBO );
            glGenBuffers( 1, & m_indexVBO );
            
            if ( mesh->pInterleaved == 0 ) {
                std::cerr << "ERORR: Mesh data is not interleaved." << std::endl;
                throw "ERROR: Mesh data is not interleaved.";
            }
            
            // Saves vertex data into the vertex VBO
            PVRTuint32 meshVerticesSizeBytes = mesh->nNumVertex * mesh->sVertex.nStride;
            glBindBuffer( GL_ARRAY_BUFFER, m_vertexVBO );
            glBufferData( GL_ARRAY_BUFFER, meshVerticesSizeBytes, mesh->pInterleaved, GL_STATIC_DRAW );
            
            // Saves index data into the index VBO
            PVRTuint32 meshIndicesSizeBytes = PVRTModelPODCountIndices( *mesh ) * mesh->sFaces.nStride;
            glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexVBO );
            glBufferData( GL_ELEMENT_ARRAY_BUFFER, meshIndicesSizeBytes, mesh->sFaces.pData, GL_STATIC_DRAW );
            
            glBindBuffer(GL_ARRAY_BUFFER, 0);
            glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
        }
        
        // Get position, & normal offsets, and vertex stride
        m_offsetPosition = mesh->sVertex.pData;
        m_offsetNormal   = mesh->sNormals.pData;
        m_vertexStride   = mesh->sVertex.nStride;
        m_numVertices    = mesh->nNumFaces * 3;
        
        // Get primitive type and index data type
        m_indexDataType = (mesh->sFaces.eType == EPODDataUnsignedShort) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_INT;
        // Assumes data is triangles. TODO: Handle other scenarios.
        
        assert( m_numVertices > 0  );
        assert( m_vertexStride > 0 );
        assert( glIsBuffer( m_vertexVBO ) );
        assert( glIsBuffer( m_indexVBO  ) );
    }
    
    ~ BBSimpleModel () {
        glDeleteBuffers( 1, & m_vertexVBO );
        glDeleteBuffers( 1, & m_indexVBO  );
    }
    
    GLuint  vertexVBO      () const { return m_vertexVBO;      }
    GLuint  indexVBO       () const { return m_indexVBO;       }
    
    GLsizei vertexStride   () const { return m_vertexStride;   }
    GLsizei numVertices    () const { return m_numVertices;    }
    GLvoid* offsetPosition () const { return m_offsetPosition; }
    GLvoid* offsetNormal   () const { return m_offsetNormal;   }
    GLenum  primitiveType  () const { return GL_TRIANGLES;     }
    GLenum  indexDataType  () const { return m_indexDataType;  }
    
    void bindBuffers () {
        glBindBuffer( GL_ARRAY_BUFFER,         m_vertexVBO );
        glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, m_indexVBO  );
    }
    
    
    void unbindBuffers () {
        glBindBuffer( GL_ARRAY_BUFFER,         0 );
        glBindBuffer( GL_ELEMENT_ARRAY_BUFFER, 0 );
    }
    
private:
    GLuint          m_vertexVBO;
    GLuint          m_indexVBO;
    
    PVRTuint32      m_vertexStride;
    PVRTuint32      m_numVertices;
    void *          m_offsetPosition;
    void *          m_offsetNormal;
    GLenum          m_indexDataType;
};

// Drawing this mess:
// Yeah: I was lazy on this project and didn't use dynamic batching...
void
BBGraphics :: drawModelWithHemisphereLighting( BBSimpleModel * model_, mat4 modelMatrix_, vec3 materialColor_ ) {
    // Check validity of block VBOs and block drawing program.
    assert( glIsProgram( m_hemisphereLightingProgram->name() ) );
    assert( model_ != nullptr );
    
    // Enable ball VBO and program.
    m_hemisphereLightingProgram->beginDrawing();
    
    model_->bindBuffers();
    
    GLsizei stride = model_->vertexStride();
    glVertexAttribPointer( m_hemisphereLightingProgram->attribLocation("position"), 3, GL_FLOAT, 0, stride, model_->offsetPosition() );
    glVertexAttribPointer( m_hemisphereLightingProgram->attribLocation("normal"), 3, GL_FLOAT, GL_FALSE, stride, model_->offsetNormal() );
    mat4 mvp = m_camera.projectionTransform * m_camera.viewTransform * modelMatrix_;
    mat3 normalMatrix = GLKMatrix3InvertAndTranspose( GLKMatrix4GetMatrix3( m_camera.viewTransform * modelMatrix_ ), nullptr );
    glUniformMatrix4fv( m_hemisphereLightingProgram->uniformLocation("modelViewProjectionMatrix"), 1, 0, mvp.m );
    glUniformMatrix3fv( m_hemisphereLightingProgram->uniformLocation("normalMatrix"), 1, 0, normalMatrix.m );
    glUniform3fv( m_hemisphereLightingProgram->uniformLocation("skyColor"), 1, m_skyColor.v );
    glUniform3fv( m_hemisphereLightingProgram->uniformLocation("groundColor"), 1, m_groundColor.v );
    glUniform3fv( m_hemisphereLightingProgram->uniformLocation("north"), 1, m_north.v );
    glUniform3fv( m_hemisphereLightingProgram->uniformLocation("materialColor"), 1, materialColor_.v );
    
    glDrawElements( model_->primitiveType(), model_->numVertices(), model_->indexDataType(), 0 );
    
    model_->unbindBuffers();
    m_hemisphereLightingProgram->endDrawing();
}