Skip to content

Commit

Permalink
#5231: Refactor GenericTextureDefinition into a simple struct. It can…
Browse files Browse the repository at this point in the history
… be used to transfer data from and to the Surface Inspector, rather than the inspector doing the math on its own this is now handled by the Face.
  • Loading branch information
codereader committed Jun 14, 2020
1 parent a3c277a commit fb5795e
Show file tree
Hide file tree
Showing 15 changed files with 193 additions and 145 deletions.
22 changes: 22 additions & 0 deletions include/ibrush.h
Expand Up @@ -84,6 +84,20 @@ struct WindingVertex
// each of which holding information about a single corner point.
typedef std::vector<WindingVertex> IWinding;

/**
* greebo: The texture definition structure containing the scale,
* rotation and shift values of an applied texture.
* At some places this is referred to as "fake" texture coordinates.
* This is not what is actually saved to the .map file, but it makes
* texture manipulations in the Surface Inspector much more human-readable.
*/
struct ShiftScaleRotation
{
double shift[2];
double rotate;
double scale[2];
};

// Interface for a face plane
class IFace
{
Expand Down Expand Up @@ -140,6 +154,14 @@ class IFace
virtual Matrix4 getProjectionMatrix() = 0;

virtual void setProjectionMatrix(const Matrix4& projection) = 0;

/**
* Calculates and returns the texture definition as shift/scale/rotate.
* This is not what is actually saved to the .map file, but it makes
* texture manipulations in the Surface Inspector much more human-readable.
*/
virtual ShiftScaleRotation getShiftScaleRotation() = 0;
virtual void setShiftScaleRotation(const ShiftScaleRotation& scr) = 0;
};

// Plane classification info used by splitting and CSG algorithms
Expand Down
7 changes: 7 additions & 0 deletions include/iselection.h
Expand Up @@ -273,6 +273,13 @@ class SelectionSystem :
*/
virtual void foreachPatch(const std::function<void(IPatch&)>& functor) = 0;

// Returns the number of currently selected faces
virtual std::size_t getSelectedFaceCount() = 0;

// Returns the reference to the singly selected face
// Calling this will cause an cmd::ExecutionFailure if getSelectedFaceCount() != 1
virtual IFace& getSingleSelectedFace() = 0;

/// Signal emitted when the selection is changed
virtual SelectionChangedSignal signal_selectionChanged() const = 0;

Expand Down
42 changes: 8 additions & 34 deletions include/itexdef.h
@@ -1,44 +1,18 @@
/*
Copyright (C) 2001-2006, William Joseph.
All Rights Reserved.
#pragma once

This file is part of GtkRadiant.
namespace brush
{

GtkRadiant is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
GtkRadiant is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GtkRadiant; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/

#if !defined (INCLUDED_ITEXDEF_H)
#define INCLUDED_ITEXDEF_H

class Matrix4;

/* greebo: The texture definition structure, containing the scale,
/**
* greebo: The texture definition structure containing the scale,
* rotation and shift values of an applied texture.
* At some places this is referred to as "fake" texture coordinates.
*/
class GenericTextureDefinition {
public:
struct ShiftScaleRotation
{
double _shift[2];
double _rotate;
double _scale[2];

virtual ~GenericTextureDefinition() {}

// Test the texture definition for insanely large values
virtual bool isSane() const = 0;
virtual Matrix4 getTransform(double width, double height) const = 0;
};

#endif
}
75 changes: 31 additions & 44 deletions radiant/ui/surfaceinspector/SurfaceInspector.cpp
Expand Up @@ -471,68 +471,55 @@ void SurfaceInspector::emitShader()

