From fccc7dad12329622ef23e68253722c6a53d0915d Mon Sep 17 00:00:00 2001 From: UnknownShadow200 Date: Tue, 12 Sep 2017 16:22:18 +1000 Subject: [PATCH] Port Picking to C. --- src/Client/ChunkUpdater.c | 12 +- src/Client/Client.vcxproj | 2 + src/Client/Client.vcxproj.filters | 6 + src/Client/EnvRenderer.c | 16 +-- src/Client/ExtMath.c | 6 + src/Client/ExtMath.h | 3 + src/Client/Game.h | 33 +---- src/Client/Lighting.c | 34 +++-- src/Client/MapGenerator.c | 52 +++---- src/Client/Picking.c | 218 ++++++++++++++++++++++++++++-- src/Client/Picking.h | 30 +++- src/Client/TerrainAtlas.c | 18 +-- src/Client/WeatherRenderer.c | 40 +++--- 13 files changed, 335 insertions(+), 135 deletions(-) diff --git a/src/Client/ChunkUpdater.c b/src/Client/ChunkUpdater.c index 6af788a95..3f5b7e1e5 100644 --- a/src/Client/ChunkUpdater.c +++ b/src/Client/ChunkUpdater.c @@ -161,6 +161,12 @@ void ChunkUpdater_OnNewMapLoaded(void) { } +Int32 ChunkUpdater_AdjustViewDist(Real32 dist) { + if (dist < CHUNK_SIZE) dist = CHUNK_SIZE; + Int32 viewDist = Math_AdjViewDist(dist); + return (viewDist + 24) * (viewDist + 24); +} + Int32 ChunkUpdater_UpdateChunksAndVisibility(Int32* chunkUpdates) { Int32 i, j = 0; Int32 viewDistSqr = ChunkUpdater_AdjustViewDist(Game_ViewDistance); @@ -349,12 +355,6 @@ void ChunkUpdater_BuildChunk(ChunkInfo* info, Int32* chunkUpdates) { ChunkUpdater_AddParts(info->TranslucentParts, MapRenderer_TranslucentPartsCount); } -Int32 ChunkUpdater_AdjustViewDist(Real32 dist) { - if (dist < CHUNK_SIZE) dist = CHUNK_SIZE; - Int32 viewDist = Math_AdjViewDist(dist); - return (viewDist + 24) * (viewDist + 24); -} - void ChunkUpdater_QuickSort(Int32 left, Int32 right) { ChunkInfo** values = MapRenderer_SortedChunks; Int32* keys = ChunkUpdater_Distances; diff --git a/src/Client/Client.vcxproj b/src/Client/Client.vcxproj index 5e8cc8115..e2ba5d1eb 100644 --- a/src/Client/Client.vcxproj +++ b/src/Client/Client.vcxproj @@ -257,6 +257,7 @@ + @@ -278,6 +279,7 @@ + diff --git a/src/Client/Client.vcxproj.filters b/src/Client/Client.vcxproj.filters index 59c60dd4d..20a3e8787 100644 --- a/src/Client/Client.vcxproj.filters +++ b/src/Client/Client.vcxproj.filters @@ -518,5 +518,11 @@ Source Files\Utils + + Source Files\Math + + + Source Files\Game + \ No newline at end of file diff --git a/src/Client/EnvRenderer.c b/src/Client/EnvRenderer.c index 66235deb9..a1e5c5731 100644 --- a/src/Client/EnvRenderer.c +++ b/src/Client/EnvRenderer.c @@ -14,6 +14,14 @@ GfxResourceID env_cloudsVb = -1, env_skyVb = -1, env_cloudsTex = -1; GfxResourceID env_cloudVertices, env_skyVertices; +Real32 EnvRenderer_BlendFactor(Real32 x) { + /* return -0.05 + 0.22 * (Math_LogE(x) * 0.25f); */ + Real32 blend = -0.13f + 0.28f * (Math_LogE(x) * 0.25f); + if (blend < 0.0f) blend = 0.0f; + if (blend > 1.0f) blend = 1.0f; + return blend; +} + BlockID EnvRenderer_BlockOn(Real32* fogDensity, PackedCol* fogCol) { Vector3 pos = Game_CurrentCameraPos; Vector3I coords; @@ -37,14 +45,6 @@ BlockID EnvRenderer_BlockOn(Real32* fogDensity, PackedCol* fogCol) { return block; } -Real32 EnvRenderer_BlendFactor(Real32 x) { - /* return -0.05 + 0.22 * (Math_LogE(x) * 0.25f); */ - Real32 blend = -0.13f + 0.28f * (Math_LogE(x) * 0.25f); - if (blend < 0.0f) blend = 0.0f; - if (blend > 1.0f) blend = 1.0f; - return blend; -} - void EnvRenderer_RenderMinimal(Real64 deltaTime) { if (World_Blocks == NULL) return; diff --git a/src/Client/ExtMath.c b/src/Client/ExtMath.c index bf58a615c..9a0bda0d2 100644 --- a/src/Client/ExtMath.c +++ b/src/Client/ExtMath.c @@ -15,6 +15,12 @@ Int32 Math_CeilDiv(Int32 a, Int32 b) { return a / b + (a % b != 0 ? 1 : 0); } +Int32 Math_Sign(Real32 value) { + if (value > 0.0f) return +1; + if (value < 0.0f) return -1; + return 0; +} + Real32 Math_Lerp(Real32 a, Real32 b, Real32 t) { return a + (b - a) * t; } diff --git a/src/Client/ExtMath.h b/src/Client/ExtMath.h index 9322b7db4..4dff85482 100644 --- a/src/Client/ExtMath.h +++ b/src/Client/ExtMath.h @@ -9,6 +9,7 @@ #define MATH_PI 3.1415926535897931f #define MATH_DEG2RAD (MATH_PI / 180.0f) #define MATH_RAD2DEG (180.0f / MATH_PI) +#define MATH_LARGENUM 1000000000.0f #define Math_AbsF(x) fabsf(x) #define Math_AbsI(x) abs(x) @@ -31,6 +32,8 @@ Int32 Math_Floor(Real32 value); Int32 Math_Log2(Int32 value); /* Performs rounding upwards integer division.*/ Int32 Math_CeilDiv(Int32 a, Int32 b); +/* Returns sign of the given value. */ +Int32 Math_Sign(Real32 value); /* Performs linear interpolation between two values. */ Real32 Math_Lerp(Real32 a, Real32 b, Real32 t); diff --git a/src/Client/Game.h b/src/Client/Game.h index b3b55115c..974fef7ee 100644 --- a/src/Client/Game.h +++ b/src/Client/Game.h @@ -11,29 +11,21 @@ /* Total rendering time(in seconds) elapsed since the client was started. */ Real64 Game_Accumulator; - /* Accumulator for the number of chunk updates performed. Reset every second. */ Int32 Game_ChunkUpdates; - /* Whether the third person camera should have their camera position clipped so as to not intersect blocks.*/ bool Game_CameraClipping; - /* Whether colour clear gfx call should be skipped. */ bool Game_SkipClear; - /* Picked pos instance used for block selection. */ PickedPos Game_SelectedPos; - /* Picked pos instance used for camera clipping. */ PickedPos Game_CameraClipPos; - /* Default index buffer ID. */ Int32 Game_DefaultIb; - /* Whether cobblestone slab to stone brick tiles should be used. */ bool Game_UseCPEBlocks; - /* Account username of the player.*/ String Game_Username; /* Verification key used for multiplayer, unique for the username and individual server. */ @@ -42,36 +34,27 @@ String Game_Mppass; String Game_IPAddress; /* Port of multiplayer server connected to, 0 if singleplayer. */ Int32 Game_Port; - /* Radius of the sphere the player can see around the position of the current camera. */ Real32 Game_ViewDistance; /* Maximum view distance player can view up to. */ Real32 Game_MaxViewDistance; /* 'Real' view distance the player has selected. */ Real32 Game_UserViewDistance; - /* Field of view for the current camera in degrees. */ Int32 Game_Fov; Int32 Game_DefaultFov, Game_ZoomFov; - /* Strategy used to limit how many frames should be displayed at most each second. */ FpsLimitMethod FpsLimit; - /* Whether lines should be rendered for each axis. */ bool Game_ShowAxisLines; - /* Whether players should animate using simple swinging parallel to their bodies. */ bool Game_SimpleArmsAnim; - /* Whether the arm model should use the classic position. */ bool Game_ClassicArmModel; - /* Whether mouse rotation on the y axis should be inverted. */ bool Game_InvertMouse; - /* Number of vertices used for rendering terrain this frame. */ Int32 Game_Vertices; - /* Model view matrix. */ Matrix Game_View; /* Projection matrix. */ @@ -79,39 +62,30 @@ Matrix Game_Projection; /* How sensitive the client is to changes in the player's mouse position. */ Int32 Game_MouseSensitivity; - /* Whether tab names autocomplete. */ bool Game_TabAutocomplete; - bool Game_UseClassicGui; bool Game_UseClassicTabList; bool Game_UseClassicOptions; bool Game_ClassicMode; bool Game_ClassicHacks; #define Game_PureClassic (Game_ClassicMode && !Game_ClassicHacks) - /* Whether suport for custom blocks is indicated to the server. */ bool Game_AllowCustomBlocks; /* Whether CPE is used at all. */ bool Game_UseCPE; /* Whether all texture packs are ignored from the server or not. */ bool Game_AllowServerTextures; - /* Whether advanced/smooth lighting mesh builder is used. */ bool Game_SmoothLighting; - /* Whether chat logging to disc is enabled. */ bool Game_ChatLogging; - /* Whether auto rotation of blocks with special suffixes is enabled. */ bool Game_AutoRotate; - /* Whether camera has smooth rotation. */ bool Game_SmoothCamera; - /* Font name used for text rendering. */ String Game_FontName; - /* Max number of chatlines drawn in chat. */ Int32 Game_ChatLines; /* Whether clicking on chatlines copies into chat input is enabled. */ @@ -165,22 +139,19 @@ static bool Game_realCursorVisible; /* Sets whether the cursor is visible. */ void Game_SetCursorVisible(bool visible); - /* Called when projection matrix is updated. */ void Game_UpdateProjection(void); - /* Updates the block at the given coordinates. */ void Game_UpdateBlock(Int32 x, Int32 y, Int32 z, BlockID block); - /* Updates the given texture. */ void Game_UpdateTexture(GfxResourceID* texId, Stream* src, bool setSkinType); - /* Sets the view distance of the game. */ void Game_SetViewDistance(Real32 distance, bool userDist); +/* Returns whether the player can pick the given block.*/ +bool Game_CanPick(BlockID block); /* Performs thread sleeping to limit the FPS. */ static void Game_LimitFPS(void); - /* Frees all resources held by the game. */ void Game_Free(void); #endif \ No newline at end of file diff --git a/src/Client/Lighting.c b/src/Client/Lighting.c index 2a7dcac54..d6e8dd573 100644 --- a/src/Client/Lighting.c +++ b/src/Client/Lighting.c @@ -29,6 +29,22 @@ void Lighting_EnvVariableChanged(EnvVar envVar) { } } +Int32 Lighting_CalcHeightAt(Int32 x, Int32 maxY, Int32 z, Int32 index) { + Int32 mapIndex = World_Pack(x, maxY, z); + Int32 y; + + for (y = maxY; y >= 0; y--) { + BlockID block = World_Blocks[mapIndex]; + if (Block_BlocksLight[block]) { + Int32 offset = (Block_LightOffset[block] >> Face_YMax) & 1; + Lighting_heightmap[index] = (Int16)(y - offset); + return y - offset; + } + mapIndex -= World_OneY; + } + Lighting_heightmap[index] = (Int16)-10; + return -10; +} Int32 Lighting_GetLightHeight(Int32 x, Int32 z) { Int32 index = (z * World_Width) + x; @@ -36,7 +52,6 @@ Int32 Lighting_GetLightHeight(Int32 x, Int32 z) { return lightH == Int16_MaxValue ? Lighting_CalcHeightAt(x, World_Height - 1, z, index) : lightH; } - /* Outside colour is same as sunlight colour, so we reuse when possible */ bool Lighting_IsLit(Int32 x, Int32 y, Int32 z) { return y > Lighting_GetLightHeight(x, z); @@ -196,23 +211,6 @@ void Lighting_OnBlockChanged(Int32 x, Int32 y, Int32 z, BlockID oldBlock, BlockI } -Int32 Lighting_CalcHeightAt(Int32 x, Int32 maxY, Int32 z, Int32 index) { - Int32 mapIndex = World_Pack(x, maxY, z); - Int32 y; - - for (y = maxY; y >= 0; y--) { - BlockID block = World_Blocks[mapIndex]; - if (Block_BlocksLight[block]) { - Int32 offset = (Block_LightOffset[block] >> Face_YMax) & 1; - Lighting_heightmap[index] = (Int16)(y - offset); - return y - offset; - } - mapIndex -= World_OneY; - } - Lighting_heightmap[index] = (Int16)-10; - return -10; -} - Int32 Lighting_InitialHeightmapCoverage(Int32 x1, Int32 z1, Int32 xCount, Int32 zCount, Int32* skip) { Int32 elemsLeft = 0, index = 0, curRunCount = 0; Int32 x, z; diff --git a/src/Client/MapGenerator.c b/src/Client/MapGenerator.c index b1ba51fd6..e0e47f2a7 100644 --- a/src/Client/MapGenerator.c +++ b/src/Client/MapGenerator.c @@ -138,6 +138,32 @@ void NotchyGen_CreateHeightmap(void) { } } +Int32 NotchyGen_CreateStrataFast(void) { + /* Make lava layer at bottom */ + Int32 mapIndex = 0; + Int32 x, y, z; + + for (z = 0; z < Gen_Length; z++) { + for (x = 0; x < Gen_Width; x++) { + Gen_Blocks[mapIndex++] = BlockID_Lava; + } + } + + /* Invariant: the lowest value dirtThickness can possible be is -14 */ + Int32 stoneHeight = minHeight - 14; + if (stoneHeight <= 0) return 1; /* no layer is fully stone */ + + /* We can quickly fill in bottom solid layers */ + for (y = 1; y <= stoneHeight; y++) { + for (z = 0; z < Gen_Length; z++) { + for (x = 0; x < Gen_Width; x++) { + Gen_Blocks[mapIndex++] = BlockID_Stone; + } + } + } + return stoneHeight; +} + void NotchyGen_CreateStrata(void) { OctaveNoise n; OctaveNoise_Init(&n, &rnd, 8); @@ -172,32 +198,6 @@ void NotchyGen_CreateStrata(void) { } } -Int32 NotchyGen_CreateStrataFast(void) { - /* Make lava layer at bottom */ - Int32 mapIndex = 0; - Int32 x, y, z; - - for (z = 0; z < Gen_Length; z++) { - for (x = 0; x < Gen_Width; x++) { - Gen_Blocks[mapIndex++] = BlockID_Lava; - } - } - - /* Invariant: the lowest value dirtThickness can possible be is -14 */ - Int32 stoneHeight = minHeight - 14; - if (stoneHeight <= 0) return 1; /* no layer is fully stone */ - - /* We can quickly fill in bottom solid layers */ - for (y = 1; y <= stoneHeight; y++) { - for (z = 0; z < Gen_Length; z++) { - for (x = 0; x < Gen_Width; x++) { - Gen_Blocks[mapIndex++] = BlockID_Stone; - } - } - } - return stoneHeight; -} - void NotchyGen_CarveCaves(void) { Int32 cavesCount = volume / 8192; String state = String_FromConstant("Carving caves"); diff --git a/src/Client/Picking.c b/src/Client/Picking.c index 0e99fe531..e2a477f93 100644 --- a/src/Client/Picking.c +++ b/src/Client/Picking.c @@ -1,30 +1,34 @@ #include "Picking.h" #include "ExtMath.h" +#include "Game.h" +#include "Intersection.h" +#include "Player.h" +#include "World.h" +#include "Funcs.h" Real32 PickedPos_dist; void PickedPos_TestAxis(PickedPos* pos, Real32 dAxis, Face fAxis) { dAxis = Math_AbsF(dAxis); if (dAxis >= PickedPos_dist) return; - PickedPos_dist = dAxis; + PickedPos_dist = dAxis; pos->ClosestFace = fAxis; } -void PickedPos_SetAsValid(PickedPos* pos, Int32 x, Int32 y, Int32 z, - Vector3 min, Vector3 max, BlockID block, Vector3 intersect) { - pos->Min = min; pos->Max = max; - pos->BlockPos = Vector3I_Create3(x, y, z); +void PickedPos_SetAsValid(PickedPos* pos, RayTracer* t, Vector3 intersect) { + pos->Min = t->Min; pos->Max = t->Max; + pos->BlockPos = Vector3I_Create3(t->X, t->Y, t->Z); pos->Valid = true; - pos->Block = block; + pos->Block = t->Block; pos->Intersect = intersect; - PickedPos_dist = 1000000000.0f; - PickedPos_TestAxis(pos, intersect.X - min.X, Face_XMin); - PickedPos_TestAxis(pos, intersect.X - max.X, Face_XMax); - PickedPos_TestAxis(pos, intersect.Y - min.Y, Face_YMin); - PickedPos_TestAxis(pos, intersect.Y - max.Y, Face_YMax); - PickedPos_TestAxis(pos, intersect.Z - min.Z, Face_ZMin); - PickedPos_TestAxis(pos, intersect.Z - max.Z, Face_ZMax); + PickedPos_dist = MATH_LARGENUM; + PickedPos_TestAxis(pos, intersect.X - t->Min.X, Face_XMin); + PickedPos_TestAxis(pos, intersect.X - t->Max.X, Face_XMax); + PickedPos_TestAxis(pos, intersect.Y - t->Min.Y, Face_YMin); + PickedPos_TestAxis(pos, intersect.Y - t->Max.Y, Face_YMax); + PickedPos_TestAxis(pos, intersect.Z - t->Min.Z, Face_ZMin); + PickedPos_TestAxis(pos, intersect.Z - t->Max.Z, Face_ZMax); Vector3I offsets[Face_Count]; offsets[Face_XMin] = Vector3I_Create3(-1, 0, 0); @@ -42,4 +46,192 @@ void PickedPos_SetAsInvalid(PickedPos* pos) { pos->TranslatedPos = Vector3I_MinusOne; pos->ClosestFace = (Face)Face_Count; pos->Block = BlockID_Air; +} + +Real32 RayTracer_Div(Real32 a, Real32 b) { + if (Math_AbsF(b) < 0.000001f) return MATH_LARGENUM; + return a / b; +} + +void RayTracer_SetVectors(RayTracer* t, Vector3 origin, Vector3 dir) { + t->Origin = origin; t->Dir = dir; + /* Rounds the position's X, Y and Z down to the nearest integer values. */ + Vector3I start; + Vector3I_Floor(&start, &origin); + /* The cell in which the ray starts. */ + t->X = start.X; t->Y = start.Y; t->Z = start.Z; + /* Determine which way we go.*/ + t->step.X = Math_Sign(dir.X); t->step.Y = Math_Sign(dir.Y); t->step.Z = Math_Sign(dir.Z); + + /* Calculate cell boundaries. When the step (i.e. direction sign) is positive, + the next boundary is AFTER our current position, meaning that we have to add 1. + Otherwise, it is BEFORE our current position, in which case we add nothing. */ + Vector3I cellBoundary; + cellBoundary.X = start.X + (t->step.X > 0 ? 1 : 0); + cellBoundary.Y = start.Y + (t->step.Y > 0 ? 1 : 0); + cellBoundary.Z = start.Z + (t->step.Z > 0 ? 1 : 0); + + /* NOTE: we want it so if dir.x = 0, tmax.x = positive infinity + Determine how far we can travel along the ray before we hit a voxel boundary. */ + t->tMax.X = RayTracer_Div(cellBoundary.X - origin.X, dir.X); /* Boundary is a plane on the YZ axis. */ + t->tMax.Y = RayTracer_Div(cellBoundary.Y - origin.Y, dir.Y); /* Boundary is a plane on the XZ axis. */ + t->tMax.Z = RayTracer_Div(cellBoundary.Z - origin.Z, dir.Z); /* Boundary is a plane on the XY axis. */ + + // Determine how far we must travel along the ray before we have crossed a gridcell. + t->tDelta.X = RayTracer_Div((Real32)t->step.X, dir.X); + t->tDelta.Y = RayTracer_Div((Real32)t->step.Y, dir.Y); + t->tDelta.Z = RayTracer_Div((Real32)t->step.Z, dir.Z); +} + +void RayTracer_Step(RayTracer* t) { + /* For each step, determine which distance to the next voxel boundary is lowest + (i.e. which voxel boundary is nearest) and walk that way. */ + if (t->tMax.X < t->tMax.Y && t->tMax.X < t->tMax.Z) { + /* tMax.X is the lowest, an YZ cell boundary plane is nearest. */ + t->X += t->step.X; + t->tMax.X += t->tDelta.X; + } + else if (t->tMax.Y < t->tMax.Z) { + /* tMax.Y is the lowest, an XZ cell boundary plane is nearest. */ + t->Y += t->step.Y; + t->tMax.Y += t->tDelta.Y; + } + else { + /* tMax.Z is the lowest, an XY cell boundary plane is nearest. */ + t->Z += t->step.Z; + t->tMax.Z += t->tDelta.Z; + } +} + +RayTracer tracer; +#define PICKING_BORDER BlockID_Bedrock +typedef bool(*IntersectTest)(PickedPos* pos); + +BlockID Picking_GetBlock(Int32 x, Int32 y, Int32 z, Vector3I origin) { + bool sides = WorldEnv_SidesBlock != BlockID_Air; + Int32 height = WorldEnv_SidesHeight; + if (height < 1) height = 1; + bool insideMap = World_IsValidPos_3I(origin); + + /* handling of blocks inside the map, above, and on borders */ + if (x >= 0 && z >= 0 && x < World_Width && z < World_Length) { + if (y >= World_Height) return 0; + if (sides && y == -1 && insideMap) return PICKING_BORDER; + if (sides && y == 0 && origin.Y < 0) return PICKING_BORDER; + + if (sides && x == 0 && y >= 0 && y < height && origin.X < 0) return PICKING_BORDER; + if (sides && z == 0 && y >= 0 && y < height && origin.Z < 0) return PICKING_BORDER; + if (sides && x == World_MaxX && y >= 0 && y < height && origin.X >= World_Width) + return PICKING_BORDER; + if (sides && z == World_MaxZ && y >= 0 && y < height && origin.Z >= World_Length) + return PICKING_BORDER; + if (y >= 0) return World_GetBlock(x, y, z); + } + + /* pick blocks on the map boundaries (when inside the map) */ + if (!sides || !insideMap) return BlockID_Air; + if (y == 0 && origin.Y < 0) return PICKING_BORDER; + + bool validX = (x == -1 || x == World_Width) && (z >= 0 && z < World_Length); + bool validZ = (z == -1 || z == World_Length) && (x >= 0 && x < World_Width); + if (y >= 0 && y < height && (validX || validZ)) return PICKING_BORDER; + return BlockID_Air; +} + +bool Picking_RayTrace(Vector3 origin, Vector3 dir, Real32 reach, PickedPos* pos, IntersectTest intersect) { + RayTracer_SetVectors(&tracer, origin, dir); + Real32 reachSq = reach * reach; + Vector3I pOrigin; + Vector3I_Floor(&pOrigin, &origin); + + Int32 i; + Vector3 blockPos; + for (i = 0; i < 10000; i++) { + Int32 x = tracer.X, y = tracer.Y, z = tracer.Z; + blockPos.X = (Real32)x; blockPos.Y = (Real32)y; blockPos.Z = (Real32)z; + tracer.Block = Picking_GetBlock(x, y, z, pOrigin); + + Vector3 minPos, maxPos; + Vector3_Add(&minPos, &blockPos, &Block_RenderMinBB[tracer.Block]); + Vector3_Add(&maxPos, &blockPos, &Block_RenderMaxBB[tracer.Block]); + + Real32 dxMin = Math_AbsF(origin.X - minPos.X), dxMax = Math_AbsF(origin.X - maxPos.X); + Real32 dyMin = Math_AbsF(origin.Y - minPos.Y), dyMax = Math_AbsF(origin.Y - maxPos.Y); + Real32 dzMin = Math_AbsF(origin.Z - minPos.Z), dzMax = Math_AbsF(origin.Z - maxPos.Z); + Real32 dx = min(dxMin, dxMax), dy = min(dyMin, dyMax), dz = min(dzMin, dzMax); + if (dx * dx + dy * dy + dz * dz > reachSq) return false; + + tracer.Min = minPos; tracer.Max = maxPos; + if (intersect(pos)) return true; + RayTracer_Step(&tracer); + } + ErrorHandler_Fail("Something went wrong, did over 10,000 iterations in Picking_RayTrace()"); +} + +bool Picking_ClipBlock(PickedPos* pos) { + if (!Game_CanPick(tracer.Block)) return false; + + /* This cell falls on the path of the ray. Now perform an additional AABB test, + since some blocks do not occupy a whole cell. */ + Real32 t0, t1; + if (!Intersection_RayIntersectsBox(tracer.Origin, tracer.Dir, tracer.Min, tracer.Max, &t0, &t1)) { + return false; + } + + Vector3 scaledDir, intersect; + Vector3_Multiply1(&scaledDir, &tracer.Dir, t0); /* scaledDir = dir * t0 */ + Vector3_Add(&intersect, &tracer.Origin, &scaledDir); /* intersect = origin + scaledDir */ + /* Only pick the block if the block is precisely within reach distance. */ + Real32 lenSq = Vector3_LengthSquared(&scaledDir); + Real32 reach = LocalPlayer_Instance.ReachDistance; + + if (lenSq <= reach * reach) { + PickedPos_SetAsValid(pos, &tracer, intersect); + } + else { + PickedPos_SetAsInvalid(pos); + } + return true; +} + +bool Picking_ClipCamera(PickedPos* pos) { + if (Block_Draw[tracer.Block] == DrawType_Gas || Block_Collide[tracer.Block] != CollideType_Solid) { + return false; + } + + Real32 t0, t1; + if (!Intersection_RayIntersectsBox(tracer.Origin, tracer.Dir, tracer.Min, tracer.Max, &t0, &t1)) { + return false; + } + + Vector3 intersect; + Vector3_Multiply1(&intersect, &tracer.Dir, t0); /* intersect = dir * t0 */ + Vector3_Add(&intersect, &tracer.Origin, &intersect); /* intersect = origin + dir * t0 */ + PickedPos_SetAsValid(pos, &tracer, intersect); + +#define PICKING_ADJUST 0.1f + switch (pos->ClosestFace) { + case Face_XMin: pos->Intersect.X -= PICKING_ADJUST; break; + case Face_XMax: pos->Intersect.X += PICKING_ADJUST; break; + case Face_YMin: pos->Intersect.Y -= PICKING_ADJUST; break; + case Face_YMax: pos->Intersect.Y += PICKING_ADJUST; break; + case Face_ZMin: pos->Intersect.Z -= PICKING_ADJUST; break; + case Face_ZMax: pos->Intersect.Z += PICKING_ADJUST; break; + } + return true; +} + +void Picking_CalculatePickedBlock(Vector3 origin, Vector3 dir, Real32 reach, PickedPos* pos) { + if (!Picking_RayTrace(origin, dir, reach, pos, Picking_ClipBlock)) { + PickedPos_SetAsInvalid(pos); + } +} + +void Picking_ClipCameraPos(Vector3 origin, Vector3 dir, Real32 reach, PickedPos* pos) { + bool noClip = !Game_CameraClipping || LocalPlayer_Instance.Hacks.Noclip; + if (noClip || !Picking_RayTrace(origin, dir, reach, pos, Picking_ClipCamera)) { + PickedPos_SetAsInvalid(pos); + Vector3_Multiply1(&pos->Intersect, &tracer.Dir, reach); /* intersect = dir * reach */ + Vector3_Add(&pos->Intersect, &tracer.Origin, &pos->Intersect); /* intersect = origin + dir * reach */ + } } \ No newline at end of file diff --git a/src/Client/Picking.h b/src/Client/Picking.h index f4d17a6c3..425939718 100644 --- a/src/Client/Picking.h +++ b/src/Client/Picking.h @@ -5,7 +5,7 @@ #include "Vectors.h" #include "BlockID.h" /* Data for picking/selecting block by the user, and clipping the camera. - Copyright 2014 - 2017 ClassicalSharp | Licensed under BSD-3 +Copyright 2014 - 2017 ClassicalSharp | Licensed under BSD-3 */ /* Describes the picked/selected block by the user and its position. */ @@ -28,14 +28,34 @@ typedef struct PickedPos_ { BlockID Block; } PickedPos; -/* Mark as having a selected block, and calculates the closest face of the selected block's position. */ -void PickedPos_SetAsValid(PickedPos* pos, Int32 x, Int32 y, Int32 z, - Vector3 min, Vector3 max, BlockID block, Vector3 intersect); +/* Implements a voxel ray tracer +http://www.xnawiki.com/index.php/Voxel_traversal +https://web.archive.org/web/20120113051728/http://www.xnawiki.com/index.php?title=Voxel_traversal +Implementation based on: "A Fast Voxel Traversal Algorithm for Ray Tracing" +John Amanatides, Andrew Woo +http://www.cse.yorku.ca/~amana/research/grid.pdf +http://www.devmaster.net/articles/raytracing_series/A%20faster%20voxel%20traversal%20algorithm%20for%20ray%20tracing.pdf +*/ +typedef struct RayTracer_ { + Int32 X, Y, Z; + Vector3 Origin, Dir; + Vector3 Min, Max; /* Block data */ + BlockID Block; /* Block data */ + Vector3I step; + Vector3 tMax, tDelta; +} RayTracer; + +/* Mark as having a selected block, and calculates the closest face of the selected block's position. */ +void PickedPos_SetAsValid(PickedPos* pos, RayTracer* t, Vector3 intersect); /* Marks as not having a selected block. */ void PickedPos_SetAsInvalid(PickedPos* pos); +void RayTracer_SetVectors(RayTracer* t, Vector3 origin, Vector3 dir); +void RayTracer_Step(RayTracer* t); +/* Determines the picked block based on the given origin and direction vector. + Marks pickedPos as invalid if a block could not be found due to going outside map boundaries + or not being able to find a suitable candiate within the given reach distance.*/ void Picking_CalculatePickedBlock(Vector3 origin, Vector3 dir, Real32 reach, PickedPos* pos); - void Picking_ClipCameraPos(Vector3 origin, Vector3 dir, Real32 reach, PickedPos* pos); #endif \ No newline at end of file diff --git a/src/Client/TerrainAtlas.c b/src/Client/TerrainAtlas.c index e122f8f19..4e7efcd70 100644 --- a/src/Client/TerrainAtlas.c +++ b/src/Client/TerrainAtlas.c @@ -12,6 +12,15 @@ void Atlas2D_UpdateState(Bitmap bmp) { Block_RecalculateSpriteBB(); } +Int32 Atlas2D_LoadTextureElement_Raw(TextureLoc texLoc, Bitmap* element) { + Int32 size = Atlas2D_ElementSize; + Int32 x = texLoc % Atlas2D_ElementsPerRow, y = texLoc / Atlas2D_ElementsPerRow; + Bitmap_CopyBlock(x * size, y * size, 0, 0, + &Atlas2D_Bitmap, element, size); + + return Gfx_CreateTexture(element, true, Gfx_Mipmaps); +} + Int32 Atlas2D_LoadTextureElement(TextureLoc texLoc) { Int32 size = Atlas2D_ElementSize; Bitmap element; @@ -34,15 +43,6 @@ Int32 Atlas2D_LoadTextureElement(TextureLoc texLoc) { } } -Int32 Atlas2D_LoadTextureElement_Raw(TextureLoc texLoc, Bitmap* element) { - Int32 size = Atlas2D_ElementSize; - Int32 x = texLoc % Atlas2D_ElementsPerRow, y = texLoc / Atlas2D_ElementsPerRow; - Bitmap_CopyBlock(x * size, y * size, 0, 0, - &Atlas2D_Bitmap, element, size); - - return Gfx_CreateTexture(element, true, Gfx_Mipmaps); -} - void Atlas2D_Free(void) { if (Atlas2D_Bitmap.Scan0 == NULL) return; Platform_MemFree(Atlas2D_Bitmap.Scan0); diff --git a/src/Client/WeatherRenderer.c b/src/Client/WeatherRenderer.c index f3a1f4775..a58162680 100644 --- a/src/Client/WeatherRenderer.c +++ b/src/Client/WeatherRenderer.c @@ -37,17 +37,6 @@ void WeatherRenderer_InitHeightmap(void) { } } -Real32 WeatherRenderer_RainHeight(Int32 x, Int32 z) { - if (x < 0 || z < 0 || x >= World_Width || z >= World_Length) { - return (Real32)WorldEnv_EdgeHeight; - } - Int32 index = (x * World_Length) + z; - Int32 height = weather_heightmap[index]; - - Int32 y = height == Int16_MaxValue ? WeatherRenderer_CalcHeightAt(x, World_MaxY, z, index) : height; - return y == -1 ? 0 : y + Block_MaxBB[World_GetBlock(x, y, z)].Y; -} - Int32 WeatherRenderer_CalcHeightAt(Int32 x, Int32 maxY, Int32 z, Int32 index) { Int32 mapIndex = (maxY * World_Length + z) * World_Width + x; Int32 y = maxY; @@ -63,6 +52,17 @@ Int32 WeatherRenderer_CalcHeightAt(Int32 x, Int32 maxY, Int32 z, Int32 index) { return -1; } +Real32 WeatherRenderer_RainHeight(Int32 x, Int32 z) { + if (x < 0 || z < 0 || x >= World_Width || z >= World_Length) { + return (Real32)WorldEnv_EdgeHeight; + } + Int32 index = (x * World_Length) + z; + Int32 height = weather_heightmap[index]; + + Int32 y = height == Int16_MaxValue ? WeatherRenderer_CalcHeightAt(x, World_MaxY, z, index) : height; + return y == -1 ? 0 : y + Block_MaxBB[World_GetBlock(x, y, z)].Y; +} + void WeatherRenderer_OnBlockChanged(Int32 x, Int32 y, Int32 z, BlockID oldBlock, BlockID newBlock) { bool didBlock = !(Block_Draw[oldBlock] == DrawType_Gas || Block_Draw[oldBlock] == DrawType_Sprite); bool nowBlock = !(Block_Draw[newBlock] == DrawType_Gas || Block_Draw[newBlock] == DrawType_Sprite); @@ -93,6 +93,12 @@ void WeatherRenderer_ContextRecreated(void) { weather_vb = Gfx_CreateDynamicVb(VertexFormat_P3fT2fC4b, weather_verticesCount); } +Real32 WeatherRenderer_AlphaAt(Real32 x) { + /* Wolfram Alpha: fit {0,178},{1,169},{4,147},{9,114},{16,59},{25,9} */ + Real32 falloff = 0.05f * x * x - 7 * x; + return 178 + falloff * WorldEnv_WeatherFade; +} + void WeatherRenderer_Render(Real64 deltaTime) { Weather weather = WorldEnv_Weather; if (weather == Weather_Sunny) return; @@ -127,10 +133,12 @@ void WeatherRenderer_Render(Real64 deltaTime) { Real32 height = pos.Y - y; if (height <= 0) continue; - if (particles && (weather_accumulator >= 0.25 || moved)) + if (particles && (weather_accumulator >= 0.25 || moved)) { ParticleManager_AddRainParticle(x, y, z); + } - Real32 alpha = WeatherRenderer_AlphaAt(dx * dx + dz * dz); + Real32 dist = (Real32)dx * (Real32)dx + (Real32)dz * (Real32)dz; + Real32 alpha = WeatherRenderer_AlphaAt(dist); /* Clamp between 0 and 255 */ alpha = alpha < 0.0f ? 0.0f : alpha; alpha = alpha > 255.0f ? 255.0f : alpha; @@ -180,12 +188,6 @@ void WeatherRenderer_Render(Real64 deltaTime) { Gfx_SetAlphaTest(false); } -Real32 WeatherRenderer_AlphaAt(Real32 x) { - /* Wolfram Alpha: fit {0,178},{1,169},{4,147},{9,114},{16,59},{25,9} */ - Real32 falloff = 0.05f * x * x - 7 * x; - return 178 + falloff * WorldEnv_WeatherFade; -} - void WeatherRenderer_FileChanged(Stream* stream) { String snow = String_FromConstant("snow.png"); String rain = String_FromConstant("rain.png");