There's nothing wrong with the billboard code per se; gnm_BlinnPhongShader binding occured in the wrong place respective to the billboard code call. Two fixes in mobj.h follow below.
Fix for Mobj_DrawUsingProgrammablePipeline():
void Mobj_DrawUsingProgrammablePipeline(IN long nSpecularMode, IN long nUseTexture) { // MLL 02-08-2018: no more ListView
MobjMesh* pMesh = 0;
MobjMat* pMaterial = 0;
long nI = 0, nCount = 0, nTexture = 0, nFlagBump = 0, nGlassIsUsed = 0, doSpherical = 0;
long nFlagTextures = Mobj_enableTextures(0, 0); // ML: 11-24-2015
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
long nVertexSize = sizeof(MobjVertexT); // Mobj_getVertexSize();
//long nMeshes = Mobj_getNumberOfMeshes();
float rShine = 64.0f; // 128.0f;
long nClockwise = Mobj_gl_cw(0, 0);
long nLights = 3; // ML 02-01-2018: keep it a uniform for when number of lights becomes per-material :)
long nLightFlags = gP.nLightFlags & 7; // ML 02-01-2018: another uniform with respective light on/off bitfield flags
bool doRefresh = true; // ML 01-18-2018: need or not to refresh current MODELVIEW matrix
long isBillboard = 0; // ML 01-18-2018: faster local cache (checked twice)
for (nI = 0; nI < gnm_numberOfMeshes; ++nI) {
// MLL 02-08-2018: !!! SLOW !!! There may be thousands of meshes like e.g. in the AmericanBeauty model!
//if (IsWindow(hCheckList)) {
// nChecked = ListView_GetCheckState(hCheckList, nI);
//}
//if (nChecked) {
pMesh = >m_meshes[nI];
if (pMesh->isVisible) {
pMaterial = pMesh->pMaterial;
isBillboard = pMaterial->isBillboard; // ML 01-18-2018:
// MLL 02-17-2018: !!! TEMPORARILY DISABLED !!!
//if (!isSphereInFrustum(grm_frustum, pMesh->centerPoint, pMesh->meshRadius)) continue; // ML: 03-14-2016
if (pMaterial->isA2C) { glEnable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); } // PAT: 01-07-2018
glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, pMaterial->ambient);
glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, pMaterial->diffuse);
glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, pMaterial->specular);
glMaterialfv(GL_FRONT_AND_BACK, GL_EMISSION, pMaterial->emissive); // ML 01-02-2018: glow
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, pMaterial->shininess * rShine);
pMaterial->ambient[3] = pMaterial->alpha;
pMaterial->diffuse[3] = pMaterial->alpha;
pMaterial->specular[3] = pMaterial->alpha;
pMaterial->emissive[3] = pMaterial->alpha; // ML 01-02-2018: glow
nTexture = 0;
doSpherical = 0; // ML: 11-26-2015
if ((pMaterial->alpha < 1.0f) || (pMaterial->illum == 12)) { nGlassIsUsed = -1; } // 11-27-2015
// 12-09-2015 ML: !!! can't do "Specular mode" in multishader !!!
// ML 01-18-2018: diffuse only mapping for billboards for now
if (!pMaterial->bumpMapID || nSpecularMode || isBillboard) {
//// Per fragment Blinn-Phong code path. // MLL 02-27-2018: bypass billboard code below
//glUseProgram(gnm_BlinnPhongShader);
// Bind the color map texture. // ML: 11-26-2015
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
if (nFlagTextures && !nSpecularMode) { // ML: 12-09-2015
if ((pMaterial->reflMapID) && (pMaterial->illum > 2)) { // 11-13-2015
doSpherical = 1;
if ((pMaterial->illum == 4) || (pMaterial->illum == 11)) { // global reflection
nTexture = nUseTexture;
} else { // individual reflection
nTexture = pMaterial->reflMapID;
}
} else if (pMaterial->illum == 11) {
doSpherical = 1;
nTexture = nUseTexture;
} else {
nTexture = pMaterial->colorMapID;
if (nTexture == 0) { nTexture = gnm_NullTexture; }
}
} else {
if (nSpecularMode) {
doSpherical = 1;
if (nFlagTextures) {
if ((pMaterial->reflMapID) && (pMaterial->illum > 2)) {
nTexture = pMaterial->reflMapID;
} else {
nTexture = pMaterial->colorMapID;
}
if (nTexture == 0) { nTexture = nUseTexture; }
} else {
nTexture = nUseTexture;
}
} else {
nTexture = gnm_NullTexture;
}
}
//GL_BindTexture(GL_TEXTURE_2D, nTexture);
glBindTexture(GL_TEXTURE_2D, nTexture);
if (isBillboard) { // ML 01-18-2018: let it ride w/o shaders for the time being
glFrontFace(GL_CW);
billBoard(isBillboard, pMesh, &doRefresh);
if (!nClockwise) {
glFrontFace(GL_CCW);
}
goto doneBillboard;
}
// Per fragment Blinn-Phong code path.
glUseProgram(gnm_BlinnPhongShader); // MLL 02-27-2018: bypass billboard code above
// Update shader parameters.
glUniform1i(glGetUniformLocation(gnm_BlinnPhongShader, "colorMap"), 0);
glUniform1i(glGetUniformLocation(gnm_BlinnPhongShader, "nLights"), nLights); // ML: 12-11-2015
glUniform1i(glGetUniformLocation(gnm_BlinnPhongShader, "nLightFlags"), nLightFlags); // ML 02-01-2018:
glUniform1i(glGetUniformLocation(gnm_BlinnPhongShader, "doSpherical"), doSpherical); // ML: 11-26-2015
glUniform1f(glGetUniformLocation(gnm_BlinnPhongShader, "materialAlpha"), pMaterial->alpha);
} else {
// Normal mapping code path.
glUseProgram(gnm_NormalMappingShader);
// Bind the specular map texture. // ML: 11-05-2015
glActiveTexture(GL_TEXTURE2); nFlagBump = -1;
glEnable(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(gnm_NormalMappingShader, "sSpecmap"), 2);
if (nFlagTextures) { // ML: 11-24-2015
if (pMaterial->specMapID) {
nTexture = pMaterial->specMapID;
} else {
if (pMaterial->colorMapID) {
nTexture = pMaterial->colorMapID; // !!! ML: LEAVE IT BE !!!
} else {
nTexture = gnm_NullTexture;
}
}
} else {
nTexture = gnm_NullTexture;
}
glBindTexture(GL_TEXTURE_2D, nTexture);
// Bind the normal map texture. // ML: 11-05-2015
glActiveTexture(GL_TEXTURE1); nFlagBump = -1;
glEnable(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(gnm_NormalMappingShader, "sNormalmap"), 1);
if (nFlagTextures) { // ML: 11-24-2015
if (pMaterial->bumpMapID) {
nTexture = pMaterial->bumpMapID;
} else {
nTexture = gnm_NullBump;
}
} else {
nTexture = gnm_NullBump;
}
glBindTexture(GL_TEXTURE_2D, nTexture);
// Bind the color map texture.
glActiveTexture(GL_TEXTURE0);
glEnable(GL_TEXTURE_2D);
glUniform1i(glGetUniformLocation(gnm_NormalMappingShader, "sBasemap"), 0);
if (nFlagTextures) { // ML: 11-24-2015
if (pMaterial->colorMapID) {
nTexture = pMaterial->colorMapID;
} else {
nTexture = gnm_NullTexture;
}
} else {
nTexture = gnm_NullTexture;
}
glBindTexture(GL_TEXTURE_2D, nTexture);
glUniform1i(glGetUniformLocation(gnm_NormalMappingShader, "nLights"), nLights); // ML: 12-11-2015
glUniform1i(glGetUniformLocation(gnm_NormalMappingShader, "nLightFlags"), nLightFlags); // ML 02-01-2018:
glUniform1f(glGetUniformLocation(gnm_NormalMappingShader, "materialAlpha"), pMaterial->alpha);
}
// MLL 02-08-2018: start using VBO's
glBindBuffer(GL_ARRAY_BUFFER, gP.nVBO);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, gP.nIBO);
// Render mesh.
glClientActiveTexture(GL_TEXTURE0);
if (gnm_hasPositions) {
glEnableClientState(GL_VERTEX_ARRAY);
//glVertexPointer(3, GL_FLOAT, nVertexSize, Mobj_getVertexBufferPosition());
glVertexPointer(3, GL_FLOAT, sizeof(MobjVertexT), (GLvoid*)0); // MLL 02-08-2018: 0 byte offset to 1st vertex pos
}
if (gnm_hasTextureCoords) {
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer(2, GL_FLOAT, nVertexSize, Mobj_getVertexBufferTexCoord());
glTexCoordPointer(2, GL_FLOAT, sizeof(MobjVertexT), (GLvoid*)12); // MLL 02-08-2018: 12 byte offset to 1st tex coords
}
if (gnm_hasNormals) {
glEnableClientState(GL_NORMAL_ARRAY);
//glNormalPointer(GL_FLOAT, nVertexSize, Mobj_getVertexBufferNormal());
glNormalPointer(GL_FLOAT, sizeof(MobjVertexT), (GLvoid*)20); // MLL 02-08-2018: 20 byte offset to 1st normal
}
if (gnm_hasTangents) {
glClientActiveTexture(GL_TEXTURE1);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer(4, GL_FLOAT, nVertexSize, Mobj_getVertexBufferTangent());
// MLL 02-21-2018: 36 (?) byte offset to 1st tangent. I don't know why it is so;
// should be 32 bytes. (buffer pack/unpack alignment?)
glTexCoordPointer(4, GL_FLOAT, sizeof(MobjVertexT), (GLvoid*)(32 + 4));
}
if (nFlagBump) {
// ML: 11-05-2015
if (gnm_hasTextureCoords) {
glClientActiveTexture(GL_TEXTURE2);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
//glTexCoordPointer(2, GL_FLOAT, nVertexSize, Mobj_getVertexBufferTexCoord());
glTexCoordPointer(2, GL_FLOAT, sizeof(MobjVertexT), (GLvoid*)12); // MLL 02-08-2018: 12 byte offset to 1st tex coords
}
}
nCount = pMesh->triangleCount * 3;
//const GLvoid* indices = Mobj_getIndexBuffer() + pMesh->startIndex;
const GLvoid* indices = (GLvoid*)(pMesh->startIndex * 4); // MLL 02-08-2018: byte offset to mesh 1st index
if (nGlassIsUsed) { // if we are using transparency then get rid of artefacts
glEnable(GL_CULL_FACE);
glFrontFace(nClockwise ? GL_CCW : GL_CW);
glDrawElements(GL_TRIANGLES, nCount, GL_UNSIGNED_INT, indices);
glFrontFace(nClockwise ? GL_CW : GL_CCW);
}
glDrawElements(GL_TRIANGLES, nCount, GL_UNSIGNED_INT, indices);
if (nGlassIsUsed) { // 11-27-2015
if (!Mobj_CullBackFace(0, 0)) { glDisable(GL_CULL_FACE); } // TODO: remove Mobj_() !!!
}
if (nFlagBump) { // ML: 11-05-2015
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (gnm_hasTextureCoords) {
glClientActiveTexture(GL_TEXTURE2);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
nFlagBump = 0;
} else { // ML 01-18-2018: else it's impossible to get rid of these shitty tex coords in immediate mode!
if (doSpherical) {
glUniform1i(glGetUniformLocation(gnm_BlinnPhongShader, "doSpherical"), 0);
}
}
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, 0);
glDisable(GL_TEXTURE_2D);
if (gnm_hasTangents) {
glClientActiveTexture(GL_TEXTURE1);
glDisableClientState(GL_TEXTURE_COORD_ARRAY);
}
// MLL 02-08-2018: unbind VBO's
glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
doneBillboard: // ML 01-21-2018: immediate mode for billboards is emulated by the OpenGL driver with an intrinsic shader,
// so our billboard epilogue should also include disabling client states even though we didn't enable them.
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, 0);
glClientActiveTexture(GL_TEXTURE0);
if (gnm_hasNormals) { glDisableClientState(GL_NORMAL_ARRAY); }
if (gnm_hasTextureCoords) { glDisableClientState(GL_TEXTURE_COORD_ARRAY); }
if (gnm_hasPositions) { glDisableClientState(GL_VERTEX_ARRAY); }
if (pMaterial->isA2C) { glDisable(GL_SAMPLE_ALPHA_TO_COVERAGE_ARB); } // PAT: 01-07-2018
}
}
glUseProgram(0);
//glDisable(GL_BLEND);
}
Fix for printFPS():
void printFPS(int x, char* str, int newFPS) {
static bool fpsInited = false;
char* s = str;
int i, j, idx = 0;
if (!fpsInited)
fpsInit(&fpsInited);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glLoadIdentity();
glTranslatef(0.0f, gP.glHeight - 1.0f, 0.0f);
glScalef(1.0f, -1.0f, 1.0f);
glMatrixMode(GL_PROJECTION);
glPushMatrix();
glLoadIdentity();
glOrtho(0.0, gP.glWidth, 0.0, gP.glHeight, -1.0, 1.0);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef(1.0f / float(g_digitW * 10), 1.0f / float(g_digitH), 1.0f);
glMatrixMode(GL_MODELVIEW);
glPushAttrib(GL_ENABLE_BIT);
glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
glDisable(GL_DEPTH_TEST);
//glDisable(GL_MULTISAMPLE_ARB); // MLL 02-27-2018: doesn't get pushed on attrib stack!
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindTexture(GL_TEXTURE_2D, gnm_fpsTexture);
x += 1;
while (*s) {
i = x + g_digitW;
idx = (*s++) - 48;
j = idx * g_digitW;//(idx << 5) / 2;
glBegin(GL_QUADS);
glTexCoord2i(j, 0); glVertex2i(x, g_digitH);
glTexCoord2i(j + g_digitW, 0); glVertex2i(i, g_digitH);
glTexCoord2i(j + g_digitW, g_digitH); glVertex2i(i, 0);
glTexCoord2i(j, g_digitH); glVertex2i(x, 0);
glEnd();
x += g_digitW;
}
glBindTexture(GL_TEXTURE_2D, 0);
glPopAttrib();
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
}