softgpu: Implement spline surface drawing (without patch subdivision).

This commit is contained in:
Tony Wasserka 2013-07-24 17:59:21 +02:00 committed by neobrain
parent f4cb929073
commit 11a94e1d14
5 changed files with 115 additions and 1 deletions

View file

@ -351,6 +351,8 @@ struct GPUgstate
int getWeightMask() const { return vertType & GE_VTYPE_WEIGHT_MASK; }
int getNumBoneWeights() const { return 1 + ((vertType & GE_VTYPE_WEIGHTCOUNT_MASK) >> GE_VTYPE_WEIGHTCOUNT_SHIFT); }
GEPatchPrimType getPatchPrimitiveType() const { return static_cast<GEPatchPrimType>(patchprimitive & 3); }
// Real data in the context ends here
};

View file

@ -315,7 +315,28 @@ void SoftGPU::ExecuteOp(u32 op, u32 diff)
int sp_vcount = (data >> 8) & 0xFF;
int sp_utype = (data >> 16) & 0x3;
int sp_vtype = (data >> 18) & 0x3;
//drawSpline(sp_ucount, sp_vcount, sp_utype, sp_vtype);
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
ERROR_LOG(G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
break;
}
void *control_points = Memory::GetPointer(gstate_c.vertexAddr);
void *indices = NULL;
if ((gstate.vertType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
if (!Memory::IsValidAddress(gstate_c.indexAddr)) {
ERROR_LOG(G3D, "Bad index address %08x!", gstate_c.indexAddr);
break;
}
indices = Memory::GetPointer(gstate_c.indexAddr);
}
if (gstate.getPatchPrimitiveType() != GE_PATCHPRIM_TRIANGLES) {
ERROR_LOG(G3D, "Unsupported patch primitive %x", gstate.patchprimitive&3);
break;
}
TransformUnit::SubmitSpline(control_points, indices, sp_ucount, sp_vcount, sp_utype, sp_vtype, gstate.patchprimitive&3, gstate.vertType);
DEBUG_LOG(G3D,"DL DRAW SPLINE: %i x %i, %i x %i", sp_ucount, sp_vcount, sp_utype, sp_vtype);
}
break;

View file

@ -138,6 +138,89 @@ static VertexData ReadVertex(VertexReader& vreader)
return vertex;
}
#define START_OPEN_U 1
#define END_OPEN_U 2
#define START_OPEN_V 4
#define END_OPEN_V 8
struct SplinePatch {
VertexData points[16];
int type;
};
void TransformUnit::SubmitSpline(void* control_points, void* indices, int count_u, int count_v, int type_u, int type_v, u32 prim_type, u32 vertex_type)
{
VertexDecoder vdecoder;
vdecoder.SetVertexType(vertex_type);
const DecVtxFormat& vtxfmt = vdecoder.GetDecVtxFmt();
static u8 buf[65536 * 48]; // yolo
u16 index_lower_bound = 0;
u16 index_upper_bound = count_u * count_v - 1;
bool indices_16bit = (vertex_type & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_16BIT;
u8* indices8 = (u8*)indices;
u16* indices16 = (u16*)indices;
if (indices)
GetIndexBounds(indices, count_u*count_v, vertex_type, &index_lower_bound, &index_upper_bound);
vdecoder.DecodeVerts(buf, control_points, index_lower_bound, index_upper_bound);
VertexReader vreader(buf, vtxfmt, vertex_type);
int num_patches_u = count_u - 3;
int num_patches_v = count_v - 3;
// TODO: Do something less idiotic to manage this buffer
SplinePatch* patches = new SplinePatch[num_patches_u * num_patches_v];
for (int patch_u = 0; patch_u < num_patches_u; ++patch_u) {
for (int patch_v = 0; patch_v < num_patches_v; ++patch_v) {
SplinePatch& patch = patches[patch_u + patch_v * num_patches_u];
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);
patch.points[point] = ReadVertex(vreader);
}
patch.type = (type_u | (type_v<<2));
if (patch_u != 0) patch.type &= ~START_OPEN_U;
if (patch_v != 0) patch.type &= ~START_OPEN_V;
if (patch_u != num_patches_u-1) patch.type &= ~END_OPEN_U;
if (patch_v != num_patches_v-1) patch.type &= ~END_OPEN_V;
}
}
for (int patch_idx = 0; patch_idx < num_patches_u*num_patches_v; ++patch_idx) {
SplinePatch& patch = patches[patch_idx];
// TODO: Should do actual patch subdivision instead of just drawing the control points!
const int tile_min_u = (patch.type & START_OPEN_U) ? 0 : 1;
const int tile_min_v = (patch.type & START_OPEN_V) ? 0 : 1;
const int tile_max_u = (patch.type & END_OPEN_U) ? 3 : 2;
const int tile_max_v = (patch.type & END_OPEN_V) ? 3 : 2;
for (int tile_u = tile_min_u; tile_u < tile_max_u; ++tile_u) {
for (int tile_v = tile_min_v; tile_v < tile_max_v; ++tile_v) {
int point_index = tile_u + tile_v*4;
VertexData v0 = patch.points[point_index];
VertexData v1 = patch.points[point_index+1];
VertexData v2 = patch.points[point_index+4];
VertexData v3 = patch.points[point_index+5];
// TODO: Backface culling etc
Clipper::ProcessTriangle(v0, v1, v2);
Clipper::ProcessTriangle(v2, v1, v0);
Clipper::ProcessTriangle(v2, v1, v3);
Clipper::ProcessTriangle(v3, v1, v2);
}
}
}
delete[] patches;
}
void TransformUnit::SubmitPrimitive(void* vertices, void* indices, u32 prim_type, int vertex_count, u32 vertex_type)
{
// TODO: Cache VertexDecoder objects

View file

@ -107,5 +107,6 @@ public:
static ScreenCoords ClipToScreen(const ClipCoords& coords);
static DrawingCoords ScreenToDrawing(const ScreenCoords& coords);
static void SubmitSpline(void* control_points, void* indices, int count_u, int count_v, int type_u, int type_v, u32 prim_type, u32 vertex_type);
static void SubmitPrimitive(void* vertices, void* indices, u32 prim_type, int vertex_count, u32 vertex_type);
};

View file

@ -503,6 +503,13 @@ enum GELogicOp
GE_LOGIC_SET=15
};
enum GEPatchPrimType
{
GE_PATCHPRIM_TRIANGLES=0,
GE_PATCHPRIM_LINES=1,
GE_PATCHPRIM_POINTS=2,
};
enum GEPaletteFormat
{
GE_CMODE_16BIT_BGR5650,