Skip to content

Commit

Permalink
Merge remote-tracking branch 'remotes/orbweaver/master'
Browse files Browse the repository at this point in the history
  • Loading branch information
codereader committed Aug 4, 2021
2 parents 6f8dc82 + 2f95d38 commit 36bb357
Show file tree
Hide file tree
Showing 14 changed files with 227 additions and 38 deletions.
Binary file added doc/img/CameraSync.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added doc/img/CameraSyncBack.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
50 changes: 50 additions & 0 deletions doc/manual.adoc
Expand Up @@ -1617,6 +1617,56 @@ TIP: For details of more advanced difficulty-dependent changes, such as
modifying the behaviour of a *single* entity (rather than an entire entity
class), consult the Dark Mod Wiki.

=== Game Connection

The Dark Mod includes functionality to dynamically interact with a DarkRadiant
session running on the same machine, allowing certain information (such as
camera position) to be synchronised both to and from the game, and for certain
entity property changes to be pushed to the running game without needing to
restart.

These features are accessed in DarkRadiant through the *Connection* menu and
buttons on the camera view toolbar.

==== Activating the connection

. In *The Dark Mod*, load the map which you are currently editing in DarkRadiant.
. Bring down the game console and enter the command
[listing]
com_automation 1
+
This should result in a status message indicating that the game is listening for
connections on a particular network port.
[listing]
Automation now listens on port 3879

The game process is now ready to exchange data with DarkRadiant.

==== Synchronising camera position

You can synchronise the DarkRadiant camera position and the game player position
in both directions: editor to game and game to editor. This feature is most
easily controlled with the buttons on the camera view toolbar, but it can also
be activated from the *Connection* menu.

[cols="^1h,3h,10"]
|===
|image:CameraSync.png[align="center",width=24]
|Game position follows DarkRadiant camera|
Any motion of the DarkRadiant camera will be transmitted in realtime to the
game, resulting in the player position moving (in *noclip* mode) to the same
position and view direction.

This is a toggled option which remains active until switched off.
|image:CameraSyncBack.png[align="center",width=24]
|Move camera to current game position|
Update the DarkRadiant camera to match the current player position and view
direction in game.

This is a single-shot command; there is no mechanism to continuously move the
DarkRadiant camera in response to player motion in game.
|===

== Command reference

