Optimise ShadowComponent rendering

This commit is contained in:
UnknownShadow200 2018-03-27 17:58:13 +11:00
parent fdd620aba6
commit 36f2dec36e
3 changed files with 86 additions and 120 deletions

View File

@ -25,42 +25,37 @@ namespace ClassicalSharp.Entities {
if (Position.Y < 0) return;
float posX = Position.X, posZ = Position.Z;
int posY = Math.Min((int)Position.Y, game.World.Height - 1);
int posY = Math.Min((int)Position.Y, game.World.MaxY);
int index = 0, vb = 0;
radius = 7f * Math.Min(entity.ModelScale.Y, 1) * entity.Model.ShadowScale;
VertexP3fT2fC4b[] verts = null;
int posCount = 0, dataCount = 0;
Vector3I* coords = stackalloc Vector3I[4];
int dataCount = 0;
ShadowData* data = stackalloc ShadowData[4];
for (int i = 0; i < 4; i++) {
coords[i] = new Vector3I(int.MinValue);
}
if (game.Entities.ShadowMode == EntityShadow.SnapToBlock) {
vb = game.Graphics.texVb; verts = game.Graphics.texVerts;
if (!GetBlocks(coords, ref posCount, posX, posZ, posY, data, ref dataCount)) return;
int x1 = Utils.Floor(posX), z1 = Utils.Floor(posZ);
float x1 = Utils.Floor(posX), z1 = Utils.Floor(posZ);
if (!GetBlocks(x1, posY, z1, data, ref dataCount)) return;
DrawSquareShadow(verts, ref index, data[0].Y, x1, z1);
} else {
vb = game.ModelCache.vb; verts = game.ModelCache.vertices;
int x1 = Utils.Floor(posX - radius/16f), z1 = Utils.Floor(posZ - radius/16f);
int x2 = Utils.Floor(posX + radius/16f), z2 = Utils.Floor(posZ + radius/16f);
float x = posX - radius/16f, z = posZ - radius/16f;
if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0)
DrawCircle(verts, ref index, data, dataCount, x, z);
x = Math.Max(posX - radius/16f, Utils.Floor(posX + radius/16f));
if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0)
DrawCircle(verts, ref index, data, dataCount, x, z);
z = Math.Max(posZ - radius/16f, Utils.Floor(posZ + radius/16f));
if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0)
DrawCircle(verts, ref index, data, dataCount, x, z);
x = posX - radius/16f;
if (GetBlocks(coords, ref posCount, x, z, posY, data, ref dataCount) && data[0].A > 0)
DrawCircle(verts, ref index, data, dataCount, x, z);
if (GetBlocks(x1, posY, z1, data, ref dataCount) && data[0].A > 0) {
DrawCircle(verts, ref index, data, dataCount, x1, z1);
}
if (x1 != x2 && GetBlocks(x2, posY, z1, data, ref dataCount) && data[0].A > 0) {
DrawCircle(verts, ref index, data, dataCount, x2, z1);
}
if (z1 != z2 && GetBlocks(x1, posY, z2, data, ref dataCount) && data[0].A > 0) {
DrawCircle(verts, ref index, data, dataCount, x1, z2);
}
if (x1 != x2 && z1 != z2 && GetBlocks(x2, posY, z2, data, ref dataCount) && data[0].A > 0) {
DrawCircle(verts, ref index, data, dataCount, x2, z2);
}
}
if (index == 0) return;
@ -86,7 +81,6 @@ namespace ClassicalSharp.Entities {
static void DrawCircle(VertexP3fT2fC4b[] verts, ref int index,
ShadowData* data, int dataCount, float x, float z) {
x = Utils.Floor(x); z = Utils.Floor(z);
Vector3 min = BlockInfo.MinBB[data[0].Block], max = BlockInfo.MaxBB[data[0].Block];
DrawCoords(verts, ref index, data[0], x + min.X, z + min.Z, x + max.X, z + max.Z);
@ -112,44 +106,46 @@ namespace ClassicalSharp.Entities {
float v2 = (z2 - cen.Z) * 16/(radius * 2) + 0.5f;
if (u2 <= 0 || v2 <= 0 || u1 >= 1 || v1 >= 1) return;
x1 = Math.Max(x1, cen.X - radius/16f); u1 = Math.Max(u1, 0);
z1 = Math.Max(z1, cen.Z - radius/16f); v1 = Math.Max(v1, 0);
x2 = Math.Min(x2, cen.X + radius/16f); u2 = Math.Min(u2, 1);
z2 = Math.Min(z2, cen.Z + radius/16f); v2 = Math.Min(v2, 1);
x1 = Math.Max(x1, cen.X - radius/16f); u1 = u1 >= 0 ? u1 : 0;
z1 = Math.Max(z1, cen.Z - radius/16f); v1 = v1 >= 0 ? v1 : 0;
x2 = Math.Min(x2, cen.X + radius/16f); u2 = u2 <= 1 ? u2 : 1;
z2 = Math.Min(z2, cen.Z + radius/16f); v2 = v2 <= 1 ? v2 : 1;
int col = new FastColour(c, c, c, data.A).Pack();
verts[index++] = new VertexP3fT2fC4b(x1, data.Y, z1, u1, v1, col);
verts[index++] = new VertexP3fT2fC4b(x2, data.Y, z1, u2, v1, col);
verts[index++] = new VertexP3fT2fC4b(x2, data.Y, z2, u2, v2, col);
verts[index++] = new VertexP3fT2fC4b(x1, data.Y, z2, u1, v2, col);
VertexP3fT2fC4b v; v.Y = data.Y; v.Colour = col;
v.X = x1; v.Z = z1; v.U = u1; v.V = v1; verts[index++] = v;
v.X = x2; v.U = u2; verts[index++] = v;
v.Z = z2; v.V = v2; verts[index++] = v;
v.X = x1; v.U = u1; verts[index++] = v;
}
static bool GetBlocks(Vector3I* coords, ref int posCount, float x, float z,
int posY, ShadowData* data, ref int index) {
int blockX = Utils.Floor(x), blockZ = Utils.Floor(z);
Vector3I p = new Vector3I(blockX, 0, blockZ);
Vector3 Position = entity.Position;
static bool GetBlocks(int x, int y, int z, ShadowData* data, ref int index) {
float posY = entity.Position.Y;
index = 0;
bool outside = x < 0 || z < 0 || x >= game.World.Width || z >= game.World.Length;
// Check we have not processed this particular block already.
for (int i = 0; i < 4; i++) {
if (coords[i] == p) return false;
data[i] = new ShadowData();
}
coords[posCount] = p; posCount++;
while (posY >= 0 && index < 4) {
BlockID block = GetShadowBlock(blockX, posY, blockZ);
posY--;
while (y >= 0 && index < 4) {
BlockID block;
if (!outside) {
block = game.World.GetBlock(x, y, z);
} else if (y == game.World.Env.EdgeHeight - 1) {
block = BlockInfo.Draw[game.World.Env.EdgeBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
} else if (y == game.World.Env.SidesHeight - 1) {
block = BlockInfo.Draw[game.World.Env.SidesBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
} else {
block = Block.Air;
}
y--;
byte draw = BlockInfo.Draw[block];
if (draw == DrawType.Gas || draw == DrawType.Sprite || BlockInfo.IsLiquid[block]) continue;
float blockY = posY + 1 + BlockInfo.MaxBB[block].Y;
if (blockY >= Position.Y + 0.01f) continue;
float blockY = (y + 1) + BlockInfo.MaxBB[block].Y;
if (blockY >= posY + 0.01f) continue;
data[index].Block = block; data[index].Y = blockY;
CalcAlpha(Position.Y, ref data[index]);
CalcAlpha(posY, ref data[index]);
index++;
// Check if the casted shadow will continue on further down.
if (BlockInfo.MinBB[block].X == 0 && BlockInfo.MaxBB[block].X == 1 &&
BlockInfo.MinBB[block].Z == 0 && BlockInfo.MaxBB[block].Z == 1) return true;
@ -157,23 +153,12 @@ namespace ClassicalSharp.Entities {
if (index < 4) {
data[index].Block = game.World.Env.EdgeBlock; data[index].Y = 0;
CalcAlpha(Position.Y, ref data[index]);
CalcAlpha(posY, ref data[index]);
index++;
}
return true;
}
static BlockID GetShadowBlock(int x, int y, int z) {
if (x < 0 || z < 0 || x >= game.World.Width || z >= game.World.Length) {
if (y == game.World.Env.EdgeHeight - 1)
return BlockInfo.Draw[game.World.Env.EdgeBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
if (y == game.World.Env.SidesHeight - 1)
return BlockInfo.Draw[game.World.Env.SidesBlock] == DrawType.Gas ? Block.Air : Block.Bedrock;
return Block.Air;
}
return game.World.GetBlock(x, y, z);
}
struct ShadowData {
public BlockID Block;
public float Y;

View File

@ -199,7 +199,7 @@ namespace ClassicalSharp {
}
void Stretch(int x1, int y1, int z1) {
int xMax = Math.Min(width, x1 + chunkSize);
int xMax = Math.Min(width, x1 + chunkSize);
int yMax = Math.Min(height, y1 + chunkSize);
int zMax = Math.Min(length, z1 + chunkSize);
#if OCCLUSION

View File

@ -460,10 +460,10 @@ void ShadowComponent_DrawCoords(VertexP3fT2fC4b** vertices, Entity* entity, Shad
if (u2 <= 0.0f || v2 <= 0.0f || u1 >= 1.0f || v1 >= 1.0f) return;
radius /= 16.0f;
x1 = max(x1, cen.X - radius); u1 = max(u1, 0.0f);
z1 = max(z1, cen.Z - radius); v1 = max(v1, 0.0f);
x2 = min(x2, cen.X + radius); u2 = min(u2, 1.0f);
z2 = min(z2, cen.Z + radius); v2 = min(v2, 1.0f);
x1 = max(x1, cen.X - radius); u1 = u1 >= 0.0f ? u1 : 0.0f;
z1 = max(z1, cen.Z - radius); v1 = v1 >= 0.0f ? v1 : 0.0f;
x2 = min(x2, cen.X + radius); u2 = u2 <= 1.0f ? u2 : 1.0f;
z2 = min(z2, cen.Z + radius); v2 = v2 <= 1.0f ? v2 : 1.0f;
PackedCol col = PACKEDCOL_CONST(255, 255, 255, data->A);
VertexP3fT2fC4b* ptr = *vertices;
@ -509,17 +509,6 @@ void ShadowComponent_DrawCircle(VertexP3fT2fC4b** vertices, Entity* entity, Shad
}
}
BlockID ShadowComponent_GetBlock(Int32 x, Int32 y, Int32 z) {
if (x < 0 || z < 0 || x >= World_Width || z >= World_Length) {
if (y == WorldEnv_EdgeHeight - 1)
return Block_Draw[WorldEnv_EdgeBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK;
if (y == WorldEnv_SidesHeight - 1)
return Block_Draw[WorldEnv_SidesBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK;
return BLOCK_AIR;
}
return World_GetBlock(x, y, z);
}
void ShadowComponent_CalcAlpha(Real32 playerY, ShadowData* data) {
Real32 height = playerY - data->Y;
if (height <= 6.0f) {
@ -534,35 +523,36 @@ void ShadowComponent_CalcAlpha(Real32 playerY, ShadowData* data) {
else data->Y += 1.0f / 4.0f;
}
bool ShadowComponent_GetBlocks(Entity* entity, Vector3I* coords, Real32 x, Real32 z, Int32 posY, ShadowData* data) {
Int32 blockX = Math_Floor(x), blockZ = Math_Floor(z);
Vector3I p = { blockX, 0, blockZ };
/* Check we have not processed this particular block already */
UInt32 i, posCount = 0;
bool ShadowComponent_GetBlocks(Entity* entity, Int32 x, Int32 y, Int32 z, ShadowData* data) {
Int32 count;
ShadowData zeroData = { 0.0f, 0, 0 };
for (i = 0; i < 4; i++) {
if (Vector3I_Equals(&coords[i], &p)) return false;
if (coords[i].X != Int32_MinValue) posCount++;
data[i] = zeroData;
}
coords[posCount] = p;
for (count = 0; count < 4; count++) { data[count] = zeroData; }
count = 0;
UInt32 count = 0;
ShadowData* cur = data;
Vector3 Position = entity->Position;
Real32 posY = entity->Position.Y;
bool outside = x < 0 || z < 0 || x >= World_Width || z >= World_Length;
while (posY >= 0 && count < 4) {
BlockID block = ShadowComponent_GetBlock(blockX, posY, blockZ);
posY--;
while (y >= 0 && count < 4) {
BlockID block;
if (!outside) {
block = World_GetBlock(x, y, z);
} else if (y == WorldEnv_EdgeHeight - 1) {
block = Block_Draw[WorldEnv_EdgeBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK;
} else if (y == WorldEnv_SidesHeight - 1) {
block = Block_Draw[WorldEnv_SidesBlock] == DRAW_GAS ? BLOCK_AIR : BLOCK_BEDROCK;
} else {
block = BLOCK_AIR;
}
y--;
UInt8 draw = Block_Draw[block];
if (draw == DRAW_GAS || draw == DRAW_SPRITE || Block_IsLiquid[block]) continue;
Real32 blockY = posY + 1.0f + Block_MaxBB[block].Y;
if (blockY >= Position.Y + 0.01f) continue;
Real32 blockY = (y + 1.0f) + Block_MaxBB[block].Y;
if (blockY >= posY + 0.01f) continue;
cur->Block = block; cur->Y = blockY;
ShadowComponent_CalcAlpha(Position.Y, cur);
ShadowComponent_CalcAlpha(posY, cur);
count++; cur++;
/* Check if the casted shadow will continue on further down. */
@ -572,7 +562,7 @@ bool ShadowComponent_GetBlocks(Entity* entity, Vector3I* coords, Real32 x, Real3
if (count < 4) {
cur->Block = WorldEnv_EdgeBlock; cur->Y = 0.0f;
ShadowComponent_CalcAlpha(Position.Y, cur);
ShadowComponent_CalcAlpha(posY, cur);
count++; cur++;
}
return true;
@ -610,42 +600,33 @@ void ShadowComponent_Draw(Entity* entity) {
ShadowComponent_radius = 7.0f * min(entity->ModelScale.Y, 1.0f) * entity->Model->ShadowScale;
VertexP3fT2fC4b vertices[128];
Vector3I coords[4];
ShadowData data[4];
for (Int32 i = 0; i < 4; i++) {
coords[i] = Vector3I_Create1(Int32_MinValue);
}
/* TODO: Should shadow component use its own VB? */
VertexP3fT2fC4b* ptr = vertices;
if (Entities_ShadowMode == SHADOW_MODE_SNAP_TO_BLOCK) {
vb = GfxCommon_texVb;
if (!ShadowComponent_GetBlocks(entity, coords, posX, posZ, posY, data)) return;
Int32 x1 = Math_Floor(posX), z1 = Math_Floor(posZ);
if (!ShadowComponent_GetBlocks(entity, x1, posY, z1, data)) return;
Real32 x1 = (Real32)Math_Floor(posX), z1 = (Real32)Math_Floor(posZ);
ShadowComponent_DrawSquareShadow(&ptr, data[0].Y, x1, z1);
} else {
vb = ModelCache_Vb;
Real32 radius = ShadowComponent_radius / 16.0f;
Int32 x1 = Math_Floor(posX - radius), z1 = Math_Floor(posZ - radius);
Int32 x2 = Math_Floor(posX + radius), z2 = Math_Floor(posZ + radius);
Real32 x = posX - radius, z = posZ - radius;
if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, x, z);
if (ShadowComponent_GetBlocks(entity, x1, posY, z1, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, (Real32)x1, (Real32)z1);
}
x = max(posX - radius, Math_Floor(posX + radius));
if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, x, z);
if (x1 != x2 && ShadowComponent_GetBlocks(entity, x2, posY, z1, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, (Real32)x2, (Real32)z1);
}
z = max(posZ - radius, Math_Floor(posZ + radius));
if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, x, z);
if (z1 != z2 && ShadowComponent_GetBlocks(entity, x1, posY, z2, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, (Real32)x1, (Real32)z2);
}
x = posX - radius;
if (ShadowComponent_GetBlocks(entity, coords, x, z, posY, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, x, z);
if (x1 != x2 && z1 != z2 && ShadowComponent_GetBlocks(entity, x2, posY, z2, data) && data[0].A > 0) {
ShadowComponent_DrawCircle(&ptr, entity, data, (Real32)x2, (Real32)z2);
}
}