-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathTextureCanvas.cpp
250 lines (209 loc) · 7.75 KB
/
TextureCanvas.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#include "TextureCanvas.h"
#include "BasicTypes.h"
#include <iostream>
using namespace std;
/** *****************************************
TextureCanvas is the display workhorse for all classes that inherit AbstractGraph.
It interacts more directly with the graphics card through OpenGL in order to display
Skittle's large Pixels. It does this by creating a polygon (or series of) and
mapping a Texture onto that polygon. Texture interpolation and smoothing is disabled
to ensure that the pixels are displayed as sharp squares.
TextureCanvas first checks for a graphics driver by trying to fetch the maximum size
of a texture. If it does not find a graphics driver, then it defaults to paint each
pixel as a colored polygon, which is slower. With a driver, it breaks the requested
display surface into multiple textureTile if the area is wider than the maximum
texture size( stored in vector< vector< textureTile > > canvas). All the data
passed from the Graph class owner is stored in vector<color> colors and is passed
in through the constructor. This means a new TextureCanvas is generated every frame.
********************************************/
TextureCanvas::TextureCanvas()
{
init();
}
TextureCanvas::TextureCanvas(vector<color>& pixels, int w, bool raggedEdge)
{
init(w, raggedEdge);
if(!useTextures){
colors = vector<color>(pixels);
createDisplayList();
}
//pad the end with white pixels, background color
for(int i = 0; i <= w; ++i)
pixels.push_back( color(128,128,128) );
height = pixels.size() / width;
if(useTextures)
loadPixelsToCard(pixels, max_size);
}
TextureCanvas::~TextureCanvas()
{
for(unsigned int i = 0; i < canvas.size(); ++i)
for(unsigned int k = 0; k < canvas[i].size(); ++k)
glDeleteTextures(1, &canvas[i][k].tex_id );
glDeleteLists(displayList, 1);
}
void TextureCanvas::init(int w, bool raggedEdge)
{
ragged = raggedEdge;
max_size = checkForDisplayDriver();
width = max(1, w);//don't divide by zero
}
int TextureCanvas::checkForDisplayDriver()
{
max_size = 0;
useTextures = false;
//verify that textures can be allocated
glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max_size);
if(max_size > 0) useTextures = true;
extern bool global_usingTextures;
if(global_usingTextures == false)
useTextures = false;
return max_size;
}
/** The method parses vector<color> pixels into the appropriate textureTile, turns it into an
unsigned char (required by OpenGL) then loads it onto the graphics card as a texture
and deletes the old data. */
void TextureCanvas::loadPixelsToCard(vector<color>& pixels, int max_size)
{
//determine the size of the texture canvas
int canvas_width = width / max_size + 1; //canvas width can be wider than one tile width
//cout << "Initial Canvas Width: " << canvas_width;
canvas_width = min((maxSaneWidth / max_size + 1),canvas_width);
//cout << " Sanity Checked: " << canvas_width << endl;
//this canvas_width and canvas_height determine the number of tiles that need to be made
int canvas_height = height / max_size + 1;
createEmptyTiles(canvas_width, canvas_height, max_size);
for(int i = 0; i < (int)pixels.size(); i++)
{
color c1 = pixels[i];
int x = (i % width) / max_size; //(horizontal Index)
int y = (i / width) / max_size; //(vertical Index)
if (x >= (int)canvas.size() || y >= (int)canvas.size()) continue;
vector<unsigned char>& current = canvas[x][y].data;
current.push_back(c1.r);
current.push_back(c1.g);
current.push_back(c1.b);
}
for(unsigned int x = 0; x < canvas.size(); ++x)
{
for(unsigned int y = 0; y < canvas[x].size(); ++y)
{
loadTexture(canvas[x][y]);//tex_ids.push_back(
canvas[x][y].data.clear();//the data has been loaded into the graphics card
}
}
}
void TextureCanvas::createEmptyTiles(int canvas_width, int canvas_height, int max_size)
{
for(int x = 0; x < canvas_width ; ++x)//populate canvas
{
canvas.push_back( vector< textureTile >() );
for(int y = 0; y < canvas_height; ++y)
{
int w = min(width - (x * max_size), max_size);
int h = min(height - (y * max_size), max_size);
canvas[x].push_back( textureTile(w, h) );
}
}
}
void TextureCanvas::display()
{
if( this == NULL)//this is to prevent a null pointer exception from crashing the program
return;//somehow, a graph called ->display() on a null pointer
else
{
if( !useTextures )
glCallList(displayList);
else
drawTextureSquare();
}
}
void TextureCanvas::createDisplayList()
{
displayList = glGenLists(1);
glNewList(displayList, GL_COMPILE);
textureFreeRender();
glEndList();
}
void TextureCanvas::drawTextureSquare()//draws from canvas
{
glPushMatrix();
//glTranslated(-1.0, 0.0, 0);
// cout << "Drawing Texture: " << canvas.size() * canvas[0].size() << endl;
for(unsigned int x =0; x < canvas.size(); ++x)
{
for(unsigned int y = 0; y < canvas[x].size(); ++y)
{
textureTile& tile = canvas[x][y];
glPushMatrix();
glTranslated(x * max_size, y*max_size, 0);
glColor3d(1.0,1.0,1.0);
glEnable (GL_TEXTURE_2D); /* enable texture mapping */
glBindTexture (GL_TEXTURE_2D, tile.tex_id); /* bind to our texture, has id of 13 */
glBegin (GL_QUADS);
glTexCoord2f (0.0f,0.0f); /* upper left corner of image */
glVertex3f (0.0f, 0.0f, 0.0f);
glTexCoord2f (1.0f, 0.0f); /* upper right corner of image */
glVertex3f (tile.width, 0.0f, 0.0f);
glTexCoord2f (1.0f, 1.0f); /* lower right corner of image */
glVertex3f (tile.width, tile.height, 0.0f);
glTexCoord2f (0.0f, 1.0f); /* lower left corner of image */
glVertex3f (0.0f, tile.height, 0.0f);
glEnd ();
glDisable (GL_TEXTURE_2D); /* disable texture mapping */
glPopMatrix();
}
}
glPopMatrix();
}
GLuint TextureCanvas::loadTexture(textureTile& tile)
{
GLuint tex_id;
glGenTextures( 1, &tex_id );
glBindTexture (GL_TEXTURE_2D, tex_id);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);//GL_NEAREST
glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glTexImage2D (GL_TEXTURE_2D, 0, GL_RGB, tile.width, tile.height, 0, GL_RGB, GL_UNSIGNED_BYTE, &tile.data[0]);
//cout << "Load Texture: " << (unsigned int) tex_id << endl;
tile.tex_id = tex_id;
return tex_id;
}
point TextureCanvas::get_position(int index)
{
int x = index % width;
int y = index / width;
return point(x, y, 0);
}
void TextureCanvas::paint_square(point position, color c)
{
glPushMatrix();
glColor3d(c.r /255.0, c.g /255.0, c.b /255.0);
glTranslated(position.x+1, position.y, position.z);
glBegin(GL_QUADS);
glVertex3d(.0, .0, 0);
glVertex3d(-1.0, .0, 0);
glVertex3d(-1, -1, 0);
glVertex3d(.0, -1, 0);
glEnd();
glPopMatrix();
}
void TextureCanvas::textureFreeRender()
{
glPushMatrix();
glTranslated(0,1,0);
cout << "Loading new set of pixels." << endl;
// cout << "Manually drawing pixels: " << colors.size() << endl;
for(int i = 0; i < (int)colors.size(); i++)
{
point p1 = get_position( i );
paint_square(p1, colors[i]);
}
glPopMatrix();
}
int TextureCanvas::getMaxSize()
{
return max_size;
}