# World to local transforms

**URL:** <https://forum.defold.com/t/world-to-local-transforms/55513>\
**Category:** Questions\
**Created:** [June 20, 2019, 6:20pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513 "2019-06-20T18:20:03Z")\
**Posts on this page:** 15\
**Page:** 1

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [June 20, 2019, 6:20pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/1 "2019-06-20T18:20:03Z")

</div>

Anyone here know their way around matrix math? I am trying to do a world-to-local coordinate transform for game objects, and can’t for the life of me figure out what I’m doing wrong. It works just fine with translation and rotation or translation and scale, but not with rotation and scale together. I’ve tried a couple different methods, tried changing the order of translation/rotation/scale, etc, but it always ends up the same.

I have a little demo/test project here: [World-to-Local Test.zip](https://forum.defold.com/uploads/default/original/3X/a/9/a9f374104999bc432b0a39609fc660118c3994d0.zip) (27.6 KB)

When you click, it converts the world position of the mouse to a local position for the “child” game object, and moves the child there. You can see that a translation and non-uniform scale on “parent” work just fine, but if you add some rotation to “parent”, it breaks. (But if you keep the rotation and reset the scale to 1, 1, 1, it also works fine.)

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [June 20, 2019, 8:49pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/2 "2019-06-20T20:49:06Z")

</div>

Well, this time it wasn’t actually my fault! The problem is that go.get\_world\_rotation() does not work for objects that are scaled.

> [@Go.get\_world\_rotation() incorrect on scaled game objects (Issue-3463)](https://forum.defold.com/t/go-get-world-rotation-incorrect-on-scaled-game-objects/15154/6):
>
> I just ran into this bug myself. It’s a deal-breaker for doing world-to-local transforms (and vice versa) with game objects. Here’s a minimal example demonstrating it: [Defold Get World Rotation Bug.zip](https://forum.defold.com/uploads/default/original/3X/c/2/c2a0e48bad01b1b32d1b30a9dad5d17ec38736c8.zip) (2.6 KB) And even a gif! [defold\_get\_world\_rotation\_bug] The line is drawn with a vector rotated with the normalized result of go.get\_world\_rotation(), so you can see that you get a wildly different rotation depending on the scale of the object. (If you don’t normalize the quaternion the vect…

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [June 21, 2019, 11:25am UTC](https://forum.defold.com/t/world-to-local-transforms/55513/3 "2019-06-21T11:25:39Z")

</div>

> [@Go.get\_world\_rotation() incorrect on scaled game objects (Issue-3463)](https://forum.defold.com/t/go-get-world-rotation-incorrect-on-scaled-game-objects-issue-3463/15154/8):
>
> I would actually be happier if we could get the entire world transform matrix of an object. It’s much more useful if you want to transform between coordinate spaces.

This.

Right now, doing world-to-local and local-to-world transforms with game objects is impossible (without knowing/assuming the full GO hierarchy and composing the transforms down the tree). I think the simplest way to fix this is to add a `go.get_world_transform()` function that returns the complete transform matrix, since I am only using `go.get_world_rotation()` to reconstruct that matrix anyway.

---

<div class="post-metadata">

**Author:** ![britzl](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/britzl/32/23_2.png) [@britzl](https://forum.defold.com/u/britzl)\
**Post date:** [June 22, 2019, 7:53am UTC](https://forum.defold.com/t/world-to-local-transforms/55513/4 "2019-06-22T07:53:55Z")

</div>

Well discuss this on Monday.

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [June 27, 2019, 11:23am UTC](https://forum.defold.com/t/world-to-local-transforms/55513/5 "2019-06-27T11:23:44Z")

</div>

@britzl Did you guys get a chance to look into this?

If it matters, the things I want it for (currently):

- Rendercam - Most of the camera functions: Follow, View Bounds, probably Shake & Recoil, should take place in world space. Right now these all break if you put a camera on a transformed parent.

- A custom level editor I’m working on - I need to transform positions for editing child objects.

---

<div class="post-metadata">

**Author:** ![britzl](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/britzl/32/23_2.png) [@britzl](https://forum.defold.com/u/britzl)\
**Post date:** [June 27, 2019, 11:55am UTC](https://forum.defold.com/t/world-to-local-transforms/55513/6 "2019-06-27T11:55:08Z")

</div>

> [@ross.grams](#):
>
> Did you guys get a chance to look into this?

Yes, it’s in our current sprint to add a function to get the game object transform.

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [June 27, 2019, 12:43pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/7 "2019-06-27T12:43:38Z")

</div>

Awesome! Thanks!

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [July 14, 2019, 5:48pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/8 "2019-07-14T17:48:37Z")

</div>

OK, now that go.get\_world\_rotation always gives the result you expect (thanks @jhonny.goransson!), my code just works! I spent a bit cleaning things up and testing and put together a little module, in case anyone else needs it.

It will be very nice and probably quite a bit faster when a go.get\_world\_transform function is added, but for now this will get the job done.

```lua

local M = {}

-- NOTE: All of these functions modify the input vector, so if you want to
-- keep the original vector, input a copy of it to the transform function.

-- Convenience function to convert to vector4 and back while multiplying.
function M.v3_by_matrix(v3, m)
	local v4 = m * vmath.vector4(v3.x, v3.y, v3.z, 1)
	v3.x, v3.y, v3.z = v4.x, v4.y, v4.z
	return v3
end

-- If you want the world-to-local matrix, just invert the result with vmath.inv().
function M.get_to_world_matrix(wp, wr, ws)
	-- Make matrix4 from rotation quaternion.
	local m = vmath.matrix4_from_quat(wr)

	-- Multiply axes by world scale.
	m.m00, m.m01, m.m02 = m.m00 * ws.x, m.m01 * ws.y, m.m02 * ws.z
	m.m10, m.m11, m.m12 = m.m10 * ws.x, m.m11 * ws.y, m.m12 * ws.z
	m.m20, m.m21, m.m22 = m.m20 * ws.x, m.m21 * ws.y, m.m22 * ws.z

	-- Plug in world position.
	m.m03, m.m13, m.m23 = wp.x, wp.y, wp.z

	return m
end

function M.local_to_world(v3, wp, wr, ws)
	-- Scale.
	v3.x = v3.x * ws.x; v3.y = v3.y * ws.y; v3.z = v3.z * ws.z
	-- Rotation.
	v3 = vmath.rotate(wr, v3)
	-- Translation.
	v3.x = v3.x + wp.x; v3.y = v3.y + wp.y; v3.z = v3.z + wp.z
	return v3
end

function M.world_to_local(v3, wp, wr, ws)
	-- Translation inverse.
	v3.x = v3.x - wp.x; v3.y = v3.y - wp.y; v3.z = v3.z - wp.z
	-- Rotation inverse.
	local r = vmath.conj(wr)
	v3 = vmath.rotate(r, v3)
	-- Scale inverse.
	v3.x = v3.x / ws.x; v3.y = v3.y / ws.y; v3.z = v3.z / ws.z
	return v3
end

return M

```

**Example usage:**

```lua
local transform = require "transform" -- The module shown above.

-- Move a child object to the world position of the mouse cursor:
local wpos = rendercam.screen_to_world_2d(action.screen_x, action.screen_y)
local wp = go.get_world_position("parent")
local wr = go.get_world_rotation("parent")
local ws = go.get_world_scale("parent")
local lpos = transform.world_to_local(vmath.vector3(wpos), wp, wr, ws)
go.set_position(lpos, "child")

```

**Caveats:**

This won’t work with skew.

In other words, if you have a parent object with non-uniform scale and a child object with rotation, transforming to and from the child’s local space won’t give a correct result. We really need to be able to get the object’s world transform directly to do this (but I don’t think it’s a very common use case).

**Some interesting performance benchmarks:**  
_I ran each of these 100,000 times and checked how long it took:_

- Empty function with return value — 1 ms
- Convert vector3 to vector4 or vice versa — 25 ms
- vmath.inv — 21 ms
- vmath.ortho\_inv — 20 ms
- M.v3\_by\_matrix — 81 ms
- Multiply vector4 \* matrix — 30 ms
- M.get\_to\_world\_matrix — 185 ms
- M.local\_to\_world — 98 ms
- M.world\_to\_local — 116 ms

So if you’re doing more than about 5 transforms with the same object at once, then it makes sense to use M.get\_to\_world\_matrix() and multiply yourself, otherwise it’s faster to use the transform functions. These times do _not_ include the original get\_world\_position, get\_world\_rotation, and get\_world\_scale calls for the module’s transform functions, just the functions themselves.

I tried a few other methods for transforming coordinates, and the ones I put here were by far the fastest (not using matrices at all).

---

<div class="post-metadata">

**Author:** ![jhonny.goransson](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/jhonny.goransson/32/3864_2.png) [@jhonny.goransson](https://forum.defold.com/u/jhonny.goransson)\
**Post date:** [July 14, 2019, 10:39pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/9 "2019-07-14T22:39:51Z")

</div>

Glad it worked! You can expect go.get\_world\_transform in the next release btw 🙂

---

<div class="post-metadata">

**Author:** ![totebo](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/totebo/32/36087_2.png) [@totebo](https://forum.defold.com/u/totebo)\
**Post date:** [November 11, 2020, 9:44am UTC](https://forum.defold.com/t/world-to-local-transforms/55513/10 "2020-11-11T09:44:18Z")

</div>

I’ve started playing with world\_to\_local() and, although it’s all magic to me, it seems to work. The value returned, however, doesn’t quite match the actual go.get\_position() value. I have an issue in my project which might be related to this slight precision discrepancy.

I’ve stripped it all down to a minimal example: [world\_to\_local.zip](https://forum.defold.com/uploads/default/original/3X/f/6/f696720eba56b52dc4f03d4342922d5117c1dbfa.zip) (3.8 KB)

```
function world_to_local(v3, wp, wr, ws)
	-- Translation inverse.
	v3.x = v3.x - wp.x; v3.y = v3.y - wp.y; v3.z = v3.z - wp.z
	-- Rotation inverse.
	local r = vmath.conj(wr)
	v3 = vmath.rotate(r, v3)
	-- Scale inverse.
	v3.x = v3.x / ws.x; v3.y = v3.y / ws.y; v3.z = v3.z / ws.z
	return v3
end

function init(self)

	-- Set parent to terrain for both crosses
	go.set_parent("cross1","terrain", true)
	go.set_parent("cross2","terrain", true)

	-- Compare go.get_position() with world_to_local() after one second.
	timer.delay(1, false, function()

		-- Get local position using world_to_local()
		local world_position = go.get_world_position("cross2")
		local wp = go.get_world_position("terrain")
		local wr = go.get_world_rotation("terrain")
		local ws = go.get_world_scale("terrain")
		local local_position = world_to_local(world_position, wp, wr, ws)

		-- Get local position using go.get_position()
		local local_position_go = go.get_position("cross2")

		print( "world_to_local(): ", local_position ) -- Returns vmath.vector3(128.02966308594, 16.321811676025, 0)
		print( "get go.get_position():", local_position_go ) -- Returns vmath.vector3(128.02966308594, 16.321823120117, 0)
		print( "---" )

	end)

end

```

What’s the reason the value is a tiny bit off? Is there a way to match the go.get\_position() and world\_to\_local() values somehow?

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [November 11, 2020, 1:01pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/11 "2020-11-11T13:01:29Z")

</div>

It’s just [floating point precision](https://forum.defold.com/t/floating-point-precision/11247) I assume. To be honest I’m surprised the X coordinate matches perfectly, with all the math it goes through.

Are you trying to compare them to see if they’re equal? If so, you’ll have to compare each coordinate yourself and account for some imprecision somehow. You could either round the numbers first, or do something like: "if (x1 - x2) \< 0.001 " - then they are the same.

---

<div class="post-metadata">

**Author:** ![totebo](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/totebo/32/36087_2.png) [@totebo](https://forum.defold.com/u/totebo)\
**Post date:** [November 11, 2020, 2:03pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/12 "2020-11-11T14:03:39Z")

</div>

> [@ross.grams](#):
>
> Are you trying to compare them to see if they’re equal?

Unfortunately not, otherwise that would have been a good solution. I’m firing raycasts from a game object, and for some reason they sometimes miss when the object is nested, probably because they start inside of the fixture. They never miss when the parent object isn’t rotated.

I’ll keep scratching at this to see if I can figure out why.

---

<div class="post-metadata">

**Author:** ![ross.grams](https://forum-defold.b-cdn.net/letter_avatar_proxy/v4/letter/r/cab0a1/32.png) [@ross.grams](https://forum.defold.com/u/ross.grams)\
**Post date:** [November 11, 2020, 3:22pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/13 "2020-11-11T15:22:14Z")

</div>

Hmm, hard to say without more info. Seems odd that something you would use a raycast for would break because of a 0.00001 pixel difference. I do usually start my raycasts a little bit “behind” where you might expect, to make them a bit forgiving.

It’s often helpful to draw some [debug lines](https://forum.defold.com/t/debug-draw-lines-boxes-circles-etc/61393) to match your raycasts.

---

<div class="post-metadata">

**Author:** ![totebo](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/totebo/32/36087_2.png) [@totebo](https://forum.defold.com/u/totebo)\
**Post date:** [November 11, 2020, 5:26pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/14 "2020-11-11T17:26:19Z")

</div>

> [@ross.grams](#):
>
> I do usually start my raycasts a little bit “behind” where you might expect, to make them a bit forgiving.
> 
> It’s often helpful to draw some [debug lines](https://forum.defold.com/t/debug-draw-lines-boxes-circles-etc/61393) to match your raycasts.

Well, I do both of these things too! 🙂 I agree, the slight difference might be a red herring.

---

<div class="post-metadata">

**Author:** ![totebo](https://forum-defold.b-cdn.net/user_avatar/forum.defold.com/totebo/32/36087_2.png) [@totebo](https://forum.defold.com/u/totebo)\
**Post date:** [November 14, 2020, 1:53pm UTC](https://forum.defold.com/t/world-to-local-transforms/55513/15 "2020-11-14T13:53:58Z")

</div>

I finally got it working!

The solution was using `local_to_world()` rather than `go.get_world_position()`. For some reason those two methods sometimes produce rather different results. Most of the time they’re the same, but sometimes, immediately after `go.set_parent()` has been used, `go.get_world_position()` is delayed by one frame. This was what caused my issue.

As it stands your functions seem to have solved all the problems I was having, and now (touch wood) everything works like it should. Thanks for being a superstar @ross.grams!