void SurfaceInspector::emitTexDef()
{
TexDef shiftScaleRotate;
ShiftScaleRotation shiftScaleRotate;

shiftScaleRotate._shift[0] = string::convert<float>(_manipulators[HSHIFT].value->GetValue().ToStdString());
shiftScaleRotate._shift[1] = string::convert<float>(_manipulators[VSHIFT].value->GetValue().ToStdString());
shiftScaleRotate._scale[0] = string::convert<float>(_manipulators[HSCALE].value->GetValue().ToStdString());
shiftScaleRotate._scale[1] = string::convert<float>(_manipulators[VSCALE].value->GetValue().ToStdString());
shiftScaleRotate._rotate = string::convert<float>(_manipulators[ROTATION].value->GetValue().ToStdString());
shiftScaleRotate.shift[0] = string::convert<float>(_manipulators[HSHIFT].value->GetValue().ToStdString());
shiftScaleRotate.shift[1] = string::convert<float>(_manipulators[VSHIFT].value->GetValue().ToStdString());
shiftScaleRotate.scale[0] = string::convert<float>(_manipulators[HSCALE].value->GetValue().ToStdString());
shiftScaleRotate.scale[1] = string::convert<float>(_manipulators[VSCALE].value->GetValue().ToStdString());
shiftScaleRotate.rotate = string::convert<float>(_manipulators[ROTATION].value->GetValue().ToStdString());

// Apply it to the selection
selection::algorithm::applyTexDefToFaces(shiftScaleRotate);
UndoableCommand undo("textureDefinitionSetSelected");

GlobalSelectionSystem().foreachFace([&](IFace& face)
{
face.setShiftScaleRotation(shiftScaleRotate);
});

SceneChangeNotify();

// Update the Texture Tools
radiant::TextureChangedMessage::Send();
// Update the TexTool instance as well
ui::TexTool::Instance().draw();
}

void SurfaceInspector::updateTexDef()
{
try
{
Face& face = selection::algorithm::getLastSelectedFace();

if (GlobalSelectionSystem().getSelectedFaceCount() == 1)
{
// This call should return a meaningful value, since we only get here when only
// a single face is selected
TextureProjection curProjection;
face.GetTexdef(curProjection);
auto& face = GlobalSelectionSystem().getSingleSelectedFace();

// Multiply the texture dimensions to the projection matrix such that
// the shift/scale/rotation represent pixel values within the image.
Vector2 shaderDims(face.getFaceShader().getWidth(), face.getFaceShader().getHeight());

TextureMatrix bpTexDef= curProjection.matrix;
bpTexDef.applyShaderDimensions(static_cast<std::size_t>(shaderDims[0]), static_cast<std::size_t>(shaderDims[1]));

// Calculate the "fake" texture properties (shift/scale/rotation)
TexDef texdef = bpTexDef.getFakeTexCoords();

if (shaderDims != Vector2(0,0))
{
// normalize again to hide the ridiculously high scale values that get created when using texlock
texdef._shift[0] = float_mod(texdef._shift[0], shaderDims[0]);
texdef._shift[1] = float_mod(texdef._shift[1], shaderDims[1]);
}
auto texdef = face.getShiftScaleRotation();

// Snap the floating point variables to the max resolution to avoid things like "1.45e-14"
texdef._shift[0] = float_snapped(texdef._shift[0], MAX_FLOAT_RESOLUTION);
texdef._shift[1] = float_snapped(texdef._shift[1], MAX_FLOAT_RESOLUTION);
texdef._scale[0] = float_snapped(texdef._scale[0], MAX_FLOAT_RESOLUTION);
texdef._scale[1] = float_snapped(texdef._scale[1], MAX_FLOAT_RESOLUTION);
texdef._rotate = float_snapped(texdef._rotate, MAX_FLOAT_RESOLUTION);
texdef.shift[0] = float_snapped(texdef.shift[0], MAX_FLOAT_RESOLUTION);
texdef.shift[1] = float_snapped(texdef.shift[1], MAX_FLOAT_RESOLUTION);
texdef.scale[0] = float_snapped(texdef.scale[0], MAX_FLOAT_RESOLUTION);
texdef.scale[1] = float_snapped(texdef.scale[1], MAX_FLOAT_RESOLUTION);
texdef.rotate = float_snapped(texdef.rotate, MAX_FLOAT_RESOLUTION);

// Load the values into the widgets
_manipulators[HSHIFT].value->SetValue(string::to_string(texdef._shift[0]));
_manipulators[VSHIFT].value->SetValue(string::to_string(texdef._shift[1]));
_manipulators[HSHIFT].value->SetValue(string::to_string(texdef.shift[0]));
_manipulators[VSHIFT].value->SetValue(string::to_string(texdef.shift[1]));

_manipulators[HSCALE].value->SetValue(string::to_string(texdef._scale[0]));
_manipulators[VSCALE].value->SetValue(string::to_string(texdef._scale[1]));
_manipulators[HSCALE].value->SetValue(string::to_string(texdef.scale[0]));
_manipulators[VSCALE].value->SetValue(string::to_string(texdef.scale[1]));

_manipulators[ROTATION].value->SetValue(string::to_string(texdef._rotate));
}
catch (selection::InvalidSelectionException&)
{
rError() << "Can't update texdef, since more than one face is selected." << std::endl;
_manipulators[ROTATION].value->SetValue(string::to_string(texdef.rotate));
}
}

Expand Down Expand Up @@ -569,7 +556,7 @@ void SurfaceInspector::doUpdate()
valueSensitivity = (selectionInfo.patchCount == 0 &&
selectionInfo.totalCount > 0 &&
selectionInfo.entityCount == 0 &&
selection::algorithm::selectedFaceCount() == 1);
GlobalSelectionSystem().getSelectedFaceCount() == 1);

