liquid brushes

Post tutorials on how to do certain tasks within game or engine code here.
Post Reply
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

liquid brushes

Post by frag.machine »

I am working in a way to create liquid brushes, and I almost was succesfull, but I need some advising in how to fix a small detail.

I decided to take a mixed solution (QuakeC + engine), so that's why I am posting the problem here.

From the QuakeC side, I am creating a new kind of func_door (func_liquid_door), adding this to the end of doors.qc:

Code: Select all

void() liquid_door_touch =
{
    if (other.health <= 0)
    {
        return;
    }

    setwater (self, other);

};

// just a simplified copy & paste from original func_door 
void() func_liquid_door =
{
    SetMovedir ();
    self.solid = SOLID_TRIGGER;
    self.movetype = MOVETYPE_PUSH;
    setorigin (self, self.origin);
    setmodel (self, self.model);
    self.classname = "liquid_door";

    self.use = door_use;

    if (!self.speed)
        self.speed = 100;
    if (!self.wait)
        self.wait = 3;
    if (!self.lip)
        self.lip = 8;
    if (!self.dmg)
        self.dmg = 2;
    if (!self.watertype)
        self.watertype = 3;

    self.pos1 = self.origin;
    self.pos2 = self.pos1 + self.movedir*(fabs(self.movedir*self.size) - self.lip);

    if (self.spawnflags & DOOR_START_OPEN)
    {
        setorigin (self, self.pos2);
        self.pos2 = self.pos1;
        self.pos1 = self.origin;
    }

    self.state = STATE_BOTTOM;
    self.touch = liquid_door_touch;
    self.think = LinkDoors;
    self.nextthink = self.ltime + 0.1;
};
The setwater (entity, entity) you see above is a new builtin I created to handle (by engine side) the .watertype and .waterlevel values. Here follows the code:

Code: Select all

qboolean	CheckPointInsideEntity (vec3_t p, edict_t *e)
{
	return (
		(p[0] >= e->v.mins[0]) &&
		(p[1] >= e->v.mins[1]) &&
		(p[2] >= e->v.mins[2]) &&
		(p[0] <= e->v.maxs[0]) &&
		(p[1] <= e->v.maxs[1]) &&
		(p[2] <= e->v.maxs[2]));
}
/**
 * setwater (entity waterbrush, entity other)
 **/
void		PF_SetWater (void)
{
	edict_t		*ent1, *ent2;
	vec3_t		point;

	ent1 = G_EDICT(OFS_PARM0);
	ent2 = G_EDICT(OFS_PARM1);

	point[0] = ent2->v.origin[0];
	point[1] = ent2->v.origin[1];
	point[2] = ent2->v.origin[2] + ent2->v.mins[2] + 1;
	ent2->v.waterlevel = 0;
	ent2->v.watertype = CONTENTS_EMPTY;
	if (CheckPointInsideEntity (point, ent1))
	{
		ent2->v.watertype  = ent1->v.watertype;
		ent2->v.waterlevel = 1;
		point[2] = ent2->v.origin[2] + (ent2->v.mins[2] + ent2->v.maxs[2]) * 0.5;
		if (CheckPointInsideEntity (point, ent1))
		{
			ent2->v.waterlevel = 2;
			point[2] = ent2->v.origin[2] + ent2->v.view_ofs[2];
			if (CheckPointInsideEntity (point, ent1))
			{
				ent2->v.waterlevel = 3;
			}
		}
	}
}

I am only resorting to engine coding because .waterlevel and .watertype are entity fields marked as "do not set in prog code, maintained by C code" in defs.qc. Otherwise there's nothing there I couldn't handle directly in QuakeC.

So, after that, I created a test map with a func_liquid_door toggled by buttons to mimic a pool being flooded and drained. And it works... kinda. The physics part works pretty well, you can swim and drown into the brush, and when toggled you can walk normally. But visually, there's no warped view, no colorshift. Checking the code, I found that (at least in FitzQuake) water warping is checked in gl_rmain.c (R_SetupView). But I have no idea how I could force the engine to render the water brush correctly, since it only checks contents against the world model. I could eventually hack something to do the trick, but I prefer to make it in an more elegant way.

Any suggestions ?
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

side note: halflife sets .skin to negative values to denote non-solid brush models.



in your client, you will need to loop through the entities (cl_entities in nq, I think) and test any brush entities for their solidity.
note that any pointcontents etc will give solid for liquid entities, thanks to quirks of qbsp.

if you use the .skin field you don't have to extend your network protocol, and it could be done automagically in the engine instead of having to use builtins.
mh
Posts: 2292
Joined: Sat Jan 12, 2008 1:38 am

