/* * Copyright (c) 2016-2022 Vera Visions LLC. * * Permission to use, copy, modify, and distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF MIND, USE, DATA OR PROFITS, WHETHER * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ enumflags { ENVFOGCTRL_CHANGED_ACTIVE, ENVFOGCTRL_CHANGED_BLEND, ENVFOGCTRL_CHANGED_START, ENVFOGCTRL_CHANGED_END, ENVFOGCTRL_CHANGED_MAXDENSITY, ENVFOGCTRL_CHANGED_FARZ, ENVFOGCTRL_CHANGED_COLOR, ENVFOGCTRL_CHANGED_COLOR2, ENVFOGCTRL_CHANGED_DIR }; /*!QUAKED env_fog_controller (1 .5 0) (-8 -8 -8) (8 8 8) EVFOGCTL_MASTER # OVERVIEW Controls fog that affects the entire map. # KEYS - "targetname" : Name - "target" : Name of an entity in the map that light will point at. - "fogenable" : Will make the fog start active if not 0. - "fogstart" : Distance from the camera of where the fog starts. - "fogend" : Distance from the camera of where the fog ends. - "fogmaxdensity" : Maximum fog density. Value between 0.00 and 1.00. - "farz" : Clip anything after the specified distance. - "fogcolor" : Primary color of the fog in RGB255. E.g. '255 0 0' for red. - "fogcolor2" : Secondary color of the fog in RGB255. Only used when 'fogblend' is 1. - "fogblend" : Whether or not to blend between fogcolor and fogcolor2. - "fogdir" : The fog directon for the secondary color. Only used when 'fogblend' is 1. - "use_angles" : If we should use the 'angles' key instead of 'fogdir'. Only used when 'fogblend' is 1. # INPUTS - "TurnOff" : Turns the entity off. - "TurnOn" : Turns the entity on. - "Toggle" : Toggles the entity to an on/off state. - "SetStartDist" : Sets fogstart. - "SetEndDist" : Sets fogend. - "SetColor" : Sets fogcolor. - "SetColorSecondary" : Sets fogcolor2. - "SetFarZ" : Sets farz. # SPAWNFLAGS - EVFOGCTL_MASTER (1) : If specified, this one will always take priority over any other active fog controllers... currently unused. # TRIVIA This entity was introduced in Half-Life 2 (2004). */ class env_fog_controller:NSPointTrigger { public: void env_fog_controller(void); #ifdef CLIENT virtual float StartToBias(void); virtual float FogRender(void); virtual void FogUpdate(void); virtual void RendererRestarted(void); virtual void ReceiveEntity(float,float); #endif #ifdef SERVER virtual float SendEntity(entity,float); virtual void EvaluateEntity(void); virtual void Trigger(entity, triggermode_t); virtual void Respawn(void); virtual void Save(float); virtual void Restore(string,string); virtual void SpawnKey(string,string); virtual void Input(entity, string, string); #endif private: #ifdef CLIENT /* temporary attributes */ float m_flNextDraw; float m_flLastDelta; #else /* main spawn attributes */ int m_iSpawnEnable; int m_iSpawnBlend; float m_flSpawnStart; float m_flSpawnEnd; float m_flSpawnMaxDensity; float m_flSpawnFarZ; vector m_vecSpawnColor; vector m_vecSpawnColor2; vector m_vecSpawnDir; int m_iUseAngles; #endif /* networked attributes */ PREDICTED_INT(m_iFogActive) PREDICTED_INT(m_iFogBlend) PREDICTED_FLOAT(m_flFogStart) PREDICTED_FLOAT(m_flFogEnd) PREDICTED_FLOAT(m_flFogMaxDensity) PREDICTED_FLOAT(m_flFogFarZ) PREDICTED_VECTOR(m_vecFogColor) PREDICTED_VECTOR(m_vecFogColor2) PREDICTED_VECTOR(m_vecFogDir) }; #ifdef CLIENT float env_fog_controller::StartToBias(void) { /* FIXME: currently we don't convert fogstart and fogend into density and bias at all. */ if (m_flFogStart < 256) return 0.2; else if (m_flFogStart < 512) return 0.1; else if (m_flFogStart < 1024) return 0.05; else if (m_flFogStart < 2048) return 0.025; else if (m_flFogStart < 4096) return 0.0175; else return 0.05; } /* this is mainly for the 'blended' fog. Which is more expensive. */ float env_fog_controller::FogRender(void) { vector p1, p2; if (!m_iFogActive) return (PREDRAW_NEXT); CSQC_UpdateSeat(); /* get the difference between camera dir and fog dir */ makevectors(m_vecFogDir); p1 = v_forward; makevectors(getproperty(VF_CL_VIEWANGLES)); p2 = v_forward; float delta = vlen(p1 - p2) / 2.0f; /* cache so we don't call 'fog' every frame */ if (delta == m_flLastDelta) return (PREDRAW_NEXT); m_flLastDelta = delta; /* we also only need to call this maybe once every second... */ if (m_flNextDraw > cltime) return (PREDRAW_NEXT); m_flNextDraw = cltime + 1.0f; /* apply the fog. wish there was a builtin for this instead... */ localcmd(sprintf("set r_fog_linear 1; fog %f %v %f %f\n", \ m_flFogEnd, vectorLerp(m_vecFogColor, m_vecFogColor2, delta), m_flFogMaxDensity, m_flFogStart)); //print(sprintf("%f (%v, %v)\n", delta, p1, p2)); return (PREDRAW_NEXT); } void env_fog_controller::FogUpdate(void) { if (!m_iFogActive) return; localcmd(sprintf("set r_fog_linear 1; fog %f %f %f %f %f %f\n", \ m_flFogEnd, m_vecFogColor[0], m_vecFogColor[1], m_vecFogColor[2], m_flFogMaxDensity, m_flFogStart)); } void env_fog_controller::RendererRestarted(void) { think = FogUpdate; nextthink = time + 0.1f; } void env_fog_controller::ReceiveEntity(float flNew, float flSendFlags) { if (flSendFlags & ENVFOGCTRL_CHANGED_ACTIVE) m_iFogActive = readbyte(); if (flSendFlags & ENVFOGCTRL_CHANGED_BLEND) m_iFogBlend = readbyte(); if (flSendFlags & ENVFOGCTRL_CHANGED_START) m_flFogStart = readfloat(); if (flSendFlags & ENVFOGCTRL_CHANGED_END) m_flFogEnd = readfloat(); if (flSendFlags & ENVFOGCTRL_CHANGED_MAXDENSITY) m_flFogMaxDensity = readfloat(); if (flSendFlags & ENVFOGCTRL_CHANGED_FARZ) m_flFogFarZ = readfloat(); if (flSendFlags & ENVFOGCTRL_CHANGED_COLOR) { m_vecFogColor[0] = readfloat(); m_vecFogColor[1] = readfloat(); m_vecFogColor[2] = readfloat(); } if (flSendFlags & ENVFOGCTRL_CHANGED_COLOR2) { m_vecFogColor2[0] = readfloat(); m_vecFogColor2[1] = readfloat(); m_vecFogColor2[2] = readfloat(); } if (flSendFlags & ENVFOGCTRL_CHANGED_DIR) { m_vecFogDir[0] = readfloat(); m_vecFogDir[1] = readfloat(); m_vecFogDir[2] = readfloat(); } FogUpdate(); if (m_iFogBlend) { predraw = FogRender; drawmask = MASK_ENGINE; } else { predraw = __NULL__; drawmask = 0; } } #else float env_fog_controller::SendEntity(entity ePVEnt, float flSendFlags) { WriteByte(MSG_ENTITY, ENT_FOGCONTROLLER); WriteFloat(MSG_ENTITY, flSendFlags); if (flSendFlags & ENVFOGCTRL_CHANGED_ACTIVE) WriteByte(MSG_ENTITY, m_iFogActive); if (flSendFlags & ENVFOGCTRL_CHANGED_BLEND) WriteByte(MSG_ENTITY, m_iFogBlend); if (flSendFlags & ENVFOGCTRL_CHANGED_START) WriteFloat(MSG_ENTITY, m_flFogStart); if (flSendFlags & ENVFOGCTRL_CHANGED_END) WriteFloat(MSG_ENTITY, m_flFogEnd); if (flSendFlags & ENVFOGCTRL_CHANGED_MAXDENSITY) WriteFloat(MSG_ENTITY, m_flFogMaxDensity); if (flSendFlags & ENVFOGCTRL_CHANGED_FARZ) WriteFloat(MSG_ENTITY, m_flFogFarZ); if (flSendFlags & ENVFOGCTRL_CHANGED_COLOR) { WriteFloat(MSG_ENTITY, m_vecFogColor[0]); WriteFloat(MSG_ENTITY, m_vecFogColor[1]); WriteFloat(MSG_ENTITY, m_vecFogColor[2]); } if (flSendFlags & ENVFOGCTRL_CHANGED_COLOR2) { WriteFloat(MSG_ENTITY, m_vecFogColor2[0]); WriteFloat(MSG_ENTITY, m_vecFogColor2[1]); WriteFloat(MSG_ENTITY, m_vecFogColor2[2]); } if (flSendFlags & ENVFOGCTRL_CHANGED_DIR) { WriteFloat(MSG_ENTITY, m_vecFogDir[0]); WriteFloat(MSG_ENTITY, m_vecFogDir[1]); WriteFloat(MSG_ENTITY, m_vecFogDir[2]); } return (1); } void env_fog_controller::EvaluateEntity(void) { if (ATTR_CHANGED(m_iFogActive)) SetSendFlags(ENVFOGCTRL_CHANGED_ACTIVE); if (ATTR_CHANGED(m_iFogBlend)) SetSendFlags(ENVFOGCTRL_CHANGED_BLEND); if (ATTR_CHANGED(m_flFogStart)) SetSendFlags(ENVFOGCTRL_CHANGED_START); if (ATTR_CHANGED(m_flFogEnd)) SetSendFlags(ENVFOGCTRL_CHANGED_END); if (ATTR_CHANGED(m_flFogMaxDensity)) SetSendFlags(ENVFOGCTRL_CHANGED_MAXDENSITY); if (ATTR_CHANGED(m_flFogFarZ)) SetSendFlags(ENVFOGCTRL_CHANGED_FARZ); if (ATTR_CHANGED(m_vecFogColor)) SetSendFlags(ENVFOGCTRL_CHANGED_COLOR); if (ATTR_CHANGED(m_vecFogColor2)) SetSendFlags(ENVFOGCTRL_CHANGED_COLOR2); if (ATTR_CHANGED(m_vecFogDir)) SetSendFlags(ENVFOGCTRL_CHANGED_DIR); SAVE_STATE(m_iFogActive) SAVE_STATE(m_iFogBlend) SAVE_STATE(m_flFogStart) SAVE_STATE(m_flFogEnd) SAVE_STATE(m_flFogMaxDensity) SAVE_STATE(m_flFogFarZ) SAVE_STATE(m_vecFogColor) SAVE_STATE(m_vecFogColor2) SAVE_STATE(m_vecFogDir) } void env_fog_controller::Trigger(entity eAct, triggermode_t iState) { switch (iState) { case TRIG_OFF: m_iFogActive = (0); break; case TRIG_ON: m_iFogActive = (1); break; default: m_iFogActive = (1-m_iFogActive); } } void env_fog_controller::Respawn(void) { SetSolid(SOLID_NOT); SetMovetype(MOVETYPE_PUSH); SetModel(GetSpawnModel()); SetOrigin(GetSpawnOrigin()); pvsflags = PVSF_IGNOREPVS; m_iFogActive = m_iSpawnEnable; m_iFogBlend = m_iSpawnBlend; m_flFogStart = m_flSpawnStart; m_flFogEnd = m_flSpawnEnd; m_flFogMaxDensity = m_flSpawnMaxDensity; m_flFogFarZ = m_flSpawnFarZ; m_vecFogColor = m_vecSpawnColor; m_vecFogColor2 = m_vecSpawnColor2; if (m_iUseAngles) m_vecFogDir = GetSpawnAngles(); else m_vecFogDir = m_vecSpawnDir; } void env_fog_controller::Save(float handle) { super::Save(handle); SaveInt(handle, "m_iSpawnEnable", m_iSpawnEnable); SaveInt(handle, "m_iSpawnBlend", m_iSpawnBlend); SaveFloat(handle, "m_flSpawnStart", m_flSpawnStart); SaveFloat(handle, "m_flSpawnEnd", m_flSpawnEnd); SaveFloat(handle, "m_flSpawnMaxDensity", m_flSpawnMaxDensity); SaveFloat(handle, "m_flSpawnFarZ", m_flSpawnFarZ); SaveVector(handle, "m_vecSpawnColor", m_vecSpawnColor); SaveVector(handle, "m_vecSpawnColor2", m_vecSpawnColor2); SaveVector(handle, "m_vecSpawnDir", m_vecSpawnDir); SaveInt(handle, "m_iUseAngles", m_iUseAngles); SaveInt(handle, "m_iFogActive", m_iFogActive); SaveInt(handle, "m_iFogBlend", m_iFogBlend); SaveFloat(handle, "m_flFogStart", m_flFogStart); SaveFloat(handle, "m_flFogEnd", m_flFogEnd); SaveFloat(handle, "m_flFogMaxDensity", m_flFogMaxDensity); SaveFloat(handle, "m_flFogFarZ", m_flFogFarZ); SaveVector(handle, "m_vecFogColor", m_vecFogColor); SaveVector(handle, "m_vecFogColor2", m_vecFogColor2); SaveVector(handle, "m_vecFogDir", m_vecFogDir); } void env_fog_controller::Restore(string strKey, string strValue) { switch (strKey) { case "m_iFogActive": m_iFogActive = stoi(strValue); break; case "m_iFogBlend": m_iFogBlend = stoi(strValue); break; case "m_flFogStart": m_flFogStart = stof(strValue); break; case "m_flFogEnd": m_flFogEnd = stof(strValue); break; case "m_flFogMaxDensity": m_flFogMaxDensity = stof(strValue); break; case "m_flFogFarZ": m_flFogFarZ = stof(strValue); break; case "m_vecFogColor": m_vecFogColor = stov(strValue); break; case "m_vecFogColor2": m_vecFogColor2 = stov(strValue); break; case "m_vecFogDir": m_vecFogDir = stov(strValue); break; case "m_iSpawnEnable": m_iSpawnEnable = stoi(strValue); break; case "m_iSpawnBlend": m_iSpawnBlend = stoi(strValue); break; case "m_flSpawnStart": m_flSpawnStart = stof(strValue); break; case "m_flSpawnEnd": m_flSpawnEnd = stof(strValue); break; case "m_flSpawnMaxDensity": m_flSpawnMaxDensity = stof(strValue); break; case "m_flSpawnFarZ": m_flSpawnFarZ = stof(strValue); break; case "m_vecSpawnColor": m_vecSpawnColor = stov(strValue); break; case "m_vecSpawnColor2": m_vecSpawnColor2 = stov(strValue); break; case "m_vecSpawnDir": m_vecSpawnDir = stov(strValue); break; case "m_iUseAngles": m_iUseAngles = stoi(strValue); break; default: super::Restore(strKey, strValue); } } void env_fog_controller::Input(entity eAct, string strKey, string strValue) { switch (strKey) { case "TurnOn": m_iFogActive = (0); break; case "TurnOff": m_iFogActive = (1); break; case "Toggle": m_iFogActive = (1-m_iFogActive); break; case "SetStartDist": m_flFogStart = stof(strValue); break; case "SetEndDist": m_flFogEnd = stof(strValue); break; case "SetMaxDensity": m_flFogMaxDensity = stof(strValue); break; case "SetFarZ": m_flFogFarZ = stof(strValue); break; case "SetColor": m_vecFogColor = stov(strValue); break; case "SetColorSecondary": m_vecFogColor2 = stov(strValue); break; default: super::Input(eAct, strKey, strValue); } } void env_fog_controller::SpawnKey(string strKey, string strValue) { switch (strKey) { case "fogenable": m_iSpawnEnable = stoi(strValue); break; case "fogstart": m_flSpawnStart = stof(strValue); break; case "fogend": m_flSpawnEnd = stof(strValue); break; case "fogblend": m_iSpawnBlend = stoi(strValue); break; case "fogmaxdensity": m_flSpawnMaxDensity = stof(strValue); break; case "farz": m_flSpawnFarZ = stof(strValue); break; case "fogcolor": m_vecSpawnColor = stov(strValue) / 255; break; case "fogcolor2": m_vecSpawnColor2 = stov(strValue) / 255; break; case "fogdir": m_vecSpawnDir = stov(strValue); break; /* because these entities aren't complicated enough */ case "use_angles": m_iUseAngles = stoi(strValue); break; default: super::SpawnKey(strKey, strValue); } } #endif void env_fog_controller::env_fog_controller(void) { #ifdef SERVER m_vecSpawnColor = [1.0, 1.0, 1.0]; m_flSpawnMaxDensity = 1.0f; #endif }