I started to answer this twice and stopped (twice) because I figured out - I don't actually understand the question.
Subject says:: "Get top left screen position of layer with ScreenToWorld ?"
- That suggests you want the vector screen position of the top,left corner of a Layer.
Body says:: "Get the position on a layer of the top left screen pixel of the viewport?"
- That suggests you want the position (not sure pix or vec?) of the top,left corner of the screen viewport relative to a layer.
Can you clarify?
Moho 14.1 » Win 11 Pro 64GB » NVIDIA GTX 1080ti 11GB
Moho 14.1 » Mac mini 2012 8GB » macOS 10.15 Catalina
Tube: SimplSam
SimplSam wrote: ↑Tue Oct 26, 2021 4:12 pmCan you clarify?
Ah sorry, it's difficult to express myself with all these technical things that I don't completely understand myself.
Basically what I'm trying to do is set a bone's position to the top-left of the screen, independent of viewport/camera/layer position.
Here's some code, which is probably doing things weirdly/inefficient/completely the wrong approach. It works in some situations, but not when the camera is placed in specific ways. I'm running the function from within "DrawMe" and the 'guiBone' is a global bone variable.
function FO_Draw:DockGUIbone(moho, view)
if guiBone ~= nil then
-- * Fix GUI-bone settings:
guiBone.fConstraints = false
-- * Get layer:
local layer = FO_Utilities:RigLayer(moho)
-- * Get top left position:
TopLeftVector2 = LM.Vector2:new()
local graphics = view:Graphics()
local pt = LM.Point:new_local()
pt:Set(0, 0)
graphics:ScreenToWorld(pt, TopLeftVector2)
-- * Flip GUI bone if nessecary:
local flippedH = false
local flipLayer = layer
local timesFlipped = 0
local totalXScale = 1
while flipLayer ~= nil do
totalXScale = totalXScale * flipLayer.fScale.value.x
if flipLayer.fFlipH:GetValue(moho.frame) or flipLayer.fScale.value.x < 0 then
timesFlipped = timesFlipped + 1
end
flipLayer = flipLayer:Parent()
end
if totalXScale < 0 then
totalXScale = totalXScale * -1
end
guiBone.fFlipH:Clear()
if (timesFlipped % 2 ~= 0) then
-- * Flipped an odd amount fo times
guiBone.fFlipH:SetValue(1, true)
end
-- * Get View rotation:
local viewRotation = graphics:ViewRotation()
-- * Get View zoom:
local viewZoom = graphics:ViewZoom()
-- * Get Camera zoom:
local cameraZoom = (moho.document.fCameraTrack:GetValue(moho.frame).z / 3.7321) / (moho.document.fCameraZoom:GetValue(moho.frame) / 2)
-- * Layer matrix without camera: (write down why...?)
local matrix = FO_Utilities:LayerMatrix(moho, moho.layer, moho.frame)
matrix:Invert()
-- * Camera Matrix: (Doesn't work for Z value or zoom?)
local cameraMatrix = LM.Matrix:new_local()
moho.document:GetCameraMatrix(moho.frame, cameraMatrix)
local cameraScale = cameraMatrix:ScaleValue()
-- * What the hell was I thinking here?
-- *********************
-- * Remember:
local camT = moho.document.fCameraTrack:GetValue(0)
local camZ = moho.document.fCameraZoom:GetValue(0)
local camR = moho.document.fCameraRoll:GetValue(0)
local camP = moho.document.fCameraPanTilt:GetValue(0)
-- * Reset:
local vec3 = LM.Vector3:new()
vec3:Set(0, 0, 3.7321)
moho.document.fCameraTrack:SetValue(0, vec3)
moho.document.fCameraZoom:SetValue(0, 2)
local vec2 = LM.Vector2:new()
vec2:Set(0, 0)
moho.document.fCameraRoll:SetValue(0, 0)
moho.document.fCameraPanTilt:SetValue(0, vec2)
-- * Get REST camera matrix:
local cameraRestMatrix = LM.Matrix:new_local()
moho.document:GetCameraMatrix(0, cameraRestMatrix)
-- * Revert:
moho.document.fCameraTrack:SetValue(0, camT)
moho.document.fCameraZoom:SetValue(0, camZ)
moho.document.fCameraRoll:SetValue(0, camR)
moho.document.fCameraPanTilt:SetValue(0, camP)
-- *********************
-- * What?
cameraMatrix:Invert()
cameraMatrix:Multiply(cameraRestMatrix)
matrix:Multiply(cameraMatrix)
-- * Topleft vec2 position:
matrix:Transform(TopLeftVector2)
-- * Add layer transform rotation to view rotation:
translation, rotationZ, scale, flip, pString, rString, sString, fString = AE_Utilities:Matrix2transform(matrix)
if flippedH then
viewRotation = -viewRotation
end
local rotation = viewRotation - rotationZ
-- * Key GUI-bone position:
guiBone.fAnimPos:Clear()
guiBone.fAnimPos:SetValue(1, TopLeftVector2)
guiBone.fPos = TopLeftVector2
-- * Key GUI-bone angle:
guiBone.fAnimAngle:Clear()
guiBone.fAnimAngle:SetValue(1, -rotation)
guiBone.fAngle = -rotation
-- * Key GUI-bone scale:
local scale = 1/totalXScale / viewZoom/3 * cameraZoom
guiBone.fAnimScale:Clear()
guiBone.fAnimScale:SetValue(1, scale)
-- * Dials:
--[[
for i = 1, #smartBoneDials do
local dial = smartBoneDials[i]
-- * dial Scale:
dial.fAnimScale:Clear()
dial.fAnimScale:SetValue(1, scale*dial.fAnimScale:GetValue(0))
-- * Dial Pos:
-- dial.fAnimPos:Clear()
local dialPos = dial.fAnimPos:GetValue(0)
dialPos:Set(dialPos.x, dialPos.y * scale)
dial.fAnimPos:SetValue(1, dialPos)
end
--]]
-- * Update viewport:
moho.layer:UpdateCurFrame()
end
end
Here's a video showing the WIP tool. The docked 'guiBone' kind of stays put, but it's because the camera is in the default z-position. (In reality, in my scenes the camera is rarely in the default z-position)
Personally, I would go with a solution that's completely independent of the camera. For example, you can make a reference of the smart bone layer, delete the original's sublayers, and set it immune to camera. The original will still control the smart bones in the reference (where the rendered art is), but as long as you break the layer transform sync, you can now place your controls anywhere.
I looked at this from the opposite direction having failed to get my ideas for the asked question to work.
starting with a mesh point (i.e. M_Point, not LM_Point) in the top left I got the point's fPos and did a world-to-screen on that - and that gave me a crazy result for the screen pixel location; but transforming the fPos with the full document matrix and then doing a world-to-screen on that gave the "right answer". IOW the vec2 you put in to world-to-screen is not the actual "world" vec2 you need... That worked for all the cases I tried - even with the orbit view and multiple camera and layer movements...
so I tried that in reverse: screen-to-world of a point then transform the world result with the inverted document matrix (which is more or less what's in the Transform layer tool that Stan modified) but that didn't work at all.
at this point I decided that I must have been doing something really dumb, so I repeated the concept with the mouse - trying to do a W-T-S on mouseEvent.vec to match mouseEvent.pt -- abject failure on my part!!
===
where I am on this:
what works:
>> W-T-S on a transformed (vec) gives the correct LM Point.
>> S-T-W on that LM Point gives a result that is tolerably equal to the transformed(vec) (+/- quantising because of screen resolution??)
What doesn't want to work: any transformation - most notably using the Invert() of the matrix used to do the transform - that I've tried so far on the transformed vec in an attempt to retrieve the original vec
local pt = LM.Point:new_local()
local m = LM.Matrix:new_local()
m:Identity()
moho.layer:GetFullTransform(moho.frame, m, moho.document)
pt:Set(0, 0)
local vec2 = view:Point2Vec(pt, m)
mesh:AddLonePoint(vec2, 0)
mesh:AppendPoint(vec2, 0)
the typo (well spotted BTW!) was because the code is actually gathered from a long sequence of failed attempts - much of which is commented out along with the reason for its failures -- and the intervening stuff I deleted left me with an inconsistent pair of get and use the matrix! (I actually had many different matrices in there - graphics, camera only, layer, doc, inverts ...) a slip of the clip!
I was looking in all the wrong places. I would have never figured that out unless I somehow stumbled upon view:Point2Vec(). It looks like is going to work after all