Your lab02a project should have the ability to load a simple model and render it.
Create a new project called lab03b_models by copying/pasting your lab02 project.
Delete all of the source files in the new project, and rebuild the project as follows:
#include "libopengl.h"
void renderScene(void)
{
glClear( GL_COLOR_BUFFER_BIT);
glFlush();
}
void setupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glOrtho(-1.0f, +1.0f, -1.0f, +1.0f, -1.0f, +1.0f);
}
int main(int argc, char* argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_SINGLE | GLUT_RGB);
glutCreateWindow("lab04");
glutInitWindowSize(800, 600);
glutDisplayFunc(renderScene);
setupRC();
glutMainLoop();
return 0;
}
#pragma once
void loadAndDraw(char* fileName);
#include "simple.h"
#include "libopengl.h"
#include <fstream>
#include <vector>
using namespace std;
void loadAndDraw(char * fileName)
{
fstream inStream;
inStream.open(fileName, ios::in);
if (inStream.fail())
return;
GLint numpolys, numLines;
inStream >> numpolys;
for (int j = 0; j < numpolys; j++)
{
inStream >> numLines;
glBegin( GL_LINE_STRIP);
for (int i = 0; i < numLines; i++)
{
float x, y;
inStream >> x >> y;
glVertex2f(x, y);
}
glEnd();
}
inStream.close();
}
Build and test.
Download bighouse.txt
Make the appropriate call to load and draw the house in the renderScene method.
struct Vertex
{
float x;
float y;
};
struct LineStrip
{
int size;
Vertex *vertices;
};
struct Model
{
int size;
LineStrip* lineStrips;
};
Reflect on the contents of bighouse.txt, and relate these structures to its content.
Introduce the following function on simple.cpp:
Model loadSimple(char *fileName)
{
Model model;
fstream inStream;
inStream.open(fileName, ios::in);
if (inStream.fail())
exit(1);
inStream >> model.size;
model.lineStrips = new LineStrip[model.size];
for (int polyIndex = 0; polyIndex < model.size; polyIndex++)
{
inStream >> model.lineStrips[polyIndex].size;
model.lineStrips[polyIndex].vertices = new Vertex[model.lineStrips[polyIndex].size];
for (int vertexIndex = 0; vertexIndex < model.lineStrips[polyIndex].size; vertexIndex++)
{
inStream >> model.lineStrips[polyIndex].vertices[vertexIndex].x >> model.lineStrips[polyIndex].vertices[vertexIndex].y;
}
}
inStream.close();
return model;
}
Place the function prototype in simple.h, and in main.cpp call this function to load the bighouse model.
Use the debugger to verify that it has been loaded successfully.
void renderSimple(Model &model)
{
for (int polyIndex=0; polyIndex < model.size; polyIndex++)
{
glBegin( GL_LINE_STRIP);
for (int vertexIndex = 0; vertexIndex < model.lineStrips[polyIndex].size; vertexIndex++)
{
glVertex2f(model.lineStrips[polyIndex].vertices[vertexIndex].x, model.lineStrips[polyIndex].vertices[vertexIndex].y);
}
glEnd();
}
}
We can improve and simplify this model by moving to STL.
Crete a new header file -model.h - and rework the model to use vectors:
struct Vertex
{
float x;
float y;
};
struct LineStrip
{
vector<Vertex> vertices;
};
struct Model
{
vector<LineStrip> lineStrips;
};
Model loadModel(char *fileName)
{
Model model;
fstream inStream;
inStream.open(fileName, ios::in);
if (inStream.fail())
exit(1);
int numberPolys;
inStream >> numberPolys;
for (int polyIndex = 0; polyIndex < numberPolys; polyIndex++)
{
LineStrip LineStrip;
int numberVertices;
inStream >> numberVertices;
for (int vertexIndex = 0; vertexIndex < numberVertices; vertexIndex++)
{
Vertex vertex;
inStream >> vertex.x >> vertex.y;
LineStrip.vertices.push_back(vertex);
}
model.lineStrips.push_back(LineStrip);
}
inStream.close();
return model;
}
void renderModel(Model &model)
{
for (unsigned int polyIndex=0; polyIndex < model.lineStrips.size(); polyIndex++)
{
glBegin( GL_LINE_STRIP);
for (unsigned int vertexIndex = 0; vertexIndex < model.lineStrips[polyIndex].vertices.size(); vertexIndex++)
{
glVertex2f(model.lineStrips[polyIndex].vertices[vertexIndex].x, model.lineStrips[polyIndex].vertices[vertexIndex].y);
}
glEnd();
}
}
Build and test.
Compare these methods with the simple versions.
struct Vertex
{
float x;
float y;
Vertex(istream& is);
void render();
};
struct LineStrip
{
vector<Vertex> vertices;
LineStrip(istream& is);
void render();
};
struct Model
{
vector<LineStrip> lineStrips;
Model(istream& is);
void render();
};
Vertex::Vertex(istream &is)
{
is >> x >> y;
}
void Vertex::render()
{
glVertex2f(x, y);
}
LineStrip::LineStrip(istream &is)
{
int size;
is >> size;
for (int i = 0; i < size; i++)
{
Vertex vertex(is);
vertices.push_back(vertex);
}
}
void LineStrip::render()
{
glBegin( GL_LINE_STRIP);
for (unsigned int i = 0; i < vertices.size(); i++)
{
vertices[i].render();
}
glEnd();
}
Model::Model(istream &is)
{
int size;
is >> size;
for (int i = 0; i < size; i++)
{
LineStrip LineStrip(is);
lineStrips.push_back(LineStrip);
}
}
void Model::render()
{
for (unsigned int i = 0; i < lineStrips.size(); i++)
{
lineStrips[i].render();
}
}
Model *model;
void renderScene(void)
{
glClear( GL_COLOR_BUFFER_BIT);
model->render();
glFlush();
}
void setupRC()
{
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glOrtho(-1.0f, +1.0f, -1.0f, +1.0f, -1.0f, +1.0f);
fstream modelStream;
modelStream.open("bighouse.txt", ios::in);
if (modelStream.fail())
exit(1);
model = new Model(modelStream);
}
#scale
150 150
#number of entities
4
# 1st entity
# 0 = line strip
0
# number of vertices
6
# vertices
40 40
40 90
70 120
100 90
100 40
40 40
# 2nd entity
# 0 = line strip
0
# number of vertices
4
# vertices
50 100
50 120
60 120
60 110
This format is a little more flexible - it has room for comments (lines beginning with a #), and also it has room for extension. Currently it supports line strips(via the index 0), but we could extend this later. It also has some meta information like the scale of the drawing (max x and y values);
We need a simple way of skiping comment lines on input of the first character in the line. This function here should be adequate:
void skipComment(istream &is)
{
char ch;
is >> ch;
if (ch == '#')
{
do
{
string buf;
getline(is, buf);
is >> ch;
} while (ch == '#');
}
is.putback(ch);
}
enum EntityType {LineStripId, TriangleId};
struct Model
{
int maxX, maxY;
vector<LineStrip> lines;
Model(istream& is);
void render();
}
To suppoer the new format, every time we read we need to skip comments.
So in Vertex:
Vertex::Vertex(istream &is)
{
skipComment(is);
is >> x >> y;
}
LineStrip::LineStrip(istream &is)
{
int size;
skipComment(is);
is >> size;
for (int i = 0; i < size; i++)
{
Vertex vertex(is);
vertices.push_back(vertex);
}
}
Model::Model(istream &is)
{
int size;
skipComment(is);
is >> maxX >> maxY;
skipComment(is);
is >> size;
int typeId;
skipComment(is);
is >> typeId;
switch (typeId)
{
case LineStripId: { LineStrip line(is);
lines.push_back(line);
break;
}
}
The above case statement has only one math - for our singe supported entity type - the Line Strip.
All the render methods should be unchanged from the previous version.
Introduce a new entity type - Triangle - which can have code 1.
it will always have three vertices.
Use this sample here as a guide:
#scale
150 150
#number of entities
4
# 1st entity
# 0 = line strip
0
# number of vertices
6
# vertices
40 40
40 90
70 120
100 90
100 40
40 40
# 2nd entity
# 0 = line strip
0
# number of vertices
4
# vertices
50 100
50 120
60 120
60 110
# 3rd entity
# 1 = triangle
1
# 3 vertices
20 10
100 22
22 22
# 1 = triangle
1
# 3 vertices
120 10
10 22
102 22
Create a new struct Triangle to model this.
Implement triangle
Store a list of triangles in the model, and extend the render method to display them
void Model::render()
{
for (unsigned int i = 0; i < lines.size(); i++)
{
lines[i].render();
}
for (unsigned int i = 0; i < triangles.size(); i++)
{
triangles[i].render();
}
}
void Model::render()
{
for (unsigned int i=0; i<entities.size(); i++)
{
entities[i]->render();
}
}