mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-09-17 11:35:08 -04:00
Optimise big maps to be even faster, optimise double/triple jump with speed to be much faster and use much less memory.
This commit is contained in:
parent
599944d943
commit
fd8e765b8b
@ -5,7 +5,7 @@ using OpenTK;
|
|||||||
namespace ClassicalSharp {
|
namespace ClassicalSharp {
|
||||||
|
|
||||||
/// <summary> Entity that performs collision detection. </summary>
|
/// <summary> Entity that performs collision detection. </summary>
|
||||||
public abstract class PhysicsEntity : Entity {
|
public abstract partial class PhysicsEntity : Entity {
|
||||||
|
|
||||||
public PhysicsEntity( Game game ) : base( game ) {
|
public PhysicsEntity( Game game ) : base( game ) {
|
||||||
}
|
}
|
||||||
@ -24,33 +24,19 @@ namespace ClassicalSharp {
|
|||||||
|
|
||||||
bool GetBoundingBox( byte block, int x, int y, int z, ref BoundingBox box ) {
|
bool GetBoundingBox( byte block, int x, int y, int z, ref BoundingBox box ) {
|
||||||
if( info.CollideType[block] != BlockCollideType.Solid ) return false;
|
if( info.CollideType[block] != BlockCollideType.Solid ) return false;
|
||||||
|
Add( x, y, z, ref info.MinBB[block], ref box.Min );
|
||||||
box.Min = new Vector3( x, y, z ) + info.MinBB[block];
|
Add( x, y, z, ref info.MaxBB[block], ref box.Max );
|
||||||
box.Max = new Vector3( x, y, z ) + info.MaxBB[block];
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct State {
|
static void Add( int x, int y, int z, ref Vector3 offset, ref Vector3 target ) {
|
||||||
public BoundingBox BlockBB;
|
target.X = x + offset.X;
|
||||||
public byte Block;
|
target.Y = y + offset.Y;
|
||||||
public float tSquared;
|
target.Z = z + offset.Z;
|
||||||
|
}
|
||||||
|
|
||||||
public State( BoundingBox bb, byte block, float tSquared ) {
|
|
||||||
BlockBB = bb;
|
|
||||||
Block = block;
|
|
||||||
this.tSquared = tSquared;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: test for corner cases, and refactor this.
|
// TODO: test for corner cases, and refactor this.
|
||||||
static State[] stateCache = new State[0];
|
|
||||||
class StateComparer : IComparer<State> {
|
|
||||||
public int Compare( State x, State y ) {
|
|
||||||
return x.tSquared.CompareTo( y.tSquared );
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static StateComparer comparer = new StateComparer();
|
|
||||||
protected void MoveAndWallSlide() {
|
protected void MoveAndWallSlide() {
|
||||||
if( Velocity == Vector3.Zero )
|
if( Velocity == Vector3.Zero )
|
||||||
return;
|
return;
|
||||||
@ -101,7 +87,7 @@ namespace ClassicalSharp {
|
|||||||
CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz );
|
CalcTime( ref vel, ref entityBB, ref blockBB, out tx, out ty, out tz );
|
||||||
if( tx > 1 || ty > 1 || tz > 1 ) continue;
|
if( tx > 1 || ty > 1 || tz > 1 ) continue;
|
||||||
float tSquared = tx * tx + ty * ty + tz * tz;
|
float tSquared = tx * tx + ty * ty + tz * tz;
|
||||||
stateCache[count++] = new State( blockBB, blockId, tSquared );
|
stateCache[count++] = new State( x, y, z, blockId, tSquared );
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -109,12 +95,17 @@ namespace ClassicalSharp {
|
|||||||
ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
|
ref BoundingBox entityBB, ref BoundingBox entityExtentBB ) {
|
||||||
bool wasOn = onGround;
|
bool wasOn = onGround;
|
||||||
onGround = false;
|
onGround = false;
|
||||||
Array.Sort( stateCache, 0, count, comparer );
|
if( count > 0 )
|
||||||
|
QuickSort( stateCache, 0, count - 1 );
|
||||||
collideX = false; collideY = false; collideZ = false;
|
collideX = false; collideY = false; collideZ = false;
|
||||||
|
BoundingBox blockBB = default(BoundingBox);
|
||||||
|
|
||||||
for( int i = 0; i < count; i++ ) {
|
for( int i = 0; i < count; i++ ) {
|
||||||
State state = stateCache[i];
|
State state = stateCache[i];
|
||||||
BoundingBox blockBB = state.BlockBB;
|
Vector3 blockPos = new Vector3( state.X >> 3, state.Y >> 3, state.Z >> 3 );
|
||||||
|
int block = (state.X & 0x7) | (state.Y & 0x7) << 3 | (state.Z & 0x7) << 6;
|
||||||
|
blockBB.Min = blockPos + info.MinBB[block];
|
||||||
|
blockBB.Max = blockPos + info.MaxBB[block];
|
||||||
if( !entityExtentBB.Intersects( blockBB ) ) continue;
|
if( !entityExtentBB.Intersects( blockBB ) ) continue;
|
||||||
|
|
||||||
float tx = 0, ty = 0, tz = 0;
|
float tx = 0, ty = 0, tz = 0;
|
||||||
@ -287,5 +278,48 @@ namespace ClassicalSharp {
|
|||||||
if( entityBB.YIntersects( blockBB ) ) ty = 0;
|
if( entityBB.YIntersects( blockBB ) ) ty = 0;
|
||||||
if( entityBB.ZIntersects( blockBB ) ) tz = 0;
|
if( entityBB.ZIntersects( blockBB ) ) tz = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
struct State {
|
||||||
|
public int X, Y, Z;
|
||||||
|
public float tSquared;
|
||||||
|
|
||||||
|
public State( int x, int y, int z, byte block, float tSquared ) {
|
||||||
|
X = x << 3; Y = y << 3; Z = z << 3;
|
||||||
|
X |= (block & 0x07);
|
||||||
|
Y |= (block & 0x38) >> 3;
|
||||||
|
Z |= (block & 0xC0) >> 6;
|
||||||
|
this.tSquared = tSquared;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static State[] stateCache = new State[0];
|
||||||
|
|
||||||
|
static void QuickSort( State[] keys, int left, int right ) {
|
||||||
|
while( left < right ) {
|
||||||
|
int i = left, j = right;
|
||||||
|
float pivot = keys[(i + j) / 2].tSquared;
|
||||||
|
// partition the list
|
||||||
|
while( i <= j ) {
|
||||||
|
while( pivot > keys[i].tSquared ) i++;
|
||||||
|
while( pivot < keys[j].tSquared ) j--;
|
||||||
|
|
||||||
|
if( i <= j ) {
|
||||||
|
State key = keys[i]; keys[i] = keys[j]; keys[j] = key;
|
||||||
|
i++; j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// recurse into the smaller subset
|
||||||
|
if( j - left <= right - i ) {
|
||||||
|
if( left < j )
|
||||||
|
QuickSort( keys, left, j );
|
||||||
|
left = i;
|
||||||
|
} else {
|
||||||
|
if( i < right )
|
||||||
|
QuickSort( keys, i, right );
|
||||||
|
right = j;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -4,10 +4,62 @@ using OpenTK;
|
|||||||
|
|
||||||
namespace ClassicalSharp {
|
namespace ClassicalSharp {
|
||||||
|
|
||||||
// TODO: optimise chunk rendering
|
|
||||||
// --> reduce iterations: liquid and sprite pass only need 1 row
|
|
||||||
public partial class MapRenderer : IDisposable {
|
public partial class MapRenderer : IDisposable {
|
||||||
|
|
||||||
|
// Render solid and fully transparent to fill depth buffer.
|
||||||
|
// These blocks are treated as having an alpha value of either none or full.
|
||||||
|
void RenderNormal() {
|
||||||
|
int[] texIds = game.TerrainAtlas1D.TexIds;
|
||||||
|
api.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
|
||||||
|
api.Texturing = true;
|
||||||
|
api.AlphaTest = true;
|
||||||
|
|
||||||
|
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||||
|
if( pendingNormal[batch] || usedNormal[batch] ) {
|
||||||
|
api.BindTexture( texIds[batch] );
|
||||||
|
RenderNormalBatch( batch );
|
||||||
|
pendingNormal[batch] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
api.AlphaTest = false;
|
||||||
|
api.Texturing = false;
|
||||||
|
DebugPickedPos();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Render translucent(liquid) blocks. These 'blend' into other blocks.
|
||||||
|
void RenderTranslucent() {
|
||||||
|
Block block = game.LocalPlayer.BlockAtHead;
|
||||||
|
drawAllFaces = block == Block.Water || block == Block.StillWater;
|
||||||
|
// First fill depth buffer
|
||||||
|
int[] texIds = game.TerrainAtlas1D.TexIds;
|
||||||
|
api.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
|
||||||
|
api.Texturing = false;
|
||||||
|
api.AlphaBlending = false;
|
||||||
|
api.ColourWrite = false;
|
||||||
|
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||||
|
if( pendingTranslucent[batch] || usedTranslucent[batch] ) {
|
||||||
|
RenderTranslucentBatchDepthPass( batch );
|
||||||
|
pendingTranslucent[batch] = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then actually draw the transluscent blocks
|
||||||
|
api.AlphaBlending = true;
|
||||||
|
api.Texturing = true;
|
||||||
|
api.ColourWrite = true;
|
||||||
|
api.DepthWrite = false; // we already calculated depth values in depth pass
|
||||||
|
|
||||||
|
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||||
|
if( !usedTranslucent[batch] ) continue;
|
||||||
|
api.BindTexture( texIds[batch] );
|
||||||
|
RenderTranslucentBatch( batch );
|
||||||
|
}
|
||||||
|
api.DepthWrite = true;
|
||||||
|
api.AlphaTest = false;
|
||||||
|
api.AlphaBlending = false;
|
||||||
|
api.Texturing = false;
|
||||||
|
}
|
||||||
|
|
||||||
const DrawMode mode = DrawMode.Triangles;
|
const DrawMode mode = DrawMode.Triangles;
|
||||||
const int maxVertex = 65536;
|
const int maxVertex = 65536;
|
||||||
const int maxIndices = maxVertex / 4 * 6;
|
const int maxIndices = maxVertex / 4 * 6;
|
||||||
|
@ -21,8 +21,7 @@ namespace ClassicalSharp {
|
|||||||
}
|
}
|
||||||
// NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
|
// NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
|
||||||
if( distances.Length > 1 )
|
if( distances.Length > 1 )
|
||||||
//FastSorter<ChunkInfo>.QuickSort( distances, chunks, 0, chunks.Length - 1 );
|
QuickSort( distances, chunks, 0, chunks.Length - 1 );
|
||||||
Array.Sort( distances, chunks, 0, chunks.Length - 1 );
|
|
||||||
|
|
||||||
Vector3I pPos = newChunkPos;
|
Vector3I pPos = newChunkPos;
|
||||||
for( int i = 0; i < chunks.Length; i++ ) {
|
for( int i = 0; i < chunks.Length; i++ ) {
|
||||||
@ -42,16 +41,8 @@ namespace ClassicalSharp {
|
|||||||
RecalcBooleans( false );
|
RecalcBooleans( false );
|
||||||
//SimpleOcclusionCulling();
|
//SimpleOcclusionCulling();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
static class FastSorter<T> {
|
static void QuickSort( int[] keys, ChunkInfo[] values, int left, int right ) {
|
||||||
|
|
||||||
static void Swap( int[] keys, T[] values, int a, int b ) {
|
|
||||||
int key = keys[a]; keys[a] = keys[b]; keys[b] = key;
|
|
||||||
T value = values[a]; values[a] = values[b]; values[b] = value;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void QuickSort( int[] keys, T[] values, int left, int right ) {
|
|
||||||
while( left < right ) {
|
while( left < right ) {
|
||||||
int i = left, j = right;
|
int i = left, j = right;
|
||||||
int pivot = keys[(i + j) / 2];
|
int pivot = keys[(i + j) / 2];
|
||||||
@ -61,7 +52,8 @@ namespace ClassicalSharp {
|
|||||||
while( pivot < keys[j] ) j--;
|
while( pivot < keys[j] ) j--;
|
||||||
|
|
||||||
if( i <= j ) {
|
if( i <= j ) {
|
||||||
Swap( keys, values, i, j );
|
int key = keys[i]; keys[i] = keys[j]; keys[j] = key;
|
||||||
|
ChunkInfo value = values[i]; values[i] = values[j]; values[j] = value;
|
||||||
i++; j--;
|
i++; j--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,6 +55,7 @@ namespace ClassicalSharp {
|
|||||||
game.MapEvents.OnNewMapLoaded += OnNewMapLoaded;
|
game.MapEvents.OnNewMapLoaded += OnNewMapLoaded;
|
||||||
game.MapEvents.EnvVariableChanged += EnvVariableChanged;
|
game.MapEvents.EnvVariableChanged += EnvVariableChanged;
|
||||||
game.Events.BlockDefinitionChanged += BlockDefinitionChanged;
|
game.Events.BlockDefinitionChanged += BlockDefinitionChanged;
|
||||||
|
game.Events.ViewDistanceChanged += ViewDistanceChanged;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Dispose() {
|
public void Dispose() {
|
||||||
@ -66,6 +67,7 @@ namespace ClassicalSharp {
|
|||||||
game.MapEvents.OnNewMapLoaded -= OnNewMapLoaded;
|
game.MapEvents.OnNewMapLoaded -= OnNewMapLoaded;
|
||||||
game.MapEvents.EnvVariableChanged -= EnvVariableChanged;
|
game.MapEvents.EnvVariableChanged -= EnvVariableChanged;
|
||||||
game.MapEvents.BlockDefinitionChanged -= BlockDefinitionChanged;
|
game.MapEvents.BlockDefinitionChanged -= BlockDefinitionChanged;
|
||||||
|
game.Events.ViewDistanceChanged -= ViewDistanceChanged;
|
||||||
builder.Dispose();
|
builder.Dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,6 +113,12 @@ namespace ClassicalSharp {
|
|||||||
builder.OnNewMap();
|
builder.OnNewMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ViewDistanceChanged( object sender, EventArgs e ) {
|
||||||
|
lastCamPos = new Vector3( float.MaxValue );
|
||||||
|
lastYaw = float.MaxValue;
|
||||||
|
lastPitch = float.MaxValue;
|
||||||
|
}
|
||||||
|
|
||||||
void RecalcBooleans( bool sizeChanged ) {
|
void RecalcBooleans( bool sizeChanged ) {
|
||||||
if( sizeChanged ) {
|
if( sizeChanged ) {
|
||||||
usedTranslucent = new bool[_1DUsed];
|
usedTranslucent = new bool[_1DUsed];
|
||||||
@ -246,12 +254,30 @@ namespace ClassicalSharp {
|
|||||||
int chunksTarget = 4;
|
int chunksTarget = 4;
|
||||||
const double targetTime = (1.0 / 30) + 0.01;
|
const double targetTime = (1.0 / 30) + 0.01;
|
||||||
void UpdateChunks( double deltaTime ) {
|
void UpdateChunks( double deltaTime ) {
|
||||||
int chunksUpdatedThisFrame = 0;
|
int chunkUpdates = 0;
|
||||||
int viewDist = game.ViewDistance < 16 ? 16 : game.ViewDistance;
|
int viewDist = game.ViewDistance < 16 ? 16 : game.ViewDistance;
|
||||||
int adjViewDistSqr = (viewDist + 24) * (viewDist + 24);
|
int adjViewDistSqr = (viewDist + 24) * (viewDist + 24);
|
||||||
chunksTarget += deltaTime < targetTime ? 1 : -1; // build more chunks if 30 FPS or over, otherwise slowdown.
|
chunksTarget += deltaTime < targetTime ? 1 : -1; // build more chunks if 30 FPS or over, otherwise slowdown.
|
||||||
Utils.Clamp( ref chunksTarget, 4, 12 );
|
Utils.Clamp( ref chunksTarget, 4, 12 );
|
||||||
|
|
||||||
|
LocalPlayer p = game.LocalPlayer;
|
||||||
|
Vector3 cameraPos = game.CurrentCameraPos;
|
||||||
|
bool samePos = cameraPos == lastCamPos && p.HeadYawDegrees == lastYaw
|
||||||
|
&& p.PitchDegrees == lastPitch;
|
||||||
|
if( samePos )
|
||||||
|
UpdateChunksStill( deltaTime, ref chunkUpdates, adjViewDistSqr );
|
||||||
|
else
|
||||||
|
UpdateChunksAndVisibility( deltaTime, ref chunkUpdates, adjViewDistSqr );
|
||||||
|
|
||||||
|
lastCamPos = cameraPos;
|
||||||
|
lastYaw = p.HeadYawDegrees; lastPitch = p.PitchDegrees;
|
||||||
|
if( !samePos || chunkUpdates != 0 )
|
||||||
|
RecalcBooleans( false );
|
||||||
|
}
|
||||||
|
Vector3 lastCamPos;
|
||||||
|
float lastYaw, lastPitch;
|
||||||
|
|
||||||
|
void UpdateChunksAndVisibility( double deltaTime, ref int chunkUpdats, int adjViewDistSqr ) {
|
||||||
for( int i = 0; i < chunks.Length; i++ ) {
|
for( int i = 0; i < chunks.Length; i++ ) {
|
||||||
ChunkInfo info = chunks[i];
|
ChunkInfo info = chunks[i];
|
||||||
if( info.Empty ) continue;
|
if( info.Empty ) continue;
|
||||||
@ -259,84 +285,40 @@ namespace ClassicalSharp {
|
|||||||
bool inRange = distSqr <= adjViewDistSqr;
|
bool inRange = distSqr <= adjViewDistSqr;
|
||||||
|
|
||||||
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
||||||
if( inRange && chunksUpdatedThisFrame < chunksTarget ) {
|
if( inRange && chunkUpdats < chunksTarget )
|
||||||
|
BuildChunk( info, ref chunkUpdats );
|
||||||
|
}
|
||||||
|
info.Visible = inRange &&
|
||||||
|
game.Culling.SphereInFrustum( info.CentreX, info.CentreY, info.CentreZ, 14 ); // 14 ~ sqrt(3 * 8^2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdateChunksStill( double deltaTime, ref int chunkUpdates, int adjViewDistSqr ) {
|
||||||
|
for( int i = 0; i < chunks.Length; i++ ) {
|
||||||
|
ChunkInfo info = chunks[i];
|
||||||
|
if( info.Empty ) continue;
|
||||||
|
int distSqr = distances[i];
|
||||||
|
bool inRange = distSqr <= adjViewDistSqr;
|
||||||
|
|
||||||
|
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
||||||
|
if( inRange && chunkUpdates < chunksTarget ) {
|
||||||
|
BuildChunk( info, ref chunkUpdates );
|
||||||
|
// only need to update the visibility of chunks in range.
|
||||||
|
info.Visible = inRange &&
|
||||||
|
game.Culling.SphereInFrustum( info.CentreX, info.CentreY, info.CentreZ, 14 ); // 14 ~ sqrt(3 * 8^2)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void BuildChunk( ChunkInfo info, ref int chunkUpdates ) {
|
||||||
game.ChunkUpdates++;
|
game.ChunkUpdates++;
|
||||||
builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
|
builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
|
||||||
ref info.NormalParts, ref info.TranslucentParts, ref info.OcclusionFlags );
|
ref info.NormalParts, ref info.TranslucentParts, ref info.OcclusionFlags );
|
||||||
|
|
||||||
if( info.NormalParts == null && info.TranslucentParts == null )
|
if( info.NormalParts == null && info.TranslucentParts == null )
|
||||||
info.Empty = true;
|
info.Empty = true;
|
||||||
chunksUpdatedThisFrame++;
|
chunkUpdates++;
|
||||||
}
|
|
||||||
}
|
|
||||||
info.Visible = inRange &&
|
|
||||||
game.Culling.SphereInFrustum( info.CentreX, info.CentreY, info.CentreZ, 14 ); // 14 ~ sqrt(3 * 8^2)
|
|
||||||
}
|
|
||||||
|
|
||||||
LocalPlayer p = game.LocalPlayer;
|
|
||||||
Vector3 cameraPos = game.CurrentCameraPos;
|
|
||||||
if( chunksUpdatedThisFrame == 0 && cameraPos == lastCamPos
|
|
||||||
&& p.HeadYawDegrees == lastYaw && p.PitchDegrees == lastPitch ) return;
|
|
||||||
|
|
||||||
lastCamPos = cameraPos;
|
|
||||||
lastYaw = p.HeadYawDegrees; lastPitch = p.PitchDegrees;
|
|
||||||
RecalcBooleans( false );
|
|
||||||
}
|
|
||||||
Vector3 lastCamPos;
|
|
||||||
float lastYaw, lastPitch;
|
|
||||||
|
|
||||||
// Render solid and fully transparent to fill depth buffer.
|
|
||||||
// These blocks are treated as having an alpha value of either none or full.
|
|
||||||
void RenderNormal() {
|
|
||||||
int[] texIds = game.TerrainAtlas1D.TexIds;
|
|
||||||
api.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
|
|
||||||
api.Texturing = true;
|
|
||||||
api.AlphaTest = true;
|
|
||||||
|
|
||||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
|
||||||
if( pendingNormal[batch] || usedNormal[batch] ) {
|
|
||||||
api.BindTexture( texIds[batch] );
|
|
||||||
RenderNormalBatch( batch );
|
|
||||||
pendingNormal[batch] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
api.AlphaTest = false;
|
|
||||||
api.Texturing = false;
|
|
||||||
DebugPickedPos();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Render translucent(liquid) blocks. These 'blend' into other blocks.
|
|
||||||
void RenderTranslucent() {
|
|
||||||
Block block = game.LocalPlayer.BlockAtHead;
|
|
||||||
drawAllFaces = block == Block.Water || block == Block.StillWater;
|
|
||||||
// First fill depth buffer
|
|
||||||
int[] texIds = game.TerrainAtlas1D.TexIds;
|
|
||||||
api.SetBatchFormat( VertexFormat.Pos3fTex2fCol4b );
|
|
||||||
api.Texturing = false;
|
|
||||||
api.AlphaBlending = false;
|
|
||||||
api.ColourWrite = false;
|
|
||||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
|
||||||
if( pendingTranslucent[batch] || usedTranslucent[batch] ) {
|
|
||||||
RenderTranslucentBatchDepthPass( batch );
|
|
||||||
pendingTranslucent[batch] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then actually draw the transluscent blocks
|
|
||||||
api.AlphaBlending = true;
|
|
||||||
api.Texturing = true;
|
|
||||||
api.ColourWrite = true;
|
|
||||||
api.DepthWrite = false; // we already calculated depth values in depth pass
|
|
||||||
|
|
||||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
|
||||||
if( !usedTranslucent[batch] ) continue;
|
|
||||||
api.BindTexture( texIds[batch] );
|
|
||||||
RenderTranslucentBatch( batch );
|
|
||||||
}
|
|
||||||
api.DepthWrite = true;
|
|
||||||
api.AlphaTest = false;
|
|
||||||
api.AlphaBlending = false;
|
|
||||||
api.Texturing = false;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user