_manipulators[HSHIFT].value->Enable(valueSensitivity);
_manipulators[VSHIFT].value->Enable(valueSensitivity);
Expand Down
28 changes: 28 additions & 0 deletions radiantcore/brush/Face.cpp
Expand Up @@ -447,6 +447,34 @@ void Face::setTexdef(const TexDef& texDef)
SetTexdef(projection);
}

ShiftScaleRotation Face::getShiftScaleRotation()
{
TextureProjection curProjection = _texdef;

// Multiply the texture dimensions to the projection matrix such that
// the shift/scale/rotation represent pixel values within the image.
Vector2 shaderDims(_shader.getWidth(), _shader.getHeight());

TextureMatrix bpTexDef = curProjection.matrix;
bpTexDef.applyShaderDimensions(static_cast<std::size_t>(shaderDims[0]), static_cast<std::size_t>(shaderDims[1]));

// Calculate the "fake" texture properties (shift/scale/rotation)
TexDef texdef = bpTexDef.getFakeTexCoords();

if (shaderDims != Vector2(0, 0))
{
// normalize again to hide the ridiculously high scale values that get created when using texlock
texdef.normalise(shaderDims[0], shaderDims[1]);
}

return texdef.getShiftScaleRotation();
}

void Face::setShiftScaleRotation(const ShiftScaleRotation& scr)
{
setTexdef(TexDef::CreateFromShiftScaleRotation(scr));
}