Post by mh »

RMQ does it via a stuffcmd of cshift to set the contents color. You can also check cl.inwater for whether or not the client in inside a water brush.
We had the power, we had the space, we had a sense of time and place
We knew the words, we knew the score, we knew what we were fighting for
Spike
Posts: 2914
Joined: Fri Nov 05, 2004 3:12 am
Location: UK
Contact:

Post by Spike »

cshift doesn't warp, only discolours (and that must look odd in the majorty of quakeworld clients, where cshift is honoured and water colouration is disabled as its an fps loss).

testing the value of cl.inwater will not work as the client will see any func_water models as fully solid if it did check them, which it won't.

tbh, there's no difference between setting fields in qc or in a builtin. Those comments are more informative than instructional.
In the case of water types, they're just normally set in the engine and it'll override the content types periodically, so cannot be used for persistant storage.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Spike wrote:side note: halflife sets .skin to negative values to denote non-solid brush models.

in your client, you will need to loop through the entities (cl_entities in nq, I think) and test any brush entities for their solidity.
note that any pointcontents etc will give solid for liquid entities, thanks to quirks of qbsp.

if you use the .skin field you don't have to extend your network protocol, and it could be done automagically in the engine instead of having to use builtins.
I was thinking about changing the behavior of SV_CheckWater(), in sv_phys.c. The current version checks only contents against the wordmodel, verifying against any brush model the entity is touching should be enough. The idea of negative values for .skin sounds good. I'll make some tests.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Baker
Posts: 3666
Joined: Tue Mar 14, 2006 5:15 am

Post by Baker »

I want to see the Quake guy face a rising lava situation or a room filling with water :D

I hope you are successful, I'm looking very forward to seeing this.
The night is young. How else can I annoy the world before sunsrise? 8) Inquisitive minds want to know ! And if they don't -- well like that ever has stopped me before ..
qbism
Posts: 1236
Joined: Thu Nov 04, 2004 5:51 am
Contact:

Post by qbism »

Did you look at Pox's qc extras? Moving water, even water on a train.

Unrelated to qc extras, bu this was in my modded engine timefrag, world.c:

Code: Select all

int SV_PointContents (vec3_t p) //qbism//jf 02-10-22 now checks inside entities
{
	int cont;
	trace_t trace;
	vec3_t p2;

	cont = SV_HullPointContents(&sv.worldmodel->hulls[0], 0, p);

	p2[2] = p[2] + 3;
	p[2] = p[2] -3;

	trace = SV_Move (p, vec3_origin, vec3_origin, p2, 0, sv.edicts);


	if (trace.inlava) return CONTENTS_LAVA; //qbism//jf 02-10-22 put the harshest first!
	else if (trace.inslime) return CONTENTS_SLIME;
	else if (trace.inwater) return CONTENTS_WATER; //qbism//jf 02-10-22 always check this LAST.  inwater set for all liquids.

	else return cont;

}

In SV_RecursiveHullCheck:

Code: Select all

// check for empty
	if (num < 0)
	{
		if (num != CONTENTS_SOLID)
		{
			trace->allsolid = false;
			if (num == CONTENTS_EMPTY)
				trace->inopen = true;
			else
				trace->inwater = true;  //qbism// 02-10-22 set inwater for any liquid
		}
		else
			trace->startsolid = true;
		return true;		// empty
	}

The end of SV_ClipMoveToEntity:

Code: Select all

//qbism//jf 02-10-22 return liquid contents of brush models.
	if (ent->v.solid == SOLID_WATER)
		trace.inwater = 1;
	else if (ent->v.solid == SOLID_SLIME)
	{
		trace.inslime = 1;
		trace.inwater = 1; //qbism//jf 02-10-22 always set inwater for any type of liquid.
	}
	else if (ent->v.solid == SOLID_LAVA)
	{
		trace.inlava = 1;
		trace.inwater = 1; //qbism//jf 02-10-22 always set inwater for any type of liquid.
	}
	return trace;
}
Maybe usefull, or at least give some ideas.
frag.machine
Posts: 2126
Joined: Sat Nov 25, 2006 1:49 pm

Post by frag.machine »

Thanks! I'll compare it against the original code and maybe I can make it work.
EDIT: Also, I need to check Pox's stuff. :)
EDIT2: Duh! I should listened to you guys and checked Pox's mod first. Awesome stuff, it will be more than enough to what I want. Thanks everyone for the help.
I know FrikaC made a cgi-bin version of the quakec interpreter once and wrote part of his website in QuakeC :) (LordHavoc)
Post Reply