루아로 HLSL의 스위즐링, 마스킹 흉내내기
루아에서 HLSL의 스위즐링과 마스킹을 흉내내는 코드 예제입니다. 루아의 메타테이블, 특히 __index와 __newindex 메타메서드를 활용하는 방법을 배우는 데 도움이 될 것입니다.
HLSL의 스위즐링(swizzling)은 HLSL 코드를 간결하게 만드는 데 큰 역할을 하는 독특한 표기법으로, 벡터 배정문에서 성분들의 순서를 임의로 지정할 수 있게 하는 것입니다. 예를 들어 한 벡터의 x 성분과 y 성분이 맞바뀐 벡터가 필요하다면,
a.x = b.y;
a.y = b.x;
a.z = b.z;
a.w = b.w;
라고 하는 대신
a = b.yxzw;
라고 하면 됩니다.
마스킹(masking)은 특정 성분들만 값이 갱신되게 하는 표기법입니다. 예를 들어 어떤 벡터의 y 성분과 w 성분만 갱신하고자 한다면
a.yw = b;
라고 쓰면 됩니다. 이는 a.y = b.y; a.w = b.w;와 동치입니다.
HLSL의 이런 표기법을 루아로 구현해 보았습니다. 코드를 세세히 설명하려고 했는데, 생각해 보니 루아를 모르는 분을 위해서는 블로그에 적합하지 않을 정도로 긴 설명이 필요할 것이고 루아를 아는 분에게는 쓸데없는 지면 낭비일 것이므로 핵심적인 아이디어와 코드만 제시하기로 하겠습니다...(루아라는 언어에 대해서는 최근 나온 프로그래밍 루아를 보시길!) 핵심 아이디어는:
- object.member라는 표기는 object["member"]와 동치이다.
- object["member"]에 대한 읽기 접근은 __index 메타메서드로 제어할 수 있다(물론 object에 "member"라는 키가 존재하지 않는 경우).
- object["member"]에 대한 쓰기 접근은 __newindex 메타메서드로 제어할 수 있다(역시 object에 "member"라는 키가 존재하지 않는 경우).
전체 코드는 다음을 클릭하세요:
[#M_ 클릭! | 숨기기 |--[[ HLSL의 swizzle, masking 흉내내기
류광(http://occamsrazr.net/)
]]
local function only_xyzw(k)
-- x, y, z, w 이외의 글자가 있으면 false를 반환
return not k:find('[^wxyz]')
end
local function valid_swizzle(k)
-- x, y, z, w로만 된 네 글자
return k:len() == 4 and only_xyzw(k)
end
local function get_elements(t, k)
if valid_swizzle(k) then
local temp = {}
local elem
for i = 1, 4 do
elem = k:sub(i, i)
temp[i] = rawget(t, elem)
end
return Vector:new{x = temp[1],
y = temp[2], z = temp[3], w = temp[4]}
else
return rawget(t, k)
end
end
local function set_elements(t, k, rhs)
if k:len() > 1 and only_xyzw(k) then
local elem
for i = 1, 4 do
elem = k:sub(i, i)
if elem ~= '' then
rawset(t, elem, rhs[elem])
end
end
else
rawset(t, k, rhs)
end
end
Vector = {}
function Vector:new (x, y, z, w)
local obj = {}
-- 기본값들
if type(x) == 'table' then
obj = x
obj.x = x.x or 0
obj.y = x.y or 0
obj.z = x.z or 0
obj.w = x.w or 0
else
obj.x = x or 0
obj.y = y or 0
obj.z = z or 0
obj.w = w or 0
end
setmetatable(obj, self)
self.__index = get_elements
self.__newindex = set_elements
return obj
end
function Vector:Print()
local v = self
if v then
print("x =", v.x, "y =", v.y,
"z =", v.z, "w =", v.w)
else
print("not a table")
end
end
-----------------------------
-- 테스트 --
-----------------------------
-- v = {x = 1, y =2, z = 3, w = 4}
v = Vector:new(1, 2, 3, 4)
v:Print()
-- x = 1 y = 2 z = 3 w = 4
v2 = v.wzyy -- 스위즐링
v2:Print()
-- x = 4 y = 3 z = 2 w = 2
v.yz = v2 -- 마스킹
v:Print()
-- x = 1 y = 3 z = 2 w = 4
-- v = v2.zy -- 오류: 부분 스위즐링은 지원하지 않음
-- 다음처럼 마스킹과 조합하면 됨
v.xy = v2.zyxx
v:Print()
-- x = 2 y = 3 z = 2 w = 4
v.d = 10 -- 새로운 필드 추가
print(v.d) -- 10
v.x = 11 -- 기존 필드 접근
v:Print()
-- x = 11 y = 3 z = 2 w = 4
_M#]