=== File menu
Expand Down
86 changes: 85 additions & 1 deletion doc/manual.html
Expand Up @@ -529,6 +529,12 @@ <h1>DarkRadiant User Guide</h1>
<li><a href="#_dark_mod_features">Dark Mod features</a>
<ul class="sectlevel2">
<li><a href="#_the_difficulty_editor">The Difficulty editor</a></li>
<li><a href="#_game_connection">Game Connection</a>
<ul class="sectlevel3">
<li><a href="#_activating_the_connection">Activating the connection</a></li>
<li><a href="#_synchronising_camera_position">Synchronising camera position</a></li>
</ul>
</li>
</ul>
</li>
<li><a href="#_command_reference">Command reference</a>
Expand Down Expand Up @@ -3211,6 +3217,84 @@ <h3 id="_the_difficulty_editor">The Difficulty editor</h3>
</table>
</div>
</div>
<div class="sect2">
<h3 id="_game_connection">Game Connection</h3>
<div class="paragraph">
<p>The Dark Mod includes functionality to dynamically interact with a DarkRadiant
session running on the same machine, allowing certain information (such as
camera position) to be synchronised both to and from the game, and for certain
entity property changes to be pushed to the running game without needing to
restart.</p>
</div>
<div class="paragraph">
<p>These features are accessed in DarkRadiant through the <strong>Connection</strong> menu and
buttons on the camera view toolbar.</p>
</div>
<div class="sect3">
<h4 id="_activating_the_connection">Activating the connection</h4>
<div class="olist arabic">
<ol class="arabic">
<li>
<p>In <strong>The Dark Mod</strong>, load the map which you are currently editing in DarkRadiant.</p>
</li>
<li>
<p>Bring down the game console and enter the command</p>
<div class="listingblock">
<div class="content">
<pre>com_automation 1</pre>
</div>
</div>
<div class="paragraph">
<p>This should result in a status message indicating that the game is listening for
connections on a particular network port.</p>
</div>
<div class="listingblock">
<div class="content">
<pre>Automation now listens on port 3879</pre>
</div>
</div>
</li>
</ol>
</div>
<div class="paragraph">
<p>The game process is now ready to exchange data with DarkRadiant.</p>
</div>
</div>
<div class="sect3">
<h4 id="_synchronising_camera_position">Synchronising camera position</h4>
<div class="paragraph">
<p>You can synchronise the DarkRadiant camera position and the game player position
in both directions: editor to game and game to editor. This feature is most
easily controlled with the buttons on the camera view toolbar, but it can also
be activated from the <strong>Connection</strong> menu.</p>
</div>
<table class="tableblock frame-all grid-all stretch">
<colgroup>
<col style="width: 7.1428%;">
<col style="width: 21.4285%;">
<col style="width: 71.4287%;">
</colgroup>
<tbody>
<tr>
<th class="tableblock halign-center valign-top"><p class="tableblock"><span class="image"><img src="img/CameraSync.png" alt="CameraSync" width="24"></span></p></th>
<th class="tableblock halign-left valign-top"><p class="tableblock">Game position follows DarkRadiant camera</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Any motion of the DarkRadiant camera will be transmitted in realtime to the
game, resulting in the player position moving (in <strong>noclip</strong> mode) to the same
position and view direction.</p>
<p class="tableblock">This is a toggled option which remains active until switched off.</p></td>
</tr>
<tr>
<th class="tableblock halign-center valign-top"><p class="tableblock"><span class="image"><img src="img/CameraSyncBack.png" alt="CameraSyncBack" width="24"></span></p></th>
<th class="tableblock halign-left valign-top"><p class="tableblock">Move camera to current game position</p></th>
<td class="tableblock halign-left valign-top"><p class="tableblock">Update the DarkRadiant camera to match the current player position and view
direction in game.</p>
<p class="tableblock">This is a single-shot command; there is no mechanism to continuously move the
DarkRadiant camera in response to player motion in game.</p></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="sect1">
Expand Down Expand Up @@ -3868,7 +3952,7 @@ <h4 id="_creating_an_assets_lst_file">Creating an assets.lst file</h4>
</div>
<div id="footer">
<div id="footer-text">
Last updated 2021-01-17 20:46:29 UTC
Last updated 2021-07-28 20:36:17 +0100
</div>
</div>
</body>
Expand Down
12 changes: 11 additions & 1 deletion include/iimage.h
Expand Up @@ -52,10 +52,20 @@ class Image
/// Return the height of the specified level in pixels
virtual std::size_t getHeight(std::size_t level = 0) const = 0;

// greebo: Returns TRUE whether this image is precompressed (DDS)
/**
* \brief Test whether this image is precompressed.
*
* Precompressed images cannot be used as input for image manipulation
* operations in shaders, due to quality loss. Note that not all DDS images
* are necessarily compressed (although uncompressed DDS images are rarely
* used in TDM).
*/
virtual bool isPrecompressed() const {
return false;
}

/// Return the OpenGL format for this image
virtual GLenum getGLFormat() const = 0;
};
typedef std::shared_ptr<Image> ImagePtr;

Expand Down
4 changes: 3 additions & 1 deletion install/gl/interaction_fp.glsl
Expand Up @@ -54,7 +54,9 @@ void main()
vec3 H = normalize(L + V);

// compute normal in tangent space from bumpmap
vec3 N = 2.0 * (texture2D(u_bumpmap, var_tex_diffuse_bump.pq).xyz - 0.5);
vec2 normalRG = texture2D(u_bumpmap, var_tex_diffuse_bump.pq).rg;
float normalB = sqrt(1.0 - pow(normalRG.r, 2.0) - pow(normalRG.g, 2.0));
vec3 N = 2.0 * (vec3(normalRG, normalB) - 0.5);
N = normalize(N);

