Index | FAQ | Search | Memberlist | Usergroups | Profile | Log in to check your private messages | Log in | Register

Origin Brushes

| Inside3d Forums Forum Index -> Mapping

View previous topic :: View next topic
Author | Message
--- | ---
Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Dec 08, 2008 9:43 pm Post subject: Origin Brushes |

Here is just a look at how one map compiler handles origin brushes in the bsp compiler. The compiler is "Zoner's tools" for Half-Life. The source code is here. This code should also be in the Quake 2 map compiler somewhere, but I don't know where to find the Quake 2 map compiler source code. I looked around at ftp.idsoftware.com and so far haven't been able to find the q2bsp source.
How origin brushes appear to be handled
It looks like origin brushes are treated specially just like water, sky, clip textures, etc.

bspfile.h in Zoner Tools hlbsp wrote:
typedef enum { CONTENTS_EMPTY = -1, CONTENTS_SOLID = -2, CONTENTS_WATER = -3,
CONTENTS_SLIME = -4,
CONTENTS_LAVA = -5,
CONTENTS_SKY = -6,
CONTENTS_ORIGIN = -7, // removed at csg time
CONTENTS_CLIP = -8, // changed to contents_solid
CONTENTS_CURRENT_0 = -9,
CONTENTS_CURRENT_90 = -10,
CONTENTS_CURRENT_180 = -11,
CONTENTS_CURRENT_270 = -12,
CONTENTS_CURRENT_UP = -13,
CONTENTS_CURRENT_DOWN = -14,
CONTENTS_TRANSLUCENT = -15,
CONTENTS_HINT = -16, // Filters down to CONTENTS_EMPTY by bsp, ENGINE SHOULD NEVER SEE THIS
#ifdef ZHLT_NULLTEX
CONTENTS_NULL = -17, // AJM // removed in csg and bsp, VIS or RAD shouldnt have to deal with this, only clip planes!
#endif
#ifdef ZHLT_DETAIL // AJM
CONTENTS_DETAIL = -18,
#endif
}

It appears the texture name must be "origin". Like how "clip" is a specially designated texture name.

