SoftGPU: Quick and dirty lines and points implementation

No line/point texturing support yet.
This commit is contained in:
Henrik Rydgård 2013-12-09 12:43:49 +01:00
parent 888892bdf6
commit 68b08ffb9f
7 changed files with 356 additions and 139 deletions

View File

@ -99,7 +99,7 @@ static inline int CalcClipMask(const ClipCoords& v)
#define CLIP_LINE(PLANE_BIT, A, B, C, D) \
{ \
if (mask & PLANE_BIT) { \
if (mask & PLANE_BIT) { \
float dp0 = CLIP_DOTPROD(0, A, B, C, D ); \
float dp1 = CLIP_DOTPROD(1, A, B, C, D ); \
int i = 0; \
@ -158,6 +158,7 @@ void ProcessQuad(const VertexData& v0, const VertexData& v1)
bottomright = &buf[i];
}
// Four triangles to do backfaces as well. Two of them will get backface culled.
ProcessTriangle(*topleft, *topright, *bottomright);
ProcessTriangle(*bottomright, *topright, *topleft);
ProcessTriangle(*bottomright, *bottomleft, *topleft);
@ -197,6 +198,7 @@ void ProcessQuad(const VertexData& v0, const VertexData& v1)
bottomright = &buf[i];
}
// Four triangles to do backfaces as well. Two of them will get backface culled.
Rasterizer::DrawTriangle(*topleft, *topright, *bottomright);
Rasterizer::DrawTriangle(*bottomright, *topright, *topleft);
Rasterizer::DrawTriangle(*bottomright, *bottomleft, *topleft);
@ -204,6 +206,23 @@ void ProcessQuad(const VertexData& v0, const VertexData& v1)
}
}
void ProcessPoint(VertexData& v0)
{
// Points need no clipping.
Rasterizer::DrawPoint(v0);
}
void ProcessLine(VertexData& v0, VertexData& v1)
{
if (gstate.isModeThrough()) {
// Actually, should clip this one too so we don't need to do bounds checks in the rasterizer.
Rasterizer::DrawLine(v0, v1);
return;
}
// TODO: 3D lines
}
void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2)
{
if (gstate.isModeThrough()) {
@ -238,7 +257,7 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2)
if (mask & CLIP_NEG_Z_BIT)
return;
for(int i = 0; i < 3; i += 3) {
for (int i = 0; i < 3; i += 3) {
int vlist[2][2*6+1];
int *inlist = vlist[0], *outlist = vlist[1];
int n = 3;
@ -276,7 +295,7 @@ void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2)
return;
}
for(int i = 0; i+3 <= numIndices; i+=3)
for (int i = 0; i+3 <= numIndices; i+=3)
{
if(indices[i] != SKIP_FLAG)
{

View File

@ -21,6 +21,8 @@
namespace Clipper {
void ProcessPoint(VertexData& v0);
void ProcessLine(VertexData& v0, VertexData& v1);
void ProcessTriangle(VertexData& v0, VertexData& v1, VertexData& v2);
void ProcessQuad(const VertexData& v0, const VertexData& v1);

View File

@ -54,7 +54,7 @@ template <unsigned int texel_size_bits>
static inline int GetPixelDataOffset(unsigned int row_pitch_bits, unsigned int u, unsigned int v)
{
if (!gstate.isTextureSwizzled())
return v * row_pitch_bits *texel_size_bits/8 / 8 + u * texel_size_bits / 8;
return (v * (row_pitch_bits * texel_size_bits >> 6)) + (u * texel_size_bits >> 3);
const int tile_size_bits = 32;
const int tiles_in_block_horizontal = 4;
@ -64,7 +64,7 @@ static inline int GetPixelDataOffset(unsigned int row_pitch_bits, unsigned int u
int tile_u = u / texels_per_tile;
int tile_idx = (v % tiles_in_block_vertical) * (tiles_in_block_horizontal) +
// TODO: not sure if the *texel_size_bits/8 factor is correct
(v / tiles_in_block_vertical) * ((row_pitch_bits*texel_size_bits/8/tile_size_bits)*tiles_in_block_vertical) +
(v / tiles_in_block_vertical) * ((row_pitch_bits*texel_size_bits/(8*tile_size_bits))*tiles_in_block_vertical) +
(tile_u % tiles_in_block_horizontal) +
(tile_u / tiles_in_block_horizontal) * (tiles_in_block_horizontal*tiles_in_block_vertical);
@ -77,7 +77,6 @@ static inline u32 LookupColor(unsigned int index, unsigned int level)
const bool mipmapShareClut = gstate.isClutSharedForMipmaps();
const int clutSharingOffset = mipmapShareClut ? 0 : level * 16;
// TODO: No idea if these bswaps are correct
switch (gstate.getClutPaletteFormat()) {
case GE_TFMT_5650:
return DecodeRGB565(reinterpret_cast<u16*>(clut)[index + clutSharingOffset]);
@ -99,9 +98,6 @@ static inline u32 LookupColor(unsigned int index, unsigned int level)
static inline void GetTexelCoordinates(int level, float s, float t, int& out_u, int& out_v)
{
s = s * getFloat24(gstate.texscaleu) + getFloat24(gstate.texoffsetu);
t = t * getFloat24(gstate.texscalev) + getFloat24(gstate.texoffsetv);
int width = 1 << (gstate.texsize[level] & 0xf);
int height = 1 << ((gstate.texsize[level]>>8) & 0xf);
@ -131,9 +127,6 @@ static inline void GetTexelCoordinates(int level, float s, float t, int& out_u,
static inline void GetTexelCoordinatesQuad(int level, float in_s, float in_t, int u[4], int v[4], int &frac_u, int &frac_v)
{
in_s = in_s * getFloat24(gstate.texscaleu) + getFloat24(gstate.texoffsetu);
in_t = in_t * getFloat24(gstate.texscalev) + getFloat24(gstate.texoffsetv);
// 8 bits of fractional UV
int width = 1 << (gstate.texsize[level] & 0xf);
int height = 1 << ((gstate.texsize[level]>>8) & 0xf);
@ -182,12 +175,12 @@ static inline void GetTexelCoordinatesQuad(int level, float in_s, float in_t, in
static inline void GetTexelCoordinatesThrough(int level, int s, int t, int& u, int& v)
{
// Not actually sure which clamp/wrap modes should be applied. Let's just wrap for now.
int width = (1 << 8) << (gstate.texsize[level] & 0xf);
int height = (1 << 8) << ((gstate.texsize[level]>>8) & 0xf);
int width = 1 << (gstate.texsize[level] & 0xf);
int height = 1 << ((gstate.texsize[level]>>8) & 0xf);
// Wrap!
u = ((unsigned int)(s) & (width - 1)) >> 8;
v = ((unsigned int)(t) & (height - 1)) >> 8;
u = ((unsigned int)(s) & (width - 1));
v = ((unsigned int)(t) & (height - 1));
}
static inline void GetTextureCoordinates(const VertexData& v0, const VertexData& v1, const VertexData& v2, int w0, int w1, int w2, float& s, float& t)
@ -842,6 +835,10 @@ void DrawTriangleSlice(
Vec2<int> d01((int)v0.screenpos.x - (int)v1.screenpos.x, (int)v0.screenpos.y - (int)v1.screenpos.y);
Vec2<int> d02((int)v0.screenpos.x - (int)v2.screenpos.x, (int)v0.screenpos.y - (int)v2.screenpos.y);
Vec2<int> d12((int)v1.screenpos.x - (int)v2.screenpos.x, (int)v1.screenpos.y - (int)v2.screenpos.y);
float texScaleU = getFloat24(gstate.texscaleu);
float texScaleV = getFloat24(gstate.texscalev);
float texOffsetU = getFloat24(gstate.texoffsetu);
float texOffsetV = getFloat24(gstate.texoffsetv);
int bias0 = IsRightSideOrFlatBottomLine(v0.screenpos.xy(), v1.screenpos.xy(), v2.screenpos.xy()) ? -1 : 0;
int bias1 = IsRightSideOrFlatBottomLine(v1.screenpos.xy(), v2.screenpos.xy(), v0.screenpos.xy()) ? -1 : 0;
@ -931,15 +928,19 @@ void DrawTriangleSlice(
int v_texel = t * 256;
frac_u = u_texel & 0xff;
frac_v = v_texel & 0xff;
u_texel >>= 8;
v_texel >>= 8;
GetTexelCoordinatesThrough(0, u_texel, v_texel, u[0], v[0]);
if (bilinear) {
GetTexelCoordinatesThrough(0, u_texel + 256, v_texel, u[1], v[1]);
GetTexelCoordinatesThrough(0, u_texel, v_texel + 256, u[2], v[2]);
GetTexelCoordinatesThrough(0, u_texel + 256, v_texel + 256, u[3], v[3]);
GetTexelCoordinatesThrough(0, u_texel + 1, v_texel, u[1], v[1]);
GetTexelCoordinatesThrough(0, u_texel, v_texel + 1, u[2], v[2]);
GetTexelCoordinatesThrough(0, u_texel + 1, v_texel + 1, u[3], v[3]);
}
} else {
float s = 0, t = 0;
GetTextureCoordinates(v0, v1, v2, w0, w1, w2, s, t);
s = s * texScaleU + texOffsetU;
t = t * texScaleV + texOffsetV;
if (bilinear) {
GetTexelCoordinatesQuad(0, s, t, u, v, frac_u, frac_v);
} else {
@ -1062,6 +1063,7 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData&
if (d01.x * d02.y - d01.y * d02.x < 0)
return;
// Is the division and multiplication just &= ~0xF ?
int minX = std::min(std::min(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16;
int minY = std::min(std::min(v0.screenpos.y, v1.screenpos.y), v2.screenpos.y) / 16 * 16;
int maxX = std::max(std::max(v0.screenpos.x, v1.screenpos.x), v2.screenpos.x) / 16 * 16;
@ -1081,6 +1083,137 @@ void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData&
GlobalThreadPool::Loop(std::bind(&DrawTriangleSlice<false>, v0, v1, v2, minX, minY, maxX, maxY, placeholder::_1, placeholder::_2), 0, range);
}
void DrawPixel(ScreenCoords pos, Vec3<int> prim_color_rgb, int prim_color_a, Vec3<int> sec_color) {
// TODO: Texturing, blending etc.
ScreenCoords scissorTL(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX1(), gstate.getScissorY1(), 0)));
ScreenCoords scissorBR(TransformUnit::DrawingToScreen(DrawingCoords(gstate.getScissorX2(), gstate.getScissorY2(), 0)));
if (pos.x < scissorTL.x || pos.y < scissorTL.y || pos.x >= scissorBR.x || pos.y >= scissorBR.y)
return;
bool clearMode = gstate.isModeClear();
// TODO: Abstract out texture mapping so we can insert it here. Too big to duplicate.
if (gstate.isColorDoublingEnabled() && !clearMode) {
// TODO: Do we need to clamp here?
prim_color_rgb *= 2;
sec_color *= 2;
}
if (!clearMode)
prim_color_rgb += sec_color;
ScreenCoords pprime = pos;
// TODO: Fogging
DrawingCoords p = TransformUnit::ScreenToDrawing(pprime);
u16 z = pos.z;
// Depth range test
// TODO: Clear mode?
if (!gstate.isModeThrough())
if (z < gstate.getDepthRangeMin() || z > gstate.getDepthRangeMax())
return;
if (gstate.isColorTestEnabled() && !clearMode)
if (!ColorTestPassed(prim_color_rgb))
return;
// TODO: Does a need to be clamped?
if (gstate.isAlphaTestEnabled() && !clearMode)
if (!AlphaTestPassed(prim_color_a))
return;
// In clear mode, it uses the alpha color as stencil.
u8 stencil = clearMode ? prim_color_a : GetPixelStencil(p.x, p.y);
// TODO: Is it safe to ignore gstate.isDepthTestEnabled() when clear mode is enabled?
if (!clearMode && (gstate.isStencilTestEnabled() || gstate.isDepthTestEnabled())) {
if (gstate.isStencilTestEnabled() && !StencilTestPassed(stencil)) {
stencil = ApplyStencilOp(gstate.getStencilOpSFail(), p.x, p.y);
SetPixelStencil(p.x, p.y, stencil);
return;
}
// Also apply depth at the same time. If disabled, same as passing.
if (gstate.isDepthTestEnabled() && !DepthTestPassed(p.x, p.y, z)) {
if (gstate.isStencilTestEnabled()) {
stencil = ApplyStencilOp(gstate.getStencilOpZFail(), p.x, p.y);
SetPixelStencil(p.x, p.y, stencil);
}
return;
} else if (gstate.isStencilTestEnabled()) {
stencil = ApplyStencilOp(gstate.getStencilOpZPass(), p.x, p.y);
}
if (gstate.isDepthTestEnabled() && gstate.isDepthWriteEnabled()) {
SetPixelDepth(p.x, p.y, z);
}
} else if (clearMode && gstate.isClearModeDepthWriteEnabled()) {
SetPixelDepth(p.x, p.y, z);
}
if (gstate.isAlphaBlendEnabled() && !clearMode) {
Vec4<int> dst = Vec4<int>::FromRGBA(GetPixelColor(p.x, p.y));
prim_color_rgb = AlphaBlendingResult(prim_color_rgb, prim_color_a, dst);
}
if (!clearMode)
prim_color_rgb = prim_color_rgb.Clamp(0, 255);
u32 new_color = Vec4<int>(prim_color_rgb.r(), prim_color_rgb.g(), prim_color_rgb.b(), stencil).ToRGBA();
u32 old_color = GetPixelColor(p.x, p.y);
// TODO: Is alpha blending still performed if logic ops are enabled?
if (gstate.isLogicOpEnabled() && !clearMode) {
// Logic ops don't affect stencil.
new_color = (stencil << 24) | (ApplyLogicOp(gstate.getLogicOp(), old_color, new_color) & 0x00FFFFFF);
}
if (clearMode) {
new_color = (new_color & ~gstate.getClearModeColorMask()) | (old_color & gstate.getClearModeColorMask());
} else {
new_color = (new_color & ~gstate.getColorMask()) | (old_color & gstate.getColorMask());
}
SetPixelColor(p.x, p.y, new_color);
}
void DrawPoint(const VertexData &v0)
{
DrawPixel(v0.screenpos, v0.color0.rgb(), v0.color0.a(), v0.color1);
}
void DrawLine(const VertexData &v0, const VertexData &v1)
{
// TODO: Use a proper line drawing algorithm that handles fractional endpoints correctly.
Vec3<int> a(v0.screenpos.x, v0.screenpos.y, v0.screenpos.z);
Vec3<int> b(v1.screenpos.x, v1.screenpos.y, v0.screenpos.z);
int dx = b.x - a.x;
int dy = b.y - a.y;
int dz = b.z - a.z;
int steps;
if (abs(dx) < abs(dy))
steps = dy;
else
steps = dx;
float xinc = (float)dx / steps;
float yinc = (float)dy / steps;
float zinc = (float)dz / steps;
float x = a.x;
float y = a.y;
float z = a.z;
for (; steps >= 0; steps--) {
// TODO: interpolate color and UV over line
DrawPixel(ScreenCoords(x, y, z), v0.color0.rgb(), v0.color0.a(), v0.color1);
x = x + xinc;
y = y + yinc;
z = z + zinc;
}
}
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer)
{
buffer.Allocate(gstate.DepthBufStride(), 512, GPU_DBG_FORMAT_8BIT);

View File

@ -25,6 +25,8 @@ namespace Rasterizer {
// Draws a triangle if its vertices are specified in counter-clockwise order
void DrawTriangle(const VertexData& v0, const VertexData& v1, const VertexData& v2);
void DrawPoint(const VertexData &v0);
void DrawLine(const VertexData &v0, const VertexData &v1);
bool GetCurrentStencilbuffer(GPUDebugBuffer &buffer);
bool GetCurrentTexture(GPUDebugBuffer &buffer);

View File

@ -60,9 +60,9 @@ GLuint OpenGL_CompileProgram(const char* vertexShader, const char* fragmentShade
GLsizei stringBufferUsage = 0;
glGetShaderiv(vertexShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderInfoLog(vertexShaderID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
if (Result && stringBufferUsage) {
// not nice
} else if(!Result) {
} else if (!Result) {
// not nice
} else {
// not nice
@ -77,9 +77,9 @@ GLuint OpenGL_CompileProgram(const char* vertexShader, const char* fragmentShade
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
glGetShaderiv(fragmentShaderID, GL_COMPILE_STATUS, &Result);
glGetShaderInfoLog(fragmentShaderID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
if (Result && stringBufferUsage) {
// not nice
} else if(!Result) {
} else if (!Result) {
// not nice
} else {
// not nice
@ -95,9 +95,9 @@ GLuint OpenGL_CompileProgram(const char* vertexShader, const char* fragmentShade
#if defined(_DEBUG) || defined(DEBUGFAST) || defined(DEBUG_GLSL)
glGetProgramiv(programID, GL_LINK_STATUS, &Result);
glGetProgramInfoLog(programID, 1024, &stringBufferUsage, stringBuffer);
if(Result && stringBufferUsage) {
if (Result && stringBufferUsage) {
// not nice
} else if(!Result && !shader_errors) {
} else if (!Result && !shader_errors) {
// not nice
}
#endif
@ -327,11 +327,13 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff)
"RECTANGLES=6,",
};
if (type != GE_PRIM_TRIANGLES && type != GE_PRIM_TRIANGLE_STRIP && type != GE_PRIM_TRIANGLE_FAN && type != GE_PRIM_RECTANGLES) {
/*
if (type == GE_PRIM_POINTS || type == GE_PRIM_LINES || type == GE_PRIM_LINE_STRIP) {
ERROR_LOG_REPORT(G3D, "Software: DL DrawPrim type: %s count: %i vaddr= %08x, iaddr= %08x", type<7 ? types[type] : "INVALID", count, gstate_c.vertexAddr, gstate_c.indexAddr);
cyclesExecuted += EstimatePerVertexCost() * count;
break;
}
*/
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
ERROR_LOG_REPORT(G3D, "Software: Bad vertex address %08x!", gstate_c.vertexAddr);

View File

@ -23,12 +23,21 @@
#include "Clipper.h"
#include "Lighting.h"
static u8 buf[65536 * 48]; // yolo
static bool outside_range_flag = false;
WorldCoords TransformUnit::ModelToWorld(const ModelCoords& coords)
{
Mat3x3<float> world_matrix(gstate.worldMatrix);
return WorldCoords(world_matrix * coords) + Vec3<float>(gstate.worldMatrix[9], gstate.worldMatrix[10], gstate.worldMatrix[11]);
}
WorldCoords TransformUnit::ModelToWorldNormal(const ModelCoords& coords)
{
Mat3x3<float> world_matrix(gstate.worldMatrix);
return WorldCoords(world_matrix * coords);
}
ViewCoords TransformUnit::WorldToView(const WorldCoords& coords)
{
Mat3x3<float> view_matrix(gstate.viewMatrix);
@ -42,8 +51,6 @@ ClipCoords TransformUnit::ViewToClip(const ViewCoords& coords)
return ClipCoords(projection_matrix * coords4);
}
static bool outside_range_flag = false;
// TODO: This is ugly
static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool set_flag = true)
{
@ -61,8 +68,10 @@ static inline ScreenCoords ClipToScreenInternal(const ClipCoords& coords, bool s
float retz = coords.z * vpz1 / coords.w + vpz2;
if (gstate.clipEnable & 0x1) {
if (retz < 0.f) retz = 0.f;
if (retz > 65535.f) retz = 65535.f;
if (retz < 0.f)
retz = 0.f;
if (retz > 65535.f)
retz = 65535.f;
}
if (set_flag && (retx > 4095.9375f || rety > 4096.9375f || retx < 0 || rety < 0 || retz < 0 || retz > 65535.f))
@ -81,8 +90,8 @@ DrawingCoords TransformUnit::ScreenToDrawing(const ScreenCoords& coords)
{
DrawingCoords ret;
// TODO: What to do when offset > coord?
ret.x = (((u32)coords.x - gstate.getOffsetX16())/16) & 0x3ff;
ret.y = (((u32)coords.y - gstate.getOffsetY16())/16) & 0x3ff;
ret.x = (((u32)coords.x - gstate.getOffsetX16()) / 16) & 0x3ff;
ret.y = (((u32)coords.y - gstate.getOffsetY16()) / 16) & 0x3ff;
ret.z = coords.z;
return ret;
}
@ -163,8 +172,9 @@ static VertexData ReadVertex(VertexReader& vreader)
vertex.screenpos = ClipToScreenInternal(vertex.clippos);
if (vreader.hasNormal()) {
vertex.worldnormal = TransformUnit::ModelToWorld(vertex.normal) - Vec3<float>(gstate.worldMatrix[9], gstate.worldMatrix[10], gstate.worldMatrix[11]);
vertex.worldnormal /= vertex.worldnormal.Length(); // TODO: Shouldn't be necessary..
vertex.worldnormal = TransformUnit::ModelToWorldNormal(vertex.normal);
// TODO: Isn't there a flag that controls whether to normalize the normal?
vertex.worldnormal /= vertex.worldnormal.Length();
}
Lighting::Process(vertex);
@ -218,10 +228,10 @@ void TransformUnit::SubmitSpline(void* control_points, void* indices, int count_
for (int point = 0; point < 16; ++point) {
int idx = (patch_u + point%4) + (patch_v + point/4) * count_u;
if (indices)
vreader.Goto(indices_16bit ? indices16[idx] : indices8[idx]);
else
vreader.Goto(idx);
if (indices)
vreader.Goto(indices_16bit ? indices16[idx] : indices8[idx]);
else
vreader.Goto(idx);
patch.points[point] = ReadVertex(vreader);
}
@ -277,7 +287,6 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
return;
}
static u8 buf[65536 * 48]; // yolo
u16 index_lower_bound = 0;
u16 index_upper_bound = vertex_count - 1;
bool indices_16bit = (vertex_type & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT;
@ -291,125 +300,174 @@ void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type
const int max_vtcs_per_prim = 3;
int vtcs_per_prim = 0;
if (prim_type == GE_PRIM_POINTS) vtcs_per_prim = 1;
else if (prim_type == GE_PRIM_LINES) vtcs_per_prim = 2;
else if (prim_type == GE_PRIM_TRIANGLES) vtcs_per_prim = 3;
else if (prim_type == GE_PRIM_RECTANGLES) vtcs_per_prim = 2;
else {
// TODO: Unsupported
switch (prim_type) {
case GE_PRIM_POINTS: vtcs_per_prim = 1; break;
case GE_PRIM_LINES: vtcs_per_prim = 2; break;
case GE_PRIM_TRIANGLES: vtcs_per_prim = 3; break;
case GE_PRIM_RECTANGLES: vtcs_per_prim = 2; break;
}
if (prim_type == GE_PRIM_POINTS || prim_type == GE_PRIM_LINES || prim_type == GE_PRIM_TRIANGLES || prim_type == GE_PRIM_RECTANGLES) {
for (int vtx = 0; vtx < vertex_count; vtx += vtcs_per_prim) {
VertexData data[max_vtcs_per_prim];
VertexData data[max_vtcs_per_prim];
for (int i = 0; i < vtcs_per_prim; ++i) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx+i] : indices8[vtx+i]);
else
vreader.Goto(vtx+i);
// TODO: Do this in two passes - first process the vertices (before indexing/stripping),
// then resolve the indices. This lets us avoid transforming shared vertices twice.
data[i] = ReadVertex(vreader);
if (outside_range_flag)
switch (prim_type) {
case GE_PRIM_POINTS:
case GE_PRIM_LINES:
case GE_PRIM_TRIANGLES:
case GE_PRIM_RECTANGLES:
{
for (int vtx = 0; vtx < vertex_count; vtx += vtcs_per_prim) {
for (int i = 0; i < vtcs_per_prim; ++i) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx+i] : indices8[vtx+i]);
else
vreader.Goto(vtx+i);
data[i] = ReadVertex(vreader);
if (outside_range_flag)
break;
}
if (outside_range_flag) {
outside_range_flag = false;
continue;
}
switch (prim_type) {
case GE_PRIM_TRIANGLES:
{
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if (!gstate.getCullMode())
Clipper::ProcessTriangle(data[2], data[1], data[0]);
else
Clipper::ProcessTriangle(data[0], data[1], data[2]);
break;
}
if (outside_range_flag) {
outside_range_flag = false;
continue;
}
}
case GE_PRIM_RECTANGLES:
Clipper::ProcessQuad(data[0], data[1]);
break;
case GE_PRIM_LINES:
Clipper::ProcessLine(data[0], data[1]);
break;
case GE_PRIM_POINTS:
Clipper::ProcessPoint(data[0]);
break;
}
}
break;
}
case GE_PRIM_LINE_STRIP:
{
int skip_count = 1; // Don't draw a line when loading the first vertex
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx] : indices8[vtx]);
else
vreader.Goto(vtx);
data[vtx & 1] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
outside_range_flag = false;
continue;
}
if (skip_count) {
--skip_count;
} else {
Clipper::ProcessLine(data[(vtx & 1) ^ 1], data[vtx & 1]);
}
}
break;
}
case GE_PRIM_TRIANGLE_STRIP:
{
int skip_count = 2; // Don't draw a triangle when loading the first two vertices
for (int vtx = 0; vtx < vertex_count; ++vtx) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx] : indices8[vtx]);
else
vreader.Goto(vtx);
data[vtx % 3] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
outside_range_flag = false;
continue;
}
if (skip_count) {
--skip_count;
continue;
}
switch (prim_type) {
case GE_PRIM_TRIANGLES:
{
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if (!gstate.getCullMode())
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);
else
} else {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
break;
}
case GE_PRIM_RECTANGLES:
Clipper::ProcessQuad(data[0], data[1]);
break;
}
}
break;
}
} else if (prim_type == GE_PRIM_TRIANGLE_STRIP) {
VertexData data[3];
unsigned int skip_count = 2; // Don't draw a triangle when loading the first two vertices
for (int vtx = 0; vtx < vertex_count; ++vtx) {
case GE_PRIM_TRIANGLE_FAN:
{
unsigned int skip_count = 1; // Don't draw a triangle when loading the first two vertices
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx] : indices8[vtx]);
vreader.Goto(indices_16bit ? indices16[0] : indices8[0]);
else
vreader.Goto(vtx);
vreader.Goto(0);
data[0] = ReadVertex(vreader);
data[vtx % 3] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
outside_range_flag = false;
continue;
}
if (skip_count) {
--skip_count;
continue;
}
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
}
}
} else if (prim_type == GE_PRIM_TRIANGLE_FAN) {
VertexData data[3];
unsigned int skip_count = 1; // Don't draw a triangle when loading the first two vertices
if (indices)
vreader.Goto(indices_16bit ? indices16[0] : indices8[0]);
else
vreader.Goto(0);
data[0] = ReadVertex(vreader);
for (int vtx = 1; vtx < vertex_count; ++vtx) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx] : indices8[vtx]);
else
vreader.Goto(vtx);
data[2 - (vtx % 2)] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
outside_range_flag = false;
continue;
}
if (skip_count) {
--skip_count;
continue;
}
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
for (int vtx = 1; vtx < vertex_count; ++vtx) {
if (indices)
vreader.Goto(indices_16bit ? indices16[vtx] : indices8[vtx]);
else
vreader.Goto(vtx);
data[2 - (vtx % 2)] = ReadVertex(vreader);
if (outside_range_flag) {
// Drop all primitives containing the current vertex
skip_count = 2;
outside_range_flag = false;
continue;
}
if (skip_count) {
--skip_count;
continue;
}
if (!gstate.isCullEnabled() || gstate.isModeClear()) {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else if ((!gstate.getCullMode()) ^ (vtx % 2)) {
// We need to reverse the vertex order for each second primitive,
// but we additionally need to do that for every primitive if CCW cullmode is used.
Clipper::ProcessTriangle(data[2], data[1], data[0]);
} else {
Clipper::ProcessTriangle(data[0], data[1], data[2]);
}
}
break;
}
}

View File

@ -108,6 +108,7 @@ struct VertexData
class TransformUnit
{
public:
static WorldCoords ModelToWorldNormal(const ModelCoords& coords);
static WorldCoords ModelToWorld(const ModelCoords& coords);
static ViewCoords WorldToView(const WorldCoords& coords);
static ClipCoords ViewToClip(const ViewCoords& coords);