Skip to content

SimpleGL API

Crystal Ren edited this page May 1, 2019 · 17 revisions

Overview

SimpleGL is a simpler, beginner-friendly version of OpenGL.

This API functions as the user documentation and will detail all the methods available from the SimpleGL library and include brief descriptions and some sample code.

The Scene Object

Scene(char *vs = nullptr, char *fs = nullptr, 
	const int width = 800, const int height = 600, 
	bool use_full_ctrl = false);

To construct a scene object, which any use of SimpleGL functionality requires, you can specify the various parameters yourself or use the default values as shown above by simply using:

Scene s();

The argument vs is the filepath to the vertex shader, fs is the filepath to the frag shader, width is the screen width of the resulting render viewbox, height is the screen height of the resulting render viewbox and use_full_ctrl determines whether or not the key control features are enabled (detailed below in the corresponding section). The shader files are needed by OpenGL, for more information please see the corresponding OpenGL documentation (Frag Shader Wiki and Vertex Shader Wiki). Their construction and explanation is outside the scope of this documentation.

The construction of the Scene object does more than just create a Scene object, it also executes much of the boilerplate set-up code that OpenGL requires.

The Scene object destructor will perform all the necessary cleanup and OpenGL teardown and is automatically called when the created Scene instance goes out of scope.

Renderable Objects

Adding/Creating an Object

Within the Scene class there is an instance method, add_obj() that is used in order to create/add any object to the scene:

ObjId add_obj(obj_type t, 
	const color c = {0.4, 0.4, 0.4}, 
	obj_params params = obj_params());

This method takes an obj_type which is of type std::variant<Shape, std::string>. The string option is for loading objects from .obj files in which case each object has a user defined name that is specified with this string. This name identifies the unique objects loaded from files. For a built-in shape or composite object, specify the Shape type, where Shape is an enum that can be any of the following:

  • sphere
  • truncatedCone
  • cylinder
  • cone
  • pyramid
  • torus
  • box
  • composite

These shapes are exactly what they sound like. Composite objects are detailed in this section.

The second parameter, color, is a glm::vec3 type that specifies the RGB value color of an object. It has the default value shown above. Creating a color object is simple:

color myColor = glm::vec3(0.8, 0.6, 0.8);

obj_params is a type that holds the parameters for an object. This parameter has a default value as well. It has two fields, 1) a glp_params field of type std::variant<params, std::string> and 2) a comp field only used for building composite objects (detailed here).

glp_params is either a params type or a string. For objects loaded from .obj files, the glp_params must be provided and must be a string set to the filepath to the .obj file's location.

If using a built-in shape or a composite shape, the glp_params can be set to params which contains two integer fields, accuracy and sides.

accuracy determines how much data it takes to describe an object, and thus what the resolution of the object will be when it's rendered. This field will affect the following shapes: sphere, truncatedCone, cylinder, and cone.

sides determines the number of sides for those built-in shapes that can accommodate a variable number of sides, currently this field only affects the pyramid shape. These fields are ignored for the shapes that are not affected by one or both of these two fields.

Here's sample code using a custom obj_params and default (empty) params to construct a pyramid and a sphere.

// construct custom obj_params
obj_params oparams;
params p = {.accuracy = 7};
oparams.glp_params = p;

// add two spheres
ObjId m_id = s.add_obj(Shape::sphere, myColor, oparams); // myColor defined elsewhere
m_id = s.add_obj(Shape::sphere, myColor);
// add pyramid
p = {.sides = 7};
oparams.glp_params = p;
m_id = s.add_obj(Shape::pyramid, myColor, oparams);

add_obj() returns an ObjId which can be used to identify the object created, and on which later object transformations can act upon. This does not affect whether the object is added to the scene, so if you don't want to perform further transformations on this object, this value can be thrown away.

Creating a Composite Object

To add a composite object, which is an object comprised of two or more other objects, you must first make the sub-objects separately using add_obj(). Then combine them by setting the comp field in obj_params with the ObjIds of the objects you want to combine.

Here's an example:

// add a composite object of a kitten mesh and a built-in cone.
ObjId cone = s.add_obj(Shape::cone, myColor); //color defined above
obj_params oparams;
oparams.glp_params = "/path/to/kitten.obj";
ObjId obj = s.add_obj("kitten", myColor, oparams);
/* we can reuse oparams because Shape::composite uses a
different part of oparams than .obj file loaded objects, 
which uses a different part of oparams than other Shape 
types and so on. */
oparams.comp = {cone, obj};
ObjId c1 = s.add_obj(Shape::composite, myColor, oparams);

Thus c1 is your composite object that comprises of a "kitten" loaded from a .obj file and a built-in cone shape. This composite object can be treated and transformed like any other non-composite object.

Object Transformation

Transformations can be applied in any order, the correct ordering is applied within the SimpleGL library.

Unless otherwise specified, all object transformation functions act on an ObjId and are instance methods of the ObjId class. Some example usages will be provided at the end of this section.

Instance Methods of ObjId

ObjId duplicate()

Duplicates an object and returns the ObjId of the newly created duplicate. This also duplicates the color and transformations applied to the object.

void hide()

Hides an object in the rendered scene.

void show()

Shows an object if it was previously hidden.

void set_color(color c)

Sets the color of an object to the passed in color parameter.

void set_model(glm::mat4 m)

This function allows the user to set the model matrix manually without using the transformation functions here (ie. instead of calling translate(), scale() etc.). SimpleGL assumes that the user will not use other helper methods if they are setting the model matrix themselves. The model matrix is a concept in OpenGL that is beyond the scope of this documentation. SimpleGL extracts this away from the user, but if you would like to use it regardless, this function allows you to do so.

