mirror of
https://github.com/hrydgard/ppsspp.git
synced 2024-11-23 13:30:02 +00:00
SoftGPU: Quick and dirty lines and points implementation
No line/point texturing support yet.
This commit is contained in:
parent
888892bdf6
commit
68b08ffb9f
@ -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)
|
||||
{
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user