1
0
mirror of https://github.com/XProger/OpenLara.git synced 2025-08-17 18:36:43 +02:00

remove raw GAPI calls from Mesh and MeshRange classes

This commit is contained in:
XProger
2018-04-26 03:46:40 +03:00
parent dc9f2ee1c5
commit ec987ecc13
7 changed files with 312 additions and 294 deletions

View File

@@ -76,6 +76,7 @@ struct ShaderCache {
compile(Core::passAmbient, Shader::ROOM, fx | FX_UNDERWATER, rsFull | RS_DISCARD);
compile(Core::passAmbient, Shader::SPRITE, fx, rsFull | RS_DISCARD);
compile(Core::passAmbient, Shader::SPRITE, fx | FX_UNDERWATER, rsFull | RS_DISCARD);
compile(Core::passAmbient, Shader::FLASH, fx, rsFull);
}
void prepareShadows(int fx) {

View File

@@ -376,6 +376,63 @@ struct Vertex {
ubyte4 light; // xyz - color, w - use premultiplied alpha
};
struct MeshRange {
int iStart;
int iCount;
int vStart;
int aIndex;
uint16 tile;
uint16 clut;
MeshRange() : iStart(0), iCount(0), vStart(0), aIndex(-1), tile(0), clut(0) {}
};
#define SHADER_ATTRIBS(E) \
E( aCoord ) \
E( aNormal ) \
E( aTexCoord ) \
E( aColor ) \
E( aLight )
#define SHADER_SAMPLERS(E) \
E( sDiffuse ) \
E( sNormal ) \
E( sReflect ) \
E( sShadow ) \
E( sEnvironment ) \
E( sMask )
#define SHADER_UNIFORMS(E) \
E( uParam ) \
E( uTexParam ) \
E( uViewProj ) \
E( uBasis ) \
E( uLightProj ) \
E( uMaterial ) \
E( uAmbient ) \
E( uFogParams ) \
E( uViewPos ) \
E( uLightPos ) \
E( uLightColor ) \
E( uAnimTexRanges ) \
E( uAnimTexOffsets ) \
E( uRoomSize ) \
E( uPosScale ) \
E( uContacts )
enum AttribType { SHADER_ATTRIBS(DECL_ENUM) aMAX };
enum SamplerType { SHADER_SAMPLERS(DECL_ENUM) sMAX };
enum UniformType { SHADER_UNIFORMS(DECL_ENUM) uMAX };
const char *AttribName[aMAX] = { SHADER_ATTRIBS(DECL_STR) };
const char *SamplerName[sMAX] = { SHADER_SAMPLERS(DECL_STR) };
const char *UniformName[uMAX] = { SHADER_UNIFORMS(DECL_STR) };
#undef SHADER_ATTRIBS
#undef SHADER_SAMPLERS
#undef SHADER_UNIFORMS
enum CullMode { cmNone, cmBack, cmFront };
enum BlendMode { bmNone, bmAlpha, bmAdd, bmMult, bmPremult, bmMAX };

View File

@@ -329,6 +329,7 @@ namespace GAPI {
int cullMode, blendMode;
// Texture
struct Texture {
uint32 ID;
int width, height, origWidth, origHeight;
@@ -460,6 +461,155 @@ namespace GAPI {
}
};
// Mesh
struct Mesh {
Index *iBuffer;
GAPI::Vertex *vBuffer;
GLuint *VAO;
GLuint ID[2];
int iCount;
int vCount;
int aCount;
bool dynamic;
Mesh(bool dynamic) : iBuffer(NULL), vBuffer(NULL), VAO(NULL), dynamic(dynamic) {
ID[0] = ID[1] = NULL;
}
void init(Index *indices, int iCount, ::Vertex *vertices, int vCount, int aCount) {
this->iCount = iCount;
this->vCount = vCount;
this->aCount = aCount;
if (Core::support.VAO)
glBindVertexArray(Core::active.VAO = 0);
#ifdef DYNGEOM_NO_VBO
if (!vertices && !indices) {
iBuffer = new Index[iCount];
vBuffer = new GAPI::Vertex[vCount];
return;
}
#endif
ASSERT(sizeof(GAPI::Vertex) == sizeof(::Vertex));
glGenBuffers(2, ID);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ID[0]);
glBindBuffer(GL_ARRAY_BUFFER, ID[1]);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iCount * sizeof(Index), indices, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vCount * sizeof(Vertex), vertices, dynamic ? GL_DYNAMIC_DRAW : GL_STATIC_DRAW);
if (Core::support.VAO && aCount) {
VAO = new GLuint[aCount];
glGenVertexArrays(aCount, VAO);
}
}
void deinit() {
if (iBuffer || vBuffer) {
delete[] iBuffer;
delete[] vBuffer;
} else {
if (VAO) {
glDeleteVertexArrays(aCount, VAO);
delete[] VAO;
}
glDeleteBuffers(2, ID);
}
}
void update(Index *indices, int iCount, ::Vertex *vertices, int vCount) {
ASSERT(sizeof(GAPI::Vertex) == sizeof(::Vertex));
if (Core::support.VAO && Core::active.VAO != 0)
glBindVertexArray(Core::active.VAO = 0);
if (indices && iCount) {
if (iBuffer) {
memcpy(iBuffer, indices, iCount * sizeof(Index));
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, iCount * sizeof(Index), indices);
}
}
if (vertices && vCount) {
if (vBuffer) {
memcpy(vBuffer, vertices, vCount * sizeof(GAPI::Vertex));
} else {
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, vCount * sizeof(GAPI::Vertex), vertices);
}
}
}
void setupFVF(GAPI::Vertex *v) const {
#ifdef FFP
glTexCoordPointer (2, GL_SHORT, sizeof(*v), &v->texCoord);
glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(*v), &v->light);
glNormalPointer ( GL_SHORT, sizeof(*v), &v->normal);
glVertexPointer (3, GL_SHORT, sizeof(*v), &v->coord);
#else
glEnableVertexAttribArray(aCoord);
glEnableVertexAttribArray(aNormal);
glEnableVertexAttribArray(aTexCoord);
glEnableVertexAttribArray(aColor);
glEnableVertexAttribArray(aLight);
glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(*v), &v->coord);
glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(*v), &v->normal);
glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(*v), &v->texCoord);
glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->color);
glVertexAttribPointer(aLight, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->light);
#endif
}
void bind(const MeshRange &range) const {
if (range.aIndex == -1) {
if (Core::active.iBuffer != ID[0])
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]);
if (Core::active.vBuffer != ID[1])
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]);
setupFVF(vBuffer + range.vStart);
} else {
ASSERT(Core::support.VAO);
GLuint vao = VAO[range.aIndex];
if (Core::active.VAO != vao)
glBindVertexArray(Core::active.VAO = vao);
}
}
void initNextRange(MeshRange &range, int &aIndex) const {
if (Core::support.VAO && VAO) {
ASSERT(aIndex < aCount);
Core::active.iBuffer = 0;
Core::active.vBuffer = 0;
Core::active.VAO = 0;
range.aIndex = aIndex++; // get new VAO index
bind(range); // bind VAO
range.aIndex = -1; // reset VAO to -1
bind(range); // bind buffers and setup vertex format
range.aIndex = aIndex - 1; // set VAO index back to the new
glBindVertexArray(Core::active.VAO = 0);
} else
range.aIndex = -1;
}
/*
void unbind() {
if (Core::support.VAO)
glBindVertexArray(Core::active.VAO = 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = 0);
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = 0);
}
*/
};
GLuint FBO, defaultFBO;
struct RenderTargetCache {
int count;

View File

@@ -22,6 +22,7 @@ namespace GAPI {
short3 coord;
};
// Texture
struct Texture {
uint8 *memory;
int width, height, origWidth, origHeight;
@@ -105,6 +106,83 @@ namespace GAPI {
}
};
// Mesh
struct Mesh {
Index *iBuffer;
GAPI::Vertex *vBuffer;
int iCount;
int vCount;
bool dynamic;
Mesh(bool dynamic) : iBuffer(NULL), vBuffer(NULL), dynamic(dynamic) {}
void init(Index *indices, int iCount, ::Vertex *vertices, int vCount, int aCount, bool dynamic) {
this->iCount = iCount;
this->vCount = vCount;
this->dynamic = dynamic;
if (dynamic)
return;
#ifdef EDRAM_MESH
iBuffer = (Index*)Core::allocEDRAM(iCount * sizeof(Index));
vBuffer = (Vertex*)Core::allocEDRAM(vCount * sizeof(Vertex));
#else
iBuffer = new Index[iCount];
vBuffer = new Vertex[vCount];
#endif
update(indices, iCount, vertices, vCount);
}
void deinit() {
if (dynamic)
return;
#ifndef EDRAM_MESH
delete[] iBuffer;
delete[] vBuffer;
#endif
}
void update(Index *indices, int iCount, ::Vertex *vertices, int vCount) {
if (dynamic) {
iBuffer = (Index*)sceGuGetMemory(iCount * sizeof(Index));
vBuffer = (Vertex*)sceGuGetMemory(vCount * sizeof(Vertex));
}
if (indices)
memcpy(iBuffer, indices, iCount * sizeof(indices[0]));
if (vertices) {
::Vertex *src = vertices;
Vertex *dst = vBuffer;
for (int i = 0; i < vCount; i++) {
dst->texCoord = short2(src->texCoord.x, src->texCoord.y);
dst->color = ubyte4(src->light.x, src->light.y, src->light.z, src->light.w); //color;
dst->normal = src->normal;
dst->coord = src->coord;
dst++;
src++;
}
}
}
void bind(const MeshRange &range) const {
ASSERT(iBuffer != NULL);
ASSERT(vBuffer != NULL);
Core::active.iBuffer = iBuffer;
Core::active.vBuffer = vBuffer + range.vStart;
}
void initNextRange(MeshRange &range, int &aIndex) const {
range.aIndex = -1;
}
};
int cullMode, blendMode;
@@ -150,7 +228,6 @@ namespace GAPI {
LOG("VRAM : %d\n", EDRAM_SIZE);
freeEDRAM();
support.maxAniso = 1;
support.maxVectors = 0;
support.shaderBinary = false;

View File

@@ -1690,6 +1690,10 @@ struct Level : IGame {
Core::setBlendMode(bmAlpha);
renderEntitiesTransp(transp);
#ifdef FFP
Core::whiteTex->bind(0);
#endif
Core::setBlendMode(bmMult);
for (int i = 0; i < level.entitiesCount; i++) {
TR::Entity &entity = level.entities[i];
@@ -1698,6 +1702,10 @@ struct Level : IGame {
controller->renderShadow(mesh);
}
Core::setBlendMode(bmNone);
#ifdef FFP
atlas->bind(0);
#endif
}
if (transp == 2) {

View File

@@ -7,57 +7,6 @@
TR::ObjectTexture barTile[5 /* UI::BAR_MAX */];
TR::ObjectTexture &whiteTile = barTile[4]; // BAR_WHITE
struct MeshRange {
int iStart;
int iCount;
int vStart;
int aIndex;
uint16 tile;
uint16 clut;
MeshRange() : iStart(0), iCount(0), vStart(0), aIndex(-1), tile(0), clut(0) {}
#ifdef FFP
#ifdef _OS_PSP
void setup(void *vBuffer) const {
Core::active.vBuffer += vStart;
}
#else
void setup(void *vBuffer) const {
GAPI::Vertex *v = (GAPI::Vertex*)vBuffer + vStart;
glTexCoordPointer (2, GL_SHORT, sizeof(*v), &v->texCoord);
glColorPointer (4, GL_UNSIGNED_BYTE, sizeof(*v), &v->light);
glNormalPointer ( GL_SHORT, sizeof(*v), &v->normal);
glVertexPointer (3, GL_SHORT, sizeof(*v), &v->coord);
}
#endif
void bind(uint32 *VAO) const {}
#else
void setup(void *vBuffer) const {
glEnableVertexAttribArray(aCoord);
glEnableVertexAttribArray(aNormal);
glEnableVertexAttribArray(aTexCoord);
glEnableVertexAttribArray(aColor);
glEnableVertexAttribArray(aLight);
GAPI::Vertex *v = (GAPI::Vertex*)vBuffer + vStart;
glVertexAttribPointer(aCoord, 4, GL_SHORT, false, sizeof(*v), &v->coord);
glVertexAttribPointer(aNormal, 4, GL_SHORT, true, sizeof(*v), &v->normal);
glVertexAttribPointer(aTexCoord, 4, GL_SHORT, true, sizeof(*v), &v->texCoord);
glVertexAttribPointer(aColor, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->color);
glVertexAttribPointer(aLight, 4, GL_UNSIGNED_BYTE, true, sizeof(*v), &v->light);
}
void bind(GLuint *VAO) const {
GLuint vao = aIndex == -1 ? 0 : VAO[aIndex];
if (Core::support.VAO && Core::active.VAO != vao)
glBindVertexArray(Core::active.VAO = vao);
}
#endif
};
#define PLANE_DETAIL 48
#define CIRCLE_SEGS 16
@@ -65,177 +14,23 @@ struct MeshRange {
#define DOUBLE_SIDED 2
#define MAX_ROOM_DYN_FACES 512
struct Mesh {
Index *iBuffer;
GAPI::Vertex *vBuffer;
#ifndef _OS_PSP
GLuint ID[2];
GLuint *VAO;
#endif
struct Mesh : GAPI::Mesh {
int aIndex;
int iCount;
int vCount;
int aCount;
int aIndex;
bool cmdBufAlloc;
#ifdef _OS_PSP
Mesh(int iCount, int vCount, bool dynamic) : iCount(iCount), vCount(vCount), aCount(0), aIndex(-1), cmdBufAlloc(true) {
iBuffer = (Index*)sceGuGetMemory(iCount * sizeof(Index));
vBuffer = (GAPI::Vertex*)sceGuGetMemory(vCount * sizeof(GAPI::Vertex));
}
#endif
Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount) : iCount(iCount), vCount(vCount), aCount(aCount), aIndex(0), cmdBufAlloc(false) {
#ifdef _OS_PSP
#ifdef EDRAM_MESH
iBuffer = (Index*)Core::allocEDRAM(iCount * sizeof(Index));
vBuffer = (GAPI::Vertex*)Core::allocEDRAM(vCount * sizeof(GAPI::Vertex));
#else
iBuffer = new Index[iCount];
vBuffer = new GAPI::Vertex[vCount];
#endif
update(indices, iCount, vertices, vCount);
#else
VAO = NULL;
if (Core::support.VAO)
glBindVertexArray(Core::active.VAO = 0);
#ifdef DYNGEOM_NO_VBO
if (!vertices && !indices) {
ID[0] = ID[1] = 0;
iBuffer = new Index[iCount];
vBuffer = new GAPI::Vertex[vCount];
return;
}
#endif
glGenBuffers(2, ID);
bind(true);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, iCount * sizeof(Index), indices, GL_STATIC_DRAW);
glBufferData(GL_ARRAY_BUFFER, vCount * sizeof(GAPI::Vertex), vertices, GL_STATIC_DRAW);
if (Core::support.VAO && aCount) {
VAO = new GLuint[aCount];
glGenVertexArrays(aCount, VAO);
}
iBuffer = NULL;
vBuffer = NULL;
#endif
}
void update(Index *indices, int iCount, Vertex *vertices, int vCount) {
#ifdef _OS_PSP
if (indices)
memcpy(iBuffer, indices, iCount * sizeof(indices[0]));
if (vertices) {
Vertex *src = vertices;
GAPI::Vertex *dst = vBuffer;
for (int i = 0; i < vCount; i++) {
dst->texCoord = short2(src->texCoord.x, src->texCoord.y);
dst->color = ubyte4(src->light.x, src->light.y, src->light.z, src->light.w); //color;
dst->normal = src->normal;
dst->coord = src->coord;
dst++;
src++;
}
}
#else
// !!! typeof vertices[0] == Vertex == GAPI::Vertex
ASSERT(sizeof(GAPI::Vertex) == sizeof(Vertex));
if (Core::support.VAO)
glBindVertexArray(Core::active.VAO = 0);
if (indices && iCount) {
if (iBuffer) {
memcpy(iBuffer, indices, iCount * sizeof(Index));
} else {
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]);
glBufferSubData(GL_ELEMENT_ARRAY_BUFFER, 0, iCount * sizeof(Index), indices);
}
}
if (vertices && vCount) {
if (vBuffer) {
memcpy(vBuffer, vertices, vCount * sizeof(GAPI::Vertex));
} else {
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]);
glBufferSubData(GL_ARRAY_BUFFER, 0, vCount * sizeof(GAPI::Vertex), vertices);
}
}
#endif
Mesh(Index *indices, int iCount, Vertex *vertices, int vCount, int aCount, bool dynamic) : GAPI::Mesh(dynamic), aIndex(0) {
init(indices, iCount, vertices, vCount, aCount);
}
virtual ~Mesh() {
#ifdef _OS_PSP
#ifndef EDRAM_MESH
if (!cmdBufAlloc) {
delete[] iBuffer;
delete[] vBuffer;
}
#endif
#else
if (iBuffer || vBuffer) {
delete[] iBuffer;
delete[] vBuffer;
} else {
if (VAO) {
glDeleteVertexArrays(aCount, VAO);
delete[] VAO;
}
glDeleteBuffers(2, ID);
}
#endif
deinit();
}
void initRange(MeshRange &range) {
#ifndef _OS_PSP
if (Core::support.VAO && VAO) {
ASSERT(aIndex < aCount);
range.aIndex = aIndex++;
range.bind(VAO);
bind(true);
range.setup(vBuffer);
unbind();
} else
#endif
range.aIndex = -1;
}
void bind(bool force = false) {
#ifdef _OS_PSP
Core::active.iBuffer = iBuffer;
Core::active.vBuffer = vBuffer;
#else
if (force || Core::active.iBuffer != ID[0])
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = ID[0]);
if (force || Core::active.vBuffer != ID[1])
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = ID[1]);
#endif
}
static void unbind() {
if (Core::support.VAO)
glBindVertexArray(Core::active.VAO = 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, Core::active.iBuffer = 0);
glBindBuffer(GL_ARRAY_BUFFER, Core::active.vBuffer = 0);
initNextRange(range, aIndex);
}
void render(const MeshRange &range) {
if (range.aIndex == -1)
bind();
#ifndef _OS_PSP
range.bind(VAO);
#endif
if (range.aIndex == -1)
range.setup(vBuffer);
bind(range);
Core::DIP(range.iStart, range.iCount, iBuffer);
}
};
@@ -277,10 +72,8 @@ uint8 intensity(int lighting) {
}
struct MeshBuilder {
#ifndef _OS_PSP
MeshRange dynRange;
Mesh *dynMesh;
#endif
Mesh *mesh;
Texture *atlas;
@@ -374,12 +167,10 @@ struct MeshBuilder {
};
MeshBuilder(TR::Level &level, Texture *atlas) : atlas(atlas), level(&level) {
#ifndef _OS_PSP
dynMesh = new Mesh(NULL, DYN_MESH_QUADS * 6, NULL, DYN_MESH_QUADS * 4, 1);
dynMesh = new Mesh(NULL, DYN_MESH_QUADS * 6, NULL, DYN_MESH_QUADS * 4, 1, true);
dynRange.vStart = 0;
dynRange.iStart = 0;
dynMesh->initRange(dynRange);
#endif
// allocate room geometry ranges
rooms = new RoomRange[level.roomsCount];
@@ -742,7 +533,7 @@ struct MeshBuilder {
LOG("MegaMesh (i:%d v:%d a:%d, size:%d)\n", iCount, vCount, aCount, int(iCount * sizeof(Index) + vCount * sizeof(GAPI::Vertex)));
// compile buffer and ranges
mesh = new Mesh(indices, iCount, vertices, vCount, aCount);
mesh = new Mesh(indices, iCount, vertices, vCount, aCount, false);
delete[] indices;
delete[] vertices;
@@ -805,9 +596,7 @@ struct MeshBuilder {
delete[] models;
delete[] sequences;
delete mesh;
#ifndef _OS_PSP
delete dynMesh;
#endif
}
void flipMap() {
@@ -1119,15 +908,15 @@ struct MeshBuilder {
y0 = y1 = y;
}
#ifndef MERGE_SPRITES
if (!expand) {
vec3 pos = vec3(float(x), float(y), float(z));
quad[0].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.t), 0 ));
quad[1].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.t), 0 ));
quad[2].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.b), 0 ));
quad[3].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.b), 0 ));
} else
#endif
#ifndef MERGE_SPRITES
if (!expand) {
vec3 pos = vec3(float(x), float(y), float(z));
quad[0].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.t), 0 ));
quad[1].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.t), 0 ));
quad[2].coord = coordTransform(pos, vec3( float(sprite.r), float(-sprite.b), 0 ));
quad[3].coord = coordTransform(pos, vec3( float(sprite.l), float(-sprite.b), 0 ));
} else
#endif
{
quad[0].coord = short4( x0, y0, z, 0 );
quad[1].coord = short4( x1, y0, z, 0 );
@@ -1226,20 +1015,11 @@ struct MeshBuilder {
addQuad(indices, iCount, vCount, 0, vertices, NULL, false); vCount += 4;
addQuad(indices, iCount, vCount, 0, vertices, NULL, false); vCount += 4;
}
void bind() {
mesh->bind();
}
void renderBuffer(Index *indices, int iCount, Vertex *vertices, int vCount) {
if (!iCount) return;
ASSERT(vCount > 0);
#ifdef _OS_PSP
MeshRange dynRange;
Mesh cmdBufMesh(iCount, vCount);
Mesh *dynMesh = &cmdBufMesh;
#endif
dynRange.iStart = 0;
dynRange.iCount = iCount;
@@ -1427,15 +1207,7 @@ struct MeshBuilder {
}
void renderShadowBlob() {
#ifdef _OS_PSP
sceGuDisable(GU_TEXTURE_2D);
#endif
mesh->render(shadowBlob);
#ifdef _OS_PSP
sceGuEnable(GU_TEXTURE_2D);
#endif
}
void renderQuad() {

View File

@@ -3,53 +3,6 @@
#include "core.h"
#define SHADER_ATTRIBS(E) \
E( aCoord ) \
E( aNormal ) \
E( aTexCoord ) \
E( aColor ) \
E( aLight )
#define SHADER_SAMPLERS(E) \
E( sDiffuse ) \
E( sNormal ) \
E( sReflect ) \
E( sShadow ) \
E( sEnvironment ) \
E( sMask )
#define SHADER_UNIFORMS(E) \
E( uParam ) \
E( uTexParam ) \
E( uViewProj ) \
E( uBasis ) \
E( uLightProj ) \
E( uMaterial ) \
E( uAmbient ) \
E( uFogParams ) \
E( uViewPos ) \
E( uLightPos ) \
E( uLightColor ) \
E( uAnimTexRanges ) \
E( uAnimTexOffsets ) \
E( uRoomSize ) \
E( uPosScale ) \
E( uContacts )
enum AttribType { SHADER_ATTRIBS(DECL_ENUM) aMAX };
enum SamplerType { SHADER_SAMPLERS(DECL_ENUM) sMAX };
enum UniformType { SHADER_UNIFORMS(DECL_ENUM) uMAX };
const char *AttribName[aMAX] = { SHADER_ATTRIBS(DECL_STR) };
const char *SamplerName[sMAX] = { SHADER_SAMPLERS(DECL_STR) };
const char *UniformName[uMAX] = { SHADER_UNIFORMS(DECL_STR) };
#undef SHADER_ATTRIBS
#undef SHADER_SAMPLERS
#undef SHADER_UNIFORMS
#undef ENUM
#undef STR
struct Shader {
vec4 params[uMAX][4];