Skip to content

Commit

Permalink
First version of Macro#draw rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
jlnr committed Mar 18, 2012
1 parent ec218af commit b85289d
Show file tree
Hide file tree
Showing 2 changed files with 154 additions and 12 deletions.
134 changes: 122 additions & 12 deletions GosuImpl/Graphics/Macro.hpp
Expand Up @@ -13,11 +13,123 @@

class Gosu::Macro : public Gosu::ImageData
{
typedef double Float;

Graphics& graphics;
VertexArrays vertexArrays;
int givenWidth, givenHeight;
int w, h;

void realDraw(double x1, double y1, double x2, double y3) const
Transform findTransformForTarget(Float x1, Float y1, Float x2, Float y2, Float x3, Float y3, Float x4, Float y4) const
{
// Transformation logic follows a discussion on the ImageMagick mailing
// list (on which ImageMagick's perspective_transform.pl is based).

// To draw a macro at an arbitrary position, we solve the following system:

// 0, 0, 1, 0, 0, 0, 0, 0 | x1
// 0, 0, 0, 0, 0, 1, 0, 0 | y1
// w, 0, 1, 0, 0, 0, -x2w, 0 | x2
// 0, 0, 0, w, 0, 1, -y2w, 0 | y2
// 0, h, 1, 0, 0, 0, 0, -x3h | x3
// 0, 0, 0, 0, h, 1, 0, -y3h | y3
// w, h, 1, 0, 0, 0, -x4w, -x4h | x4
// 0, 0, 0, w, h, 1, -y4w, -y4h | y4

// Equivalent:

// 0, 0, 1, 0, 0, 0, 0, 0 | x1
// 0, 0, 0, 0, 0, 1, 0, 0 | y1
// w, 0, 0, 0, 0, 0, -x2w, 0 | x2-x1
// 0, 0, 0, w, 0, 0, -y2w, 0 | y2-y1
// 0, h, 0, 0, 0, 0, 0, -x3h | x3-x1
// 0, 0, 0, 0, h, 0, 0, -y3h | y3-y1
// 0, 0, 0, 0, 0, 0, (x2-x4)w, (x3-x4)h | x1-x2-x3+x4
// 0, 0, 0, 0, 0, 0, (y2-y4)w, (y3-y4)h | y1-y2-y3+y4

// Since this matrix is relatively sparse, we unroll all three solving paths.

static const Transform nullTransform = { 0 };

// Row 7 is useless
if (x2 == x4 && x3 == x4)
return nullTransform;
// Row 8 is useless
if (y2 == y3 && y3 == y4)
return nullTransform;

Float c[8];

// Rows 1, 2
c[2] = x1, c[5] = y1;

// Use row 7 to eliminate the left cell in row 8
// Row8 = Row8 - factor78 * Row7
assert (x2 != x4);
Float factor78 = (y2-y4) / (x2-x4);
Float remCell8 = (y3-y4)*h - (x3-x4)*h * factor78;
Float rightSide8 = (y1-y2-y3+y4) - (x1-x2-x3+x4) * factor78;
assert (remCell8 != 0);
c[7] = rightSide8 / remCell8;

// 0, 0, 1, 0, 0, 0, 0, 0 | x1
// 0, 0, 0, 0, 0, 1, 0, 0 | y1
// w, 0, 0, 0, 0, 0, -x2w, 0 | x2-x1
// 0, 0, 0, w, 0, 0, -y2w, 0 | y2-y1
// 0, h, 0, 0, 0, 0, 0, -x3h | x3-x1
// 0, 0, 0, 0, h, 0, 0, -y3h | y3-y1
// 0, 0, 0, 0, 0, 0, (x2-x4)w, (x3-x4)h | x1-x2-x3+x4
// 0, 0, 0, 0, 0, 0, 0, remCell8 | rightSide8

// Use the remainding value in row 8 to eliminate the right value in row 7.
// Row7 = Row7 - factor87 * Row8
assert (remCell8 != 0);
Float factor87 = (x3-x4)*h / remCell8;
Float remCell7 = (x2-x4)*w;
Float rightSide7 = (x1-x2-x3+x4) - rightSide8 * factor87;
assert (remCell7 != 0);
c[6] = rightSide7 / remCell7;

// 0, 0, 1, 0, 0, 0, 0, 0 | x1
// 0, 0, 0, 0, 0, 1, 0, 0 | y1
// w, 0, 0, 0, 0, 0, -x2w, 0 | x2-x1
// 0, 0, 0, w, 0, 0, -y2w, 0 | y2-y1
// 0, h, 0, 0, 0, 0, 0, -x3h | x3-x1
// 0, 0, 0, 0, h, 0, 0, -y3h | y3-y1
// 0, 0, 0, 0, 0, 0, remCell7, 0 | rightSide7
// 0, 0, 0, 0, 0, 0, 0, remCell8 | rightSide8

// Use the new rows 7 and 8 to calculate c0, c1, c3 & c4.
// Row3 = Row3 - factor73 * Row7
Float factor73 = -x2*w / remCell7;
Float remCell3 = w;
Float rightSide3 = (x2-x1) - rightSide7 * factor73;
c[0] = rightSide3 / remCell3;
// Row4 = Row4 - factor74 * Row7
Float factor74 = -y2*w / remCell7;
Float remCell4 = w;
Float rightSide4 = (y2-y1) - rightSide7 * factor74;
c[3] = rightSide4 / remCell4;
// Row5 = Row5 - factor85 * Row7
Float factor85 = -x3*h / remCell8;
Float remCell5 = h;
Float rightSide5 = (x3-x1) - rightSide8 * factor85;
c[1] = rightSide5 / remCell5;
// Row6 = Row6 - factor86 * Row8
Float factor86 = -y3*h / remCell8;
Float remCell6 = h;
Float rightSide6 = (y3-y1) - rightSide8 * factor86;
c[4] = rightSide6 / remCell6;

Transform result = {
c[0], c[3], 0, c[6],
c[1], c[4], 0, c[7],
0, 0, 1, 0,
c[2], c[5], 0, 1
};
return result;
}

void drawVertexArrays(Float x1, Float y1, Float x2, Float y2, Float x3, Float y3, Float x4, Float y4) const
{
// TODO: Macros should not be split up because they have different transforms! This is insane.
// They should be premultiplied and have the same transform by definition. Then, the transformation
Expand All @@ -27,14 +139,14 @@ class Gosu::Macro : public Gosu::ImageData
glEnable(GL_BLEND);
glMatrixMode(GL_MODELVIEW);

Transform transform =
findTransformForTarget(x1, y1, x2, y2, x3, y3, x4, y4);

for (VertexArrays::const_iterator it = vertexArrays.begin(), end = vertexArrays.end(); it != end; ++it)
{
glPushMatrix();
it->renderState.apply();

glTranslated(x1, y1, 0);
glScaled((x2 - x1) / width(), (y3 - y1) / height(), 1);

glMultMatrixd(&transform[0]);
glInterleavedArrays(GL_T2F_C4UB_V3F, 0, &it->vertices[0]);
glDrawArrays(GL_QUADS, 0, it->vertices.size());
glPopMatrix();
Expand All @@ -44,19 +156,19 @@ class Gosu::Macro : public Gosu::ImageData

public:
Macro(Graphics& graphics, DrawOpQueue& queue, int width, int height)
: graphics(graphics), givenWidth(width), givenHeight(height)
: graphics(graphics), w(width), h(height)
{
queue.compileTo(vertexArrays);
}

int width() const
{
return givenWidth;
return w;
}

int height() const
{
return givenHeight;
return h;
}

void draw(double x1, double y1, Color c1,
Expand All @@ -65,11 +177,9 @@ class Gosu::Macro : public Gosu::ImageData
double x4, double y4, Color c4,
ZPos z, AlphaMode mode) const
{
if (x1 != x3 || x2 != x4 || y1 != y2 || y3 != y4)
throw std::invalid_argument("Macros cannot be rotated yet");
if (c1 != 0xffffffff || c2 != 0xffffffff || c3 != 0xffffffff || c4 != 0xffffffff)
throw std::invalid_argument("Macros cannot be tinted with colors yet");
std::tr1::function<void()> f = std::tr1::bind(&Macro::realDraw, this, x1, y1, x2, y3);
std::tr1::function<void()> f = std::tr1::bind(&Macro::drawVertexArrays, this, x1, y1, x2, y2, x3, y3, x4, y4);
graphics.scheduleGL(f, z);
}

Expand Down
32 changes: 32 additions & 0 deletions feature_tests/record_draw_as_quad.rb
@@ -0,0 +1,32 @@
$LOAD_PATH << '../lib'
require 'gosu/preview'

DEST = [[12, 295], [300, 265], [23, 42], [101, 11]]

class FindMatrix < Gosu::Window
def initialize
super 500, 300

self.caption = "Macro draw_as_quad - hold <tab> to to see Image draw_as_quad"

@image = Gosu::Image.new('/Users/jlnr/Pictures/Internetz/sadsadrobot.jpg')
@macro = record(@image.width, @image.height) { @image.draw 0, 0, 0 }
end

def draw
Gosu::draw_quad\
0, 0, Gosu::Color::WHITE,
width, 0, Gosu::Color::WHITE,
0, height, Gosu::Color::WHITE,
width, height, Gosu::Color::WHITE, 0

args = DEST.map { |p| [p[0], p[1], Gosu::Color::WHITE] }.flatten + [0]
if button_down? Gosu::KbTab then
@image.draw_as_quad *args
else
@macro.draw_as_quad *args
end
end
end

FindMatrix.new.show

0 comments on commit b85289d

Please sign in to comment.