Skip to content

Commit

Permalink
Initial implementation of single-axis texture fitting
Browse files Browse the repository at this point in the history
If the button is toggled, a value of -1 will be passed to the implementing
code, indicating that a particular axis should not be fitted. Currently the
result isn't quite correct: rather than preserving aspect ratio, it is
preserving the *current* texture transformation for the non-fitted axis (i.e.
it doesn't touch the shift/scale values for the non-fitted axis at all).
  • Loading branch information
Matthew Mott committed Jul 22, 2021
1 parent 996d02d commit 31d1c02
Show file tree
Hide file tree
Showing 3 changed files with 37 additions and 21 deletions.
33 changes: 21 additions & 12 deletions radiant/ui/surfaceinspector/SurfaceInspector.cpp
Expand Up @@ -161,9 +161,10 @@ void SurfaceInspector::connectEvents()
{
// Be sure to connect these signals BEFORE the buttons are connected
// to the events, so that the doUpdate() call gets invoked after the actual event has been fired.
_fitTexture.fitButton->Bind(wxEVT_BUTTON, [=](wxCommandEvent&) { onFit(); });
_fitTexture.fitButton->Bind(wxEVT_BUTTON,
[=](wxCommandEvent&) { onFit(Axis::BOTH); });

_flipTexture.flipX->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this);
_flipTexture.flipX->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this);
_flipTexture.flipY->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this);
_alignTexture.top->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this);
_alignTexture.bottom->Connect(wxEVT_BUTTON, wxCommandEventHandler(SurfaceInspector::onUpdateAfterButtonClick), NULL, this);
Expand Down Expand Up @@ -217,7 +218,7 @@ void SurfaceInspector::keyChanged()
_callbackActive = false;
}

wxSpinCtrlDouble* SurfaceInspector::makeFitSpinBox()
wxSpinCtrlDouble* SurfaceInspector::makeFitSpinBox(Axis axis)
{
wxSpinCtrlDouble* box = new wxSpinCtrlDouble(this, wxID_ANY);

Expand All @@ -229,7 +230,7 @@ wxSpinCtrlDouble* SurfaceInspector::makeFitSpinBox()
box->SetDigits(2);

// Perform a fit operation when the value changes
box->Bind(wxEVT_SPINCTRLDOUBLE, [=](wxSpinDoubleEvent&) { onFit(); });
box->Bind(wxEVT_SPINCTRLDOUBLE, [=](wxSpinDoubleEvent&) { onFit(axis); });

return box;
}
Expand All @@ -240,13 +241,13 @@ wxBoxSizer* SurfaceInspector::createFitTextureRow()

// Create widgets from left to right
_fitTexture.label = new wxStaticText(this, wxID_ANY, _(LABEL_FIT_TEXTURE));
_fitTexture.width = makeFitSpinBox();
_fitTexture.width = makeFitSpinBox(Axis::X);
_fitTexture.width->SetToolTip(
"Number of whole texture images to fit horizontally. Use the spin "
"buttons to change the value and apply the result immediately."
);
_fitTexture.x = new wxStaticText(this, wxID_ANY, "x");
_fitTexture.height = makeFitSpinBox();
_fitTexture.height = makeFitSpinBox(Axis::Y);
_fitTexture.height->SetToolTip(
"Number of whole texture images to fit vertically. Use the spin "
"buttons to change the value and apply the result immediately."
Expand Down Expand Up @@ -615,26 +616,34 @@ void SurfaceInspector::doUpdate()
ui::PatchInspector::Instance().queueUpdate();
}

void SurfaceInspector::fitTexture()
void SurfaceInspector::fitTexture(Axis axis)
{
// If the preserve aspect button is disabled, we always fit on both axes
if (!_fitTexture.preserveAspectButton->GetValue())
axis = Axis::BOTH;

double repeatX = _fitTexture.width->GetValue();
double repeatY = _fitTexture.height->GetValue();

if (repeatX > 0.0 && repeatY > 0.0)
{
GlobalCommandSystem().executeCommand("FitTexture", repeatX, repeatY);
}
// 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));
}
else
{
// Invalid repeatX && repeatY values
wxutil::Messagebox::ShowError(_("Both fit values must be > 0."));
}
}

void SurfaceInspector::onFit()
void SurfaceInspector::onFit(Axis axis)
{
// Call the according member method
fitTexture();
// Fit the texture then update our widget values with the new transform
fitTexture(axis);
doUpdate();
}

Expand Down
11 changes: 5 additions & 6 deletions radiant/ui/surfaceinspector/SurfaceInspector.h
Expand Up @@ -146,7 +146,6 @@ class SurfaceInspector :

// Widget construction
void populateWindow();
wxSpinCtrlDouble* makeFitSpinBox();
wxBoxSizer* createFitTextureRow();

// Connect IEvents to the widgets
Expand All @@ -161,15 +160,15 @@ class SurfaceInspector :
// Applies the entered shader to the current selection
void emitShader();

// Executes the fit command for the selection
void fitTexture();
// Fit texture on one or both axes
enum class Axis { X, Y, BOTH };
wxSpinCtrlDouble* makeFitSpinBox(Axis axis);
void fitTexture(Axis axis);
void onFit(Axis axis);

// The callback when the "select shader" button is pressed, opens the ShaderChooser dialog
void onShaderSelect(wxCommandEvent& ev);

// The callback for the Fit Texture button
void onFit();

// If any of the control button get clicked, an update is performed
void onUpdateAfterButtonClick(wxCommandEvent& ev);

Expand Down
14 changes: 11 additions & 3 deletions radiantcore/brush/TextureProjection.cpp
Expand Up @@ -169,10 +169,13 @@ void TextureProjection::transformLocked(std::size_t width, std::size_t height, c

setTransform((float)width, (float)height, stTransformed2stOriginal);
normalise((float)width, (float)height);
}
}

// Fits a texture to a brush face
void TextureProjection::fitTexture(std::size_t width, std::size_t height, const Vector3& normal, const Winding& w, float s_repeat, float t_repeat) {
void TextureProjection::fitTexture(std::size_t width, std::size_t height,
const Vector3& normal, const Winding& w,
float s_repeat, float t_repeat)
{
if (w.size() < 3) {
return;
}
Expand All @@ -198,7 +201,12 @@ void TextureProjection::fitTexture(std::size_t width, std::size_t height, const
bounds.extents.z() = 1;

// the bounds of a perfectly fitted texture transform
AABB perfect(Vector3(s_repeat * 0.5, t_repeat * 0.5, 0), Vector3(s_repeat * 0.5, t_repeat * 0.5, 1));
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));

// the difference between the current texture transform and the perfectly fitted transform
Matrix4 diffMatrix = Matrix4::getTranslation(bounds.origin - perfect.origin);
Expand Down

0 comments on commit 31d1c02

Please sign in to comment.