From 68771b5e00c867cc6afcf2efb76c1b6cfd13a2af Mon Sep 17 00:00:00 2001 From: Mario Date: Mon, 14 May 2018 18:05:47 +1000 Subject: [PATCH] Half working branch for ECS --- qcsrc/Makefile | 3 +- qcsrc/client/progs.inc | 2 +- qcsrc/ecs/_mod.inc | 6 ++ qcsrc/ecs/cl_main.qc | 85 ++++++++++++++++ qcsrc/ecs/components/physics.qh | 6 ++ qcsrc/ecs/events/physics.qh | 3 + qcsrc/ecs/main.qh | 8 ++ qcsrc/ecs/sv_main.qc | 54 ++++++++++ qcsrc/ecs/systems/cl_physics.qc | 2 + qcsrc/ecs/systems/input.qc | 2 + qcsrc/ecs/systems/physics.qc | 170 ++++++++++++++++++++++++++++++++ qcsrc/ecs/systems/sv_physics.qc | 2 + qcsrc/server/progs.inc | 2 +- 13 files changed, 341 insertions(+), 4 deletions(-) create mode 100644 qcsrc/ecs/cl_main.qc create mode 100644 qcsrc/ecs/sv_main.qc diff --git a/qcsrc/Makefile b/qcsrc/Makefile index d09b2c5ccf..0217e01281 100644 --- a/qcsrc/Makefile +++ b/qcsrc/Makefile @@ -7,7 +7,7 @@ WORKDIR ?= ../.tmp QCCFLAGS_WATERMARK ?= $(shell git describe --tags --dirty='~') VER = $(subst *,\*,$(QCCFLAGS_WATERMARK)) NDEBUG ?= 1 -XONOTIC ?= 1 +XONOTIC ?= 0 ENABLE_EFFECTINFO ?= 0 ENABLE_DEBUGDRAW ?= 0 ENABLE_DEBUGTRACE ?= 0 @@ -52,7 +52,6 @@ QCCFLAGS ?= \ -std=gmqcc \ -Ooverlap-locals \ -O3 \ - $(QCCFLAGS_WERROR) \ -Wall \ $(QCCFLAGS_WTFS) \ -flno -futf8 -fno-bail-on-werror \ diff --git a/qcsrc/client/progs.inc b/qcsrc/client/progs.inc index 3b607a7e14..0ce1984472 100644 --- a/qcsrc/client/progs.inc +++ b/qcsrc/client/progs.inc @@ -17,8 +17,8 @@ #include #include -#include #endif +#include #ifdef BUILD_MOD #include diff --git a/qcsrc/ecs/_mod.inc b/qcsrc/ecs/_mod.inc index 48b7069b24..5a26fbda09 100644 --- a/qcsrc/ecs/_mod.inc +++ b/qcsrc/ecs/_mod.inc @@ -1,5 +1,11 @@ // generated file; do not modify #include +#ifdef CSQC + #include +#endif +#ifdef SVQC + #include +#endif #include #include diff --git a/qcsrc/ecs/cl_main.qc b/qcsrc/ecs/cl_main.qc new file mode 100644 index 0000000000..e84c8f3c88 --- /dev/null +++ b/qcsrc/ecs/cl_main.qc @@ -0,0 +1,85 @@ +#if !XONOTIC + entity me; + + /** 6 bits provides up to 64 remembered states */ + const int SNAP_BITS = 13; + .int snap; + .float times[1 << SNAP_BITS]; + + void CSQC_Init() + { + entity it = me = spawn(); + it.mins = '-16 -16 -24'; + it.maxs = '+16 +16 +45'; + it.com_phys = true; + it.com_phys_nogravityonground = true; + it.com_phys_stepheight = 31; + it.com_phys_jumpvel = 260; + it.com_phys_friction = 6; + //it.com_phys_gravity = '0 0 800'; + it.com_phys_noclip = true; + + it.com_phys_pos = '0 0 200'; + } + + void CSQC_Ent_Update(entity this, bool isnew) + { + int id = ReadByte(); + FOREACH(LinkedEntities, it.m_id == id, { + if (isnew) this.classname = it.netname; + it.m_read(this, NULL, isnew); + break; + }); + } + + void rec() + { + me.ARRAY_INDEX(float, times, me.snap) = time; + // me.times[me.snap] = time; + me.snap = (me.snap + 1) & BITS(SNAP_BITS); + } + + NET_HANDLE(ENT_OBJECT, bool isnew) + { + if (isnew) + { + this.com_phys = true; + precache_model("models/player/erebus.iqm"); + _setmodel(this, "models/player/erebus.iqm"); + this.drawmask = MASK_NORMAL; + } + this.com_phys_pos_prev = this.com_phys_pos; + this.com_phys_ang_prev = this.com_phys_ang; + serialize(ENT_OBJECT, 0, this); + this.com_phys_pos = this.origin; + this.com_phys_ang = this.angles; + return true; + } + + void CSQC_UpdateView(entity this, float w, float h) + { + entity it = me; + it.com_in_move = input_movevalues; + it.com_in_angles = input_angles; + it.com_in_jump = input_buttons & BIT(1); + makevectors(it.com_in_angles); + vector dir = normalize(it.com_in_move); + vector upvec = '0 0 1'; + vector vel = (v_forward * dir.x + v_right * dir.y + upvec * dir.z); + if (!it.com_phys_noclip) vel = vec_reflect(vel, upvec, 0); + vel = normalize(vel); + vel *= 360 * frametime * 8; + it.com_phys_vel += vel; + + systems_update(); + + setproperty(VF_ORIGIN, it.origin + '0 0 35'); + addentities(MASK_NORMAL); // .drawmask + renderscene(); + clearscene(); + + Net_Flush(); + IL_ENDFRAME(); + } +#endif + \ No newline at end of file diff --git a/qcsrc/ecs/components/physics.qh b/qcsrc/ecs/components/physics.qh index f150a296c2..aa4a046031 100644 --- a/qcsrc/ecs/components/physics.qh +++ b/qcsrc/ecs/components/physics.qh @@ -24,3 +24,9 @@ COMPONENT(phys); .bool com_phys_water; .bool com_phys_friction_air; .bool move_qcphysics; + +.bool com_phys_nogravityonground; +.float com_phys_stepheight; +.float com_phys_jumpvel; +.float com_phys_bounce; +.bool com_phys_noclip; diff --git a/qcsrc/ecs/events/physics.qh b/qcsrc/ecs/events/physics.qh index df32c557d1..dc965d9189 100644 --- a/qcsrc/ecs/events/physics.qh +++ b/qcsrc/ecs/events/physics.qh @@ -1,3 +1,6 @@ #pragma once EVENT(phys_land, (entity this)); + +EVENT(phys_stepfall, (entity this)); +EVENT(phys_stepland, (entity this)); diff --git a/qcsrc/ecs/main.qh b/qcsrc/ecs/main.qh index 724cb1ef89..a28de528ce 100644 --- a/qcsrc/ecs/main.qh +++ b/qcsrc/ecs/main.qh @@ -2,4 +2,12 @@ #include "lib.qh" +REGISTER_NET_LINKED(ENT_OBJECT); + +#define serialize_ENT_OBJECT(stream, this) \ + MACRO_BEGIN \ + serialize_vector(stream, this.origin); \ + serialize_vector(stream, this.angles); \ + MACRO_END + void systems_update(); diff --git a/qcsrc/ecs/sv_main.qc b/qcsrc/ecs/sv_main.qc new file mode 100644 index 0000000000..49b4204fbb --- /dev/null +++ b/qcsrc/ecs/sv_main.qc @@ -0,0 +1,54 @@ +#if !XONOTIC +// interpolating with the engine: +// it.movetype = MOVETYPE_STEP; // engine only interpolates this movetype +// it.flags |= FL_FLY; // don't apply forces +// it.solid = SOLID_NOT; // don't make stepping sounds + +entity testent; + +bool net_object_send(entity this, entity to, int sendflags) +{ + WriteHeader(MSG_ENTITY, ENT_OBJECT); + serialize(ENT_OBJECT, MSG_ENTITY, this); + return true; +} + +spawnfunc(worldspawn) +{ + static_init(); + static_init_late(); + // static_init_precache(); + + entity it = testent = spawn(); + precache_model("models/player/erebus.iqm"); + _setmodel(it, "models/player/erebus.iqm"); + + //Net_LinkEntity(it, true, 0, net_object_send); + + __spawnfunc_spawn_all(); +} + +void PlayerPreThink(entity this) +{ + // the truth: time + // what the client knows: time - this.ping + // for fairness, don't compensate shots beyond 400ms + // what the client sees: _ - this.cl_interp; +} + +void StartFrame() +{ + float f = 1 / autocvar_xon_sys_phys_dt; + float n = 5; + float x = ((floor(time * f) / f) % n) / n; + vector norg = '0 1 0' * x * 500; + if (norg != testent.origin) + { + if (!norg) testent.effects |= EF_TELEPORT_BIT; + testent.SendFlags |= 1; + setorigin(testent, norg); + testent.angles = '0 -360 0' * x; + // testent.origin = norg; + } +} +#endif diff --git a/qcsrc/ecs/systems/cl_physics.qc b/qcsrc/ecs/systems/cl_physics.qc index fa087b5eb4..10b5d6579d 100644 --- a/qcsrc/ecs/systems/cl_physics.qc +++ b/qcsrc/ecs/systems/cl_physics.qc @@ -1,5 +1,6 @@ #include "physics.qh" +#if XONOTIC void sys_phys_fix(entity this, float dt) { this.team = myteam + 1; // is this correct? @@ -26,3 +27,4 @@ void sys_phys_pregame_hold(entity this) {} void sys_phys_spectator_control(entity this) {} void sys_phys_fixspeed(entity this, float maxspeed_mod) {} +#endif diff --git a/qcsrc/ecs/systems/input.qc b/qcsrc/ecs/systems/input.qc index eac36250ba..d41bcda098 100644 --- a/qcsrc/ecs/systems/input.qc +++ b/qcsrc/ecs/systems/input.qc @@ -2,6 +2,8 @@ void sys_in_update(entity this, float dt) { +#if XONOTIC this.com_in_jump = PHYS_INPUT_BUTTON_JUMP(this); this.com_in_crouch = PHYS_INPUT_BUTTON_CROUCH(this); +#endif } diff --git a/qcsrc/ecs/systems/physics.qc b/qcsrc/ecs/systems/physics.qc index 8896b5a442..e1dc6705a6 100644 --- a/qcsrc/ecs/systems/physics.qc +++ b/qcsrc/ecs/systems/physics.qc @@ -1,6 +1,7 @@ #include "physics.qh" #include "input.qh" +#if XONOTIC .int disableclientprediction; void sys_phys_simulate(entity this, float dt); @@ -494,3 +495,172 @@ void sys_phys_update_single(entity this) { sys_phys_simulate_simple(this, frametime); } + +#else +const int PHYSICS_TRACE_PLANE_MAX = 5; + +// NOTE: currently unsuitable for players +void sys_phys_update(entity this, float dt) +{ + // x: { 60: 0.5, 45: ~0.7, 30: ~0.9 }, from: 'vec2(0, 1) * vec2(cos(90 - x), sin(90 - x))' + // read as 'within x degrees' + float maxgrounddot = 0.5; + float maxstepdot = 0.7; + vector upvec = '0 0 1'; + float groundsnap = 1; + bool jump = this.com_in_jump; + bool jumpstep = true; + + vector mn = this.mins; + vector mx = this.maxs; + + vector acc = this.com_phys_acc; + vector vel = this.com_phys_vel; + vector pos = this.com_phys_pos_prev = this.com_phys_pos; + bool onground = this.com_phys_ground; + + bool nogravityonground = this.com_phys_nogravityonground; + float stepheight = this.com_phys_stepheight; + float stepdownheight = -stepheight; + float jumpvel = this.com_phys_jumpvel; + float bounce = this.com_phys_bounce; + float friction = this.com_phys_friction; + float gravity = this.com_phys_gravity.z; + bool noclip = this.com_phys_noclip; + if (noclip) + { + jump = false; + nogravityonground = false; + } + + vector g = upvec * -gravity; + + // apply accelaration in two steps: https://www.niksula.hut.fi/~hkankaan/Homepages/gravity.html + // alternatives: rk4, verlet, euler + vel += (acc + g) * dt / 2; + { + if (onground || noclip) + { + if (nogravityonground) + { + g = '0 0 0'; + if (vel * upvec < 0) vel = vec_reflect(vel, upvec, 0); // kill downward velocity + } + if (jump) + { + vel += upvec * jumpvel; + } + else // the first landing frame is free + { + // friction + vector slide = noclip ? vel : vec_reflect(vel, upvec, 0); + vector push = vel - slide; + // TODO: slick + slide *= 1 - friction * dt; + vel = slide + push; + } + } + vector step = vel * dt; + bool pass = false; + bool foundground = false; // assume until proven otherwise + if (nogravityonground) foundground = true; // override + bool steplimit = 1; + if (noclip) + { + pass = true; + } + else + { + for (int i = 0; i < PHYSICS_TRACE_PLANE_MAX; ++i) + { + vector p0 = pos; + vector p1 = p0 + step; + tracebox(p0, mn, mx, p1, MOVE_NORMAL, this); + float frac = trace_fraction; + vector norm = trace_plane_normal; + if (frac == 1) + { + // all clear + if (steplimit > 0 && onground && vel * upvec <= 0) + { + // can we step down? + tracebox(p1, mn, mx, p1 + upvec * stepdownheight, MOVE_NORMAL, this); + if (trace_fraction == 1) + { + // no stairs here + } + else if (trace_plane_normal * upvec >= maxstepdot) + { + // step down + step += upvec * (stepdownheight * trace_fraction); + } + } + pass = true; + break; + } + // hit something + if (norm * upvec >= maxgrounddot) foundground = true; + if (steplimit > 0 && (jumpstep || onground)) // try: vel * upvec >= 0 + { + // can we step up? + vector slide = vec_reflect(step, upvec, 0); // remove fall component + vector p1 = p0 + slide; // step is here + tracebox(p1 + upvec * stepheight, mn, mx, p1, MOVE_NORMAL, this); + if (trace_fraction < 1 && trace_plane_normal * upvec >= maxstepdot) + { + // there is a step in front of us, get above it + // TODO: not if it's slippery (slick) + vector stepup = upvec * (1 - trace_fraction) * stepheight; + tracebox(p0, mn, mx, p0 + stepup, MOVE_NORMAL, this); + if (trace_fraction == 1) + { + // go over + tracebox(p0 + stepup, mn, mx, p1 + stepup, MOVE_NORMAL, this); + if (trace_fraction == 1) + { + // all clear + steplimit -= 1; + pos += stepup; + if (vel * upvec < 0) vel = vec_reflect(vel, upvec, 0); // kill downward velocity + step = p1 - p0; + pass = true; + break; + } + } + } + } + // no stairs here + pos += frac * step; + vel = vec_reflect(vel, norm, bounce); + step = (1 - frac) * vel * dt; + continue; + } + } + if (nogravityonground) + { + vector p1 = pos + step; + tracebox(p1, mn, mx, p1 - groundsnap * upvec, MOVE_NORMAL, this); + foundground = trace_plane_normal * upvec >= maxgrounddot; + } + if (pass) + { + pos += step; + if (!foundground) + { + if (onground) emit(phys_stepfall, this); + } + else + { + if (!onground) emit(phys_stepland, this); + } + onground = foundground; + } + } + vel += (acc + g) * dt / 2; + + this.com_phys_acc = acc; + this.com_phys_vel = vel; + this.com_phys_pos = pos; + this.com_phys_ground = onground; +} +#endif diff --git a/qcsrc/ecs/systems/sv_physics.qc b/qcsrc/ecs/systems/sv_physics.qc index c3594c0136..59f8b07d0e 100644 --- a/qcsrc/ecs/systems/sv_physics.qc +++ b/qcsrc/ecs/systems/sv_physics.qc @@ -1,5 +1,6 @@ #include "physics.qh" +#if XONOTIC void sys_phys_fix(entity this, float dt) { WarpZone_PlayerPhysics_FixVAngle(this); @@ -121,3 +122,4 @@ STATIC_INIT(sys_phys) entity listener = new_pure(sys_phys); subscribe(listener, phys_land, sys_phys_land); } +#endif diff --git a/qcsrc/server/progs.inc b/qcsrc/server/progs.inc index fd0b0c99e2..8103351421 100644 --- a/qcsrc/server/progs.inc +++ b/qcsrc/server/progs.inc @@ -14,8 +14,8 @@ #include #include -#include #endif +#include #ifdef BUILD_MOD #include -- 2.39.2