Quote:
static contents_t TextureContents(const char* const name) { if (!strncasecmp(name, "sky", 3)) return CONTENTS_SKY; // =====================================================================================
//Cpt_Andrew - Env_Sky Check
// =====================================================================================
if (!strncasecmp(name, "env_sky", 3))
return CONTENTS_SKY;
// =====================================================================================
if (!strncasecmp(name + 1, "!lava", 5))
return CONTENTS_LAVA;
if (!strncasecmp(name + 1, "!slime", 6))
return CONTENTS_SLIME;
if (name[0] == '!') //optimized -- don't check for current unless it's liquid (KGP)
{
if (!strncasecmp(name, "!cur_90", 7))
return CONTENTS_CURRENT_90;
if (!strncasecmp(name, "!cur_0", 6))
return CONTENTS_CURRENT_0;
if (!strncasecmp(name, "!cur_270", )
return CONTENTS_CURRENT_270;
if (!strncasecmp(name, "!cur_180", )
return CONTENTS_CURRENT_180;
if (!strncasecmp(name, "!cur_up", 7))
return CONTENTS_CURRENT_UP;
if (!strncasecmp(name, "!cur_dwn", )
return CONTENTS_CURRENT_DOWN;
return CONTENTS_WATER; //default for liquids
}
if (!strncasecmp(name, "origin", 6))
return CONTENTS_ORIGIN;

A function that returns a string version of the name of the brush type

Quote:
// ===================================================================================== // ContentsToString // ===================================================================================== const char* ContentsToString(const contents_t type)
{
switch (type)
{
case CONTENTS_EMPTY:
return "EMPTY";
case CONTENTS_SOLID:
return "SOLID";
case CONTENTS_WATER:
return "WATER";
case CONTENTS_SLIME:
return "SLIME";
case CONTENTS_LAVA:
return "LAVA";
case CONTENTS_SKY:
return "SKY";
case CONTENTS_ORIGIN:
return "ORIGIN";

Ok, looks like the map compiler better reject any brush that isn't an entity. So an origin brush can't be part of the "world", must be a func_something or other entity type.

Quote:
// check to make sure we dont have an origin brush as part of worldspawn if ((b->entitynum == 0) || (strcmp("func_group", ValueForKey(&g_entities[b->entitynum], "classname"))==0)) { if (contents == CONTENTS_ORIGIN) {
Fatal(assume_BRUSH_NOT_ALLOWED_IN_WORLD, "Entity %i, Brush %i: %s brushes not allowed in world\n(did you forget to tie this origin brush to a rotating entity?)", b->entitynum, b->brushnum, ContentsToString(contents));
}
}

And here, for completeness is the part where the brush gets an all clear if it is an entity texture.

Quote:
// otherwise its not worldspawn, therefore its an entity. check to make sure this brush is allowed // to be an entity. switch (contents) { case CONTENTS_SOLID:
case CONTENTS_WATER:
case CONTENTS_SLIME:
case CONTENTS_LAVA:
case CONTENTS_ORIGIN:
case CONTENTS_CLIP:
#ifdef ZHLT_NULLTEX // AJM
case CONTENTS_NULL:
break;
#endif
default:
Fatal(assume_BRUSH_NOT_ALLOWED_IN_ENTITY, "Entity %i, Brush %i: %s brushes not allowed in entity", b->entitynum, b->brushnum, ContentsToString(contents));
break;
}

The brush isn't part of the clipping hull. I think I understand the concept of a clipping hull, but I'm still learning about the bsp so I don't claim to know completely what is going on here:

Quote:
void CreateBrush(const int brushnum) { brush_t* b; int contents; int h;
b = &g_mapbrushes[brushnum];
contents = b->contents;
if (contents == CONTENTS_ORIGIN)
return;
// HULL 0
MakeBrushPlanes(b);
MakeHullFaces(b, &b->hulls[0]);
// these brush types do not need to be represented in the clipping hull
switch (contents)
{
case CONTENTS_LAVA:
case CONTENTS_SLIME:
case CONTENTS_WATER:
case CONTENTS_TRANSLUCENT:
case CONTENTS_HINT:
return;
}

This is in parse_brush. I am thinking it probably is identifying the center point for rotation but I really don't know for sure.

static contents_t ParseBrush(entity_t* mapent) { ... wrote:

Code:
if (contents == CONTENTS_ORIGIN) { char string[MAXTOKEN]; vec3_t origin; b->contents = CONTENTS_SOLID;
CreateBrush(mapent->firstbrush + b->brushnum); // to get sizes
b->contents = contents;
for (i = 0; i < NUM_HULLS; i++)
{
b->hulls[i].faces = NULL;
}
if (b->entitynum != 0) // Ignore for WORLD (code elsewhere enforces no ORIGIN in world message)
{
VectorAdd(b->hulls[0].bounds.m_Mins, b->hulls[0].bounds.m_Maxs, origin);
VectorScale(origin, 0.5, origin);
safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)origin[0], (int)origin[1], (int)origin[2]);
SetKeyValue(&g_entities[b->entitynum], "origin", string);
}
}

Treat origin brushes like clip brushes, it seems:

bool ParseMapEntity() wrote:

Code:
if ((contents != CONTENTS_CLIP) && (contents != CONTENTS_ORIGIN)) all_clip = false;

Apparently, an origin brush is actually part of the rotating object entity:

Quote:

Code:
// if the given entity only has one brush and its an origin brush if ((mapent->numbrushes == 1) && (g_mapbrushes[mapent->firstbrush].contents == CONTENTS_ORIGIN)) { brushhull_t* hull = g_mapbrushes[mapent->firstbrush].hulls; Error("Entity %i, contains ONLY an origin brush near (%.0f,%.0f,%.0f)\n",
this_entity, hull->bounds.m_Mins[0], hull->bounds.m_Mins[1], hull->bounds.m_Mins[2]);
}

And maybe something again about identifying the center of the origin brush:

Quote:

Code:
// ===================================================================================== // SetModelCenters // ===================================================================================== static void SetModelCenters(int entitynum) {
int i;
int last;
char string[MAXTOKEN];
entity_t* e = &g_entities[entitynum];
BoundingBox bounds;
vec3_t center;
if ((entitynum == 0) || (e->numbrushes == 0)) // skip worldspawn and point entities
return;
if (!*ValueForKey(e, "light_origin")) // skip if its not a zhlt_flags light_origin
return;
for (i = e->firstbrush, last = e->firstbrush + e->numbrushes; i < last; i++)
{
if (g_mapbrushes[i].contents != CONTENTS_ORIGIN)
{
bounds.add(g_mapbrushes[i].hulls->bounds);
}
}
VectorAdd(bounds.m_Mins, bounds.m_Maxs, center);
VectorScale(center, 0.5, center);
safe_snprintf(string, MAXTOKEN, "%i %i %i", (int)center[0], (int)center[1], (int)center[2]);
SetKeyValue(e, "model_center", string);
}

I'm probably going to try to track down the q2bsp source code. I imagine this all works the same. The license for Zoner Tools is a little weird and goes like this:

Quote:

Code:
II. LICENCE 0) This code is protected by the GPL, a link to the GPL can be found at the end of this page. 1) In addition to the GPL, the Valve SDK 2.3 EULA overrides any rights you may have obtained in the GPL, when needed. 2) The iD Quake 2 Licence overrides portions of both the Valve EULA, and the GPL where needed, please contact iD for information on this subject.

Back to top |

leileilol Joined: 15 Oct 2004 Posts: 1321 |

Posted: Tue Dec 09, 2008 2:58 pm Post subject: |

https://zerowing.idsoftware.com/svn/radiant/Q2Tools/

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 10, 2008 7:11 am Post subject: |

Thank you. I'd very much rather use pure GPL code than something with a very strange license.

Back to top |

goldenboy Joined: 05 Sep 2008 Posts: 310 Location: Kiel |

Posted: Wed Dec 17, 2008 5:52 pm Post subject: |

Interesting, thanks. I can see that neither treeqbsp nor txqbsp support any "weird" content types, only clip. Oh well, after reading your post, a couple light bulbs went on. How much does the engine have to support? I guess quite a bit more? Hopefully, we can switch RMQ totally to origin brushes instead of hiprotate one day. We'll have to include the map compiler as well as the engine, but there's not really a problem with that.
Treat origin brushes like clip brushes, it seems:
Yeah, or like hint, skip... what have you.
Apparently, an origin brush is actually part of the rotating object entity:
Yes, that was clear to me before, because of the way you construct them in a map editor. You select both the origin brush and the (door, whatever) and turn both into an entity. Yep. And when the map loads, all that remains from the origin brush is its ... origin, I guess, because it's treated like a special content type. Or maybe it's indeed stripped by the map compiler. Too tired to think about that now.
identifying the center point for rotation
Yeah, that must be done so the engine knows what to rotate the brush around - otherwise it rotates it around '0 0 0'.
It's all a big hack, but a much better one than the hiprotate business. Just thinking about func_movewalls makes me sick.

Back to top |

metlslime Joined: 05 Feb 2008 Posts: 177 |

Posted: Wed Dec 17, 2008 10:36 pm Post subject: |

well, if you want to get rid of func_movewalls, you need more than just implement origin brushes, you also need collision of AABBs against rotated BSPs hulls. However, even with the collision hackery, origin brushes would still be a quality of life improvement for mappers.

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Wed Dec 17, 2008 11:02 pm Post subject: |

goldenboy wrote:
How much does the engine have to support? I guess quite a bit more?

The engine and QuakeC side looks easy:
http://www.quake-1.com/docs/quakesrc.org/129.html
The map compiler is the missing link.
I believe DarkPlaces and FTE support origin brushes and both support Half-Life .bsp, which supports origin brushes and can be compiled from Quake .map files (i.e. Worldcraft 3.3 or Hammer, but you have to use, say, Zoner's tools instead of aguirRe's txqbsp obviously).
Really if someone made a workable sample map and functional QuakeC for testing [via Half-Life .bsp at first], then there would be working model to use a test basis to ensure the map compiler changes were done properly.
/Is dodging the .bsp version # topic.

Back to top |

MeTcHsteekle Joined: 15 May 2008 Posts: 397 Location: its a secret |

Posted: Wed Dec 17, 2008 11:08 pm Post subject: |

Quote:
eally if someone made a workable sample map and functional QuakeC for testing [via Half-Life .bsp at first], then there would be working model to use a test basis to ensure the map compiler changes were done properly.

what about the half-life to quake map experiments? perhaps the code from that could help as a starting point?
/shooting in the dark with this
_________________
bah

Back to top |

goldenboy Joined: 05 Sep 2008 Posts: 310 Location: Kiel |

Posted: Thu Dec 18, 2008 10:19 am Post subject: |

RMQ has the func_door_rotating from Quakelife, and for the rotation testing I used FTE (there is a testmap, of course not compiled with a proper map compiler yet). It does rotate, just not around the right origin. Don't know if FTE supports origin brushes though, might have to use that (cool) tutorial.
Unless someone is faster, I'll start the map compiler hackery in the new year. I'll use bjptools, because I hope it compiles on both Linux and Windows.
The collision is hopefully someone else's problem (looks at engine coders) ... You can get squished all right in FTE, though, thus I thought this wouldn't be a big issue. I'm often wrong though.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Dec 18, 2008 10:45 am Post subject: |

Every engine is compatible with origin brushes. There isn't any special code there regarding them. The only thing engines lack is physics. (some software renderers can be a bit picky about some rotated bsps too however). Baker, you missed the bit where it offsets all other brushes by the origin.
Or maybe that's already part of the code?
_________________
What's a signature?

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Thu Dec 18, 2008 11:55 am Post subject: |

Spike wrote:
Baker, you missed the bit where it offsets all other brushes by the origin. Or maybe that's already part of the code?

Wouldn't surprise me, at this point I know very little about what exactly a bsp compiler is doing and was trying a pilot effort to unwrap how the origin brushes work into it.
Goldenboy, if you have a sample origin brush map (+source) and progs (+source), can you zip them and upload them somewhere public like Quaketastic.
It would make it easier for someone wanting to see if they could successfully add it to the map compiler to test their work and lower the effort required.
For example, I might take a shot at it [I may or may not be .bsp knowledgeable enough to be successful] but without a sample map and progs to test it, it makes it hard.

Back to top |

Spike Joined: 05 Nov 2004 Posts: 944 Location: UK |

Posted: Thu Dec 18, 2008 10:42 pm Post subject: |

well, basically all an origin brush does is set the origin field of an entity without causing the entity to otherwise move. It really only matters if the entity(bsp model) rotates. the ent's new origin should be (max+min)/2 of the origin brush, so that the pivot of the ent is at the center of the origin brush.
you can't just set the origin to the middle of the object, as that would make awkward doors.
The origin brushes are thus invisible, and make no sense if they're the only thing that makes up the model
But because the client will translate the model based on the entitiy's origin, you need to update the other brushes in the model the other way, thus subtracting the origin from each vertex to reverse what the client will do.
The end result is a model that can freely pivot around its own origin.
In Quake, the moving trains move to an entity placed where their min position would be. But then, in quake, all bsp model origins are at '0 0 0' initially, and the mins is usually positive, thus it moves to (target.origin-self.mins). Obviously, this maths still works if you used an origin brush, as the mins is then closer to '0 0 0' presumably. So much for examples.
_________________
What's a signature?

Back to top |

goldenboy Joined: 05 Sep 2008 Posts: 310 Location: Kiel |

Posted: Fri Dec 19, 2008 12:59 pm Post subject: |

http://www.quaketastic.com/upload/files/misc/rotation.7z There is a bsp, map, wad and progs.dat with source. Good luck hacking the map compiler. I'll gladly join the fray, but not now. May I suggest Treeqbsp as a basis for the hackery. May I further pimp bjptools:
http://qexpo.tastyspleen.net/uploaded/157/bjptools-090908.zip
Um, and to explain a bit better how to make a func_door_rotating without a .def or .fgd file, simply create a func_illusionary instead, change the classname to func_door_rotating, and create a "distance" key and set it to like 100.
There is a QUAKED definition in the code though, which can be copy/pasted and saved as a .def.
gb

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Fri Dec 19, 2008 11:16 pm Post subject: |

goldenboy wrote:
http://www.quaketastic.com/upload/files/misc/rotation.7z There is a bsp, map, wad and progs.dat with source. Good luck hacking the map compiler. I'll gladly join the fray, but not now. May I suggest Treeqbsp as a basis for the hackery. May I further pimp bjptools:
http://qexpo.tastyspleen.net/uploaded/157/bjptools-090908.zip
Um, and to explain a bit better how to make a func_door_rotating without a .def or .fgd file, simply create a func_illusionary instead, change the classname to func_door_rotating, and create a "distance" key and set it to like 100.
There is a QUAKED definition in the code though, which can be copy/pasted and saved as a .def.
gb

Thanks. Weekends are bad for me, but as soon as the weekend is over I'll get on it.
If I get stuck, I'll post here. I'll likely be using txqbsp.

Back to top |

Baker Joined: 14 Mar 2006 Posts: 1538 |

Posted: Mon Jan 12, 2009 9:58 pm Post subject: |

Relevant link, storing here so I can find it without forum digging: http://www.superjer.com/learn/func_door_rotating.php http://forums.inside3d.com/viewtopic.php?p=13056&highlight=another#13056

Back to top |

Display posts from previous:

| Inside3d Forums Forum Index -> Mapping | All times are GMT
Page 1 of 1

Jump to:

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum

Powered by phpBB © 2004 phpBB Group