Combine PickedPos and RayTracer into one struct

This commit is contained in:
UnknownShadow200 2020-03-23 15:09:44 +11:00
parent 37109cb18a
commit 3e0e83a60a
12 changed files with 106 additions and 124 deletions

View File

@ -11,7 +11,7 @@
#include "Options.h"
struct _CameraData Camera;
static struct PickedPos cameraClipPos;
static struct RayTracer cameraClipPos;
static Vec2 cam_rotOffset;
static cc_bool cam_isForwardThird;
static int cam_deltaX, cam_deltaY;
@ -42,12 +42,12 @@ static void PerspectiveCamera_GetView(struct Matrix* mat) {
Matrix_MulBy(mat, &Camera.TiltM);
}
static void PerspectiveCamera_GetPickedBlock(struct PickedPos* pos) {
static void PerspectiveCamera_GetPickedBlock(struct RayTracer* t) {
struct Entity* p = &LocalPlayer_Instance.Base;
Vec3 dir = Vec3_GetDirVector(p->Yaw * MATH_DEG2RAD, p->Pitch * MATH_DEG2RAD);
Vec3 eyePos = Entity_GetEyePosition(p);
float reach = LocalPlayer_Instance.ReachDistance;
Picking_CalcPickedBlock(&eyePos, &dir, reach, pos);
Picking_CalcPickedBlock(&eyePos, &dir, reach, t);
}
#define CAMERA_SENSI_FACTOR (0.0002f / 3.0f * MATH_RAD2DEG)

View File

@ -5,7 +5,7 @@
/* Represents a camera, may be first or third person.
Copyright 2014-2019 ClassiCube | Licensed under BSD-3
*/
struct PickedPos;
struct RayTracer;
struct Camera;
/* Shared data for cameras. */
@ -55,7 +55,7 @@ struct Camera {
void (*LoseFocus)(void);
/* Calculates selected block in the world, based on camera's current state */
void (*GetPickedBlock)(struct PickedPos* pos);
void (*GetPickedBlock)(struct RayTracer* t);
/* Zooms the camera in or out when scrolling mouse wheel. */
cc_bool (*Zoom)(float amount);

View File

@ -38,7 +38,7 @@ struct _GameData Game;
int Game_Port;
cc_bool Game_UseCPEBlocks;
struct PickedPos Game_SelectedPos;
struct RayTracer Game_SelectedPos;
int Game_ViewDistance, Game_MaxViewDistance, Game_UserViewDistance;
int Game_Fov, Game_DefaultFov, Game_ZoomFov;
@ -545,7 +545,7 @@ static void Game_Render3D(double delta, float t) {
/* Need to render again over top of translucent block, as the selection outline */
/* is drawn without writing to the depth buffer */
if (Game_SelectedPos.Valid && !Game_HideGui && Blocks.Draw[Game_SelectedPos.Block] == DRAW_TRANSLUCENT) {
if (Game_SelectedPos.Valid && !Game_HideGui && Blocks.Draw[Game_SelectedPos.block] == DRAW_TRANSLUCENT) {
PickedPosRenderer_Render();
}
@ -641,7 +641,7 @@ static void Game_RenderFrame(double delta) {
if (!Gui_GetBlocksWorld() && World.Blocks) {
Game_Render3D(delta, t);
} else {
PickedPos_SetAsInvalid(&Game_SelectedPos);
RayTracer_SetInvalid(&Game_SelectedPos);
}
Gui_RenderGui(delta);

View File

@ -19,7 +19,7 @@ CC_VAR extern struct _GameData {
int ChunkUpdates;
} Game;
extern struct PickedPos Game_SelectedPos;
extern struct RayTracer Game_SelectedPos;
extern cc_bool Game_UseCPEBlocks;
extern String Game_Username;

View File

@ -678,7 +678,7 @@ static void DoDeleteBlock(void) {
/* always play delete animations, even if we aren't deleting a block */
HeldBlockRenderer_ClickAnim(true);
pos = Game_SelectedPos.BlockPos;
pos = Game_SelectedPos.pos;
if (!Game_SelectedPos.Valid || !World_Contains(pos.X, pos.Y, pos.Z)) return;
old = World_GetBlock(pos.X, pos.Y, pos.Z);
@ -710,7 +710,7 @@ static void DoPlaceBlock(void) {
static void DoPickBlock(void) {
IVec3 pos;
BlockID cur;
pos = Game_SelectedPos.BlockPos;
pos = Game_SelectedPos.pos;
if (!World_Contains(pos.X, pos.Y, pos.Z)) return;
cur = World_GetBlock(pos.X, pos.Y, pos.Z);

View File

@ -42,7 +42,7 @@ x,3,0, x,2,0, x,2,3, x,3,3,
0,0,z, 0,1,z, 3,1,z, 3,0,z,\
0,3,z, 0,2,z, 3,2,z, 3,3,z,
void PickedPosRenderer_Update(struct PickedPos* selected) {
void PickedPosRenderer_Update(struct RayTracer* selected) {
static const cc_uint8 indices[288] = {
PickedPos_Y(0) PickedPos_Y(3) /* YMin, YMax */
PickedPos_X(0) PickedPos_X(3) /* XMin, XMax */

View File

@ -4,10 +4,10 @@
/* Renders an outline around the block the player is looking at.
Copyright 2014-2019 ClassiCube | Licensed under BSD-3
*/
struct PickedPos;
struct RayTracer;
struct IGameComponent;
extern struct IGameComponent PickedPosRenderer_Component;
void PickedPosRenderer_Render(void);
void PickedPosRenderer_Update(struct PickedPos* selected);
void PickedPosRenderer_Update(struct RayTracer* selected);
#endif

View File

@ -10,51 +10,44 @@
#include "Camera.h"
static float pickedPos_dist;
static void PickedPos_TestAxis(struct PickedPos* pos, float dAxis, Face fAxis) {
static void TestAxis(struct RayTracer* t, float dAxis, Face fAxis) {
dAxis = Math_AbsF(dAxis);
if (dAxis >= pickedPos_dist) return;
pickedPos_dist = dAxis;
pos->Closest = fAxis;
t->Closest = fAxis;
}
void PickedPos_SetAsValid(struct PickedPos* pos, struct RayTracer* t, const Vec3* intersect) {
pos->BlockPos.X = t->X;
pos->BlockPos.Y = t->Y;
pos->BlockPos.Z = t->Z;
pos->TranslatedPos = pos->BlockPos;
pos->Valid = true;
pos->Block = t->Block;
pos->Intersect = *intersect;
pos->Min = t->Min; pos->Max = t->Max;
static void SetAsValid(struct RayTracer* t) {
t->TranslatedPos = t->pos;
t->Valid = true;
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);
TestAxis(t, t->Intersect.X - t->Min.X, FACE_XMIN);
TestAxis(t, t->Intersect.X - t->Max.X, FACE_XMAX);
TestAxis(t, t->Intersect.Y - t->Min.Y, FACE_YMIN);
TestAxis(t, t->Intersect.Y - t->Max.Y, FACE_YMAX);
TestAxis(t, t->Intersect.Z - t->Min.Z, FACE_ZMIN);
TestAxis(t, t->Intersect.Z - t->Max.Z, FACE_ZMAX);
switch (pos->Closest) {
case FACE_XMIN: pos->TranslatedPos.X--; break;
case FACE_XMAX: pos->TranslatedPos.X++; break;
case FACE_ZMIN: pos->TranslatedPos.Z--; break;
case FACE_ZMAX: pos->TranslatedPos.Z++; break;
case FACE_YMIN: pos->TranslatedPos.Y--; break;
case FACE_YMAX: pos->TranslatedPos.Y++; break;
switch (t->Closest) {
case FACE_XMIN: t->TranslatedPos.X--; break;
case FACE_XMAX: t->TranslatedPos.X++; break;
case FACE_ZMIN: t->TranslatedPos.Z--; break;
case FACE_ZMAX: t->TranslatedPos.Z++; break;
case FACE_YMIN: t->TranslatedPos.Y--; break;
case FACE_YMAX: t->TranslatedPos.Y++; break;
}
}
void PickedPos_SetAsInvalid(struct PickedPos* pos) {
static const IVec3 blockPos = { -1, -1, -1 };
pos->BlockPos = blockPos;
pos->TranslatedPos = blockPos;
void RayTracer_SetInvalid(struct RayTracer* t) {
static const IVec3 pos = { -1, -1, -1 };
t->pos = pos;
t->TranslatedPos = pos;
pos->Valid = false;
pos->Block = BLOCK_AIR;
pos->Closest = FACE_COUNT;
t->Valid = false;
t->block = BLOCK_AIR;
t->Closest = FACE_COUNT;
}
static float RayTracer_Div(float a, float b) {
@ -63,22 +56,21 @@ static float RayTracer_Div(float a, float b) {
}
void RayTracer_Init(struct RayTracer* t, const Vec3* origin, const Vec3* dir) {
IVec3 start, cellBoundary;
t->Origin = *origin; t->Dir = *dir;
IVec3 cellBoundary;
t->origin = *origin; t->dir = *dir;
/* Rounds the position's X, Y and Z down to the nearest integer values. */
IVec3_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.*/
IVec3_Floor(&t->pos, origin);
/* 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. */
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);
cellBoundary.X = t->pos.X + (t->step.X > 0 ? 1 : 0);
cellBoundary.Y = t->pos.Y + (t->step.Y > 0 ? 1 : 0);
cellBoundary.Z = t->pos.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. */
@ -97,22 +89,21 @@ void RayTracer_Step(struct RayTracer* t) {
(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->pos.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->pos.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->pos.Z += t->step.Z;
t->tMax.Z += t->tDelta.Z;
}
}
static struct RayTracer tracer;
#define PICKING_BORDER BLOCK_BEDROCK
typedef cc_bool (*IntersectTest)(struct PickedPos* pos);
typedef cc_bool (*IntersectTest)(struct RayTracer* t);
static BlockID Picking_GetInside(int x, int y, int z) {
cc_bool sides;
@ -150,7 +141,7 @@ static BlockID Picking_GetOutside(int x, int y, int z, IVec3 origin) {
return BLOCK_AIR;
}
static cc_bool RayTrace(const Vec3* origin, const Vec3* dir, float reach, struct PickedPos* pos, IntersectTest intersect) {
static cc_bool RayTrace(struct RayTracer* t, const Vec3* origin, const Vec3* dir, float reach, IntersectTest intersect) {
IVec3 pOrigin;
cc_bool insideMap;
float reachSq;
@ -161,89 +152,88 @@ static cc_bool RayTrace(const Vec3* origin, const Vec3* dir, float reach, struct
float dzMin, dzMax, dz;
int i, x, y, z;
RayTracer_Init(&tracer, origin, dir);
RayTracer_Init(t, origin, dir);
IVec3_Floor(&pOrigin, origin);
insideMap = World_Contains(pOrigin.X, pOrigin.Y, pOrigin.Z);
reachSq = reach * reach;
for (i = 0; i < 25000; i++) {
x = tracer.X; y = tracer.Y; z = tracer.Z;
x = t->pos.X; y = t->pos.Y; z = t->pos.Z;
v.X = (float)x; v.Y = (float)y; v.Z = (float)z;
tracer.Block = insideMap ? Picking_GetInside(x, y, z) : Picking_GetOutside(x, y, z, pOrigin);
Vec3_Add(&minBB, &v, &Blocks.RenderMinBB[tracer.Block]);
Vec3_Add(&maxBB, &v, &Blocks.RenderMaxBB[tracer.Block]);
t->block = insideMap ? Picking_GetInside(x, y, z) : Picking_GetOutside(x, y, z, pOrigin);
Vec3_Add(&t->Min, &v, &Blocks.RenderMinBB[t->block]);
Vec3_Add(&t->Max, &v, &Blocks.RenderMaxBB[t->block]);
dxMin = Math_AbsF(origin->X - minBB.X); dxMax = Math_AbsF(origin->X - maxBB.X);
dyMin = Math_AbsF(origin->Y - minBB.Y); dyMax = Math_AbsF(origin->Y - maxBB.Y);
dzMin = Math_AbsF(origin->Z - minBB.Z); dzMax = Math_AbsF(origin->Z - maxBB.Z);
dxMin = Math_AbsF(origin->X - t->Min.X); dxMax = Math_AbsF(origin->X - t->Max.X);
dyMin = Math_AbsF(origin->Y - t->Min.Y); dyMax = Math_AbsF(origin->Y - t->Max.Y);
dzMin = Math_AbsF(origin->Z - t->Min.Z); dzMax = Math_AbsF(origin->Z - t->Max.Z);
dx = min(dxMin, dxMax); dy = min(dyMin, dyMax); dz = min(dzMin, dzMax);
if (dx * dx + dy * dy + dz * dz > reachSq) return false;
tracer.Min = minBB; tracer.Max = maxBB;
if (intersect(pos)) return true;
RayTracer_Step(&tracer);
if (intersect(t)) return true;
RayTracer_Step(t);
}
Logger_Abort("Something went wrong, did over 25,000 iterations in Picking_RayTrace()");
return false;
}
static cc_bool ClipBlock(struct PickedPos* pos) {
Vec3 scaledDir, intersect;
static cc_bool ClipBlock(struct RayTracer* t) {
Vec3 scaledDir;
float lenSq, reach;
float t0, t1;
if (!Game_CanPick(tracer.Block)) return false;
if (!Game_CanPick(t->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. */
if (!Intersection_RayIntersectsBox(tracer.Origin, tracer.Dir, tracer.Min, tracer.Max, &t0, &t1)) return false;
if (!Intersection_RayIntersectsBox(t->origin, t->dir, t->Min, t->Max, &t0, &t1)) return false;
Vec3_Mul1(&scaledDir, &tracer.Dir, t0); /* scaledDir = dir * t0 */
Vec3_Add(&intersect, &tracer.Origin, &scaledDir); /* intersect = origin + scaledDir */
Vec3_Mul1(&scaledDir, &t->dir, t0); /* scaledDir = dir * t0 */
Vec3_Add(&t->Intersect, &t->origin, &scaledDir); /* intersect = origin + scaledDir */
/* Only pick the block if the block is precisely within reach distance. */
lenSq = Vec3_LengthSquared(&scaledDir);
reach = LocalPlayer_Instance.ReachDistance;
if (lenSq <= reach * reach) {
PickedPos_SetAsValid(pos, &tracer, &intersect);
SetAsValid(t);
} else {
PickedPos_SetAsInvalid(pos);
RayTracer_SetInvalid(t);
}
return true;
}
const static Vec3 picking_adjust = { 0.1f, 0.1f, 0.1f };
static cc_bool ClipCamera(struct PickedPos* pos) {
static cc_bool ClipCamera(struct RayTracer* t) {
Vec3 intersect;
float t0, t1;
if (Blocks.Draw[tracer.Block] == DRAW_GAS || Blocks.Collide[tracer.Block] != COLLIDE_SOLID) return false;
if (!Intersection_RayIntersectsBox(tracer.Origin, tracer.Dir, tracer.Min, tracer.Max, &t0, &t1)) return false;
if (Blocks.Draw[t->block] == DRAW_GAS || Blocks.Collide[t->block] != COLLIDE_SOLID) return false;
if (!Intersection_RayIntersectsBox(t->origin, t->dir, t->Min, t->Max, &t0, &t1)) return false;
/* Need to collide with slightly outside block, to avoid camera clipping issues */
Vec3_Sub(&tracer.Min, &tracer.Min, &picking_adjust);
Vec3_Add(&tracer.Max, &tracer.Max, &picking_adjust);
Intersection_RayIntersectsBox(tracer.Origin, tracer.Dir, tracer.Min, tracer.Max, &t0, &t1);
Vec3_Sub(&t->Min, &t->Min, &picking_adjust);
Vec3_Add(&t->Max, &t->Max, &picking_adjust);
Intersection_RayIntersectsBox(t->origin, t->dir, t->Min, t->Max, &t0, &t1);
Vec3_Mul1(&intersect, &tracer.Dir, t0); /* intersect = dir * t0 */
Vec3_Add(&intersect, &tracer.Origin, &intersect); /* intersect = origin + dir * t0 */
PickedPos_SetAsValid(pos, &tracer, &intersect);
Vec3_Mul1(&intersect, &t->dir, t0); /* intersect = dir * t0 */
Vec3_Add(&t->Intersect, &t->origin, &intersect); /* intersect = origin + dir * t0 */
SetAsValid(t);
return true;
}
void Picking_CalcPickedBlock(const Vec3* origin, const Vec3* dir, float reach, struct PickedPos* pos) {
if (!RayTrace(origin, dir, reach, pos, ClipBlock)) {
PickedPos_SetAsInvalid(pos);
void Picking_CalcPickedBlock(const Vec3* origin, const Vec3* dir, float reach, struct RayTracer* t) {
if (!RayTrace(t, origin, dir, reach, ClipBlock)) {
RayTracer_SetInvalid(t);
}
}
void Picking_ClipCameraPos(const Vec3* origin, const Vec3* dir, float reach, struct PickedPos* pos) {
void Picking_ClipCameraPos(const Vec3* origin, const Vec3* dir, float reach, struct RayTracer* t) {
cc_bool noClip = !Camera.Clipping || LocalPlayer_Instance.Hacks.Noclip;
if (noClip || !RayTrace(origin, dir, reach, pos, ClipCamera)) {
PickedPos_SetAsInvalid(pos);
Vec3_Mul1(&pos->Intersect, dir, reach); /* intersect = dir * reach */
Vec3_Add(&pos->Intersect, origin, &pos->Intersect); /* intersect = origin + dir * reach */
if (noClip || !RayTrace(t, origin, dir, reach, ClipCamera)) {
RayTracer_SetInvalid(t);
Vec3_Mul1(&t->Intersect, dir, reach); /* intersect = dir * reach */
Vec3_Add(&t->Intersect, origin, &t->Intersect); /* intersect = origin + dir * reach */
}
}

View File

@ -6,18 +6,6 @@
Copyright 2014-2019 ClassiCube | Licensed under BSD-3
*/
/* Describes the picked/selected block by the user and its position. */
struct PickedPos {
Vec3 Min; /* Minimum coords of the block's bounding box.*/
Vec3 Max; /* Maximum coords of the block's bounding box. */
Vec3 Intersect; /* Coords at which the ray intersected this block. */
IVec3 BlockPos; /* Coords of the block */
IVec3 TranslatedPos; /* Coords of the neighbouring block that is closest to the player */
cc_bool Valid; /* Whether this instance actually has a selected block currently */
Face Closest; /* Face of the picked block that is closet to the player */
BlockID Block; /* Block ID of the picked block */
};
/* 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
@ -28,22 +16,26 @@ 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
*/
struct RayTracer {
int X, Y, Z;
Vec3 Origin, Dir;
Vec3 Min, Max; /* Block data */
BlockID Block; /* Block data */
IVec3 pos; /* Coordinates of block within world */
Vec3 origin, dir;
Vec3 Min, Max; /* Min/max coords of block's bounding box. */
BlockID block;
IVec3 step;
Vec3 tMax, tDelta;
/* Result only data */
Vec3 Intersect; /* Coords at which the ray exactly intersected this block. */
IVec3 TranslatedPos; /* Coords of the neighbouring block that is closest to the player */
cc_bool Valid; /* Whether the ray tracer actually intersected with a block */
Face Closest; /* Face of the intersected block that is closet to the player */
};
void PickedPos_SetAsValid(struct PickedPos* pos, struct RayTracer* t, const Vec3* intersect);
void PickedPos_SetAsInvalid(struct PickedPos* pos);
void RayTracer_SetInvalid(struct RayTracer* t);
void RayTracer_Init(struct RayTracer* t, const Vec3* origin, const Vec3* dir);
void RayTracer_Step(struct 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_CalcPickedBlock(const Vec3* origin, const Vec3* dir, float reach, struct PickedPos* pos);
void Picking_ClipCameraPos(const Vec3* origin, const Vec3* dir, float reach, struct PickedPos* pos);
void Picking_CalcPickedBlock(const Vec3* origin, const Vec3* dir, float reach, struct RayTracer* t);
void Picking_ClipCameraPos(const Vec3* origin, const Vec3* dir, float reach, struct RayTracer* t);
#endif

View File

@ -738,7 +738,7 @@ static const char* cpe_clientExtensions[34] = {
static void CPE_SetMapEnvUrl(cc_uint8* data);
#define Ext_Deg2Packed(x) ((int)((x) * 65536.0f / 360.0f))
void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct PickedPos* pos) {
void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct RayTracer* t) {
struct Entity* p = &LocalPlayer_Instance.Base;
cc_uint8 data[15];
@ -750,13 +750,13 @@ void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct
Stream_SetU16_BE(&data[5], Ext_Deg2Packed(p->Pitch));
data[7] = targetId;
Stream_SetU16_BE(&data[8], pos->BlockPos.X);
Stream_SetU16_BE(&data[10], pos->BlockPos.Y);
Stream_SetU16_BE(&data[12], pos->BlockPos.Z);
Stream_SetU16_BE(&data[8], t->pos.X);
Stream_SetU16_BE(&data[10], t->pos.Y);
Stream_SetU16_BE(&data[12], t->pos.Z);
data[14] = 255;
/* Our own face values differ from CPE block face */
switch (pos->Closest) {
switch (t->Closest) {
case FACE_XMAX: data[14] = 0; break;
case FACE_XMIN: data[14] = 1; break;
case FACE_YMAX: data[14] = 2; break;

View File

@ -46,7 +46,7 @@ extern cc_uint16 Net_PacketSizes[OPCODE_COUNT];
extern Net_Handler Net_Handlers[OPCODE_COUNT];
#define Net_Set(opcode, handler, size) Net_Handlers[opcode] = handler; Net_PacketSizes[opcode] = size;
struct PickedPos;
struct RayTracer;
void Protocol_RemoveEntity(EntityID id);
void Protocol_Reset(void);
void Protocol_Tick(void);
@ -56,5 +56,5 @@ void Classic_SendChat(const String* text, cc_bool partial);
void Classic_WritePosition(Vec3 pos, float yaw, float pitch);
void Classic_WriteSetBlock(int x, int y, int z, cc_bool place, BlockID block);
void Classic_SendLogin(const String* username, const String* verKey);
void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct PickedPos* pos);
void CPE_SendPlayerClick(int button, cc_bool pressed, cc_uint8 targetId, struct RayTracer* t);
#endif

View File

@ -6,7 +6,7 @@
Copyright 2014-2019 ClassiCube | Licensed under BSD-3
*/
struct PickedPos;
struct RayTracer;
struct Stream;
struct IGameComponent;
struct ScheduledTask;