diff --git a/GPU/GLES/DisplayListInterpreter.cpp b/GPU/GLES/DisplayListInterpreter.cpp index 1662d17c06..47ebb62592 100644 --- a/GPU/GLES/DisplayListInterpreter.cpp +++ b/GPU/GLES/DisplayListInterpreter.cpp @@ -995,7 +995,7 @@ void GLES_GPU::ExecuteOp(u32 op, u32 diff) case GE_CMD_ALPHATEST: DEBUG_LOG(G3D,"DL Alpha test settings"); - shaderManager.DirtyUniform(DIRTY_ALPHAREF); + shaderManager.DirtyUniform(DIRTY_ALPHACOLORREF); break; case GE_CMD_TEXFUNC: diff --git a/GPU/GLES/DisplayListInterpreter.h b/GPU/GLES/DisplayListInterpreter.h index 8fe6ae971a..e7faf8c45a 100644 --- a/GPU/GLES/DisplayListInterpreter.h +++ b/GPU/GLES/DisplayListInterpreter.h @@ -52,7 +52,7 @@ private: // TransformPipeline.cpp void TransformAndDrawPrim(void *verts, void *inds, int prim, int vertexCount, float *customUV, int forceIndexType, int *bytesRead = 0); void ApplyDrawState(); - void Flush(); + void Flush(int prim); void UpdateViewportAndProjection(); void DrawBezier(int ucount, int vcount); void DoBlockTransfer(); diff --git a/GPU/GLES/FragmentShaderGenerator.cpp b/GPU/GLES/FragmentShaderGenerator.cpp index 0b4003c3a2..a7f11113bd 100644 --- a/GPU/GLES/FragmentShaderGenerator.cpp +++ b/GPU/GLES/FragmentShaderGenerator.cpp @@ -79,8 +79,8 @@ char *GenerateFragmentShader() if (doTexture) WRITE(p, "uniform sampler2D tex;\n"); - if (gstate.alphaTestEnable & 1) - WRITE(p, "uniform vec4 u_alpharef;\n"); + if ((gstate.alphaTestEnable & 1) || (gstate.colorTestEnable & 1)) + WRITE(p, "uniform vec4 u_alphacolorref;\n"); if (gstate.fogEnable & 1) { WRITE(p, "uniform vec3 u_fogcolor;\n"); WRITE(p, "uniform vec2 u_fogcoef;\n"); @@ -163,9 +163,20 @@ char *GenerateFragmentShader() int alphaTestFunc = gstate.alphatest & 7; const char *alphaTestFuncs[] = { "#", "#", " == ", " != ", " < ", " <= ", " > ", " >= " }; // never/always don't make sense if (alphaTestFuncs[alphaTestFunc][0] != '#') - WRITE(p, "if (!(v.a %s u_alpharef.x)) discard;", alphaTestFuncs[alphaTestFunc]); + WRITE(p, "if (!(v.a %s u_alphacolorref.a)) discard;", alphaTestFuncs[alphaTestFunc]); } + // Disabled for now until we actually find a need for it. + /* + if (gstate.colorTestEnable & 1) { + // TODO: There are some colortestmasks we could handle. + int colorTestFunc = gstate.colortest & 3; + const char *colorTestFuncs[] = { "#", "#", " == ", " != " }; // never/always don't make sense} + int colorTestMask = gstate.colormask; + if (colorTestFuncs[colorTestFunc][0] != '#') + WRITE(p, "if (!(v.rgb %s u_alphacolorref.rgb)) discard;", colorTestFuncs[colorTestFunc]); + }*/ + if (gstate.isFogEnabled()) { // Haven't figured out how to adjust the depth range yet. // WRITE(p, " v = mix(v, u_fogcolor, u_fogcoef.x + u_fogcoef.y * v_depth;\n"); diff --git a/GPU/GLES/ShaderManager.cpp b/GPU/GLES/ShaderManager.cpp index 78303948c8..34c24c0aeb 100644 --- a/GPU/GLES/ShaderManager.cpp +++ b/GPU/GLES/ShaderManager.cpp @@ -79,18 +79,21 @@ LinkedShader::LinkedShader(Shader *vs, Shader *fs) u_texenv = glGetUniformLocation(program, "u_texenv"); u_fogcolor = glGetUniformLocation(program, "u_fogcolor"); u_fogcoef = glGetUniformLocation(program, "u_fogcoef"); - u_alpharef = glGetUniformLocation(program, "u_alpharef"); + u_alphacolorref = glGetUniformLocation(program, "u_alphacolorref"); a_position = glGetAttribLocation(program, "a_position"); a_color0 = glGetAttribLocation(program, "a_color0"); a_color1 = glGetAttribLocation(program, "a_color1"); a_texcoord = glGetAttribLocation(program, "a_texcoord"); + a_normal = glGetAttribLocation(program, "a_normal"); + a_weight0123 = glGetAttribLocation(program, "a_weight0123"); + a_weight4567 = glGetAttribLocation(program, "a_weight4567"); glUseProgram(program); // Default uniform values glUniform1i(u_tex, 0); // The rest, use the "dirty" mechanism. - dirtyUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_TEXENV | DIRTY_ALPHAREF; + dirtyUniforms = DIRTY_PROJMATRIX | DIRTY_PROJTHROUGHMATRIX | DIRTY_TEXENV | DIRTY_ALPHACOLORREF; } LinkedShader::~LinkedShader() { @@ -135,8 +138,8 @@ void LinkedShader::use() { if (u_texenv != -1 && (dirtyUniforms & DIRTY_TEXENV)) { SetColorUniform3(u_texenv, gstate.texenvcolor); } - if (u_alpharef != -1 && (dirtyUniforms & DIRTY_ALPHAREF)) { - glUniform4f(u_alpharef, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f, 0.0f, 0.0f, 0.0f); + if (u_alphacolorref != -1 && (dirtyUniforms & DIRTY_ALPHACOLORREF)) { + glUniform4f(u_alphacolorref, 0.0f, 0.0f, 0.0f, ((float)((gstate.alphatest >> 8) & 0xFF)) / 255.0f); } if (u_fogcolor != -1 && (dirtyUniforms & DIRTY_FOGCOLOR)) { SetColorUniform3(u_fogcolor, gstate.fogcolor); diff --git a/GPU/GLES/ShaderManager.h b/GPU/GLES/ShaderManager.h index 33c5b308dc..5b4a98ee8c 100644 --- a/GPU/GLES/ShaderManager.h +++ b/GPU/GLES/ShaderManager.h @@ -40,8 +40,9 @@ struct LinkedShader int a_color0; int a_color1; int a_texcoord; - // int a_blendWeight0123; - // int a_blendWeight4567; + int a_normal; + int a_weight0123; + int a_weight4567; int u_tex; int u_proj; @@ -49,7 +50,7 @@ struct LinkedShader int u_texenv; // Fragment processing inputs - int u_alpharef; + int u_alphacolorref; int u_fogcolor; int u_fogcoef; @@ -66,7 +67,7 @@ enum DIRTY_FOGCOLOR = (1 << 2), DIRTY_FOGCOEF = (1 << 3), DIRTY_TEXENV = (1 << 4), - DIRTY_ALPHAREF = (1 << 5), + DIRTY_ALPHACOLORREF = (1 << 5), DIRTY_COLORREF = (1 << 6), DIRTY_LIGHT0 = (1 << 12), diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index b5569bfea4..f0aeec1a7d 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -203,6 +203,15 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte VertexDecoder dec; dec.SetVertexType(gstate.vertType); dec.DecodeVerts(decoded, verts, inds, prim, vertexCount, &indexLowerBound, &indexUpperBound); + if (bytesRead) + *bytesRead = vertexCount * dec.VertexSize(); + + // And here we should return, having collected the morphed but untransformed vertices. + // Note that DecodeVerts should convert strips into indexed lists etc, adding to our + // current vertex buffer and index buffer. + + // The rest below here should only execute on Flush. + #if 0 for (int i = indexLowerBound; i <= indexUpperBound; i++) { PrintDecodedVertex(decoded[i], gstate.vertType); @@ -222,9 +231,6 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte gpuStats.numDrawCalls++; gpuStats.numVertsTransformed += vertexCount; - if (bytesRead) - *bytesRead = vertexCount * dec.VertexSize(); - bool throughmode = (gstate.vertType & GE_VTYPE_THROUGH_MASK) != 0; /* @@ -535,8 +541,12 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte } } + // TODO: This should not be done on every drawcall, we should collect vertex data + // until critical state changes. That's when we draw (flush). + ApplyDrawState(); UpdateViewportAndProjection(); + LinkedShader *program = shaderManager_->ApplyShader(prim); // TODO: Make a cache for glEnableVertexAttribArray and glVertexAttribPtr states, these spam the gDebugger log. @@ -561,6 +571,61 @@ void GLES_GPU::TransformAndDrawPrim(void *verts, void *inds, int prim, int verte if (program->a_color1 != -1) glDisableVertexAttribArray(program->a_color1); } +struct GlTypeInfo { + GLuint type; + int count; + GLboolean normalized; +}; + +const GlTypeInfo GLComp[8] = { + {0}, // DEC_NONE, + {GL_FLOAT, 1, GL_FALSE}, // DEC_FLOAT_1, + {GL_FLOAT, 2, GL_FALSE}, // DEC_FLOAT_2, + {GL_FLOAT, 3, GL_FALSE}, // DEC_FLOAT_3, + {GL_FLOAT, 4, GL_FALSE}, // DEC_FLOAT_4, + {GL_BYTE, 3, GL_TRUE}, // DEC_S8_3, + {GL_SHORT, 3, GL_TRUE},// DEC_S16_3, + {GL_BYTE, 4, GL_TRUE},// DEC_U8_4, +}; + +static inline void VertexAttribSetup(int attrib, int fmt, int stride, u8 *ptr) { + if (attrib != -1 && fmt) { + const GlTypeInfo &type = GLComp[fmt]; + glEnableVertexAttribArray(attrib); + glVertexAttribPointer(attrib, type.count, type.type, type.normalized, stride, ptr); + } +} +static inline void VertexAttribDisable(int attrib, int fmt) { + if (attrib != -1 && fmt) { + glDisableVertexAttribArray(attrib); + } +} + +// TODO: Use VBO and get rid of the vertexData pointers - with that, we will supply only offsets +static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt, u8 *vertexData) { + VertexAttribSetup(program->a_weight0123, decFmt.w0fmt, decFmt.stride, vertexData + decFmt.w0off); + VertexAttribSetup(program->a_weight4567, decFmt.w1fmt, decFmt.stride, vertexData + decFmt.w1off); + VertexAttribSetup(program->a_texcoord, decFmt.uvfmt, decFmt.stride, vertexData + decFmt.uvoff); + VertexAttribSetup(program->a_color0, decFmt.c0fmt, decFmt.stride, vertexData + decFmt.c0off); + VertexAttribSetup(program->a_color1, decFmt.c1fmt, decFmt.stride, vertexData + decFmt.c1off); + VertexAttribSetup(program->a_normal, decFmt.nrmfmt, decFmt.stride, vertexData + decFmt.nrmoff); + VertexAttribSetup(program->a_position, decFmt.posfmt, decFmt.stride, vertexData + decFmt.posoff); +} + +static void DesetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt) { + VertexAttribDisable(program->a_weight0123, decFmt.w0fmt); + VertexAttribDisable(program->a_weight4567, decFmt.w1fmt); + VertexAttribDisable(program->a_texcoord, decFmt.uvfmt); + VertexAttribDisable(program->a_color0, decFmt.c0fmt); + VertexAttribDisable(program->a_color1, decFmt.c1fmt); + VertexAttribDisable(program->a_normal, decFmt.nrmfmt); + VertexAttribDisable(program->a_position, decFmt.posfmt); +} + +void GLES_GPU::Flush(int prim) { + // TODO +} + void GLES_GPU::ApplyDrawState() { diff --git a/GPU/GLES/VertexDecoder.cpp b/GPU/GLES/VertexDecoder.cpp index 33f3281865..91f9aebf0f 100644 --- a/GPU/GLES/VertexDecoder.cpp +++ b/GPU/GLES/VertexDecoder.cpp @@ -22,8 +22,7 @@ #include "VertexDecoder.h" -void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype) -{ +void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype) { if (vtype & GE_VTYPE_NRM_MASK) printf("N: %f %f %f\n", vtx.normal[0], vtx.normal[1], vtx.normal[2]); if (vtype & GE_VTYPE_TC_MASK) printf("TC: %f %f\n", vtx.uv[0], vtx.uv[1]); if (vtype & GE_VTYPE_COL_MASK) printf("C: %02x %02x %02x %02x\n", vtx.color[0], vtx.color[1], vtx.color[2], vtx.color[3]); @@ -37,67 +36,137 @@ const int nrmsize[4] = {0,3,6,12}, nrmalign[4] = {0,1,2,4}; const int possize[4] = {0,3,6,12}, posalign[4] = {0,1,2,4}; const int wtsize[4] = {0,1,2,4}, wtalign[4] = {0,1,2,4}; -inline int align(int n, int align) -{ +inline int align(int n, int align) { return (n + (align - 1)) & ~(align - 1); } -void VertexDecoder::SetVertexType(u32 fmt) -{ +int DecFmtSize(u8 fmt) { + switch (fmt) { + case DEC_NONE: return 0; + case DEC_FLOAT_1: return 4; + case DEC_FLOAT_2: return 8; + case DEC_FLOAT_3: return 12; + case DEC_FLOAT_4: return 16; + case DEC_S8_3: return 4; + case DEC_S16_3: return 8; + case DEC_U8_4: return 4; + default: + return 0; + } +} + +// This is what the software transform spits out, and thus w +DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt) { + DecVtxFormat tfm = {0}; + int size = 0; + int offset = 0; + // Weights disappear during transform. + if (fmt.uvfmt) { + // UV always becomes float2. + tfm.uvfmt = DEC_FLOAT_2; + tfm.uvoff = offset; + offset += DecFmtSize(tfm.uvfmt); + } + // We always (?) get two colors out, they're floats (although we'd probably be fine with less precision). + tfm.c0fmt = DEC_FLOAT_4; + tfm.c0off = offset; + offset += DecFmtSize(tfm.c0fmt); + tfm.c1fmt = DEC_FLOAT_3; // color1 (specular) doesn't have alpha. + tfm.c1off = offset; + offset += DecFmtSize(tfm.c1fmt); + // We never get a normal, it's gone. + // But we do get a position, and it's always float3. + tfm.posfmt = DEC_FLOAT_3; + tfm.posoff = offset; + offset += DecFmtSize(tfm.posfmt); + // Update stride. + tfm.stride = offset; + return tfm; +} + +void VertexDecoder::SetVertexType(u32 fmt) { fmt = fmt; throughmode = (fmt & GE_VTYPE_THROUGH) != 0; int biggest = 0; size = 0; - tc = fmt & 0x3; - col = (fmt >> 2) & 0x7; - nrm = (fmt >> 5) & 0x3; - pos = (fmt >> 7) & 0x3; + tc = fmt & 0x3; + col = (fmt >> 2) & 0x7; + nrm = (fmt >> 5) & 0x3; + pos = (fmt >> 7) & 0x3; weighttype = (fmt >> 9) & 0x3; - idx = (fmt >> 11) & 0x3; + idx = (fmt >> 11) & 0x3; morphcount = ((fmt >> 18) & 0x7)+1; - nweights = ((fmt >> 14) & 0x7)+1; + nweights = ((fmt >> 14) & 0x7)+1; + + int decOff = 0; + memset(&decFmt, 0, sizeof(decFmt)); DEBUG_LOG(G3D,"VTYPE: THRU=%i TC=%i COL=%i POS=%i NRM=%i WT=%i NW=%i IDX=%i MC=%i", (int)throughmode, tc,col,pos,nrm,weighttype,nweights,idx,morphcount); - if (weighttype) - { + if (weighttype) { // && nweights? //size = align(size, wtalign[weighttype]); unnecessary size += wtsize[weighttype] * nweights; if (wtalign[weighttype] > biggest) biggest = wtalign[weighttype]; + + if (nweights < 5) { + decFmt.w0off = decOff; + decFmt.w0fmt = DEC_FLOAT_1 + nweights - 1; + } else { + decFmt.w0off = decOff; + decFmt.w0fmt = DEC_FLOAT_4; + decFmt.w1off = decOff + 4 * 4; + decFmt.w1fmt = DEC_FLOAT_1 + nweights - 5; + } + decOff += nweights * 4; } - if (tc) - { + if (tc) { size = align(size, tcalign[tc]); tcoff = size; size += tcsize[tc]; if (tcalign[tc] > biggest) biggest = tcalign[tc]; + + // All UV decode to DEC_FLOAT2 currently. + decFmt.uvfmt = DEC_FLOAT_2; + decFmt.uvoff = decOff; + decOff += DecFmtSize(decFmt.uvfmt); } - if (col) - { + if (col) { size = align(size, colalign[col]); coloff = size; size += colsize[col]; if (colalign[col] > biggest) biggest = colalign[col]; - } - else - { + + // All color formats decode to DEC_U8_4 currently. + // They can become floats later during transform though. + decFmt.c0fmt = DEC_U8_4; + decFmt.c0off = decOff; + decOff += DecFmtSize(decFmt.c0fmt); + } else { coloff = 0; } - if (nrm) - { + if (nrm) { size = align(size, nrmalign[nrm]); nrmoff = size; size += nrmsize[nrm]; if (nrmalign[nrm] > biggest) biggest = nrmalign[nrm]; + + // The normal formats match the gl formats perfectly, let's use 'em. + switch (nrm) { + case GE_VTYPE_NRM_8BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S8_3; break; + case GE_VTYPE_NRM_16BIT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_S16_3; break; + case GE_VTYPE_NRM_FLOAT >> GE_VTYPE_NRM_SHIFT: decFmt.nrmfmt = DEC_FLOAT_3; break; + } + decFmt.nrmoff = decOff; + decOff += DecFmtSize(decFmt.nrmfmt); } //if (pos) - there's always a position @@ -107,6 +176,19 @@ void VertexDecoder::SetVertexType(u32 fmt) size += possize[pos]; if (posalign[pos] > biggest) biggest = posalign[pos]; + + if (throughmode) { + decFmt.posfmt = DEC_FLOAT_3; + } else { + // The non-through-mode position formats match the gl formats perfectly, let's use 'em. + switch (pos) { + case GE_VTYPE_POS_8BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S8_3; break; + case GE_VTYPE_POS_16BIT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_S16_3; break; + case GE_VTYPE_POS_FLOAT >> GE_VTYPE_POS_SHIFT: decFmt.posfmt = DEC_FLOAT_3; break; + } + } + decFmt.posoff = decOff; + decOff += DecFmtSize(decFmt.posfmt); } size = align(size, biggest); @@ -343,14 +425,16 @@ void VertexDecoder::DecodeVerts(DecodedVertex *decoded, const void *verts, const case GE_VTYPE_POS_8BIT >> 7: { + float multiplier = 1.0f / 127.0f; + if (throughmode) multiplier = 1.0f; const s8 *sv = (const s8*)(ptr + posoff); for (int j = 0; j < 3; j++) - v[j] = sv[j] / 127.f; + v[j] = sv[j] * multiplier; } break; default: - ERROR_LOG(G3D,"Unknown position format %i",pos); + ERROR_LOG(G3D, "Unknown position format %i",pos); break; } } else { diff --git a/GPU/GLES/VertexDecoder.h b/GPU/GLES/VertexDecoder.h index 464ebf72a1..fb02f42218 100644 --- a/GPU/GLES/VertexDecoder.h +++ b/GPU/GLES/VertexDecoder.h @@ -21,6 +21,36 @@ #include "../Globals.h" #include "base/basictypes.h" +// DecVtxFormat - vertex formats for PC +// Kind of like a D3D VertexDeclaration. +// Can write code to easily bind these using OpenGL, or read these manually. +// No morph support, that is taken care of by the VertexDecoder. + +enum { + DEC_NONE, + DEC_FLOAT_1, + DEC_FLOAT_2, + DEC_FLOAT_3, + DEC_FLOAT_4, + DEC_S8_3, + DEC_S16_3, + DEC_U8_4, +}; + +int DecFmtSize(u8 fmt); + +struct DecVtxFormat { + u8 w0fmt; u8 w0off; // first 4 weights + u8 w1fmt; u8 w1off; // second 4 weights + u8 uvfmt; u8 uvoff; + u8 c0fmt; u8 c0off; // First color + u8 c1fmt; u8 c1off; + u8 nrmfmt; u8 nrmoff; + u8 posfmt; u8 posoff; + short stride; +}; + +// This is going away soon enough. struct DecodedVertex { float pos[3]; // in case of morph, preblend during decode @@ -30,6 +60,7 @@ struct DecodedVertex float weights[8]; // ugh, expensive }; +// This struct too. struct TransformedVertex { float x, y, z; // in case of morph, preblend during decode @@ -38,31 +69,37 @@ struct TransformedVertex float color1[4]; // prelit }; +DecVtxFormat GetTransformedVtxFormat(const DecVtxFormat &fmt); // Right now // - only contains computed information // - does decoding in nasty branchfilled loops // Future TODO +// - should be cached, not recreated every time +// - will compile into list of called functions // - will compile into lighting fast specialized x86 and ARM // - will not bother translating components that can be read directly -// by OpenGL ES. Will still have to translate 565 colors, and things +// by OpenGL ES. Will still have to translate 565 colors and things // like that. DecodedVertex will not be a fixed struct. Will have to // do morphing here. -// -// We want 100% perf on 1Ghz even in vertex complex games! class VertexDecoder { public: VertexDecoder() : coloff(0), nrmoff(0), posoff(0) {} ~VertexDecoder() {} + void SetVertexType(u32 vtype); + + const DecVtxFormat &GetDecVtxFmt() { return decFmt; } void DecodeVerts(DecodedVertex *decoded, const void *verts, const void *inds, int prim, int count, int *indexLowerBound, int *indexUpperBound) const; bool hasColor() const { return col != 0; } int VertexSize() const { return size; } private: u32 fmt; + DecVtxFormat decFmt; + bool throughmode; int biggest; int size; @@ -84,6 +121,114 @@ private: int nweights; }; + +// Reads decoded vertex formats in a convenient way. For software transform and debugging. +class VertexReader +{ +public: + VertexReader(u8 *data, const DecVtxFormat &decFmt) : data_(data), decFmt_(decFmt) {} + + void ReadPos(float pos[3]) { + switch (decFmt_.posfmt) { + case DEC_FLOAT_3: memcpy(pos, data_ + decFmt_.posoff, 12); break; + case DEC_S16_3: + { + s16 *p = (s16 *)(data_ + decFmt_.posoff); + for (int i = 0; i < 3; i++) + pos[i] = p[i] / 32767.0f; + } + break; + case DEC_S8_3: + { + s8 *p = (s8 *)(data_ + decFmt_.posoff); + for (int i = 0; i < 3; i++) + pos[i] = p[i] / 127.0f; + } + break; + } + } + + void ReadNrm(float nrm[3]) { + switch (decFmt_.nrmoff) { + case DEC_FLOAT_3: memcpy(nrm, data_ + decFmt_.nrmoff, 12); break; + case DEC_S16_3: + { + s16 *p = (s16 *)(data_ + decFmt_.nrmoff); + for (int i = 0; i < 3; i++) + nrm[i] = p[i] / 32767.0f; + } + break; + case DEC_S8_3: + { + s8 *p = (s8 *)(data_ + decFmt_.nrmoff); + for (int i = 0; i < 3; i++) + nrm[i] = p[i] / 127.0f; + } + break; + } + } + + void ReadUV(float uv[2]) { + switch (decFmt_.uvfmt) { + case DEC_FLOAT_2: + memcpy(uv, data_ + decFmt_.nrmoff, 8); break; + } + } + + void ReadColor0(float color[4]) { + switch (decFmt_.c0fmt) { + case DEC_U8_4: + { + u8 *p = (u8 *)(data_ + decFmt_.c0off); + for (int i = 0; i < 4; i++) + color[i] = p[i] / 255.0f; + } + break; + case DEC_FLOAT_4: + memcpy(color, data_ + decFmt_.c0off, 16); break; + } + } + + void ReadColor1(float color[4]) { + switch (decFmt_.c1fmt) { + case DEC_U8_4: + { + u8 *p = (u8 *)(data_ + decFmt_.c1off); + for (int i = 0; i < 4; i++) + color[i] = p[i] / 255.0f; + } + break; + case DEC_FLOAT_4: + memcpy(color, data_ + decFmt_.c1off, 16); break; + } + } + + void ReadWeights(float weights[8]) { + switch (decFmt_.w0fmt) { + case DEC_FLOAT_1: memcpy(weights, data_ + decFmt_.w0off, 4); break; + case DEC_FLOAT_2: memcpy(weights, data_ + decFmt_.w0off, 8); break; + case DEC_FLOAT_3: memcpy(weights, data_ + decFmt_.w0off, 12); break; + case DEC_FLOAT_4: memcpy(weights, data_ + decFmt_.w0off, 16); break; + } + switch (decFmt_.w1fmt) { + case DEC_FLOAT_1: memcpy(weights + 4, data_ + decFmt_.w1off, 4); break; + case DEC_FLOAT_2: memcpy(weights + 4, data_ + decFmt_.w1off, 8); break; + case DEC_FLOAT_3: memcpy(weights + 4, data_ + decFmt_.w1off, 12); break; + case DEC_FLOAT_4: memcpy(weights + 4, data_ + decFmt_.w1off, 16); break; + } + } + + void Next() { + data_ += decFmt_.stride; + } + +private: + u8 *data_; + DecVtxFormat decFmt_; + int vtype_; +}; + // Debugging utilities void PrintDecodedVertex(const DecodedVertex &vtx, u32 vtype); +