Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/orbweaver/master' into 3.1
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Jun 9, 2022
2 parents d43ac8a + 2408472 commit 516213b
Show file tree
Hide file tree
Showing 24 changed files with 358 additions and 189 deletions.
Binary file modified doc/img/LightInspector.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
102 changes: 78 additions & 24 deletions doc/manual.adoc
Expand Up @@ -256,9 +256,9 @@ and fully lit by in-game light sources. While the 2D view is the main interface
for creating and aligning level geometry, the 3D view is a vital tool for tasks
such as texturing, or configuring light parameters.

IMPORTANT: The fully lit rendering mode in DarkRadiant is very limited, and only
offers a partial view of what the game engine will ultimately render. In
particular, there are no shadows or foglights.
IMPORTANT: The fully lit rendering mode in DarkRadiant is not identical to what
the game engine will ultimately render. Certain advanced rendering features such
as reflections and fog lights are not currently supported.

The 3D camera view provides its own toolbar which can be used to configure
various display settings.
Expand Down Expand Up @@ -827,6 +827,7 @@ needs to align across all of the brushes, regardless of how they are moved or
resized), you will want to disable *Texture Lock*.
|===

[[ShaderClipboard]]
==== Using the shader clipboard

While constructing a map it will frequently be necessary to apply the same
Expand Down Expand Up @@ -972,18 +973,24 @@ properties using the light inspector, which is accessed with the *L* key.

image::LightInspector.png[align="center"]

TIP: The light inspector can change the properties of a single light, or
multiple selected lights simultaneously.

*Light volume (omni vs projected)*:: The majority of lights in a map will be the
default, omnidirectional shape. An omni light is a simple axis-aligned cuboid
which emits light in all directions from its center to its edges.
default, omnidirectional shape. An omni light is a simple cuboid which emits
light in all directions from its center to its edges.
+
A projected light is pyramid-shaped, and emits light from the tip of the pyramid
towards the base. Projected lights behave more like spotlights in real-life, and
can be used to highlight particular areas or project images of windows onto the
floor.

*Colour*:: Use the colour selector button to display a standard colour selection
dialog, or enter the RGB values directly using the text box. As well as changing
the hue, the light colour also governs the overall brightness of the light.
dialog. As well as changing the hue, the light colour also governs the overall
brightness of the light. You can use the slider below the colour button to
adjust the brightness of the selected light(s) without changing the hue, with
realtime feedback displayed in the 3D camera view if lighting preview mode is
enabled.

*Texture*:: The falloff texture controls the shape of the lit area when rendered
in-game; the square texture chosen here will be mapped directly onto the
Expand Down Expand Up @@ -1114,6 +1121,7 @@ which shows a brief explanation of certain properties. If a property has help
text available, the question mark icon will be shown in the *?* column.
|===

[[ReparentingPrimitives]]
=== Reparenting primitives

Whenever a new brush or patch is created, it will automatically be made a child
Expand All @@ -1138,34 +1146,40 @@ like — into *func_static* entities to improve in-game performance and reduce t
chances for map compilation problems caused by excessively complex world
geometry.

The *func_static* entity class is not the only type of entity which can contain
primitives: there are several other *func_* entities which perform various
functions, for example *func_rotating* which allows geometry to rotate
continuously. DarkRadiant offers dedicated commands to convert to/from a
*func_static* since this is a very common operation, however the ability to
<<SelectChildPrimitives,select>>, <<AddRemoveChildPrimitives,add and remove>>
individual primitives behaves the same for all types of primitive-containing
entity.

==== Converting primitives into func_static

To convert one or more existing primitives into a *func_static* entity, simply
select all of the primitives, right-click in the 2D window, and choose *Convert
to func_static*.

[[SelectChildPrimitives]]
==== Manipulating individual child primitives

After converting a primitive, a number of changes are noticeable:

