diff --git a/include/corelib.h b/include/corelib.h index 04c9c19..1bc8151 100644 --- a/include/corelib.h +++ b/include/corelib.h @@ -361,6 +361,20 @@ public: } }; +class SimpleColor final : private NonCopyable +{ +public: + int red, green, blue; + + inline void reset (void) { + red = green = blue = 0; + } + + inline const int avg (void) const { + return (red + green + blue) / (sizeof (SimpleColor) / sizeof (int)); + } +}; + class Vector final { public: float x, y, z; @@ -380,15 +394,15 @@ public: return &x; } - inline const Vector operator+ (const Vector &right) const { + inline const Vector operator + (const Vector &right) const { return Vector (x + right.x, y + right.y, z + right.z); } - inline const Vector operator- (const Vector &right) const { + inline const Vector operator - (const Vector &right) const { return Vector (x - right.x, y - right.y, z - right.z); } - inline const Vector operator- (void) const { + inline const Vector operator - (void) const { return Vector (-x, -y, -z); } @@ -396,26 +410,26 @@ public: return Vector (right.x * vec, right.y * vec, right.z * vec); } - inline const Vector operator* (float vec) const { + inline const Vector operator * (float vec) const { return Vector (vec * x, vec * y, vec * z); } - inline const Vector operator/ (float vec) const { + inline const Vector operator / (float vec) const { const float inv = 1 / vec; return Vector (inv * x, inv * y, inv * z); } // cross product - inline const Vector operator^ (const Vector &right) const { + inline const Vector operator ^ (const Vector &right) const { return Vector (y * right.z - z * right.y, z * right.x - x * right.z, x * right.y - y * right.x); } // dot product - inline float operator| (const Vector &right) const { + inline float operator | (const Vector &right) const { return x * right.x + y * right.y + z * right.z; } - inline const Vector &operator+= (const Vector &right) { + inline const Vector &operator += (const Vector &right) { x += right.x; y += right.y; z += right.z; @@ -423,7 +437,7 @@ public: return *this; } - inline const Vector &operator-= (const Vector &right) { + inline const Vector &operator -= (const Vector &right) { x -= right.x; y -= right.y; z -= right.z; @@ -431,7 +445,7 @@ public: return *this; } - inline const Vector &operator*= (float vec) { + inline const Vector &operator *= (float vec) { x *= vec; y *= vec; z *= vec; @@ -439,7 +453,7 @@ public: return *this; } - inline const Vector &operator/= (float vec) { + inline const Vector &operator /= (float vec) { const float inv = 1 / vec; x *= inv; @@ -449,15 +463,15 @@ public: return *this; } - inline bool operator== (const Vector &right) const { + inline bool operator == (const Vector &right) const { return fequal (x, right.x) && fequal (y, right.y) && fequal (z, right.z); } - inline bool operator!= (const Vector &right) const { + inline bool operator != (const Vector &right) const { return !fequal (x, right.x) && !fequal (y, right.y) && !fequal (z, right.z); } - inline const Vector &operator= (const Vector &right) { + inline const Vector &operator = (const Vector &right) { x = right.x; y = right.y; z = right.z; diff --git a/include/engine.h b/include/engine.h index 3940c01..94a4320 100644 --- a/include/engine.h +++ b/include/engine.h @@ -395,3 +395,43 @@ public: return cr::clamp (static_cast (value * scale), -32767, 32767); } }; + + +class LightMeasure final : public Singleton { +private: + lightstyle_t m_lightstyle[MAX_LIGHTSTYLES]; + int m_lightstyleValue[MAX_LIGHTSTYLEVALUE]; + bool m_doAnimation; + + SimpleColor m_point; + model_t *m_worldModel; + +public: + LightMeasure (void) : m_worldModel (nullptr), m_doAnimation (false) { + initializeLightstyles (); + m_point.reset (); + } + +public: + void initializeLightstyles (void); + void animateLight (void); + + bool recursiveLightPoint (const mnode_t *node, const Vector &start, const Vector &end); + float getLightLevel (const Vector &point); + +public: + inline void resetWorldModel (void) { + m_worldModel = nullptr; + } + + inline void setWorldModel (model_t *model) { + if (m_worldModel) { + return; + } + m_worldModel = model; + } + + inline void enableAnimation (bool enable) { + m_doAnimation = enable; + } +}; \ No newline at end of file diff --git a/include/engine/extdll.h b/include/engine/extdll.h index 4eef052..eb5a0e6 100644 --- a/include/engine/extdll.h +++ b/include/engine/extdll.h @@ -61,6 +61,7 @@ using namespace cr::types; #include "const.h" #include "progdefs.h" +#include "model.h" #define MAX_ENT_LEAFS 48 diff --git a/include/engine/model.h b/include/engine/model.h new file mode 100644 index 0000000..cf55458 --- /dev/null +++ b/include/engine/model.h @@ -0,0 +1,218 @@ +/*** + * + * Copyright (c) 1999-2005, Valve Corporation. All rights reserved. + * + * This product contains software technology licensed from Id + * Software, Inc. ("Id Technology"). Id Technology (c) 1996 Id Software, Inc. + * All Rights Reserved. + * + * This source code contains proprietary and confidential information of + * Valve LLC and its suppliers. Access to this code is restricted to + * persons who have executed a written SDK license with Valve. Any access, + * use or distribution of this code by or to any unlicensed person is illegal. + * + ****/ + +// stripped down version of com_model.h & pm_info.h + +#ifndef MODEL_H +#define MODEL_H + +typedef vec_t vec2_t[2]; +typedef vec_t vec4_t[4]; + +typedef int qboolean; +typedef unsigned char byte; + +#define VERTEXSIZE 7 +#define MAXLIGHTMAPS 4 +#define NUM_AMBIENTS 4 +#define MAX_MAP_HULLS 4 +#define MAX_PHYSINFO_STRING 256 +#define MAX_PHYSENTS 600 +#define MAX_MOVEENTS 64 +#define MAX_LIGHTSTYLES 64 +#define MAX_LIGHTSTYLEVALUE 256 +#define SURF_DRAWTILED 0x20 + +typedef struct mplane_s { + vec3_t normal; + float dist; + byte type; // for fast side tests + byte signbits; // signx + (signy<<1) + (signz<<1) + byte pad[2]; +} mplane_t; + +typedef struct { + vec3_t position; +} mvertex_t; + +typedef struct { + float vecs[2][4]; // [s/t] unit vectors in world space [i][3] is the s/t offset relative to the origin s or t = dot( 3Dpoint, vecs[i] ) + vecs[i][3] + float mipadjust; // mipmap limits for very small surfaces + struct texture_t* texture; + int flags; // sky or slime, no lightmap or 256 subdivision +} mtexinfo_t; + +typedef struct mnode_s { + int contents; // 0, to differentiate from leafs + int visframe; // node needs to be traversed if current + float minmaxs[6]; // for bounding box culling + struct mnode_s* parent; + mplane_t* plane; + struct mnode_s* children[2]; + unsigned short firstsurface; + unsigned short numsurfaces; +} mnode_t; + +typedef struct { + byte r, g, b; +} color24; + +typedef struct msurface_s { + int visframe; // should be drawn when node is crossed + mplane_t* plane; // pointer to shared plane + int flags; // see SURF_ #defines + int firstedge; // look up in model->surfedges[], negative numbers + int numedges; // are backwards edges + short texturemins[2]; + short extents[2]; + int light_s, light_t; // gl lightmap coordinates + struct glpoly_t* polys; // multiple if warped + struct msurface_s* texturechain; + mtexinfo_t* texinfo; + int dlightframe; // last frame the surface was checked by an animated light + int dlightbits; // dynamically generated. Indicates if the surface illumination is modified by an animated light. + int lightmaptexturenum; + byte styles[MAXLIGHTMAPS]; + int cached_light[MAXLIGHTMAPS]; // values currently used in lightmap + msurface_s* lightmapchain; // for new dlights rendering (was cached_dlight) + color24* samples; // note: this is the actual lightmap data for this surface + struct decal_t* pdecals; +} msurface_t; + +typedef struct cache_user_s { + void* data; // extradata +} cache_user_t; + +typedef struct hull_s { + struct dclipnode_t* clipnodes; + mplane_t* planes; + int firstclipnode; + int lastclipnode; + vec3_t clip_mins; + vec3_t clip_maxs; +} hull_t; + +typedef struct model_s { + char name[64]; // model name + qboolean needload; // bmodels and sprites don't cache normally + int type; // model type + int numframes; // sprite's framecount + byte* mempool; // private mempool (was synctype) + int flags; // hl compatibility + vec3_t mins, maxs; // bounding box at angles '0 0 0' + float radius; + int firstmodelsurface; + int nummodelsurfaces; + int numsubmodels; + struct dmodel_t* submodels; // or studio animations + int numplanes; + mplane_t* planes; + int numleafs; // number of visible leafs, not counting 0 + struct mleaf_t* leafs; + int numvertexes; + mvertex_t* vertexes; + int numedges; + struct medge_t* edges; + int numnodes; + mnode_t* nodes; + int numtexinfo; + mtexinfo_t* texinfo; + int numsurfaces; + msurface_t* surfaces; + int numsurfedges; + int* surfedges; + int numclipnodes; + struct dclipnode_t* clipnodes; + int nummarksurfaces; + msurface_t** marksurfaces; + hull_t hulls[MAX_MAP_HULLS]; + int numtextures; + texture_t** textures; + byte* visdata; + color24* lightdata; + char* entities; + cache_user_t cache; // only access through Mod_Extradata +} model_t; + +struct lightstyle_t { + int length; + char map[MAX_LIGHTSTYLES]; +}; + +typedef struct physent_s { + char name[32]; // name of model, or "player" or "world". + int player; + vec3_t origin; // model's origin in world coordinates. + struct model_s* model; // only for bsp models +} physent_t; + +typedef struct playermove_s { + int player_index; // So we don't try to run the PM_CheckStuck nudging too quickly. + qboolean server; // For debugging, are we running physics code on server side? + qboolean multiplayer; // 1 == multiplayer server + float time; // realtime on host, for reckoning duck timing + float frametime; // Duration of this frame + vec3_t forward, right, up; // Vectors for angles + vec3_t origin; // Movement origin. + vec3_t angles; // Movement view angles. + vec3_t oldangles; // Angles before movement view angles were looked at. + vec3_t velocity; // Current movement direction. + vec3_t movedir; // For waterjumping, a forced forward velocity so we can fly over lip of ledge. + vec3_t basevelocity; // Velocity of the conveyor we are standing, e.g. + vec3_t view_ofs; // Our eye position. + float flDuckTime; // Time we started duck + qboolean bInDuck; // In process of ducking or ducked already? + int flTimeStepSound; // Next time we can play a step sound + int iStepLeft; + float flFallVelocity; + vec3_t punchangle; + float flSwimTime; + float flNextPrimaryAttack; + int effects; // MUZZLE FLASH, e.g. + int flags; // FL_ONGROUND, FL_DUCKING, etc. + int usehull; // 0 = regular player hull, 1 = ducked player hull, 2 = point hull + float gravity; // Our current gravity and friction. + float friction; + int oldbuttons; // Buttons last usercmd + float waterjumptime; // Amount of time left in jumping out of water cycle. + qboolean dead; // Are we a dead player? + int deadflag; + int spectator; // Should we use spectator physics model? + int movetype; // Our movement type, NOCLIP, WALK, FLY + int onground; + int waterlevel; + int watertype; + int oldwaterlevel; + char sztexturename[256]; + char chtexturetype; + float maxspeed; + float clientmaxspeed; // Player specific maxspeed + int iuser1; + int iuser2; + int iuser3; + int iuser4; + float fuser1; + float fuser2; + float fuser3; + float fuser4; + vec3_t vuser1; + vec3_t vuser2; + vec3_t vuser3; + vec3_t vuser4; + int numphysent; + physent_t physents[MAX_PHYSENTS]; +} playermove_t; + +#endif \ No newline at end of file diff --git a/include/yapb.h b/include/yapb.h index b5209d3..9a80def 100644 --- a/include/yapb.h +++ b/include/yapb.h @@ -1431,6 +1431,7 @@ private: float m_pathDisplayTime; float m_arrowDisplayTime; float m_waypointDisplayTime[MAX_WAYPOINTS]; + float m_waypointLightLevel[MAX_WAYPOINTS]; int m_findWPIndex; int m_facingAtIndex; char m_infoBuffer[MAX_PRINT_BUFFER]; @@ -1458,6 +1459,7 @@ public: void initExperience (void); void initVisibility (void); void initTypes (void); + void initLightLevels (void); void addPath (int addIndex, int pathIndex, float distance); @@ -1544,6 +1546,11 @@ public: return m_numWaypoints; } + // get the light level of waypoint + inline float getLightLevel (int id) const { + return m_waypointLightLevel[id]; + } + // free's socket handle void closeSocket (int sock); @@ -1567,6 +1574,7 @@ static auto &waypoints = Waypoint::ref (); static auto &bots = BotManager::ref (); static auto &engine = Engine::ref (); static auto &rng = RandomSequence::ref (); +static auto &illum = LightMeasure::ref (); // prototypes of bot functions... extern int getWeaponData (bool isString, const char *weaponAlias, int weaponIndex = -1); diff --git a/project/yapb.vcxproj b/project/yapb.vcxproj index 7cdbfa5..2758a68 100644 --- a/project/yapb.vcxproj +++ b/project/yapb.vcxproj @@ -12,6 +12,7 @@ + diff --git a/project/yapb.vcxproj.filters b/project/yapb.vcxproj.filters index 027bc0d..85fb1d0 100644 --- a/project/yapb.vcxproj.filters +++ b/project/yapb.vcxproj.filters @@ -54,6 +54,9 @@ include + + include\engine + diff --git a/source/engine.cpp b/source/engine.cpp index ca80a0d..8fd3bfe 100644 --- a/source/engine.cpp +++ b/source/engine.cpp @@ -1102,4 +1102,146 @@ void Engine::processMessages (void *ptr) { logEntry (true, LL_FATAL, "Network message handler error. Call to unrecognized message id (%d).\n", m_msgBlock.msg); } m_msgBlock.state++; // and finally update network message state +} + +void LightMeasure::initializeLightstyles (void) { + // this function initializes lighting information... + + // reset all light styles + for (int i = 0; i < MAX_LIGHTSTYLES; i++) { + m_lightstyle[i].length = 0; + m_lightstyle[i].map[0] = 0x0; + } + + for (int i = 0; i < MAX_LIGHTSTYLEVALUE; i++) { + m_lightstyleValue[i] = 264; + } +} + +void LightMeasure::animateLight (void) { + // this function performs light animations + + if (!m_doAnimation) { + return; + } + + // 'm' is normal light, 'a' is no light, 'z' is double bright + const int index = static_cast (engine.timebase () * 10.0f); + + for (int j = 0; j < MAX_LIGHTSTYLES; j++) { + if (!m_lightstyle[j].length) { + m_lightstyleValue[j] = 256; + continue; + } + int value = m_lightstyle[j].map[index % m_lightstyle[j].length] - 'a'; + m_lightstyleValue[j] = value * 22; + } +} + +inline bool LightMeasure::recursiveLightPoint (const mnode_t *node, const Vector &start, const Vector &end) { + if (node->contents < 0) { + return false; + } + + // determine which side of the node plane our points are on, fixme: optimize for axial + auto plane = node->plane; + + float front = (start | plane->normal) - plane->dist; + float back = (end | plane->normal) - plane->dist; + + int side = front < 0.0f; + + // if they're both on the same side of the plane, don't bother to split just check the appropriate child + if ((back < 0.0f) == side) { + return recursiveLightPoint (node->children[side], start, end); + } + + // calculate mid point + float frac = front / (front - back); + auto mid = start + (end - start) * frac; + + // go down front side + if (recursiveLightPoint (node->children[side], start, mid)) { + return true; // hit something + } + + // blow it off if it doesn't split the plane... + if ((back < 0.0f) == side) { + return false; // didn't hit anything + } + + // check for impact on this node + // lightspot = mid; + // lightplane = plane; + auto surf = reinterpret_cast (m_worldModel->surfaces) + node->firstsurface; + + for (int i = 0; i < node->numsurfaces; i++, surf++) { + if (surf->flags & SURF_DRAWTILED) { + continue; // no lightmaps + } + auto tex = surf->texinfo; + + // see where in lightmap space our intersection point is + int s = static_cast ((mid | Vector (tex->vecs[0])) + tex->vecs[0][3]); + int t = static_cast ((mid | Vector (tex->vecs[1])) + tex->vecs[1][3]); + + // not in the bounds of our lightmap? punt... + if (s < surf->texturemins[0] || t < surf->texturemins[1]) { + continue; + } + + // assuming a square lightmap (fixme: which ain't always the case), lets see if it lies in that rectangle. if not, punt... + int ds = s - surf->texturemins[0]; + int dt = t - surf->texturemins[1]; + + if (ds > surf->extents[0] || dt > surf->extents[1]) { + continue; + } + + if (!surf->samples) { + return true; + } + ds >>= 4; + dt >>= 4; + + m_point.reset (); // reset point color. + + int smax = (surf->extents[0] >> 4) + 1; + int tmax = (surf->extents[1] >> 4) + 1; + int size = smax * tmax; + + auto lightmap = surf->samples + dt * smax + ds; + + // compute the lightmap color at a particular point + for (int maps = 0u; maps < MAXLIGHTMAPS && surf->styles[maps] != 255u; ++maps) { + uint32 scale = m_lightstyleValue[surf->styles[maps]]; + + m_point.red += lightmap->r * scale; + m_point.green += lightmap->g * scale; + m_point.blue += lightmap->b * scale; + + lightmap += size; // skip to next lightmap + } + m_point.red >>= 8u; + m_point.green >>= 8u; + m_point.blue >>= 8u; + + return true; + } + return recursiveLightPoint (node->children[!side], mid, end); // go down back side +} + +float LightMeasure::getLightLevel (const Vector &point) { + if (!m_worldModel) { + return 0.0f; + } + + if (!m_worldModel->lightdata) { + return 255.0f; + } + + Vector endPoint (point); + endPoint.z -= 2048.0f; + + return recursiveLightPoint (m_worldModel->nodes, point, endPoint) == false ? 0.0f : 100 * cr::sqrtf (cr::min (75.0f, static_cast (m_point.avg ())) / 75.0f); } \ No newline at end of file diff --git a/source/interface.cpp b/source/interface.cpp index aaa003d..ef6ab2c 100644 --- a/source/interface.cpp +++ b/source/interface.cpp @@ -19,6 +19,7 @@ ConVar yb_version ("yb_version", PRODUCT_VERSION, VT_READONLY); ConVar mp_startmoney ("mp_startmoney", nullptr, VT_NOREGISTER, true, "800"); int handleBotCommands (edict_t *ent, const char *arg0, const char *arg1, const char *arg2, const char *arg3, const char *arg4, const char *arg5, const char *self) { + // adding one bot with random parameters to random team if (stricmp (arg0, "addbot") == 0 || stricmp (arg0, "add") == 0) { bots.addbot (arg4, arg1, arg2, arg3, arg5, true); @@ -352,6 +353,18 @@ int handleBotCommands (edict_t *ent, const char *arg0, const char *arg1, const c waypoints.cachePoint (); } + else if (stricmp (arg1, "glp") == 0) { + + + for (int i = 0; i < waypoints.length (); i++) { + + + engine.print ("%d - %f", i, waypoints.getLightLevel (i)); + } + + + } + // do a cleanup else if (stricmp (arg1, "clean") == 0) { if (!isEmptyStr (arg2)) { @@ -2097,6 +2110,9 @@ void ServerActivate (edict_t *pentEdictList, int edictCount, int clientMax) { // do a level initialization engine.levelInitialize (); + // update worldmodel + illum.resetWorldModel (); + // do level initialization stuff here... waypoints.init (); waypoints.load (); @@ -2139,6 +2155,9 @@ void ServerDeactivate (void) { // set state to unprecached engine.setUnprecached (); + // enable lightstyle animations on level change + illum.enableAnimation (true); + // xash is not kicking fakeclients on changelevel if (g_gameFlags & GAME_XASH_ENGINE) { bots.kickEveryone (true, false); @@ -2164,6 +2183,9 @@ void StartFrame (void) { // run periodic update of bot states bots.frame (); + // update lightstyle animations + illum.animateLight (); + // record some stats of all players on the server for (int i = 0; i < engine.maxClients (); i++) { edict_t *player = engine.entityOfIndex (i + 1); @@ -2229,6 +2251,9 @@ void StartFrame (void) { } bots.calculatePingOffsets (); + // calculate light levels for all waypoints if needed + waypoints.initLightLevels (); + if (g_gameFlags & GAME_METAMOD) { static auto dmActive = g_engfuncs.pfnCVarGetPointer ("csdm_active"); static auto freeForAll = g_engfuncs.pfnCVarGetPointer ("mp_freeforall"); @@ -2786,7 +2811,7 @@ typedef void (*entity_func_t) (entvars_t *); gamedll_funcs_t gameDLLFunc; SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { - // this function is called right after FuncPointers_t() by the engine in the game DLL (or + // this function is called right after GiveFnptrsToDll() by the engine in the game DLL (or // what it BELIEVES to be the game DLL), in order to copy the list of MOD functions that can // be called by the engine, into a memory block pointed to by the functionTable pointer // that is passed into this function (explanation comes straight from botman). This allows @@ -2824,6 +2849,19 @@ SHARED_LIBRARAY_EXPORT int GetEntityAPI2 (gamefuncs_t *functionTable, int *) { functionTable->pfnStartFrame = StartFrame; functionTable->pfnUpdateClientData = UpdateClientData; + functionTable->pfnPM_Move = [] (playermove_t *playerMove, int server) { + // this is the player movement code clients run to predict things when the server can't update + // them often enough (or doesn't want to). The server runs exactly the same function for + // moving players. There is normally no distinction between them, else client-side prediction + // wouldn't work properly (and it doesn't work that well, already...) + + illum.setWorldModel (playerMove->physents[0u].model); + + if (g_gameFlags & GAME_METAMOD) { + RETURN_META (MRES_IGNORED); + } + g_functionTable.pfnPM_Move (playerMove, server); + }; return TRUE; } @@ -2861,7 +2899,7 @@ SHARED_LIBRARAY_EXPORT int GetNewDLLFunctions (newgamefuncs_t *functionTable, in } if (!api_GetNewDLLFunctions (functionTable, interfaceVersion)) { - logEntry (true, LL_FATAL, "GetNewDLLFunctions: ERROR - Not Initialized."); + logEntry (true, LL_ERROR, "GetNewDLLFunctions: ERROR - Not Initialized."); return FALSE; } diff --git a/source/waypoint.cpp b/source/waypoint.cpp index 7269d1c..25e5297 100644 --- a/source/waypoint.cpp +++ b/source/waypoint.cpp @@ -1367,6 +1367,22 @@ void Waypoint::initVisibility (void) { fp.close (); } +void Waypoint::initLightLevels (void) { + // this function get's the light level for each waypoin on the map + + // no waypoints ? no light levels, and only one-time init + if (!m_numWaypoints || !cr::fzero (m_waypointLightLevel[0])) { + return; + } + engine.print ("!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!1"); + // update light levels for all waypoints + for (int i = 0; i < m_numWaypoints; i++) { + m_waypointLightLevel[i] = illum.getLightLevel (m_paths[i]->origin); + } + // disable lightstyle animations on finish (will be auto-enabled on mapchange) + illum.enableAnimation (false); +} + void Waypoint::initTypes (void) { m_terrorPoints.clear (); m_ctPoints.clear (); @@ -1506,7 +1522,8 @@ bool Waypoint::load (void) { } for (int i = 0; i < m_numWaypoints; i++) { - m_waypointDisplayTime[i] = 0.0; + m_waypointDisplayTime[i] = 0.0f; + m_waypointLightLevel[i] = 0.0f; } initPathMatrix (); @@ -2679,6 +2696,7 @@ Waypoint::Waypoint (void) { memset (m_visLUT, 0, sizeof (m_visLUT)); memset (m_waypointDisplayTime, 0, sizeof (m_waypointDisplayTime)); + memset (m_waypointLightLevel, 0, sizeof (m_waypointLightLevel)); memset (m_infoBuffer, 0, sizeof (m_infoBuffer)); m_waypointPaths = false;