void Face::applyShaderFromFace(const Face& other)
{
// Retrieve the textureprojection from the source face
Expand Down
3 changes: 3 additions & 0 deletions radiantcore/brush/Face.h
Expand Up @@ -144,6 +144,9 @@ class Face :
// The incoming values are measured in pixels and will be scaled internally.
void setTexdef(const TexDef& texDef);

ShiftScaleRotation getShiftScaleRotation() override;
void setShiftScaleRotation(const ShiftScaleRotation& scr) override;

/**
* greebo: Copies the shader (texdef) from the other face,
* and attempts to move the texture such that the transition
Expand Down
63 changes: 51 additions & 12 deletions radiantcore/brush/TexDef.cpp
Expand Up @@ -5,7 +5,8 @@
#include "math/Vector2.h"

// Constructor
TexDef::TexDef() {
TexDef::TexDef()
{
_shift[0] = 0;
_shift[1] = 0;
_rotate = 0;
Expand All @@ -14,55 +15,65 @@ TexDef::TexDef() {
}

// Constructs a TexDef out of the given transformation matrix plus width/height
TexDef::TexDef(double width, double height, const Matrix4& transform) {
TexDef::TexDef(double width, double height, const Matrix4& transform)
{
_scale[0] = static_cast<double>((1.0 / Vector2(transform[0], transform[4]).getLength()) / width);
_scale[1] = static_cast<double>((1.0 / Vector2(transform[1], transform[5]).getLength()) / height);

_rotate = static_cast<double>(-radians_to_degrees(arctangent_yx(-transform[4], transform[0])));

if (_rotate == -180.0f) {
if (_rotate == -180.0f)
{
_rotate = 180.0f;
}

_shift[0] = transform[12] * width;
_shift[1] = transform[13] * height;

// If the 2d cross-product of the x and y axes is positive, one of the axes has a negative scale.
if (Vector2(transform[0], transform[4]).crossProduct(Vector2(transform[1], transform[5])) > 0) {
if (_rotate >= 180.0f) {
if (Vector2(transform[0], transform[4]).crossProduct(Vector2(transform[1], transform[5])) > 0)
{
if (_rotate >= 180.0f)
{
_rotate -= 180.0f;
_scale[0] = -_scale[0];
}
else {
else
{
_scale[1] = -_scale[1];
}
}
}

void TexDef::shift(double s, double t) {
void TexDef::shift(double s, double t)
{
_shift[0] += s;
_shift[1] += t;
}

void TexDef::scale(double s, double t) {
void TexDef::scale(double s, double t)
{
_scale[0] += s;
_scale[1] += t;
}

void TexDef::rotate(double angle) {
void TexDef::rotate(double angle)
{
_rotate += angle;
_rotate = static_cast<double>(float_to_integer(_rotate) % 360);
}

// Checks the TexDef for insanely large values
bool TexDef::isSane() const {
bool TexDef::isSane() const
{
return fabs(_shift[0]) < (1 << 16)
&& fabs(_shift[1]) < (1 << 16);
}

// All texture-projection translation (shift) values are congruent modulo the dimensions of the texture.
// This function normalises shift values to the smallest positive congruent values.
void TexDef::normalise(double width, double height) {
void TexDef::normalise(double width, double height)
{
// it may be useful to also normalise the rotation here, if this function is used elsewhere.
_shift[0] = float_mod(_shift[0], width);
_shift[1] = float_mod(_shift[1], height);
Expand All @@ -72,7 +83,8 @@ void TexDef::normalise(double width, double height) {
* Transforms constructed from quake's texdef format
* are (-shift)*(1/scale)*(-rotate) with x translation sign flipped.
* This would really make more sense if it was inverseof(shift*rotate*scale).. oh well.*/
Matrix4 TexDef::getTransform(double width, double height) const {
Matrix4 TexDef::getTransform(double width, double height) const
{
Matrix4 transform;
double inverse_scale[2];

Expand All @@ -94,3 +106,30 @@ Matrix4 TexDef::getTransform(double width, double height) const {

return transform;
}

ShiftScaleRotation TexDef::getShiftScaleRotation() const
{
ShiftScaleRotation result;

result.shift[0] = _shift[0];
result.shift[1] = _shift[1];
result.rotate = _rotate;
result.scale[0] = _scale[0];
result.scale[1] = _scale[1];

return result;
}

TexDef TexDef::CreateFromShiftScaleRotation(const ShiftScaleRotation& scr)
{
TexDef texDef;

texDef._shift[0] = scr.shift[0];
texDef._shift[1] = scr.shift[1];
texDef._rotate = scr.rotate;
texDef._scale[0] = scr.scale[0];
texDef._scale[1] = scr.scale[1];

return texDef;
}

0 comments on commit fb5795e

Please sign in to comment.