void reset_model()

Clears any transformations applied to the object.

void translate(glm::vec3 translation)

Translates the object by the passed in a glm::vec3 amount where the parameter determines the (x,y,z) translation amount relative to the current transformation.

void set_translation(const glm::vec3 translation)

Translates the object by the passed in a glm::vec3 amount where the parameter determines the (x,y,z) translation amount where the translation is absolute with respect to the initial location.

void scale(glm::vec3 factor)

Scales the object using the glm::vec3 parameter that allows scaling factors to be specified for each of the three Cartesian directions (x,y,z) where the scaling is relative to the current transformation.

void scale(double factor)

Scales the object using a singular double parameter that scales by that factor equally in all three Cartesian directions (x,y,z) where the scaling is relative to the current transformation.

void set_scale(const glm::vec3 factor)

Scales the object using the glm::vec3 parameter that allows scaling factors to be specified for each of the three Cartesian directions (x,y,z) where the scaling is absolute with respect to the initial object.

void set_scale(const double factor)

Scales the object using a singular double parameter that scales by that factor equally in all three Cartesian directions (x,y,z) where the scaling is absolute with respect to the initial object.

void rotate(float angle, glm::vec3 axis)

Rotates the object the given parameter angle around the given glm::vec3 parameter axis where the rotation is relative to the current transformation.

void set_rotation(const float angle, glm::vec3 axis)

Rotates the object the given parameter angle around the given glm::vec3 parameter axis where the rotation is absolute with respect to the initial orientation.

glm::vec3 get_loc()

Gets the location of the object as a coordinate point in the glm::vec3 form.

bool operator==(const ObjId& b) const

Overloaded == to allow comparison between two ObjIds.

Other Methods

std::ostream& operator<<(std::ostream& os, 
	transformation& t)

This function is not an instance method of the ObjId class. Overloads << so that transformations can be pretty printed, with each of the following printed: overall model matrix, rotation matrix, translation matrix, scaling matrix, and whether or not the object is hidden.

std::ostream& operator<<(std::ostream& os, color& c)

This function is not an instance method of the ObjId class. Overloads << so that color vector can be pretty printed.

Example Usage

Below is some sample code that uses several of the above methods.

// translating and scaling a created object
ObjId floor = s.add_obj(Shape::box, {0.6, 0.6, 0.6} );
floor.translate(glm::vec3( -35, -4, -35));
floor.scale({70, 0.01, 70});

// add a composite object of a kitten mesh and a default cone.
ObjId cone = s.add_obj(Shape::cone, myColor);
ObjId obj = s.add_obj("kitten", myColor, oparams);
oparams.comp = {cone, obj};
ObjId c1 = s.add_obj(Shape::composite, myColor, oparams);

// order of transformations doesn't matter
c1.scale(0.5);
c1.translate(glm::vec3(-1, 0, 0));
cone.translate(glm::vec3(0, 0.2, 0));
cone.scale({0.1, 0.3, 0.1});
obj.rotate(20, glm::vec3(0, 1, 0));

// add a copy by duplicating each sub-object then creating a composite
oparams.comp = {cone.duplicate(), obj.duplicate()};
ObjId c2 = s.add_obj(Shape::composite, color, oparams);
c2.translate(glm::vec3(1, 0, 0)); // translate it

// add a copy by directly duplicating the composite
ObjId c3 = c2.duplicate();
c3.translate(glm::vec3(1, 1, 0)); //translate it

Render the Scene

After loading all the objects you want into the scene, it's time to finally render the scene. Scene rendering is done by one function:

std::error_condition render()

This function is simply used like this:

Scene s;
\\ some code to add renderable objects
s.render();

render() runs in a loop where each loop run renders the frame once. The loop is only interrupted when glfwWindowShouldClose() becomes true, which happens when the user tries to close the resulting render window (ie. by hitting the esc key). This GLFW function is further documented here.

The loop in render() also has a user callback insertion functionality. This is discussed more here.

In the case of an error, render() returns an std::error_condition which can be checked by the user and which will include a descriptive message about what went wrong.

Scene Utility Functions

The following functions all act on a Scene object.

void setCallback(std::function<void(Scene *)> callback)

Set a user defined function that takes a Scene pointer and returns void as a callback function that gets called once per render loop (ie. once per frame). For a specific example, see the end of this section.

void remove_obj_all(obj_type t)

Removes all instances of a certain type of object from the scene, just pass in the type of object to be removed.

void setLightPos(const glm::vec3 pos)

Sets the light position to the given glm::vec3 which represents an (x,y,z) coordinate.

void setSmoothing(double smooth)

Sets the smoothing factor for the framerate smoothed average calculation. The smoothing factor determines how much the current frame rate affects the averaged framerate. The higher the value, the more smooth the framerate is and the smaller the effect from large changes in framerate. To turn smoothing off, set this to 0.

double getFramerate()

Returns the smoothed average framerate.

std::chrono::milliseconds getDeltaFrameTime()

Returns the last measurement of the time between two rendered frames in std::chrono::milliseconds.

Example Usage

Scene s;
void printFramerate(Scene *scene_ptr) {
    std::cout << "Framerate: " << scene_ptr->getFramerate() << "\n";
}
s.setCallback(printFramerate);

Note

See the Maintainer's Notes for documentation on SimpleGL implementation details.

See the Tutorial for a walkthrough detailing how to write your first SimpleGL program.

Written with StackEdit.

Clone this wiki locally