// compute the diffuse term
Expand Down
20 changes: 11 additions & 9 deletions libs/RGBAImage.h
Expand Up @@ -22,16 +22,17 @@ class RGBAImage :
public Image,
public util::Noncopyable
{
std::size_t _width;
std::size_t _height;

public:
RGBAPixel* pixels;

std::size_t width;
std::size_t height;

RGBAImage(std::size_t _width, std::size_t _height) :
pixels(new RGBAPixel[_width * _height]),
width(_width),
height(_height)
/// Construct image and initialise internal storage
RGBAImage(std::size_t width, std::size_t height):
_width(width),
_height(height),
pixels(new RGBAPixel[_width * _height])
{}

~RGBAImage()
Expand All @@ -44,9 +45,10 @@ class RGBAImage :
{
return reinterpret_cast<byte*>(pixels);
}
std::size_t getWidth(std::size_t = 0) const override { return width; }
std::size_t getHeight(std::size_t = 0) const override { return height; }
std::size_t getWidth(std::size_t = 0) const override { return _width; }
std::size_t getHeight(std::size_t = 0) const override { return _height; }
std::size_t getLevels() const override { return 1; }
GLenum getGLFormat() const override { return GL_RGBA; }

/* BindableTexture implementation */
TexturePtr bindTexture(const std::string& name) const
Expand Down
24 changes: 19 additions & 5 deletions radiant/ui/surfaceinspector/SurfaceInspector.cpp
Expand Up @@ -627,17 +627,31 @@ void SurfaceInspector::fitTexture(Axis axis)

if (repeatX > 0.0 && repeatY > 0.0)
{
// User-specified fit values must always be >0, but we use -1 internally
// to signal not to fit on a particular axis.
GlobalCommandSystem().executeCommand("FitTexture",
(axis == Axis::Y ? -1 : repeatX),
(axis == Axis::X ? -1 : repeatY));
GlobalCommandSystem().executeCommand("FitTexture", repeatX, repeatY);
}
else
{
// Invalid repeatX && repeatY values
wxutil::Messagebox::ShowError(_("Both fit values must be > 0."));
return;
}

// If only fitting on a single axis, propagate the scale factor for the
// fitted axis to the non-fitted axis, which should preserve the texture
// aspect ratio.
if (axis != Axis::BOTH)
{
GlobalSelectionSystem().foreachFace(
[&](IFace& face) {
ShiftScaleRotation texdef = face.getShiftScaleRotation();
if (axis == Axis::X)
texdef.scale[1] = texdef.scale[0];
else
texdef.scale[0] = texdef.scale[1];
face.setShiftScaleRotation(texdef);
}
);
}
}