. The primitive may be drawn in a different colour.
. The primitive will no longer be resizeable by dragging its boundary with the
mouse.
. When the primitive is selected, the *Entity Inspector* will no longer show the
*worldspawn* entity, but a new entity with a *classname* of `func_static`. You
can set spawnargs on this entity like any other (including giving it a custom
name).
. Selecting any primitive contained by a *func_static* will cause a small X/Y/Z
axis widget to be drawn at the entity's origin position (which may be inside one
of the primitives, or outside all of them, depending on their layout).
*worldspawn* entity, but a new entity with a different *classname* (e.g.
`func_static`). You can set spawnargs on this entity like any other (including
giving it a custom name).
. Selecting any contained primitive will cause a small X/Y/Z axis widget to be
drawn at the entity's origin position (which may be inside one of the
primitives, or outside all of them, depending on their layout).
. If there are multiple primitives contained within a single entity, selecting
any individual primitive will cause all of the entity's primitives to be
selected.

==== Selecting individual child primitives

As mentioned in the previous section, selecting any primitive which comprises
part of a *func_static* will cause the entire entity and all of its child
primitives to be selected together. This allows you to easily move the entire
static object by simply dragging any one of its primitives.
selected. This allows you to easily move the entire static object by simply
dragging any one of its primitives.

However, it is still possible to perform operations on a single primitive, for
example resizing a brush, by selecting it with the *TAB* key. Each press of
Expand All @@ -1180,6 +1194,7 @@ list of entity properties. If an entire entity is selected, the text will appear
similar to `Entity&nbsp;1`, whereas with a primitive selected it will read
`Entity&nbsp;1,&nbsp;Primitive&nbsp;1`.

[[AddRemoveChildPrimitives]]
==== Adding or removing primitives

Once you have created a *func_static* or similar entity from a number of
Expand Down Expand Up @@ -1786,6 +1801,39 @@ content into a file and load it later as an actual map fragment.
TIP: To unparent *all* of an entity's primitives and convert them back into
worldspawn, just right-click in the 2D view and choose *Revert to worldspawn*.

*Merge selected entities*:: Convert two more more selected entities into a
single entity which contains all of the contained brushes and patches. Only
works for entities which can contain primitives (e.g. func_static).

*Copy shader*:: Copy the shader from the selected face to the
<<ShaderClipboard,shader clipboard>>.

*Paste shader*:: Paste the shader currently on the <<ShaderClipboard,shader
clipboard>> to all selected faces.

*Clear selection*:: De-select all selected objects.

*Invert selection*:: De-select all selected objects, and select all unselected
objects.

*Select complete tall*:: Convert the currently-selected brush into a selection
volume, selecting all objects which are completely contained within its outline
in the current 2D view (ignoring the third dimension). See
<<BrushBasedSelection,brush-based selection>>.

*Select inside*:: Convert the currently-selected brush into a selection volume,
selecting all objects which are completely contained within it in all three
dimensions. See <<BrushBasedSelection, brush-based selection>>.

*Select fully inside*:: Like *Select inside*, except that contained brushes
which touch the boundary of the selection brush will not be selected.

*Select children*:: Select primitives which are children of the
currently-selected entity. See <<ReparentingPrimitives,reparenting primitives>>.

*Select parent entities*:: Select the parent entity of the currently-selected
primitive. See <<ReparentingPrimitives,reparenting primitives>>.

== Configuring DarkRadiant

DarkRadiant offers a large number of configurable options which can be used to
Expand Down Expand Up @@ -1823,9 +1871,15 @@ The *Camera* page contains options relating to the movement and behaviour of the
rather than smooth motion.

*Enable far clip plane*:: You can completely disable the <<Using3DCameraView,far
clip plane>> by unchecking this option. This will avoid the need to manage the
position of the far clip plane, but may negatively impact rendering performance
in large or complex maps.
clip plane>> by unchecking this option. This will avoid the need to manage the
position of the far clip plane, but may negatively impact rendering performance
in large or complex maps.
+
NOTE: Technically it is not actually possible to _disable_ the far clip plane,
since having a far clip plane is a requirement for 3D rendering to work
correctly. This command in fact sets the far clip plane to a very high value,
e.g. 32768. If your map is very large, it is conceivable that you will still see
some far clipping behaviour.

