329 lines
6.9 KiB
Plaintext
329 lines
6.9 KiB
Plaintext
/*
|
|
* 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.
|
|
*/
|
|
|
|
var int autocvar_r_drawdecals = TRUE;
|
|
var int autocvar_sp_decals = 128;
|
|
var int autocvar_mp_decals = 128;
|
|
var int autocvar_cl_decals = 128;
|
|
|
|
enumflags
|
|
{
|
|
DECALFL_ORIGIN,
|
|
DECALFL_ANGLE,
|
|
DECALFL_MATERIAL
|
|
};
|
|
|
|
#ifdef CLIENT
|
|
const string g_decal_shader = \
|
|
"{\n" \
|
|
"polygonOffset\n" \
|
|
"{\n" \
|
|
"clampmap %s\n" \
|
|
"rgbgen vertex\n" \
|
|
"blendfunc blend\n" \
|
|
"}\n" \
|
|
"}";
|
|
#endif
|
|
|
|
#ifdef SERVER
|
|
float
|
|
decal::SendEntity(entity ePEnt, float changedflags)
|
|
{
|
|
if (clienttype(ePEnt) != CLIENTTYPE_REAL)
|
|
return (0);
|
|
|
|
if (!m_strTexture)
|
|
return (0);
|
|
|
|
WriteByte(MSG_ENTITY, ENT_DECAL);
|
|
WriteByte(MSG_ENTITY, changedflags);
|
|
|
|
if (changedflags & DECALFL_ORIGIN) {
|
|
WriteCoord(MSG_ENTITY, origin[0]);
|
|
WriteCoord(MSG_ENTITY, origin[1]);
|
|
WriteCoord(MSG_ENTITY, origin[2]);
|
|
}
|
|
|
|
if (changedflags & DECALFL_ANGLE) {
|
|
WriteCoord(MSG_ENTITY, angles[0]);
|
|
WriteCoord(MSG_ENTITY, angles[1]);
|
|
WriteCoord(MSG_ENTITY, angles[2]);
|
|
}
|
|
|
|
if (changedflags & DECALFL_MATERIAL)
|
|
WriteString(MSG_ENTITY, m_strTexture);
|
|
|
|
return (1);
|
|
}
|
|
#else
|
|
void
|
|
decal::ReceiveEntity(void)
|
|
{
|
|
float changedflags = readbyte();
|
|
|
|
if (changedflags & DECALFL_ORIGIN) {
|
|
origin[0] = readcoord();
|
|
origin[1] = readcoord();
|
|
origin[2] = readcoord();
|
|
setorigin(this, origin);
|
|
}
|
|
|
|
if (changedflags & DECALFL_ANGLE) {
|
|
angles[0] = readcoord();
|
|
angles[1] = readcoord();
|
|
angles[2] = readcoord();
|
|
}
|
|
|
|
if (changedflags & DECALFL_MATERIAL)
|
|
m_strTexture = readstring();
|
|
|
|
size = drawgetimagesize(m_strTexture);
|
|
|
|
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
|
|
BuildShader();
|
|
} else {
|
|
m_strShader = m_strTexture;
|
|
}
|
|
|
|
makevectors(angles);
|
|
float surf = getsurfacenearpoint(world, origin);
|
|
vector s_dir = getsurfacepointattribute(world, surf, 0, SPA_S_AXIS);
|
|
vector t_dir = getsurfacepointattribute(world, surf, 0, SPA_T_AXIS);
|
|
mins = v_up / size[0];
|
|
maxs = t_dir / size[1];
|
|
color = getlight(origin) / 255;
|
|
drawmask = MASK_ENGINE;
|
|
}
|
|
|
|
float
|
|
decal::predraw(void)
|
|
{
|
|
vector vecPlayer;
|
|
int s = (float)getproperty(VF_ACTIVESEAT);
|
|
pSeat = &g_seats[s];
|
|
vecPlayer = pSeat->m_vecPredictedOrigin;
|
|
|
|
decal dcl = (decal)self;
|
|
if (!autocvar_r_drawdecals) {
|
|
return (PREDRAW_NEXT);
|
|
}
|
|
|
|
/* skip empty decals */
|
|
if (!dcl.m_strShader)
|
|
return (PREDRAW_NEXT);
|
|
|
|
/* don't draw us, unnecessary */
|
|
if (checkpvs(vecPlayer, this) == FALSE) {
|
|
return (PREDRAW_NEXT);
|
|
}
|
|
|
|
adddecal(dcl.m_strShader, dcl.origin, dcl.mins, dcl.maxs, dcl.color, 1.0f);
|
|
addentity(dcl);
|
|
return (PREDRAW_NEXT);
|
|
}
|
|
|
|
void
|
|
decal::BuildShader(void)
|
|
{
|
|
/* skip empty decals */
|
|
if (!m_strTexture || !m_strTexture)
|
|
return;
|
|
|
|
m_strShader = Decal_Precache(m_strTexture);
|
|
}
|
|
#endif
|
|
|
|
/* this is like Decal_Parse, but better */
|
|
void
|
|
decal::Place(vector org, string dname)
|
|
{
|
|
if (!dname)
|
|
return;
|
|
|
|
decal_pickwall(this, org);
|
|
|
|
/* we never hit any wall. */
|
|
if (g_tracedDecal.fraction == 1.0f) {
|
|
NSWarning("Placing of decal %S failed at %v", dname, org);
|
|
|
|
if (classname != "tempdecal")
|
|
remove(this);
|
|
return;
|
|
}
|
|
|
|
origin = g_tracedDecal.endpos;
|
|
setorigin(this, origin);
|
|
|
|
/* FIXME: more universal check? */
|
|
if (getsurfacetexture(trace_ent, getsurfacenearpoint(trace_ent, g_tracedDecal.endpos)) == "sky") {
|
|
return;
|
|
}
|
|
|
|
makevectors(vectoangles(g_tracedDecal.endpos - origin));
|
|
vector cpl = v_forward - (v_forward * g_tracedDecal.normal) * g_tracedDecal.normal;
|
|
|
|
if (g_tracedDecal.normal[2] == 0) {
|
|
cpl = [0, 0, 1];
|
|
}
|
|
|
|
angles = vectoangles(cpl, g_tracedDecal.normal);
|
|
m_strTexture = dname;
|
|
|
|
#ifdef SERVER
|
|
angles = vectoangles(cpl, g_tracedDecal.normal);
|
|
solid = SOLID_NOT;
|
|
pvsflags = PVSF_NOREMOVE | PVSF_IGNOREPVS;
|
|
SendFlags = 1;
|
|
#else
|
|
size = drawgetimagesize(m_strTexture);
|
|
|
|
if (serverkeyfloat("*bspversion") == BSPVER_HL) {
|
|
BuildShader();
|
|
} else {
|
|
m_strShader = m_strTexture;
|
|
}
|
|
|
|
makevectors(angles);
|
|
float surf = getsurfacenearpoint(world, origin);
|
|
vector s_dir = getsurfacepointattribute(world, surf, 0, SPA_S_AXIS);
|
|
vector t_dir = getsurfacepointattribute(world, surf, 0, SPA_T_AXIS);
|
|
mins = v_up / size[0];
|
|
maxs = t_dir / size[1];
|
|
color = getlight(origin) / 255;
|
|
drawmask = MASK_ENGINE;
|
|
#endif
|
|
}
|
|
|
|
decal g_decals;
|
|
void Decals_Init(void)
|
|
{
|
|
int max;
|
|
|
|
#ifdef SERVER
|
|
max = cvar("sv_playerslots") == 1 ? autocvar_sp_decals : autocvar_mp_decals;
|
|
#else
|
|
max = autocvar_cl_decals;
|
|
#endif
|
|
|
|
/* let's not glitch this up */
|
|
if (max <= 0) {
|
|
max = 5;
|
|
}
|
|
|
|
decal nextdecal = spawn(decal);
|
|
g_decals = nextdecal;
|
|
for (int i = 0; i <= max; i++) {
|
|
nextdecal.classname = "tempdecal";
|
|
nextdecal.owner = spawn(decal);
|
|
|
|
if (i == max) {
|
|
nextdecal.owner = g_decals;
|
|
} else {
|
|
nextdecal = (decal)nextdecal.owner;
|
|
}
|
|
}
|
|
}
|
|
|
|
decal Decals_Next(vector pos)
|
|
{
|
|
decal ret = g_decals;
|
|
g_decals = (decal)g_decals.owner;
|
|
|
|
/* Check for a tempdecal within a radius of 8 units and overwrite that one
|
|
* instead */
|
|
for (entity b = world; (b = find(b, ::classname, "tempdecal"));) {
|
|
if (vlen(b.origin - pos) < 4) {
|
|
return b;
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
/* Generalized Decal Placing Function */
|
|
void Decals_Place(vector pos, string dname)
|
|
{
|
|
float bsp_version = serverkeyfloat("*bspversion");
|
|
|
|
switch (bsp_version) {
|
|
case BSPVER_HL:
|
|
case BSPVER_RBSP:
|
|
case BSPVER_Q3:
|
|
case BSPVER_RTCW:
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
decal x = Decals_Next(pos);
|
|
x.Place(pos, dname);
|
|
}
|
|
|
|
#ifdef CLIENT
|
|
string
|
|
Decal_Precache(string decalTex)
|
|
{
|
|
string shaderName = sprintf("decal_%s", decalTex);
|
|
string shaderBuff = sprintf(g_decal_shader, decalTex);
|
|
precache_pic(decalTex); /* precache */
|
|
shaderforname(shaderName, shaderBuff);
|
|
return shaderName;
|
|
}
|
|
|
|
void
|
|
Decal_Reload(void)
|
|
{
|
|
for (entity b = world; (b = find(b, ::classname, "tempdecal"));) {
|
|
decal d = (decal)b;
|
|
d.BuildShader();
|
|
}
|
|
for (entity b = world; (b = find(b, ::classname, "decal"));) {
|
|
decal d = (decal)b;
|
|
d.BuildShader();
|
|
}
|
|
}
|
|
|
|
void
|
|
Decal_Parse(void)
|
|
{
|
|
decal new;
|
|
|
|
/* convert us to an object of type decal */
|
|
spawnfunc_decal();
|
|
new = (decal)self;
|
|
new.ReceiveEntity();
|
|
}
|
|
|
|
void
|
|
Decal_Shutdown(void)
|
|
{
|
|
for (entity b = world; (b = find(b, ::classname, "tempdecal"));) {
|
|
remove(b);
|
|
}
|
|
for (entity b = world; (b = find(b, ::classname, "decal"));) {
|
|
remove(b);
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void
|
|
decal::decal(void)
|
|
{
|
|
m_strShader = __NULL__;
|
|
m_strTexture = __NULL__;
|
|
setsize(this, [0,0,0], [0,0,0]);
|
|
}
|