Skip to content

Commit

Permalink
#5785: Adjust TexToolRotateSelected algorithm to respect the texture …
Browse files Browse the repository at this point in the history
…aspect ratio
  • Loading branch information
codereader committed Oct 23, 2021
1 parent 94c045a commit fae8ba0
Show file tree
Hide file tree
Showing 3 changed files with 82 additions and 2 deletions.
2 changes: 1 addition & 1 deletion radiantcore/selection/algorithm/Texturing.cpp
Expand Up @@ -105,7 +105,7 @@ TextureRotator::TextureRotator(const Vector2& pivot, double angle, double textur

void TextureRotator::RotatePatch(IPatch& patch, double angle)
{
RotateNode(std::make_shared<textool::PatchNode>(patch), angle, 1.0);
RotateNode(std::make_shared<textool::PatchNode>(patch), angle, patch.getTextureAspectRatio());
}

void TextureRotator::RotateFace(IFace& face, double angle)
Expand Down
6 changes: 5 additions & 1 deletion radiantcore/selection/textool/TextureToolSelectionSystem.cpp
Expand Up @@ -844,8 +844,12 @@ void TextureToolSelectionSystem::rotateSelectionCmd(const cmd::ArgumentList& arg
return;
}

auto material = GlobalMaterialManager().getMaterial(GlobalTextureToolSceneGraph().getActiveMaterial());
auto texture = material->getEditorImage();
auto aspectRatio = static_cast<float>(texture->getWidth()) / texture->getHeight();

Vector2 pivot{ accumulator.getBounds().origin.x(), accumulator.getBounds().origin.y() };
selection::algorithm::TextureRotator rotator(pivot, angle, 1.0);
selection::algorithm::TextureRotator rotator(pivot, angle, aspectRatio);
foreachSelectedNode(rotator);
}

Expand Down
76 changes: 76 additions & 0 deletions test/TextureTool.cpp
Expand Up @@ -2098,4 +2098,80 @@ TEST_F(TextureToolTest, FlipSingleFaceT)
performFaceFlipTest(1);
}

TEST_F(TextureToolTest, RotateSelectedPreservesPatchTexelScale)
{
auto material = "textures/a_1024x512";
auto worldspawn = GlobalMapModule().findOrInsertWorldspawn();
auto patchNode = algorithm::createPatchFromBounds(worldspawn, AABB(Vector3(4, 50, 60), Vector3(64, 128, 256)), "textures/a_1024x512");
auto patch = Node_getIPatch(patchNode);
patch->fitTexture(1, 1);
Node_setSelected(patchNode, true);

// The texture bounds should be 1x1, since the texture is fitted
auto bounds = algorithm::getTextureSpaceBounds(*patch);

EXPECT_NEAR(bounds.extents.x() * 2, 1, 0.01) << "Patch UV bounds should be 1x1 before rotating";
EXPECT_NEAR(bounds.extents.y() * 2, 1, 0.01) << "Patch UV bounds should be 1x1 before rotating";

render::TextureToolView view;
view.constructFromTextureSpaceBounds(bounds, TEXTOOL_WIDTH, TEXTOOL_HEIGHT);

// Test-select in the middle of the bounds
performPointSelection(Vector2(bounds.origin.x(), bounds.origin.y()), view);

// Item should be selected
EXPECT_EQ(getAllSelectedTextoolNodes().size(), 1) << "Only one patch should be selected";

GlobalCommandSystem().executeCommand("TexToolRotateSelected", cmd::Argument(45));
GlobalCommandSystem().executeCommand("TexToolRotateSelected", cmd::Argument(45));

auto boundsAfter = algorithm::getTextureSpaceBounds(*patch);

EXPECT_NEAR(boundsAfter.extents.x() * 2, 0.5, 0.01) << "Patch UV bounds should be 0.5x2 after rotating";
EXPECT_NEAR(boundsAfter.extents.y() * 2, 2, 0.01) << "Patch UV bounds should be 0.5x2 after rotating";
}

TEST_F(TextureToolTest, RotateSelectedPreservesFaceTexelScale)
{
auto material = "textures/a_1024x512";
auto worldspawn = GlobalMapModule().findOrInsertWorldspawn();
auto test1024x512Node = algorithm::createCuboidBrush(worldspawn, AABB({ 128, 32, 16 }, { 128, 32, 16 }), material);
algorithm::foreachFace(*Node_getIBrush(test1024x512Node), [](IFace& face)
{
// Shift all unrelated faces
if (math::isNear(face.getPlane3().normal(), { 0, 0, 1 }, 0.01)) return;

face.fitTexture(1, 1);
face.shiftTexdef(3, 3);
});
Node_getIBrush(test1024x512Node)->evaluateBRep();
Node_setSelected(test1024x512Node, true);

auto& face = *algorithm::findBrushFaceWithNormal(Node_getIBrush(test1024x512Node), { 0, 0, 1 });
face.fitTexture(1, 1);

auto bounds = algorithm::getTextureSpaceBounds(face);

EXPECT_NEAR(bounds.extents.x() * 2, 1, 0.01) << "Face UV bounds should be 1x1 before rotating";
EXPECT_NEAR(bounds.extents.y() * 2, 1, 0.01) << "Face UV bounds should be 1x1 before rotating";

render::TextureToolView view;
view.constructFromTextureSpaceBounds(bounds, TEXTOOL_WIDTH, TEXTOOL_HEIGHT);

// Test-select in the middle of the bounds
performPointSelection(Vector2(bounds.origin.x(), bounds.origin.y()), view);

// Item should be selected
EXPECT_EQ(getAllSelectedTextoolNodes().size(), 1) << "Only one face should be selected";

GlobalCommandSystem().executeCommand("TexToolRotateSelected", cmd::Argument(45));
GlobalCommandSystem().executeCommand("TexToolRotateSelected", cmd::Argument(45));

Node_getIBrush(test1024x512Node)->evaluateBRep();
auto boundsAfter = algorithm::getTextureSpaceBounds(face);

EXPECT_NEAR(boundsAfter.extents.x() * 2, 0.5, 0.01) << "Face UV bounds should be 0.5x2 after rotating";
EXPECT_NEAR(boundsAfter.extents.y() * 2, 2, 0.01) << "Face UV bounds should be 0.5x2 after rotating";
}

}

0 comments on commit fae8ba0

Please sign in to comment.