*Invert mouse vertical axis*:: Enable this option to flip the sense of the
vertical camera motion when freelook mode is enabled, so that moving the mouse
Expand Down
63 changes: 45 additions & 18 deletions include/iselectiontest.h
Expand Up @@ -231,35 +231,62 @@ class SelectionTest
virtual void TestPoint(const Vector3& point, SelectionIntersection& best) = 0;
virtual void TestPolygon(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0;
virtual void TestLineStrip(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0;
virtual void TestLines(const VertexPointer& vertices, std::size_t count, SelectionIntersection& best) = 0;
virtual void TestTriangles(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0;
virtual void TestQuads(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0;
virtual void TestQuadStrip(const VertexPointer& vertices, const IndexPointer& indices, SelectionIntersection& best) = 0;
};
typedef std::shared_ptr<SelectionTest> SelectionTestPtr;

/**
* @brief Abstract interface for an object which collects possibly-selected
* objects, keeping track of the best intersection which typically determines
* which candidate object should be selected when more than one is possible.
*
* This object exposes a stateful interface. A call to pushSelectable() sets the
* given ISelectable object as the "current" selectable. Subsequent calls to
* addIntersection() apply to this current selectable, replacing the current
* intersection if the newly-submitted one is better. The operation is completed
* when popSelectable() is called, and the current selectable along with its
* best intersection is added to the internal list(s).
*
* Despite the usage of "push" and "pop" terminology, there is only one current
* selectable, and no internal stack.
*/
class Selector
{
public:
virtual ~Selector() {}
virtual void pushSelectable(ISelectable& selectable) = 0;
virtual void popSelectable() = 0;
virtual void addIntersection(const SelectionIntersection& intersection) = 0;
};
virtual ~Selector() {}

inline void Selector_add(Selector& selector, ISelectable& selectable)
{
selector.pushSelectable(selectable);
selector.addIntersection(SelectionIntersection(0, 0));
selector.popSelectable();
}
/// Set the given object as the current selectable
virtual void pushSelectable(ISelectable& selectable) = 0;

inline void Selector_add(Selector& selector, ISelectable& selectable, const SelectionIntersection& intersection)
{
selector.pushSelectable(selectable);
selector.addIntersection(intersection);
selector.popSelectable();
}
/// Commit the current selectable, storing it along with its best intersection
virtual void popSelectable() = 0;

/**
* @brief Add a candidate intersection for the current selectable.
*
* The candidate intersection is only stored if it is a better fit than the
* best intersection seen so far.
*/
virtual void addIntersection(const SelectionIntersection& intersection) = 0;

/// Add a selectable object and immediately commit it with a null intersection
void addWithNullIntersection(ISelectable& selectable)
{
pushSelectable(selectable);
addIntersection(SelectionIntersection(0, 0));
popSelectable();
}

/// Add a selectable object and immediately commit it with the given intersection
void addWithIntersection(ISelectable& selectable, const SelectionIntersection& intersection)
{
pushSelectable(selectable);
addIntersection(intersection);
popSelectable();
}
};

class VolumeTest;
class SelectionTestable
Expand Down
24 changes: 12 additions & 12 deletions libs/dragplanes.h
Expand Up @@ -85,47 +85,47 @@ class DragPlanes
if (planes[0].normal().dot(corners[1]) > 0 && planes[0].normal().dot(corners[2]) > 0 &&
planes[0].normal().dot(corners[5]) > 0 && planes[0].normal().dot(corners[6]) > 0)
{
Selector_add(selector, _selectableRight);
selector.addWithNullIntersection(_selectableRight);
selectedPlaneCallback(planes[0]);
//rMessage() << "right\n";
}

if (planes[1].normal().dot(corners[0]) > 0 && planes[1].normal().dot(corners[3]) > 0 &&
planes[1].normal().dot(corners[4]) > 0 && planes[1].normal().dot(corners[7]) > 0)
{
Selector_add(selector, _selectableLeft);
selector.addWithNullIntersection(_selectableLeft);
selectedPlaneCallback(planes[1]);
//rMessage() << "left\n";
}

if (planes[2].normal().dot(corners[0]) > 0 && planes[2].normal().dot(corners[1]) > 0 &&
planes[2].normal().dot(corners[4]) > 0 && planes[2].normal().dot(corners[5]) > 0)
{
Selector_add(selector, _selectableFront);
selector.addWithNullIntersection(_selectableFront);
selectedPlaneCallback(planes[2]);
//rMessage() << "front\n";
}

if (planes[3].normal().dot(corners[2]) > 0 && planes[3].normal().dot(corners[3]) > 0 &&
planes[3].normal().dot(corners[6]) > 0 && planes[3].normal().dot(corners[7]) > 0)
{
Selector_add(selector, _selectableBack);
selector.addWithNullIntersection(_selectableBack);
selectedPlaneCallback(planes[3]);
//rMessage() << "back\n";
}

if (planes[4].normal().dot(corners[0]) > 0 && planes[4].normal().dot(corners[1]) > 0 &&
planes[4].normal().dot(corners[2]) > 0 && planes[4].normal().dot(corners[3]) > 0)
{
Selector_add(selector, _selectableTop);
selector.addWithNullIntersection(_selectableTop);
selectedPlaneCallback(planes[4]);
//rMessage() << "top\n";
}

if (planes[5].normal().dot(corners[4]) > 0 && planes[5].normal().dot(corners[5]) > 0 &&
planes[5].normal().dot(corners[6]) > 0 && planes[5].normal().dot(corners[7]) > 0)
{
Selector_add(selector, _selectableBottom);
selector.addWithNullIntersection(_selectableBottom);
selectedPlaneCallback(planes[5]);
//rMessage() << "bottom\n";
}
Expand All @@ -140,32 +140,32 @@ class DragPlanes

if (selectedPlanes.contains(-planes[0]))
{
Selector_add(selector, _selectableRight);
selector.addWithNullIntersection(_selectableRight);
}

if (selectedPlanes.contains(-planes[1]))
{
Selector_add(selector, _selectableLeft);
selector.addWithNullIntersection(_selectableLeft);
}

if (selectedPlanes.contains(-planes[2]))
{
Selector_add(selector, _selectableFront);
selector.addWithNullIntersection(_selectableFront);
}

if (selectedPlanes.contains(-planes[3]))
{
Selector_add(selector, _selectableBack);
selector.addWithNullIntersection(_selectableBack);
}

if (selectedPlanes.contains(-planes[4]))
{
Selector_add(selector, _selectableTop);
selector.addWithNullIntersection(_selectableTop);
}

if (selectedPlanes.contains(-planes[5]))
{
Selector_add(selector, _selectableBottom);
selector.addWithNullIntersection(_selectableBottom);
}
}

Expand Down
6 changes: 6 additions & 0 deletions libs/math/Vector3.h
Expand Up @@ -68,6 +68,12 @@ class BasicVector3
BasicVector3(const T* array): _v(array[0], array[1], array[2])
{}

/// Construct from another BasicVector3 with a compatible element type
template<typename U> BasicVector3(const BasicVector3<U>& other)
: BasicVector3(other.x(), other.y(), other.z())
{
}

/**
* Named constructor, returning a vector on the unit sphere for the given spherical coordinates.
*/
Expand Down
10 changes: 8 additions & 2 deletions libs/math/Vector4.h
Expand Up @@ -49,8 +49,14 @@ class BasicVector4
: _v(x_, y_, z_, w_)
{}

// Construct a BasicVector4 out of a Vector3 plus a W value (default 1)
BasicVector4(const BasicVector3<T>& other, T w_ = 1)
/// Construct from another BasicVector4 with a compatible element type
template<typename U> BasicVector4(const BasicVector4<U>& other)
: BasicVector4(other.x(), other.y(), other.z(), other.w())
{}

/// Construct from a BasicVector3 of compatible element type, plus an optional W value
template <typename U, typename W = float>
BasicVector4(const BasicVector3<U>& other, W w_ = 1.0f)
{
_v[0] = other.x();
_v[1] = other.y();
Expand Down

0 comments on commit 516213b

Please sign in to comment.