mirror of
https://github.com/ClassiCube/ClassiCube.git
synced 2025-01-24 10:02:08 -05:00
Modularise MapRenderer class.
This commit is contained in:
parent
10ca60c2d6
commit
8956cc5abd
6 changed files with 701 additions and 665 deletions
|
@ -248,10 +248,10 @@
|
|||
<Compile Include="Program.cs" />
|
||||
<Compile Include="Properties\AssemblyInfo.cs" />
|
||||
<Compile Include="Rendering\BlockHandRenderer.cs" />
|
||||
<Compile Include="Rendering\ChunkSorter.cs" />
|
||||
<Compile Include="Rendering\ChunkUpdater.cs" />
|
||||
<Compile Include="Rendering\EnvRenderer.cs" />
|
||||
<Compile Include="Rendering\MapRenderer.Occlusion.cs" />
|
||||
<Compile Include="Rendering\MapRenderer.Rendering.cs" />
|
||||
<Compile Include="Rendering\MapRenderer.Sorting.cs" />
|
||||
<Compile Include="Rendering\MinimalEnvRenderer.cs" />
|
||||
<Compile Include="Rendering\FrustumCulling.cs" />
|
||||
<Compile Include="Rendering\MapBordersRenderer.cs" />
|
||||
|
|
|
@ -121,8 +121,8 @@ namespace ClassicalSharp {
|
|||
forwardThirdPersonCam = new ForwardThirdPersonCamera( this );
|
||||
Camera = firstPersonCam;
|
||||
DefaultFov = Options.GetInt( OptionsKey.FieldOfView, 1, 150, 70 );
|
||||
Fov = Fov;
|
||||
ZoomFov = Fov;
|
||||
Fov = DefaultFov;
|
||||
ZoomFov = DefaultFov;
|
||||
UpdateProjection();
|
||||
CommandManager = new CommandManager();
|
||||
CommandManager.Init( this );
|
||||
|
|
|
@ -1,75 +1,80 @@
|
|||
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
||||
using System;
|
||||
using ClassicalSharp.GraphicsAPI;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Renderers {
|
||||
|
||||
public partial class MapRenderer : IDisposable {
|
||||
|
||||
void UpdateSortOrder() {
|
||||
Vector3 cameraPos = game.CurrentCameraPos;
|
||||
Vector3I newChunkPos = Vector3I.Floor( cameraPos );
|
||||
newChunkPos.X = (newChunkPos.X & ~0x0F) + 8;
|
||||
newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8;
|
||||
newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8;
|
||||
if( newChunkPos == chunkPos ) return;
|
||||
|
||||
chunkPos = newChunkPos;
|
||||
for( int i = 0; i < distances.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
distances[i] = Utils.DistanceSquared( info.CentreX, info.CentreY, info.CentreZ, chunkPos.X, chunkPos.Y, chunkPos.Z );
|
||||
}
|
||||
// NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
|
||||
if( distances.Length > 1 )
|
||||
QuickSort( distances, chunks, 0, chunks.Length - 1 );
|
||||
|
||||
Vector3I pPos = newChunkPos;
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
int dX1 = (info.CentreX - 8) - pPos.X, dX2 = (info.CentreX + 8) - pPos.X;
|
||||
int dY1 = (info.CentreY - 8) - pPos.Y, dY2 = (info.CentreY + 8) - pPos.Y;
|
||||
int dZ1 = (info.CentreZ - 8) - pPos.Z, dZ2 = (info.CentreZ + 8) - pPos.Z;
|
||||
|
||||
// Back face culling: make sure that the chunk is definitely entirely back facing.
|
||||
info.DrawLeft = !(dX1 <= 0 && dX2 <= 0);
|
||||
info.DrawRight = !(dX1 >= 0 && dX2 >= 0);
|
||||
info.DrawFront = !(dZ1 <= 0 && dZ2 <= 0);
|
||||
info.DrawBack = !(dZ1 >= 0 && dZ2 >= 0);
|
||||
info.DrawBottom = !(dY1 <= 0 && dY2 <= 0);
|
||||
info.DrawTop = !(dY1 >= 0 && dY2 >= 0);
|
||||
}
|
||||
RecalcBooleans( false );
|
||||
//SimpleOcclusionCulling();
|
||||
}
|
||||
|
||||
static void QuickSort( int[] keys, ChunkInfo[] values, int left, int right ) {
|
||||
while( left < right ) {
|
||||
int i = left, j = right;
|
||||
int pivot = keys[(i + j) / 2];
|
||||
// partition the list
|
||||
while( i <= j ) {
|
||||
while( pivot > keys[i] ) i++;
|
||||
while( pivot < keys[j] ) j--;
|
||||
|
||||
if( 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--;
|
||||
}
|
||||
}
|
||||
|
||||
// recurse into the smaller subset
|
||||
if( j - left <= right - i ) {
|
||||
if( left < j )
|
||||
QuickSort( keys, values, left, j );
|
||||
left = i;
|
||||
} else {
|
||||
if( i < right )
|
||||
QuickSort( keys, values, i, right );
|
||||
right = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
||||
using System;
|
||||
using ClassicalSharp.GraphicsAPI;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Renderers {
|
||||
|
||||
public static class ChunkSorter {
|
||||
|
||||
public static void UpdateSortOrder( Game game, ChunkUpdater updater ) {
|
||||
Vector3 cameraPos = game.CurrentCameraPos;
|
||||
Vector3I newChunkPos = Vector3I.Floor( cameraPos );
|
||||
newChunkPos.X = (newChunkPos.X & ~0x0F) + 8;
|
||||
newChunkPos.Y = (newChunkPos.Y & ~0x0F) + 8;
|
||||
newChunkPos.Z = (newChunkPos.Z & ~0x0F) + 8;
|
||||
if( newChunkPos == updater.chunkPos ) return;
|
||||
|
||||
ChunkInfo[] chunks = game.MapRenderer.chunks;
|
||||
int[] distances = updater.distances;
|
||||
updater.chunkPos = newChunkPos;
|
||||
|
||||
for( int i = 0; i < distances.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
distances[i] = Utils.DistanceSquared( info.CentreX, info.CentreY, info.CentreZ,
|
||||
updater.chunkPos.X, updater.chunkPos.Y, updater.chunkPos.Z );
|
||||
}
|
||||
|
||||
// NOTE: Over 5x faster compared to normal comparison of IComparer<ChunkInfo>.Compare
|
||||
if( distances.Length > 1 )
|
||||
QuickSort( distances, chunks, 0, chunks.Length - 1 );
|
||||
|
||||
Vector3I pPos = newChunkPos;
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
int dX1 = (info.CentreX - 8) - pPos.X, dX2 = (info.CentreX + 8) - pPos.X;
|
||||
int dY1 = (info.CentreY - 8) - pPos.Y, dY2 = (info.CentreY + 8) - pPos.Y;
|
||||
int dZ1 = (info.CentreZ - 8) - pPos.Z, dZ2 = (info.CentreZ + 8) - pPos.Z;
|
||||
|
||||
// Back face culling: make sure that the chunk is definitely entirely back facing.
|
||||
info.DrawLeft = !(dX1 <= 0 && dX2 <= 0);
|
||||
info.DrawRight = !(dX1 >= 0 && dX2 >= 0);
|
||||
info.DrawFront = !(dZ1 <= 0 && dZ2 <= 0);
|
||||
info.DrawBack = !(dZ1 >= 0 && dZ2 >= 0);
|
||||
info.DrawBottom = !(dY1 <= 0 && dY2 <= 0);
|
||||
info.DrawTop = !(dY1 >= 0 && dY2 >= 0);
|
||||
}
|
||||
updater.RecalcBooleans( false );
|
||||
//SimpleOcclusionCulling();
|
||||
}
|
||||
|
||||
static void QuickSort( int[] keys, ChunkInfo[] values, int left, int right ) {
|
||||
while( left < right ) {
|
||||
int i = left, j = right;
|
||||
int pivot = keys[(i + j) / 2];
|
||||
// partition the list
|
||||
while( i <= j ) {
|
||||
while( pivot > keys[i] ) i++;
|
||||
while( pivot < keys[j] ) j--;
|
||||
|
||||
if( 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--;
|
||||
}
|
||||
}
|
||||
|
||||
// recurse into the smaller subset
|
||||
if( j - left <= right - i ) {
|
||||
if( left < j )
|
||||
QuickSort( keys, values, left, j );
|
||||
left = i;
|
||||
} else {
|
||||
if( i < right )
|
||||
QuickSort( keys, values, i, right );
|
||||
right = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
350
ClassicalSharp/Rendering/ChunkUpdater.cs
Normal file
350
ClassicalSharp/Rendering/ChunkUpdater.cs
Normal file
|
@ -0,0 +1,350 @@
|
|||
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
||||
using System;
|
||||
using ClassicalSharp.Entities;
|
||||
using ClassicalSharp.Events;
|
||||
using ClassicalSharp.GraphicsAPI;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Renderers {
|
||||
|
||||
/// <summary> Manages the process of building/deleting chunk meshes,
|
||||
/// in addition to calculating the visibility of chunks. </summary>
|
||||
public sealed class ChunkUpdater : IDisposable {
|
||||
|
||||
Game game;
|
||||
IGraphicsApi api;
|
||||
int _1DUsed = 1;
|
||||
ChunkMeshBuilder builder;
|
||||
BlockInfo info;
|
||||
|
||||
int width, height, length;
|
||||
internal int[] distances;
|
||||
internal Vector3I chunkPos = new Vector3I( int.MaxValue );
|
||||
int elementsPerBitmap = 0;
|
||||
MapRenderer renderer;
|
||||
|
||||
public ChunkUpdater( Game game, MapRenderer renderer ) {
|
||||
this.game = game;
|
||||
this.renderer = renderer;
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
renderer.totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
RecalcBooleans( true );
|
||||
|
||||
builder = new ChunkMeshBuilder( game );
|
||||
api = game.Graphics;
|
||||
elementsPerBitmap = game.TerrainAtlas1D.elementsPerBitmap;
|
||||
info = game.BlockInfo;
|
||||
|
||||
game.Events.TerrainAtlasChanged += TerrainAtlasChanged;
|
||||
game.WorldEvents.OnNewMap += OnNewMap;
|
||||
game.WorldEvents.OnNewMapLoaded += OnNewMapLoaded;
|
||||
game.WorldEvents.EnvVariableChanged += EnvVariableChanged;
|
||||
game.Events.BlockDefinitionChanged += BlockDefinitionChanged;
|
||||
game.Events.ViewDistanceChanged += ViewDistanceChanged;
|
||||
game.Events.ProjectionChanged += ProjectionChanged;
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
ClearChunkCache();
|
||||
renderer.chunks = null;
|
||||
renderer.unsortedChunks = null;
|
||||
game.Events.TerrainAtlasChanged -= TerrainAtlasChanged;
|
||||
game.WorldEvents.OnNewMap -= OnNewMap;
|
||||
game.WorldEvents.OnNewMapLoaded -= OnNewMapLoaded;
|
||||
game.WorldEvents.EnvVariableChanged -= EnvVariableChanged;
|
||||
game.WorldEvents.BlockDefinitionChanged -= BlockDefinitionChanged;
|
||||
game.Events.ViewDistanceChanged -= ViewDistanceChanged;
|
||||
game.Events.ProjectionChanged -= ProjectionChanged;
|
||||
builder.Dispose();
|
||||
}
|
||||
|
||||
public void Refresh() {
|
||||
chunkPos = new Vector3I( int.MaxValue );
|
||||
renderer.totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
if( renderer.chunks == null || game.World.IsNotLoaded ) return;
|
||||
ClearChunkCache();
|
||||
CreateChunkCache();
|
||||
}
|
||||
|
||||
void RefreshBorders( int clipLevel ) {
|
||||
chunkPos = new Vector3I( int.MaxValue );
|
||||
if( renderer.chunks == null || game.World.IsNotLoaded ) return;
|
||||
|
||||
int index = 0;
|
||||
for( int z = 0; z < chunksZ; z++ )
|
||||
for( int y = 0; y < chunksY; y++ )
|
||||
for( int x = 0; x < chunksX; x++ )
|
||||
{
|
||||
bool isBorder = x == 0 || z == 0 || x == (chunksX - 1) || z == (chunksZ - 1);
|
||||
if( isBorder && (y * 16) < clipLevel )
|
||||
DeleteChunk( renderer.unsortedChunks[index] );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnvVariableChanged( object sender, EnvVarEventArgs e ) {
|
||||
if( e.Var == EnvVar.SunlightColour || e.Var == EnvVar.ShadowlightColour ) {
|
||||
Refresh();
|
||||
} else if( e.Var == EnvVar.EdgeLevel ) {
|
||||
int oldClip = builder.clipLevel;
|
||||
builder.clipLevel = Math.Max( 0, game.World.SidesHeight );
|
||||
RefreshBorders( Math.Max( oldClip, builder.clipLevel ) );
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainAtlasChanged( object sender, EventArgs e ) {
|
||||
bool refreshRequired = elementsPerBitmap != game.TerrainAtlas1D.elementsPerBitmap;
|
||||
elementsPerBitmap = game.TerrainAtlas1D.elementsPerBitmap;
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
|
||||
if( refreshRequired ) Refresh();
|
||||
RecalcBooleans( true );
|
||||
}
|
||||
|
||||
void BlockDefinitionChanged( object sender, EventArgs e ) {
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
RecalcBooleans( true );
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProjectionChanged( object sender, EventArgs e ) {
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
}
|
||||
|
||||
void OnNewMap( object sender, EventArgs e ) {
|
||||
game.ChunkUpdates = 0;
|
||||
ClearChunkCache();
|
||||
for( int i = 0; i < renderer.totalUsed.Length; i++ )
|
||||
renderer.totalUsed[i] = 0;
|
||||
|
||||
renderer.chunks = null;
|
||||
renderer.unsortedChunks = null;
|
||||
chunkPos = new Vector3I( int.MaxValue, int.MaxValue, int.MaxValue );
|
||||
builder.OnNewMap();
|
||||
}
|
||||
|
||||
void ViewDistanceChanged( object sender, EventArgs e ) {
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
lastYaw = float.MaxValue;
|
||||
lastPitch = float.MaxValue;
|
||||
}
|
||||
|
||||
internal void RecalcBooleans( bool sizeChanged ) {
|
||||
if( sizeChanged ) {
|
||||
renderer.usedTranslucent = new bool[_1DUsed];
|
||||
renderer.usedNormal = new bool[_1DUsed];
|
||||
renderer.pendingTranslucent = new bool[_1DUsed];
|
||||
renderer.pendingNormal = new bool[_1DUsed];
|
||||
}
|
||||
|
||||
for( int i = 0; i < _1DUsed; i++ ) {
|
||||
renderer.pendingTranslucent[i] = true;
|
||||
renderer.usedTranslucent[i] = false;
|
||||
renderer.pendingNormal[i] = true;
|
||||
renderer.usedNormal[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int chunksX, chunksY, chunksZ;
|
||||
void OnNewMapLoaded( object sender, EventArgs e ) {
|
||||
width = NextMultipleOf16( game.World.Width );
|
||||
height = NextMultipleOf16( game.World.Height );
|
||||
length = NextMultipleOf16( game.World.Length );
|
||||
chunksX = width >> 4;
|
||||
chunksY = height >> 4;
|
||||
chunksZ = length >> 4;
|
||||
|
||||
renderer.chunks = new ChunkInfo[chunksX * chunksY * chunksZ];
|
||||
renderer.unsortedChunks = new ChunkInfo[chunksX * chunksY * chunksZ];
|
||||
distances = new int[renderer.chunks.Length];
|
||||
CreateChunkCache();
|
||||
builder.OnNewMapLoaded();
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
lastYaw = float.MaxValue;
|
||||
lastPitch = float.MaxValue;
|
||||
}
|
||||
|
||||
void ClearChunkCache() {
|
||||
if( renderer.chunks == null ) return;
|
||||
for( int i = 0; i < renderer.chunks.Length; i++ )
|
||||
DeleteChunk( renderer.chunks[i] );
|
||||
renderer.totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
}
|
||||
|
||||
void DeleteChunk( ChunkInfo info ) {
|
||||
info.Empty = false;
|
||||
#if OCCLUSION
|
||||
info.OcclusionFlags = 0;
|
||||
info.OccludedFlags = 0;
|
||||
#endif
|
||||
DeleteData( ref info.NormalParts );
|
||||
DeleteData( ref info.TranslucentParts );
|
||||
}
|
||||
|
||||
void DeleteData( ref ChunkPartInfo[] parts ) {
|
||||
DecrementUsed( parts );
|
||||
if( parts == null ) return;
|
||||
|
||||
for( int i = 0; i < parts.Length; i++ )
|
||||
api.DeleteVb( parts[i].VbId );
|
||||
parts = null;
|
||||
}
|
||||
|
||||
void CreateChunkCache() {
|
||||
int index = 0;
|
||||
for( int z = 0; z < length; z += 16 )
|
||||
for( int y = 0; y < height; y += 16 )
|
||||
for( int x = 0; x < width; x += 16 )
|
||||
{
|
||||
renderer.chunks[index] = new ChunkInfo( x, y, z );
|
||||
renderer.unsortedChunks[index] = renderer.chunks[index];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
static int NextMultipleOf16( int value ) {
|
||||
return (value + 0x0F) & ~0x0F;
|
||||
}
|
||||
|
||||
public void RedrawBlock( int x, int y, int z, byte block, int oldHeight, int newHeight ) {
|
||||
int cx = x >> 4, bX = x & 0x0F;
|
||||
int cy = y >> 4, bY = y & 0x0F;
|
||||
int cz = z >> 4, bZ = z & 0x0F;
|
||||
// NOTE: It's a lot faster to only update the chunks that are affected by the change in shadows,
|
||||
// rather than the entire column.
|
||||
int newLightcy = newHeight < 0 ? 0 : newHeight >> 4;
|
||||
int oldLightcy = oldHeight < 0 ? 0 : oldHeight >> 4;
|
||||
ResetChunkAndBelow( cx, cy, cz, newLightcy, oldLightcy );
|
||||
|
||||
if( bX == 0 && cx > 0 && NeedsUpdate( x, y, z, x - 1, y, z ) )
|
||||
ResetChunkAndBelow( cx - 1, cy, cz, newLightcy, oldLightcy );
|
||||
if( bY == 0 && cy > 0 && NeedsUpdate( x, y, z, x, y - 1, z ) )
|
||||
ResetChunkAndBelow( cx, cy - 1, cz, newLightcy, oldLightcy );
|
||||
if( bZ == 0 && cz > 0 && NeedsUpdate( x, y, z, x, y, z - 1 ) )
|
||||
ResetChunkAndBelow( cx, cy, cz - 1, newLightcy, oldLightcy );
|
||||
|
||||
if( bX == 15 && cx < chunksX - 1 && NeedsUpdate( x, y, z, x + 1, y, z ) )
|
||||
ResetChunkAndBelow( cx + 1, cy, cz, newLightcy, oldLightcy );
|
||||
if( bY == 15 && cy < chunksY - 1 && NeedsUpdate( x, y, z, x, y + 1, z ) )
|
||||
ResetChunkAndBelow( cx, cy + 1, cz, newLightcy, oldLightcy );
|
||||
if( bZ == 15 && cz < chunksZ - 1 && NeedsUpdate( x, y, z, x, y, z + 1 ) )
|
||||
ResetChunkAndBelow( cx, cy, cz + 1, newLightcy, oldLightcy );
|
||||
}
|
||||
|
||||
bool NeedsUpdate( int x1, int y1, int z1, int x2, int y2, int z2 ) {
|
||||
byte b1 = game.World.GetBlock( x1, y1, z1 );
|
||||
byte b2 = game.World.GetBlock( x2, y2, z2 );
|
||||
return (!info.IsOpaque[b1] && info.IsOpaque[b2]) || !(info.IsOpaque[b1] && b2 == 0);
|
||||
}
|
||||
|
||||
void ResetChunkAndBelow( int cx, int cy, int cz, int newLightCy, int oldLightCy ) {
|
||||
if( newLightCy == oldLightCy ) {
|
||||
ResetChunk( cx, cy, cz );
|
||||
} else {
|
||||
int cyMax = Math.Max( newLightCy, oldLightCy );
|
||||
int cyMin = Math.Min( oldLightCy, newLightCy );
|
||||
for( cy = cyMax; cy >= cyMin; cy-- ) {
|
||||
ResetChunk( cx, cy, cz );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetChunk( int cx, int cy, int cz ) {
|
||||
if( cx < 0 || cy < 0 || cz < 0 ||
|
||||
cx >= chunksX || cy >= chunksY || cz >= chunksZ ) return;
|
||||
DeleteChunk( renderer.unsortedChunks[cx + chunksX * ( cy + cz * chunksY )] );
|
||||
}
|
||||
|
||||
int chunksTarget = 4;
|
||||
const double targetTime = (1.0 / 30) + 0.01;
|
||||
public void UpdateChunks( double deltaTime ) {
|
||||
int chunkUpdates = 0;
|
||||
int viewDist = Utils.AdjViewDist( game.ViewDistance < 16 ? 16 : game.ViewDistance );
|
||||
int adjViewDistSqr = (viewDist + 24) * (viewDist + 24);
|
||||
chunksTarget += deltaTime < targetTime ? 1 : -1; // build more chunks if 30 FPS or over, otherwise slowdown.
|
||||
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 ) {
|
||||
ChunkInfo[] chunks = renderer.chunks;
|
||||
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 && 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 ) {
|
||||
ChunkInfo[] chunks = renderer.chunks;
|
||||
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++;
|
||||
builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
|
||||
ref info.NormalParts, ref info.TranslucentParts );
|
||||
|
||||
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
||||
info.Empty = true;
|
||||
} else {
|
||||
IncrementUsed( info.NormalParts );
|
||||
IncrementUsed( info.TranslucentParts );
|
||||
}
|
||||
chunkUpdates++;
|
||||
}
|
||||
|
||||
void IncrementUsed( ChunkPartInfo[] parts ) {
|
||||
if( parts == null ) return;
|
||||
for( int i = 0; i < parts.Length; i++ ) {
|
||||
if( parts[i].IndicesCount == 0 ) continue;
|
||||
renderer.totalUsed[i]++;
|
||||
}
|
||||
}
|
||||
|
||||
void DecrementUsed( ChunkPartInfo[] parts ) {
|
||||
if( parts == null ) return;
|
||||
for( int i = 0; i < parts.Length; i++ ) {
|
||||
if( parts[i].IndicesCount == 0 ) continue;
|
||||
renderer.totalUsed[i]--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,266 +0,0 @@
|
|||
// ClassicalSharp copyright 2014-2016 UnknownShadow200 | Licensed under MIT
|
||||
using System;
|
||||
using ClassicalSharp.GraphicsAPI;
|
||||
using OpenTK;
|
||||
|
||||
namespace ClassicalSharp.Renderers {
|
||||
|
||||
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.P3fT2fC4b );
|
||||
api.Texturing = true;
|
||||
api.AlphaTest = true;
|
||||
|
||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||
if( totalUsed[batch] <= 0 ) continue;
|
||||
if( pendingNormal[batch] || usedNormal[batch] ) {
|
||||
api.BindTexture( texIds[batch] );
|
||||
RenderNormalBatch( batch );
|
||||
pendingNormal[batch] = false;
|
||||
}
|
||||
}
|
||||
api.AlphaTest = false;
|
||||
api.Texturing = false;
|
||||
#if DEBUG_OCCLUSION
|
||||
DebugPickedPos();
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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.P3fT2fC4b );
|
||||
api.Texturing = false;
|
||||
api.AlphaBlending = false;
|
||||
api.ColourWrite = false;
|
||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||
if( totalUsed[batch] <= 0 ) continue;
|
||||
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( totalUsed[batch] <= 0 ) continue;
|
||||
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 int maxVertex = 65536;
|
||||
const int maxIndices = maxVertex / 4 * 6;
|
||||
void RenderNormalBatch( int batch ) {
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
#if OCCLUSION
|
||||
if( info.NormalParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.NormalParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
|
||||
ChunkPartInfo part = info.NormalParts[batch];
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
usedNormal[batch] = true;
|
||||
if( part.IndicesCount > maxIndices )
|
||||
DrawBigPart( info, ref part );
|
||||
else
|
||||
DrawPart( info, ref part );
|
||||
|
||||
if( part.spriteCount > 0 ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.spriteCount, 0 );
|
||||
api.FaceCulling = false;
|
||||
}
|
||||
game.Vertices += part.IndicesCount;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTranslucentBatch( int batch ) {
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
#if OCCLUSION
|
||||
if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.TranslucentParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
ChunkPartInfo part = info.TranslucentParts[batch];
|
||||
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
DrawTranslucentPart( info, ref part );
|
||||
game.Vertices += part.IndicesCount;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderTranslucentBatchDepthPass( int batch ) {
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
#if OCCLUSION
|
||||
if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.TranslucentParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
|
||||
ChunkPartInfo part = info.TranslucentParts[batch];
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
usedTranslucent[batch] = true;
|
||||
DrawTranslucentPart( info, ref part );
|
||||
}
|
||||
}
|
||||
|
||||
void DrawPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = info.DrawLeft && part.leftCount > 0;
|
||||
bool drawRight = info.DrawRight && part.rightCount > 0;
|
||||
bool drawBottom = info.DrawBottom && part.bottomCount > 0;
|
||||
bool drawTop = info.DrawTop && part.topCount > 0;
|
||||
bool drawFront = info.DrawFront && part.frontCount > 0;
|
||||
bool drawBack = info.DrawBack && part.backCount > 0;
|
||||
|
||||
if( drawLeft && drawRight ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
if( drawBottom && drawTop ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawBottom ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
} else if( drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
|
||||
bool drawAllFaces = false;
|
||||
void DrawTranslucentPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = (drawAllFaces || info.DrawLeft) && part.leftCount > 0;
|
||||
bool drawRight = (drawAllFaces || info.DrawRight) && part.rightCount > 0;
|
||||
bool drawBottom = (drawAllFaces || info.DrawBottom) && part.bottomCount > 0;
|
||||
bool drawTop = (drawAllFaces || info.DrawTop) && part.topCount > 0;
|
||||
bool drawFront = (drawAllFaces || info.DrawFront) && part.frontCount > 0;
|
||||
bool drawBack = (drawAllFaces || info.DrawBack) && part.backCount > 0;
|
||||
|
||||
if( drawLeft && drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
if( drawBottom && drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
} else if( drawBottom ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
} else if( drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBigPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = info.DrawLeft && part.leftCount > 0;
|
||||
bool drawRight = info.DrawRight && part.rightCount > 0;
|
||||
bool drawBottom = info.DrawBottom && part.bottomCount > 0;
|
||||
bool drawTop = info.DrawTop && part.topCount > 0;
|
||||
bool drawFront = info.DrawFront && part.frontCount > 0;
|
||||
bool drawBack = info.DrawBack && part.backCount > 0;
|
||||
|
||||
if( drawLeft && drawRight ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
// Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case.
|
||||
if( drawBottom && drawTop ) {
|
||||
api.FaceCulling = true;
|
||||
if( part.IndicesCount > maxIndices ) {
|
||||
int part1Count = maxIndices - part.bottomIndex;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.bottomIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
}
|
||||
api.FaceCulling = false;
|
||||
} else if( drawBottom ) {
|
||||
int part1Count;
|
||||
if( part.IndicesCount > maxIndices &&
|
||||
( part1Count = maxIndices - part.bottomIndex ) < part.bottomCount ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.bottomIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
}
|
||||
} else if( drawTop ) {
|
||||
int part1Count;
|
||||
if( part.IndicesCount > maxIndices &&
|
||||
( part1Count = maxIndices - part.topIndex ) < part.topCount ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.topIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7,371 +7,318 @@ using OpenTK;
|
|||
|
||||
namespace ClassicalSharp.Renderers {
|
||||
|
||||
// TODO: optimise chunk rendering
|
||||
// --> reduce iterations: liquid and sprite pass only need 1 row
|
||||
public partial class MapRenderer : IDisposable {
|
||||
public class ChunkInfo {
|
||||
|
||||
class ChunkInfo {
|
||||
|
||||
public ushort CentreX, CentreY, CentreZ;
|
||||
public bool Visible = true, Empty = false;
|
||||
public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop;
|
||||
#if OCCLUSION
|
||||
public bool Visited = false, Occluded = false;
|
||||
public byte OcclusionFlags, OccludedFlags, DistanceFlags;
|
||||
#endif
|
||||
|
||||
public ChunkPartInfo[] NormalParts;
|
||||
public ChunkPartInfo[] TranslucentParts;
|
||||
|
||||
public ChunkInfo( int x, int y, int z ) {
|
||||
CentreX = (ushort)(x + 8);
|
||||
CentreY = (ushort)(y + 8);
|
||||
CentreZ = (ushort)(z + 8);
|
||||
}
|
||||
public ushort CentreX, CentreY, CentreZ;
|
||||
public bool Visible = true, Empty = false;
|
||||
public bool DrawLeft, DrawRight, DrawFront, DrawBack, DrawBottom, DrawTop;
|
||||
#if OCCLUSION
|
||||
public bool Visited = false, Occluded = false;
|
||||
public byte OcclusionFlags, OccludedFlags, DistanceFlags;
|
||||
#endif
|
||||
|
||||
public ChunkPartInfo[] NormalParts;
|
||||
public ChunkPartInfo[] TranslucentParts;
|
||||
|
||||
public ChunkInfo( int x, int y, int z ) {
|
||||
CentreX = (ushort)(x + 8);
|
||||
CentreY = (ushort)(y + 8);
|
||||
CentreZ = (ushort)(z + 8);
|
||||
}
|
||||
}
|
||||
|
||||
public partial class MapRenderer : IDisposable {
|
||||
|
||||
Game game;
|
||||
IGraphicsApi api;
|
||||
int _1DUsed = 1;
|
||||
ChunkMeshBuilder builder;
|
||||
BlockInfo info;
|
||||
|
||||
int width, height, length;
|
||||
ChunkInfo[] chunks, unsortedChunks;
|
||||
int[] distances;
|
||||
Vector3I chunkPos = new Vector3I( int.MaxValue, int.MaxValue, int.MaxValue );
|
||||
int elementsPerBitmap = 0;
|
||||
bool[] usedTranslucent, usedNormal, pendingTranslucent, pendingNormal;
|
||||
int[] totalUsed;
|
||||
internal ChunkInfo[] chunks, unsortedChunks;
|
||||
internal bool[] usedTranslucent, usedNormal;
|
||||
internal bool[] pendingTranslucent, pendingNormal;
|
||||
internal int[] totalUsed;
|
||||
ChunkUpdater updater;
|
||||
|
||||
public MapRenderer( Game game ) {
|
||||
this.game = game;
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
RecalcBooleans( true );
|
||||
|
||||
builder = new ChunkMeshBuilder( game );
|
||||
api = game.Graphics;
|
||||
elementsPerBitmap = game.TerrainAtlas1D.elementsPerBitmap;
|
||||
info = game.BlockInfo;
|
||||
|
||||
game.Events.TerrainAtlasChanged += TerrainAtlasChanged;
|
||||
game.WorldEvents.OnNewMap += OnNewMap;
|
||||
game.WorldEvents.OnNewMapLoaded += OnNewMapLoaded;
|
||||
game.WorldEvents.EnvVariableChanged += EnvVariableChanged;
|
||||
game.Events.BlockDefinitionChanged += BlockDefinitionChanged;
|
||||
game.Events.ViewDistanceChanged += ViewDistanceChanged;
|
||||
game.Events.ProjectionChanged += ProjectionChanged;
|
||||
updater = new ChunkUpdater( game, this );
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
ClearChunkCache();
|
||||
chunks = null;
|
||||
unsortedChunks = null;
|
||||
game.Events.TerrainAtlasChanged -= TerrainAtlasChanged;
|
||||
game.WorldEvents.OnNewMap -= OnNewMap;
|
||||
game.WorldEvents.OnNewMapLoaded -= OnNewMapLoaded;
|
||||
game.WorldEvents.EnvVariableChanged -= EnvVariableChanged;
|
||||
game.WorldEvents.BlockDefinitionChanged -= BlockDefinitionChanged;
|
||||
game.Events.ViewDistanceChanged -= ViewDistanceChanged;
|
||||
game.Events.ProjectionChanged -= ProjectionChanged;
|
||||
builder.Dispose();
|
||||
}
|
||||
public void Dispose() { updater.Dispose(); }
|
||||
|
||||
public void Refresh() {
|
||||
chunkPos = new Vector3I( int.MaxValue );
|
||||
totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
if( chunks == null || game.World.IsNotLoaded ) return;
|
||||
ClearChunkCache();
|
||||
CreateChunkCache();
|
||||
}
|
||||
|
||||
void RefreshBorders( int clipLevel ) {
|
||||
chunkPos = new Vector3I( int.MaxValue );
|
||||
if( chunks == null || game.World.IsNotLoaded ) return;
|
||||
|
||||
int index = 0;
|
||||
for( int z = 0; z < chunksZ; z++ )
|
||||
for( int y = 0; y < chunksY; y++ )
|
||||
for( int x = 0; x < chunksX; x++ )
|
||||
{
|
||||
bool isBorder = x == 0 || z == 0 || x == (chunksX - 1) || z == (chunksZ - 1);
|
||||
if( isBorder && (y * 16) < clipLevel )
|
||||
DeleteChunk( unsortedChunks[index] );
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
void EnvVariableChanged( object sender, EnvVarEventArgs e ) {
|
||||
if( e.Var == EnvVar.SunlightColour || e.Var == EnvVar.ShadowlightColour ) {
|
||||
Refresh();
|
||||
} else if( e.Var == EnvVar.EdgeLevel ) {
|
||||
int oldClip = builder.clipLevel;
|
||||
builder.clipLevel = Math.Max( 0, game.World.SidesHeight );
|
||||
RefreshBorders( Math.Max( oldClip, builder.clipLevel ) );
|
||||
}
|
||||
}
|
||||
|
||||
void TerrainAtlasChanged( object sender, EventArgs e ) {
|
||||
bool refreshRequired = elementsPerBitmap != game.TerrainAtlas1D.elementsPerBitmap;
|
||||
elementsPerBitmap = game.TerrainAtlas1D.elementsPerBitmap;
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
|
||||
if( refreshRequired ) Refresh();
|
||||
RecalcBooleans( true );
|
||||
}
|
||||
|
||||
void BlockDefinitionChanged( object sender, EventArgs e ) {
|
||||
_1DUsed = game.TerrainAtlas1D.CalcMaxUsedRow( game.TerrainAtlas, game.BlockInfo );
|
||||
RecalcBooleans( true );
|
||||
Refresh();
|
||||
}
|
||||
|
||||
void ProjectionChanged( object sender, EventArgs e ) {
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
}
|
||||
|
||||
void OnNewMap( object sender, EventArgs e ) {
|
||||
game.ChunkUpdates = 0;
|
||||
ClearChunkCache();
|
||||
for( int i = 0; i < totalUsed.Length; i++ )
|
||||
totalUsed[i] = 0;
|
||||
|
||||
chunks = null;
|
||||
unsortedChunks = null;
|
||||
chunkPos = new Vector3I( int.MaxValue, int.MaxValue, int.MaxValue );
|
||||
builder.OnNewMap();
|
||||
}
|
||||
|
||||
void ViewDistanceChanged( object sender, EventArgs e ) {
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
lastYaw = float.MaxValue;
|
||||
lastPitch = float.MaxValue;
|
||||
}
|
||||
|
||||
void RecalcBooleans( bool sizeChanged ) {
|
||||
if( sizeChanged ) {
|
||||
usedTranslucent = new bool[_1DUsed];
|
||||
usedNormal = new bool[_1DUsed];
|
||||
pendingTranslucent = new bool[_1DUsed];
|
||||
pendingNormal = new bool[_1DUsed];
|
||||
}
|
||||
|
||||
for( int i = 0; i < _1DUsed; i++ ) {
|
||||
pendingTranslucent[i] = true; usedTranslucent[i] = false;
|
||||
pendingNormal[i] = true; usedNormal[i] = false;
|
||||
}
|
||||
}
|
||||
|
||||
int chunksX, chunksY, chunksZ;
|
||||
void OnNewMapLoaded( object sender, EventArgs e ) {
|
||||
width = NextMultipleOf16( game.World.Width );
|
||||
height = NextMultipleOf16( game.World.Height );
|
||||
length = NextMultipleOf16( game.World.Length );
|
||||
chunksX = width >> 4;
|
||||
chunksY = height >> 4;
|
||||
chunksZ = length >> 4;
|
||||
|
||||
chunks = new ChunkInfo[chunksX * chunksY * chunksZ];
|
||||
unsortedChunks = new ChunkInfo[chunksX * chunksY * chunksZ];
|
||||
distances = new int[chunks.Length];
|
||||
CreateChunkCache();
|
||||
builder.OnNewMapLoaded();
|
||||
lastCamPos = new Vector3( float.MaxValue );
|
||||
lastYaw = float.MaxValue;
|
||||
lastPitch = float.MaxValue;
|
||||
}
|
||||
|
||||
void ClearChunkCache() {
|
||||
if( chunks == null ) return;
|
||||
for( int i = 0; i < chunks.Length; i++ )
|
||||
DeleteChunk( chunks[i] );
|
||||
totalUsed = new int[game.TerrainAtlas1D.TexIds.Length];
|
||||
}
|
||||
|
||||
void DeleteChunk( ChunkInfo info ) {
|
||||
info.Empty = false;
|
||||
#if OCCLUSION
|
||||
info.OcclusionFlags = 0;
|
||||
info.OccludedFlags = 0;
|
||||
#endif
|
||||
DeleteData( ref info.NormalParts );
|
||||
DeleteData( ref info.TranslucentParts );
|
||||
}
|
||||
|
||||
void DeleteData( ref ChunkPartInfo[] parts ) {
|
||||
DecrementUsed( parts );
|
||||
if( parts == null ) return;
|
||||
|
||||
for( int i = 0; i < parts.Length; i++ )
|
||||
api.DeleteVb( parts[i].VbId );
|
||||
parts = null;
|
||||
}
|
||||
|
||||
void CreateChunkCache() {
|
||||
int index = 0;
|
||||
for( int z = 0; z < length; z += 16 )
|
||||
for( int y = 0; y < height; y += 16 )
|
||||
for( int x = 0; x < width; x += 16 )
|
||||
{
|
||||
chunks[index] = new ChunkInfo( x, y, z );
|
||||
unsortedChunks[index] = chunks[index];
|
||||
index++;
|
||||
}
|
||||
}
|
||||
|
||||
static int NextMultipleOf16( int value ) {
|
||||
return (value + 0x0F) & ~0x0F;
|
||||
}
|
||||
public void Refresh() { updater.Refresh(); }
|
||||
|
||||
public void RedrawBlock( int x, int y, int z, byte block, int oldHeight, int newHeight ) {
|
||||
int cx = x >> 4, bX = x & 0x0F;
|
||||
int cy = y >> 4, bY = y & 0x0F;
|
||||
int cz = z >> 4, bZ = z & 0x0F;
|
||||
// NOTE: It's a lot faster to only update the chunks that are affected by the change in shadows,
|
||||
// rather than the entire column.
|
||||
int newLightcy = newHeight < 0 ? 0 : newHeight >> 4;
|
||||
int oldLightcy = oldHeight < 0 ? 0 : oldHeight >> 4;
|
||||
ResetChunkAndBelow( cx, cy, cz, newLightcy, oldLightcy );
|
||||
|
||||
if( bX == 0 && cx > 0 && NeedsUpdate( x, y, z, x - 1, y, z ) )
|
||||
ResetChunkAndBelow( cx - 1, cy, cz, newLightcy, oldLightcy );
|
||||
if( bY == 0 && cy > 0 && NeedsUpdate( x, y, z, x, y - 1, z ) )
|
||||
ResetChunkAndBelow( cx, cy - 1, cz, newLightcy, oldLightcy );
|
||||
if( bZ == 0 && cz > 0 && NeedsUpdate( x, y, z, x, y, z - 1 ) )
|
||||
ResetChunkAndBelow( cx, cy, cz - 1, newLightcy, oldLightcy );
|
||||
|
||||
if( bX == 15 && cx < chunksX - 1 && NeedsUpdate( x, y, z, x + 1, y, z ) )
|
||||
ResetChunkAndBelow( cx + 1, cy, cz, newLightcy, oldLightcy );
|
||||
if( bY == 15 && cy < chunksY - 1 && NeedsUpdate( x, y, z, x, y + 1, z ) )
|
||||
ResetChunkAndBelow( cx, cy + 1, cz, newLightcy, oldLightcy );
|
||||
if( bZ == 15 && cz < chunksZ - 1 && NeedsUpdate( x, y, z, x, y, z + 1 ) )
|
||||
ResetChunkAndBelow( cx, cy, cz + 1, newLightcy, oldLightcy );
|
||||
}
|
||||
|
||||
bool NeedsUpdate( int x1, int y1, int z1, int x2, int y2, int z2 ) {
|
||||
byte b1 = game.World.GetBlock( x1, y1, z1 );
|
||||
byte b2 = game.World.GetBlock( x2, y2, z2 );
|
||||
return (!info.IsOpaque[b1] && info.IsOpaque[b2]) || !(info.IsOpaque[b1] && b2 == 0);
|
||||
}
|
||||
|
||||
void ResetChunkAndBelow( int cx, int cy, int cz, int newLightCy, int oldLightCy ) {
|
||||
if( newLightCy == oldLightCy ) {
|
||||
ResetChunk( cx, cy, cz );
|
||||
} else {
|
||||
int cyMax = Math.Max( newLightCy, oldLightCy );
|
||||
int cyMin = Math.Min( oldLightCy, newLightCy );
|
||||
for( cy = cyMax; cy >= cyMin; cy-- ) {
|
||||
ResetChunk( cx, cy, cz );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResetChunk( int cx, int cy, int cz ) {
|
||||
if( cx < 0 || cy < 0 || cz < 0 ||
|
||||
cx >= chunksX || cy >= chunksY || cz >= chunksZ ) return;
|
||||
DeleteChunk( unsortedChunks[cx + chunksX * ( cy + cz * chunksY )] );
|
||||
updater.RedrawBlock( x, y, z, block, oldHeight, newHeight );
|
||||
}
|
||||
|
||||
public void Render( double deltaTime ) {
|
||||
if( chunks == null ) return;
|
||||
UpdateSortOrder();
|
||||
UpdateChunks( deltaTime );
|
||||
ChunkSorter.UpdateSortOrder( game, updater );
|
||||
updater.UpdateChunks( deltaTime );
|
||||
|
||||
RenderNormal();
|
||||
game.MapBordersRenderer.Render( deltaTime );
|
||||
RenderTranslucent();
|
||||
game.Players.DrawShadows();
|
||||
}
|
||||
|
||||
|
||||
// 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.P3fT2fC4b );
|
||||
api.Texturing = true;
|
||||
api.AlphaTest = true;
|
||||
Console.WriteLine( _1DUsed );
|
||||
|
||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||
if( totalUsed[batch] <= 0 ) continue;
|
||||
if( pendingNormal[batch] || usedNormal[batch] ) {
|
||||
api.BindTexture( texIds[batch] );
|
||||
RenderNormalBatch( batch );
|
||||
pendingNormal[batch] = false;
|
||||
}
|
||||
}
|
||||
api.AlphaTest = false;
|
||||
api.Texturing = false;
|
||||
#if DEBUG_OCCLUSION
|
||||
DebugPickedPos();
|
||||
#endif
|
||||
}
|
||||
|
||||
// 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.P3fT2fC4b );
|
||||
api.Texturing = false;
|
||||
api.AlphaBlending = false;
|
||||
api.ColourWrite = false;
|
||||
for( int batch = 0; batch < _1DUsed; batch++ ) {
|
||||
if( totalUsed[batch] <= 0 ) continue;
|
||||
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( totalUsed[batch] <= 0 ) continue;
|
||||
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 int maxVertex = 65536;
|
||||
const int maxIndices = maxVertex / 4 * 6;
|
||||
void RenderNormalBatch( int batch ) {
|
||||
for( int i = 0; i < chunks.Length; i++ ) {
|
||||
ChunkInfo info = chunks[i];
|
||||
#if OCCLUSION
|
||||
if( info.NormalParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.NormalParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
|
||||
int chunksTarget = 4;
|
||||
const double targetTime = (1.0 / 30) + 0.01;
|
||||
void UpdateChunks( double deltaTime ) {
|
||||
int chunkUpdates = 0;
|
||||
int viewDist = Utils.AdjViewDist( game.ViewDistance < 16 ? 16 : game.ViewDistance );
|
||||
int adjViewDistSqr = (viewDist + 24) * (viewDist + 24);
|
||||
chunksTarget += deltaTime < targetTime ? 1 : -1; // build more chunks if 30 FPS or over, otherwise slowdown.
|
||||
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 );
|
||||
ChunkPartInfo part = info.NormalParts[batch];
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
usedNormal[batch] = true;
|
||||
if( part.IndicesCount > maxIndices )
|
||||
DrawBigPart( info, ref part );
|
||||
else
|
||||
DrawPart( info, ref part );
|
||||
|
||||
if( part.spriteCount > 0 ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.spriteCount, 0 );
|
||||
api.FaceCulling = false;
|
||||
}
|
||||
game.Vertices += part.IndicesCount;
|
||||
}
|
||||
}
|
||||
Vector3 lastCamPos;
|
||||
float lastYaw, lastPitch;
|
||||
|
||||
void UpdateChunksAndVisibility( double deltaTime, ref int chunkUpdats, int adjViewDistSqr ) {
|
||||
|
||||
void RenderTranslucentBatch( int batch ) {
|
||||
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 OCCLUSION
|
||||
if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.TranslucentParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
ChunkPartInfo part = info.TranslucentParts[batch];
|
||||
|
||||
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
||||
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)
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
DrawTranslucentPart( info, ref part );
|
||||
game.Vertices += part.IndicesCount;
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateChunksStill( double deltaTime, ref int chunkUpdates, int adjViewDistSqr ) {
|
||||
void RenderTranslucentBatchDepthPass( int batch ) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
#if OCCLUSION
|
||||
if( info.TranslucentParts == null || !info.Visible || info.Occluded ) continue;
|
||||
#else
|
||||
if( info.TranslucentParts == null || !info.Visible ) continue;
|
||||
#endif
|
||||
|
||||
ChunkPartInfo part = info.TranslucentParts[batch];
|
||||
if( part.IndicesCount == 0 ) continue;
|
||||
usedTranslucent[batch] = true;
|
||||
DrawTranslucentPart( info, ref part );
|
||||
}
|
||||
}
|
||||
|
||||
void BuildChunk( ChunkInfo info, ref int chunkUpdates ) {
|
||||
game.ChunkUpdates++;
|
||||
builder.GetDrawInfo( info.CentreX - 8, info.CentreY - 8, info.CentreZ - 8,
|
||||
ref info.NormalParts, ref info.TranslucentParts );
|
||||
void DrawPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = info.DrawLeft && part.leftCount > 0;
|
||||
bool drawRight = info.DrawRight && part.rightCount > 0;
|
||||
bool drawBottom = info.DrawBottom && part.bottomCount > 0;
|
||||
bool drawTop = info.DrawTop && part.topCount > 0;
|
||||
bool drawFront = info.DrawFront && part.frontCount > 0;
|
||||
bool drawBack = info.DrawBack && part.backCount > 0;
|
||||
|
||||
if( info.NormalParts == null && info.TranslucentParts == null ) {
|
||||
info.Empty = true;
|
||||
} else {
|
||||
IncrementUsed( info.NormalParts );
|
||||
IncrementUsed( info.TranslucentParts );
|
||||
if( drawLeft && drawRight ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
chunkUpdates++;
|
||||
}
|
||||
|
||||
void IncrementUsed( ChunkPartInfo[] parts ) {
|
||||
if( parts == null ) return;
|
||||
for( int i = 0; i < parts.Length; i++ ) {
|
||||
if( parts[i].IndicesCount == 0 ) continue;
|
||||
totalUsed[i]++;
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
if( drawBottom && drawTop ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawBottom ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
} else if( drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void DecrementUsed( ChunkPartInfo[] parts ) {
|
||||
if( parts == null ) return;
|
||||
for( int i = 0; i < parts.Length; i++ ) {
|
||||
if( parts[i].IndicesCount == 0 ) continue;
|
||||
totalUsed[i]--;
|
||||
bool drawAllFaces = false;
|
||||
void DrawTranslucentPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = (drawAllFaces || info.DrawLeft) && part.leftCount > 0;
|
||||
bool drawRight = (drawAllFaces || info.DrawRight) && part.rightCount > 0;
|
||||
bool drawBottom = (drawAllFaces || info.DrawBottom) && part.bottomCount > 0;
|
||||
bool drawTop = (drawAllFaces || info.DrawTop) && part.topCount > 0;
|
||||
bool drawFront = (drawAllFaces || info.DrawFront) && part.frontCount > 0;
|
||||
bool drawBack = (drawAllFaces || info.DrawBack) && part.backCount > 0;
|
||||
|
||||
if( drawLeft && drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
if( drawBottom && drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
} else if( drawBottom ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
} else if( drawTop ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
|
||||
void DrawBigPart( ChunkInfo info, ref ChunkPartInfo part ) {
|
||||
api.BindVb( part.VbId );
|
||||
bool drawLeft = info.DrawLeft && part.leftCount > 0;
|
||||
bool drawRight = info.DrawRight && part.rightCount > 0;
|
||||
bool drawBottom = info.DrawBottom && part.bottomCount > 0;
|
||||
bool drawTop = info.DrawTop && part.topCount > 0;
|
||||
bool drawFront = info.DrawFront && part.frontCount > 0;
|
||||
bool drawBack = info.DrawBack && part.backCount > 0;
|
||||
|
||||
if( drawLeft && drawRight ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount + part.rightCount, part.leftIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawLeft ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.leftCount, part.leftIndex );
|
||||
} else if( drawRight ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.rightCount, part.rightIndex );
|
||||
}
|
||||
|
||||
if( drawFront && drawBack ) {
|
||||
api.FaceCulling = true;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount + part.backCount, part.frontIndex );
|
||||
api.FaceCulling = false;
|
||||
} else if( drawFront ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.frontCount, part.frontIndex );
|
||||
} else if( drawBack ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.backCount, part.backIndex );
|
||||
}
|
||||
|
||||
// Special handling for top and bottom as these can go over 65536 vertices and we need to adjust the indices in this case.
|
||||
if( drawBottom && drawTop ) {
|
||||
api.FaceCulling = true;
|
||||
if( part.IndicesCount > maxIndices ) {
|
||||
int part1Count = maxIndices - part.bottomIndex;
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.bottomIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount + part.topCount, part.bottomIndex );
|
||||
}
|
||||
api.FaceCulling = false;
|
||||
} else if( drawBottom ) {
|
||||
int part1Count;
|
||||
if( part.IndicesCount > maxIndices &&
|
||||
( part1Count = maxIndices - part.bottomIndex ) < part.bottomCount ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.bottomIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.bottomCount, part.bottomIndex );
|
||||
}
|
||||
} else if( drawTop ) {
|
||||
int part1Count;
|
||||
if( part.IndicesCount > maxIndices &&
|
||||
( part1Count = maxIndices - part.topIndex ) < part.topCount ) {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part1Count, part.topIndex );
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount - part1Count, maxVertex, 0 );
|
||||
} else {
|
||||
api.DrawIndexedVb_TrisT2fC4b( part.topCount, part.topIndex );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue