RE: Editor GUI design

Uffe Friis Lichtenberg (
Sun, 9 Jun 1996 20:58:34 +0200 (METDST)

Date: Sun, 9 Jun 1996 20:58:34 +0200 (METDST)
From: Uffe Friis Lichtenberg <>
Subject: RE: Editor GUI design
In-Reply-To: <9606071219.104978@lat1192.l...>

On Fri, 7 Jun 1996, Olivier Montanuy wrote:

> Building a Quake levels should be more like building a space station
> (well, if you already practiced this as a hobby...).


> You start from a
> room, and add more room elements. Convex, like the brushes, but instead
> of being filled, their are empty.
> The calculation cannot be more complex than with current brushes.

Well, that's OK if you want to build a space station/mine/and so on, but for
other architectural ideas it might not be ideal. I agree the _calculations_
can't be more complex than the other way round, but converting them back to
.map is surely gonna be more complex when you do subtractive CSG operations
rather than additive.

Still, a good editor should support both ways...

> It's as constructive as your approach, but with different stuff.
> and at least you don't have to wonder about the "exterior", and possible
> leakage (which are going to be a bore).

Well sure, leakage is gonna suck, but with proper .map validation routines it
should be possible to detect automatically and perhaps even fix

> Ok for the functions. But I think it's the current .MAP format that causes
> troubles. Whatever CSG you use, you'll have to convert the result into a set of
> filled brushes, which will make a lot of data.

Exactly. It the <CSG tree representation of brushes> to .map function that
will be the hard one to code.

I have been thinking somewhat like this:

BTW: This is C++ which makes me think: what would be of most interest to the
rest of the Quake editing community, C or C++? (Mads: I'm not going to use
Delphi, no I'm not! ;)

class CSGtree
CSGtree(Brush b); // Instantiate leaf
// + other constructors and the usual stuff

CSGtree operator+(const CSGtree other) const;
// return union of *this and other

CSGtree operator*(const CSGtree other) const;
// return intersection of *this and other

CSGtree operator-(void) const;
// (unary minus... is this the correct syntax? can't really remember)
// return negation of *this

PolygonMesh toPolygonMesh(void) const;
// return polygon mesh representing CSGtree

CSGtree normalize(void) const;
// return `normalized' CSG representation (self-made terminology)
// (ie. one using only unions; an any-order traversal of the
// tree would produce output suitable for a .map file)

Brush toBrush(void) const);
// return a single brush representing *this (if possible)

CSGtree transform(Matrix4x4 m) const;
// transform node and leaves using the given matrix

CSGtree translate(Vector3 v) const;
// translate node and leaves using the given vector
// (ie. setup matrix and use transform() for naive implementation

CSGtree rotateX(float a) const;
// rotate...

// + all the other handy editing functions you could think of...

(Of course this interface is very `top-level' and should be optimized with
references or pointers where appropriate. Also non-const (but state-changing)
functions should be available to offer efficiency where you know you won't be
needing the `old' instance. This should give the general idea... Otherwise
tell me I'm rambling :)

(I'm assuming proper classes for PolygonMesh and Brush, but this should be
somewhat more trivial. PolygonMesh would probably just be a list of polygons,
but rendering could possibly benefit from using BSP or other pre-sorting, as
they shouldn't be recalculated constantly. A Brush would probably just be a
list of planes. Here BSP might be useful too, who knows.)

All operations should then be performed upon CSG representations with
operators at the nodes and brushes at the leaves.

The tricky functions will be toPolygonMesh(), normalize() and toBrush().

toPolygonMesh() should be managable. The trick will be to keep the polygon
count relatively low (ideally as low as possible). If the given CSGtree is a
leaf (ie. a brush) it should be (relatively) trivial to perform all
intersections between the planes to get list of polygons.
Otherwise you would just apply the funcion recursively and then
unite/intersect/negate polygon meshes. `Dead' polygons (ie. polygons inside
the object, never visible) should of course be culled.
Real problem: speed and accuracy. Check out 3D Studios boolean operations
on their triangle-meshes. Verry buggy/slow/inadequate.
Speed will probably never reach real-time rates, so an editor will need to
remember the PolygonMesh returned by toPolygonMesh() and only force a
recalculation when necessary.

normalize() should identify the smallest amount of brushes necessary for the
conversion. Unions are easy: they convert one-to-one, but if the union can be
represented as one brush (ie. it is still convex) it should be reduced to
that (using toBrush()). Intersections are easy too: just collapsing the two
brush representations (list of planes) produces correct output, but redundant
planes (ie. planes that have no influence on the objects shape) should be
As goes for negation: well, I'll have to think on that (anybody?) as this
might influence the rest of the process. Perhaps converting a negation of an
n-planed brush to n-large-enough-brushes defining the outside as inside would
be sufficient. But it must also be applicable to a CSG'ed child... Hmmm...

toBrush() simply checks wether the CSG representation is convex and, if
so, converts it to a plane-list (ie. brush). Redundant planes are
stripped. (But how to identify these? Oh well, paper and pencil...)

I have a feeling all this work has been done before, so I'll probably be
doing some litterature searching soon. Anybody know any good texts on

> I agree with this... as long as the level is some kind of building with layers.
> With caves, it's useless.

Of course. I forgot layers! A good editor _must_ be able to define part of
the scene as a group (/layer) and enable the user to easily distinguish
between the groups (/layers). Probably using colours.

This of course has no relation to what you just said but the word `layer'
triggered something :)

For caves you are right. The only way to do this easily is to walk around
inside the level. Perspective and all...

> How about VR helmets, now.

Heh... Even if I could afford one (which I can't) I wouldn't use one, let
alone buy one. The quality of available/semi-affordable VR helmets I have
tried is low.... (I take this was a jest anyway ;)

Uffe. [uphfe]

"This .signature hasn't been left unintentionally void of blankness"