void SurfaceInspector::onFit(Axis axis)
Expand Down
8 changes: 2 additions & 6 deletions radiantcore/brush/TextureProjection.cpp
Expand Up @@ -201,12 +201,8 @@ void TextureProjection::fitTexture(std::size_t width, std::size_t height,
bounds.extents.z() = 1;

// the bounds of a perfectly fitted texture transform
AABB perfect(Vector3(s_repeat > 0 ? s_repeat * 0.5 : bounds.origin.x(),
t_repeat > 0 ? t_repeat * 0.5 : bounds.origin.y(),
0),
Vector3(s_repeat > 0 ? s_repeat * 0.5 : bounds.extents.x(),
t_repeat > 0 ? t_repeat * 0.5 : bounds.extents.y(),
1));
AABB perfect(Vector3(s_repeat * 0.5, t_repeat * 0.5, 0),
Vector3(s_repeat * 0.5, t_repeat * 0.5, 1));

// the difference between the current texture transform and the perfectly fitted transform
Matrix4 diffMatrix = Matrix4::getTranslation(bounds.origin - perfect.origin);
Expand Down
24 changes: 12 additions & 12 deletions radiantcore/imagefile/TGALoader.cpp
Expand Up @@ -46,10 +46,10 @@ class Flip11 {}; // both
template<typename PixelDecoder>
void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip00&)
{
RGBAPixel* end = image.pixels + (image.height * image.width);
for(RGBAPixel* row = end; row != image.pixels; row -= image.width)
RGBAPixel* end = image.pixels + (image.getHeight() * image.getWidth());
for(RGBAPixel* row = end; row != image.pixels; row -= image.getWidth())
{
for(RGBAPixel* pixel = row - image.width; pixel != row; ++pixel)
for(RGBAPixel* pixel = row - image.getWidth(); pixel != row; ++pixel)
{
decode(istream, *pixel);
}
Expand All @@ -59,10 +59,10 @@ void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGB
template<typename PixelDecoder>
void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip01&)
{
RGBAPixel* end = image.pixels + (image.height * image.width);
for(RGBAPixel* row = image.pixels; row != end; row += image.width)
RGBAPixel* end = image.pixels + (image.getHeight() * image.getWidth());
for(RGBAPixel* row = image.pixels; row != end; row += image.getWidth())
{
for(RGBAPixel* pixel = row; pixel != row + image.width; ++pixel)
for(RGBAPixel* pixel = row; pixel != row + image.getWidth(); ++pixel)
{
decode(istream, *pixel);
}
Expand All @@ -72,10 +72,10 @@ void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGB
template<typename PixelDecoder>
void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip10&)
{
RGBAPixel* end = image.pixels + (image.height * image.width);
for(RGBAPixel* row = end; row != image.pixels; row -= image.width)
RGBAPixel* end = image.pixels + (image.getHeight() * image.getWidth());
for(RGBAPixel* row = end; row != image.pixels; row -= image.getWidth())
{
for(RGBAPixel* pixel = row; pixel != row - image.width;)
for(RGBAPixel* pixel = row; pixel != row - image.getWidth();)
{
decode(istream, *--pixel);
}
Expand All @@ -85,10 +85,10 @@ void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGB
template<typename PixelDecoder>
void image_decode(stream::PointerInputStream& istream, PixelDecoder& decode, RGBAImage& image, const Flip11&)
{
RGBAPixel* end = image.pixels + (image.height * image.width);
for(RGBAPixel* row = image.pixels; row != end; row += image.width)
RGBAPixel* end = image.pixels + (image.getHeight() * image.getWidth());
for(RGBAPixel* row = image.pixels; row != end; row += image.getWidth())
{
for(RGBAPixel* pixel = row + image.width; pixel != row;)
for(RGBAPixel* pixel = row + image.getWidth(); pixel != row;)
{
decode(istream, *--pixel);
}
Expand Down
13 changes: 10 additions & 3 deletions radiantcore/imagefile/dds.cpp
Expand Up @@ -105,6 +105,7 @@ class DDSImage: public Image, public util::Noncopyable
}
std::size_t getLevels() const override { return _mipMapInfo.size(); }
bool isPrecompressed() const override { return _compressed; }
GLenum getGLFormat() const override { return _format; }

/* BindableTexture implementation */
TexturePtr bindTexture(const std::string& name) const
Expand Down Expand Up @@ -199,6 +200,7 @@ static const std::map<std::string, GLenum> GL_FMT_FOR_FOURCC
{ "DXT1", GL_COMPRESSED_RGBA_S3TC_DXT1_EXT },
{ "DXT3", GL_COMPRESSED_RGBA_S3TC_DXT3_EXT },
{ "DXT5", GL_COMPRESSED_RGBA_S3TC_DXT5_EXT },
{ "ATI2", GL_COMPRESSED_RG_RGTC2 }
};

// Map uncompressed DDS bit depths to GLenum memory layouts
Expand All @@ -219,6 +221,7 @@ DDSImagePtr LoadDDSFromStream(InputStream& stream)
if (!header.isValid())
{
rError() << "Invalid DDS header" << std::endl;
return {};
}

// Extract basic metadata: width, height, format and mipmap count
Expand Down Expand Up @@ -268,12 +271,16 @@ DDSImagePtr LoadDDSFromStream(InputStream& stream)
DDSImagePtr image(new DDSImage(size));

// Set the format of this DDS image
if (GL_FMT_FOR_FOURCC.count(compressionFormat) == 1)
if (GL_FMT_FOR_FOURCC.count(compressionFormat) == 1) {
image->setFormat(GL_FMT_FOR_FOURCC.at(compressionFormat), true);
else if (GL_FMT_FOR_BITDEPTH.count(bitDepth) == 1)
}
else if (GL_FMT_FOR_BITDEPTH.count(bitDepth) == 1) {
image->setFormat(GL_FMT_FOR_BITDEPTH.at(bitDepth), false);
else
}
else {
rError() << "Unknown DDS format (" << compressionFormat << ")" << std::endl;
return {};
}

// Load the mipmaps into the allocated memory
for (std::size_t i = 0; i < mipMapInfo.size(); ++i)
Expand Down

0 comments on commit 36bb357

Please sign in to comment.