1
\$\begingroup\$

So I'm trying to create a zoom function for my 2D camera.

The best result I've got so far is:

zooming

But something still feels off, and I just can't seem to pinpoint it.

My code currently, which simply takes the substraction between before and after zoom:

void Zoom(float value, float mx, float my, float w, float h) { float bmwx = (w - (w - mx)) * this->zoom, bmwy = (h - (h - my)) * this->zoom; // before if (value < 0.0f) this->zoom *= 0.9f; if (value > 0.0f) this->zoom *= 1.1f; float amwx = (w - (w - mx)) * this->zoom, amwy = (h - (h - my)) * this->zoom; // after this->center.x += amwx - bmwx; this->center.y += amwy - bmwy; } 

My other function, for matrix construction:

void Update(float mx, float my, float w, float h) { float hw = w * 0.5f, hh = h * 0.5f; float left = -hw + this->center.x; float right = hw + this->center.x; float top = -hh + this->center.y; float bottom = hh + this->center.y; this->ortho.InitOrthographic(drx::gfx::ogl::n, drx::gfx::ogl::f, left, right, top, bottom); this->mZoom.LoadIdentity(); this->mZoom.Scale(this->zoom, this->zoom, this->zoom); this->matrix = this->ortho * this->mZoom; } 

Update, to current code (Update: which also fails if I move the central point):

void Zoom(float value, float mx, float my, float w, float h) { this->os.x += mx * this->zoom; this->os.y += my * this->zoom; if (value < 0.0f) this->zoom *= 0.75f; if (value > 0.0f) this->zoom *= 1.25f; this->os.x -= mx * this->zoom; this->os.y -= my * this->zoom; } void Update(float mx, float my, float w, float h) { float hw = w * 0.5f, hh = h * 0.5f; float left = -hw + this->center.x; float right = hw + this->center.x; float top = -hh + this->center.y; float bottom = hh + this->center.y; this->ortho.InitOrthographic(drx::gfx::ogl::n, drx::gfx::ogl::f, left, right, top, bottom); this->mZoom.LoadIdentity(); this->mZoom.Scale(this->zoom, this->zoom, this->zoom); this->mZoom.Translate(this->os.x, this->os.y, 0.0f); this->matrix = this->ortho * this->mZoom; } 

more zooming

\$\endgroup\$
4
  • \$\begingroup\$ A lot of this calculation cancels out for example in (w - (w - mx)), w is subtracted from itself so this could be replaced with just mx the same thing happens four times in your calculation. \$\endgroup\$ Commented Jun 20, 2024 at 5:57
  • \$\begingroup\$ @DavidT Yeah, I came to the same conclusion, updated the post with current code. Which still calculates the before and after, but instead of making changes to the center, I added a translation to the matrix calculation for offset. \$\endgroup\$ Commented Jun 20, 2024 at 6:18
  • \$\begingroup\$ Does the zoom update a single canvas, or every object's position and scaling? \$\endgroup\$ Commented Jun 20, 2024 at 10:47
  • \$\begingroup\$ @liggiorgio It provides a matrix, which with objects will position and scale. \$\endgroup\$ Commented Jun 20, 2024 at 11:00

2 Answers 2

3
\$\begingroup\$

Answering my own question.

As it turns out, all I was missing, was a screen to world space conversion.

void Zoom(float value, float mx, float my, float w, float h) { float x = (2.0f * mx) / w - 1.0f; float y = 1.0f - (2.0f * my) / h; drx::util::V4F mndc = { x, y, 0.0f, 1.0f }; drx::util::V4F mouse = this->matrix.Inve() * mndc; this->os.x += mouse.x * this->zoom; this->os.y += mouse.y * this->zoom; this->zoom *= 1.0f + value; this->os.x -= mouse.x * this->zoom; this->os.y -= mouse.y * this->zoom; } 

And the result is quite exact: workingzoom

\$\endgroup\$
0
\$\begingroup\$

Sorry I am not directly answering your question but I implemented this in one of my app. It is not in c++ but in lua. Some code:

-- current zoom local currzoom = 0.5 local zoomstart = currzoom canvaslayer:setScale(currzoom) -- thank you very much hgy29 & keszegh :-) local pointerstartx, pointerstarty = 0, 0 local canvasposx, canvasposy = 0, 0 local offsetx, offsety = 0, 0 stage:addEventListener(Event.MOUSE_DOWN, function(e) pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry) e:stopPropagation() end) stage:addEventListener(Event.MOUSE_MOVE, function(e) canvasposx, canvasposy = canvaslayer:getPosition() offsetx = e.rx - pointerstartx offsety = e.ry - pointerstarty canvasposx += offsetx canvasposy += offsety canvaslayer:setPosition(canvasposx, canvasposy) pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry) e:stopPropagation() end) stage:addEventListener(Event.MOUSE_WHEEL, function(e) -- e.wheel = +- 120 zoomstart = currzoom pointerstartx, pointerstarty = stage:globalToLocal(e.rx, e.ry) canvasposx, canvasposy = canvaslayer:getPosition() -- local viewportZoom=math.clamp(self.zoomStart*2^((self.panStart.ry-newY)/100),_MIN_ZOOM,_MAX_ZOOM) currzoom = zoomstart + e.wheel/120/4 if currzoom <= 0.2 then currzoom = 0.2 end if currzoom >= 2 then currzoom = 2 end canvaslayer:setScale(currzoom) local viewportx = canvasposx + (pointerstartx - canvasposx) * (1 - currzoom/zoomstart) local viewporty = canvasposy + (pointerstarty - canvasposy) * (1 - currzoom/zoomstart) canvaslayer:setPosition(viewportx, viewporty) e:stopPropagation() end) 

And the link to the example https://wiki.gideros.rocks/index.php/Ftf_snippets#ZOOM_SPRITE_TO_MOUSE_POSITION_.40hgy29.2C_.40keszegh

Hope this could somehow help you!

\$\endgroup\$
3
  • \$\begingroup\$ I tried scissoring your code for the zooming part, but couldn't get the desired result. \$\endgroup\$ Commented Jun 20, 2024 at 6:20
  • \$\begingroup\$ Can you describe how the result differed from what you desired? The more specific you can be, the better it guides answers in useful directions. \$\endgroup\$ Commented Jun 20, 2024 at 12:27
  • \$\begingroup\$ @DMGregory For me it zoomed towards 0, 0, but that could be that I couldn't understand the variables correctly. \$\endgroup\$ Commented Jun 20, 2024 at 16:44

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.