456 lines
14 KiB
C
456 lines
14 KiB
C
/* */
|
|
|
|
#define GL_GLEXT_PROTOTYPES
|
|
|
|
#include <assert.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <GL/gl.h>
|
|
#include <GL/glu.h>
|
|
#include "glm.h"
|
|
#include "readtex.h"
|
|
#include "shaderutil.h"
|
|
|
|
|
|
/* defines */
|
|
#define T(x) model->triangles[(x)]
|
|
|
|
|
|
/* glmDraw: Renders the model to the current OpenGL context using the
|
|
* mode specified.
|
|
*
|
|
* model - initialized GLMmodel structure
|
|
* mode - a bitwise OR of values describing what is to be rendered.
|
|
* GLM_NONE - render with only vertices
|
|
* GLM_FLAT - render with facet normals
|
|
* GLM_SMOOTH - render with vertex normals
|
|
* GLM_TEXTURE - render with texture coords
|
|
* GLM_COLOR - render with colors (color material)
|
|
* GLM_MATERIAL - render with materials
|
|
* GLM_COLOR and GLM_MATERIAL should not both be specified.
|
|
* GLM_FLAT and GLM_SMOOTH should not both be specified.
|
|
*/
|
|
GLvoid
|
|
glmDraw(GLMmodel* model, GLuint mode)
|
|
{
|
|
GLuint i;
|
|
GLMgroup* group;
|
|
|
|
assert(model);
|
|
assert(model->vertices);
|
|
|
|
/* do a bit of warning */
|
|
if (mode & GLM_FLAT && !model->facetnorms) {
|
|
printf("glmDraw() warning: flat render mode requested "
|
|
"with no facet normals defined.\n");
|
|
mode &= ~GLM_FLAT;
|
|
}
|
|
if (mode & GLM_SMOOTH && !model->normals) {
|
|
printf("glmDraw() warning: smooth render mode requested "
|
|
"with no normals defined.\n");
|
|
mode &= ~GLM_SMOOTH;
|
|
}
|
|
if (mode & GLM_TEXTURE && !model->texcoords) {
|
|
printf("glmDraw() warning: texture render mode requested "
|
|
"with no texture coordinates defined.\n");
|
|
mode &= ~GLM_TEXTURE;
|
|
}
|
|
if (mode & GLM_FLAT && mode & GLM_SMOOTH) {
|
|
printf("glmDraw() warning: flat render mode requested "
|
|
"and smooth render mode requested (using smooth).\n");
|
|
mode &= ~GLM_FLAT;
|
|
}
|
|
if (mode & GLM_COLOR && !model->materials) {
|
|
printf("glmDraw() warning: color render mode requested "
|
|
"with no materials defined.\n");
|
|
mode &= ~GLM_COLOR;
|
|
}
|
|
if (mode & GLM_MATERIAL && !model->materials) {
|
|
printf("glmDraw() warning: material render mode requested "
|
|
"with no materials defined.\n");
|
|
mode &= ~GLM_MATERIAL;
|
|
}
|
|
if (mode & GLM_COLOR && mode & GLM_MATERIAL) {
|
|
printf("glmDraw() warning: color and material render mode requested "
|
|
"using only material mode\n");
|
|
mode &= ~GLM_COLOR;
|
|
}
|
|
if (mode & GLM_COLOR)
|
|
glEnable(GL_COLOR_MATERIAL);
|
|
if (mode & GLM_MATERIAL)
|
|
glDisable(GL_COLOR_MATERIAL);
|
|
|
|
glPushMatrix();
|
|
glTranslatef(model->position[0], model->position[1], model->position[2]);
|
|
|
|
glBegin(GL_TRIANGLES);
|
|
group = model->groups;
|
|
while (group) {
|
|
if (mode & GLM_MATERIAL) {
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT,
|
|
model->materials[group->material].ambient);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE,
|
|
model->materials[group->material].diffuse);
|
|
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR,
|
|
model->materials[group->material].specular);
|
|
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS,
|
|
model->materials[group->material].shininess);
|
|
}
|
|
|
|
if (mode & GLM_COLOR) {
|
|
glColor3fv(model->materials[group->material].diffuse);
|
|
}
|
|
|
|
for (i = 0; i < group->numtriangles; i++) {
|
|
if (mode & GLM_FLAT)
|
|
glNormal3fv(&model->facetnorms[3 * T(group->triangles[i]).findex]);
|
|
|
|
if (mode & GLM_SMOOTH)
|
|
glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[0]]);
|
|
if (mode & GLM_TEXTURE)
|
|
glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[0]]);
|
|
glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[0]]);
|
|
#if 0
|
|
printf("%f %f %f\n",
|
|
model->vertices[3 * T(group->triangles[i]).vindices[0] + X],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[0] + Y],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[0] + Z]);
|
|
#endif
|
|
|
|
if (mode & GLM_SMOOTH)
|
|
glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[1]]);
|
|
if (mode & GLM_TEXTURE)
|
|
glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[1]]);
|
|
glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[1]]);
|
|
#if 0
|
|
printf("%f %f %f\n",
|
|
model->vertices[3 * T(group->triangles[i]).vindices[1] + X],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[1] + Y],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[1] + Z]);
|
|
#endif
|
|
|
|
if (mode & GLM_SMOOTH)
|
|
glNormal3fv(&model->normals[3 * T(group->triangles[i]).nindices[2]]);
|
|
if (mode & GLM_TEXTURE)
|
|
glTexCoord2fv(&model->texcoords[2*T(group->triangles[i]).tindices[2]]);
|
|
glVertex3fv(&model->vertices[3 * T(group->triangles[i]).vindices[2]]);
|
|
#if 0
|
|
printf("%f %f %f\n",
|
|
model->vertices[3 * T(group->triangles[i]).vindices[2] + X],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[2] + Y],
|
|
model->vertices[3 * T(group->triangles[i]).vindices[2] + Z]);
|
|
#endif
|
|
|
|
}
|
|
|
|
group = group->next;
|
|
}
|
|
glEnd();
|
|
|
|
glPopMatrix();
|
|
}
|
|
|
|
|
|
void
|
|
glmMakeVBOs(GLMmodel *model)
|
|
{
|
|
uint bytes, vertexFloats, i;
|
|
float *buffer;
|
|
|
|
vertexFloats = 3;
|
|
model->posOffset = 0;
|
|
|
|
if (model->numnormals > 0) {
|
|
assert(model->numnormals == model->numvertices);
|
|
model->normOffset = vertexFloats * sizeof(GLfloat);
|
|
vertexFloats += 3;
|
|
}
|
|
|
|
if (model->numtexcoords > 0) {
|
|
assert(model->numtexcoords == model->numvertices);
|
|
model->texOffset = vertexFloats * sizeof(GLfloat);
|
|
vertexFloats += 2;
|
|
}
|
|
|
|
model->vertexSize = vertexFloats;
|
|
|
|
bytes = (model->numvertices + 1) * vertexFloats * sizeof(float);
|
|
|
|
buffer = (float *) malloc(bytes);
|
|
for (i = 0; i < model->numvertices; i++) {
|
|
/* copy vertex pos */
|
|
uint j = 0;
|
|
buffer[i * vertexFloats + j++] = model->vertices[i * 3 + 0];
|
|
buffer[i * vertexFloats + j++] = model->vertices[i * 3 + 1];
|
|
buffer[i * vertexFloats + j++] = model->vertices[i * 3 + 2];
|
|
if (model->numnormals > 0) {
|
|
buffer[i * vertexFloats + j++] = model->normals[i * 3 + 0];
|
|
buffer[i * vertexFloats + j++] = model->normals[i * 3 + 1];
|
|
buffer[i * vertexFloats + j++] = model->normals[i * 3 + 2];
|
|
}
|
|
if (model->numtexcoords > 0) {
|
|
buffer[i * vertexFloats + j++] = model->texcoords[i * 2 + 0];
|
|
buffer[i * vertexFloats + j++] = model->texcoords[i * 2 + 1];
|
|
}
|
|
}
|
|
|
|
glGenBuffersARB(1, &model->vbo);
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, model->vbo);
|
|
glBufferDataARB(GL_ARRAY_BUFFER_ARB, bytes, buffer, GL_STATIC_DRAW_ARB);
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
|
|
|
free(buffer);
|
|
}
|
|
|
|
|
|
static void
|
|
_glmLoadTexture(GLMmaterial *mat)
|
|
{
|
|
if (mat->map_kd) {
|
|
GLint imgWidth, imgHeight;
|
|
GLenum imgFormat;
|
|
GLubyte *image = NULL;
|
|
|
|
glGenTextures(1, &mat->texture_kd);
|
|
|
|
image = LoadRGBImage( mat->map_kd, &imgWidth, &imgHeight, &imgFormat );
|
|
if (!image) {
|
|
/*fprintf(stderr, "Couldn't open texture %s\n", mat->map_kd);*/
|
|
free(mat->map_kd);
|
|
mat->map_kd = NULL;
|
|
mat->texture_kd = 0;
|
|
return;
|
|
}
|
|
if (0)
|
|
printf("load texture %s %d x %d\n", mat->map_kd, imgWidth, imgHeight);
|
|
|
|
glBindTexture(GL_TEXTURE_2D, mat->texture_kd);
|
|
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, imgWidth, imgHeight,
|
|
imgFormat, GL_UNSIGNED_BYTE, image);
|
|
free(image);
|
|
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR );
|
|
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
|
|
}
|
|
}
|
|
|
|
void
|
|
glmLoadTextures(GLMmodel *model)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < model->nummaterials; i++) {
|
|
GLMmaterial *mat = &model->materials[i];
|
|
_glmLoadTexture(mat);
|
|
}
|
|
}
|
|
|
|
|
|
void
|
|
glmDrawVBO(GLMmodel *model)
|
|
{
|
|
GLMgroup* group;
|
|
|
|
assert(model->vbo);
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, model->vbo);
|
|
|
|
glVertexPointer(3, GL_FLOAT, model->vertexSize * sizeof(float),
|
|
(void *) model->posOffset);
|
|
glEnableClientState(GL_VERTEX_ARRAY);
|
|
|
|
if (model->numnormals > 0) {
|
|
glNormalPointer(GL_FLOAT, model->vertexSize * sizeof(float),
|
|
(void *) model->normOffset);
|
|
glEnableClientState(GL_NORMAL_ARRAY);
|
|
}
|
|
|
|
if (model->numtexcoords > 0) {
|
|
glTexCoordPointer(2, GL_FLOAT, model->vertexSize * sizeof(float),
|
|
(void *) model->texOffset);
|
|
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
glPushMatrix();
|
|
glTranslatef(model->position[0], model->position[1], model->position[2]);
|
|
glScalef(model->scale, model->scale, model->scale);
|
|
|
|
for (group = model->groups; group; group = group->next) {
|
|
if (group->numtriangles > 0) {
|
|
|
|
glmShaderMaterial(&model->materials[group->material]);
|
|
|
|
glDrawRangeElements(GL_TRIANGLES,
|
|
group->minIndex, group->maxIndex,
|
|
3 * group->numtriangles,
|
|
GL_UNSIGNED_INT, group->triIndexes);
|
|
}
|
|
}
|
|
|
|
glPopMatrix();
|
|
|
|
glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
|
|
glDisableClientState(GL_VERTEX_ARRAY);
|
|
glDisableClientState(GL_NORMAL_ARRAY);
|
|
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
|
|
}
|
|
|
|
|
|
|
|
/* glmList: Generates and returns a display list for the model using
|
|
* the mode specified.
|
|
*
|
|
* model - initialized GLMmodel structure
|
|
* mode - a bitwise OR of values describing what is to be rendered.
|
|
* GLM_NONE - render with only vertices
|
|
* GLM_FLAT - render with facet normals
|
|
* GLM_SMOOTH - render with vertex normals
|
|
* GLM_TEXTURE - render with texture coords
|
|
* GLM_COLOR - render with colors (color material)
|
|
* GLM_MATERIAL - render with materials
|
|
* GLM_COLOR and GLM_MATERIAL should not both be specified.
|
|
* GLM_FLAT and GLM_SMOOTH should not both be specified.
|
|
*/
|
|
GLuint
|
|
glmList(GLMmodel* model, GLuint mode)
|
|
{
|
|
GLuint list;
|
|
|
|
list = glGenLists(1);
|
|
glNewList(list, GL_COMPILE);
|
|
glmDraw(model, mode);
|
|
glEndList();
|
|
|
|
return list;
|
|
}
|
|
|
|
|
|
|
|
static const char *VertexShader =
|
|
"varying vec3 normal; \n"
|
|
"void main() { \n"
|
|
" gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex; \n"
|
|
" normal = gl_NormalMatrix * gl_Normal; \n"
|
|
" gl_TexCoord[0] = gl_MultiTexCoord0; \n"
|
|
"} \n";
|
|
|
|
/**
|
|
* Two %s substitutions:
|
|
* diffuse texture? true/false
|
|
* specular texture? true/false
|
|
*/
|
|
static const char *TexFragmentShader =
|
|
"uniform vec4 ambient, diffuse, specular; \n"
|
|
"uniform vec4 ambientLight, diffuseLight, specularLight; \n"
|
|
"uniform float shininess; \n"
|
|
"uniform sampler2D diffTex; \n"
|
|
"uniform samplerCube specTex; \n"
|
|
"varying vec3 normal; \n"
|
|
"\n"
|
|
"void main() \n"
|
|
"{ \n"
|
|
" vec4 diffTerm, specTerm; \n"
|
|
" float dotProd = max(dot(gl_LightSource[0].position.xyz, \n"
|
|
" normalize(normal)), 0.0);\n"
|
|
" float dotProd2 = max(dot(-gl_LightSource[0].position.xyz, \n"
|
|
" normalize(normal)), 0.0);\n"
|
|
" dotProd += dotProd2; \n"
|
|
" \n"
|
|
" diffTerm = diffuse * diffuseLight * dotProd; \n"
|
|
" if (%s) \n"
|
|
" diffTerm *= texture2D(diffTex, gl_TexCoord[0].st); \n"
|
|
" \n"
|
|
" specTerm = specular * specularLight * pow(dotProd, shininess); \n"
|
|
" if (%s) \n"
|
|
" specTerm *= textureCube(specTex, normal); \n"
|
|
" \n"
|
|
" gl_FragColor = ambient * ambientLight + diffTerm + specTerm; \n"
|
|
"} \n";
|
|
|
|
|
|
void
|
|
glmShaderMaterial(GLMmaterial *mat)
|
|
{
|
|
static const float ambientLight[4] = { 0.1, 0.1, 0.1, 0.0 };
|
|
static const float diffuseLight[4] = { 0.75, 0.75, 0.75, 1.0 };
|
|
static const float specularLight[4] = { 1.0, 1.0, 1.0, 0.0 };
|
|
|
|
if (!mat->prog) {
|
|
/* make shader now */
|
|
char newShader[10000];
|
|
GLuint vs, fs;
|
|
const char *diffuseTex = mat->texture_kd ? "true" : "false";
|
|
const char *specularTex = mat->texture_ks ? "true" : "false";
|
|
GLint uAmbientLight, uDiffuseLight, uSpecularLight;
|
|
|
|
/* replace %d with 0 or 1 */
|
|
sprintf(newShader, TexFragmentShader, diffuseTex, specularTex);
|
|
if (0)
|
|
printf("===== new shader =====\n%s\n============\n", newShader);
|
|
|
|
vs = CompileShaderText(GL_VERTEX_SHADER, VertexShader);
|
|
fs = CompileShaderText(GL_FRAGMENT_SHADER, newShader);
|
|
mat->prog = LinkShaders(vs, fs);
|
|
assert(mat->prog);
|
|
|
|
glUseProgram(mat->prog);
|
|
|
|
mat->uAmbient = glGetUniformLocation(mat->prog, "ambient");
|
|
mat->uDiffuse = glGetUniformLocation(mat->prog, "diffuse");
|
|
mat->uSpecular = glGetUniformLocation(mat->prog, "specular");
|
|
mat->uShininess = glGetUniformLocation(mat->prog, "shininess");
|
|
mat->uDiffTex = glGetUniformLocation(mat->prog, "diffTex");
|
|
mat->uSpecTex = glGetUniformLocation(mat->prog, "specTex");
|
|
|
|
uAmbientLight = glGetUniformLocation(mat->prog, "ambientLight");
|
|
uDiffuseLight = glGetUniformLocation(mat->prog, "diffuseLight");
|
|
uSpecularLight = glGetUniformLocation(mat->prog, "specularLight");
|
|
|
|
glUniform4fv(mat->uAmbient, 1, mat->ambient);
|
|
glUniform4fv(mat->uDiffuse, 1, mat->diffuse);
|
|
glUniform4fv(mat->uSpecular, 1, mat->specular);
|
|
glUniform1f(mat->uShininess, mat->shininess);
|
|
glUniform1i(mat->uDiffTex, 0);
|
|
glUniform1i(mat->uSpecTex, 1);
|
|
|
|
glUniform4fv(uAmbientLight, 1, ambientLight);
|
|
glUniform4fv(uDiffuseLight, 1, diffuseLight);
|
|
glUniform4fv(uSpecularLight, 1, specularLight);
|
|
}
|
|
|
|
glActiveTexture(GL_TEXTURE1);
|
|
if (mat->texture_ks)
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, mat->texture_ks);
|
|
else
|
|
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
|
|
|
|
glActiveTexture(GL_TEXTURE0);
|
|
if (mat->texture_kd)
|
|
glBindTexture(GL_TEXTURE_2D, mat->texture_kd);
|
|
else
|
|
glBindTexture(GL_TEXTURE_2D, 0);
|
|
|
|
if (mat->diffuse[3] < 1.0) {
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
}
|
|
else {
|
|
glDisable(GL_BLEND);
|
|
}
|
|
|
|
glUseProgram(mat->prog);
|
|
}
|
|
|
|
|
|
void
|
|
glmSpecularTexture(GLMmodel *model, uint cubeTex)
|
|
{
|
|
uint i;
|
|
|
|
for (i = 0; i < model->nummaterials; i++) {
|
|
model->materials[i].texture_ks = cubeTex;
|
|
}
|
|
}
|