RE: Idea for an LDCad script
2022-05-30, 15:30 (This post was last modified: 2022-05-30, 15:31 by Stefan Frenz. Edit Reason: removed inserted empty lines )
2022-05-30, 15:30 (This post was last modified: 2022-05-30, 15:31 by Stefan Frenz. Edit Reason: removed inserted empty lines )
Thank you Roland for your help and LDCad 1.7-alpha-2. The discussed "place on ground" seems to work in a first version. Also porting the collision detection from Java to LUA will take more time...
At the moment the script can:
1: collect all parts recursively and print their filename/description together with its bounding box
2: move all parts/submodel-references so that the lowest point has y==0
3: tbd: check collision of parts by comparing pairs of triangles of pairs of parts
The current implementation collects all parts at first which would not be necessary for case 1 (just print instead of collect+print) and case 2 (just collect maxY instead of all bounding boxes of all parts), so this implementation is far from being optimal in regards to timing or memory usage. But it helps me porting my collision detection and maybe it is useful for others doing similar stuff.
At the moment the script can:
1: collect all parts recursively and print their filename/description together with its bounding box
2: move all parts/submodel-references so that the lowest point has y==0
3: tbd: check collision of parts by comparing pairs of triangles of pairs of parts
The current implementation collects all parts at first which would not be necessary for case 1 (just print instead of collect+print) and case 2 (just collect maxY instead of all bounding boxes of all parts), so this implementation is far from being optimal in regards to timing or memory usage. But it helps me porting my collision detection and maybe it is useful for others doing similar stuff.
Code:
Part={triangles={}, orig={}, level=0, pMinX=math.huge,pMinY=math.huge,pMinZ=math.huge, pMaxX=-math.huge,pMaxY=-math.huge,pMaxZ=-math.huge}
function Part:new(orig, level)
o={}
setmetatable(o, self)
self.__index=self
self.orig=orig or {}
self.level=level or 0
self.pMinX=math.huge
self.pMinY=math.huge
self.pMinZ=math.huge
self.pMaxX=-math.huge
self.pMaxY=-math.huge
self.pMaxZ=-math.huge
return o
end
function Part:add(tri)
table.insert(self.triangles, tri)
if tri.pMinX<self.pMinX then
self.pMinX=tri.pMinX
end
if tri.pMinY<self.pMinY then
self.pMinY=tri.pMinY
end
if tri.pMinZ<self.pMinZ then
self.pMinZ=tri.pMinZ
end
if tri.pMaxX>self.pMaxX then
self.pMaxX=tri.pMaxX
end
if tri.pMaxY>self.pMaxY then
self.pMaxY=tri.pMaxY
end
if tri.pMaxZ>self.pMaxZ then
self.pMaxZ=tri.pMaxZ
end
end
function Part:info()
return self.orig:getFileName()..' ('..self.orig:getDescription()..'): '..self.pMinX..' '..self.pMinY..' '..self.pMinZ..' to '..self.pMaxX..' '..self.pMaxY..' '..self.pMaxZ
end
Triangle={p1={}, p2={}, p3={}, pMinX=math.huge,pMinY=math.huge,pMinZ=math.huge, pMaxX=-math.huge,pMaxY=-math.huge,pMaxZ=-math.huge}
function Triangle:new(p1, p2, p3)
o={}
setmetatable(o, self)
self.__index=self
self.p1=p1
self.p2=p2
self.p3=p3
self.pMinX=math.huge
self.pMinY=math.huge
self.pMinZ=math.huge
self.pMaxX=-math.huge
self.pMaxY=-math.huge
self.pMaxZ=-math.huge
self:check(p1)
self:check(p2)
self:check(p3)
-- print('vec: ', p1,' / ',p2,' / ',p3)
-- print('min/max: ', self.pMinX,' ',self.pMinY,' ',self.pMinZ, ' to ', self.pMaxX,' ',self.pMaxY,' ',self.pMaxZ)
return o
end
function Triangle:check(p)
local pX,pY,pZ=p:get()
if pX<self.pMinX then
self.pMinX=pX
end
if pY<self.pMinY then
self.pMinY=pY
end
if pZ<self.pMinZ then
self.pMinZ=pZ
end
if pX>self.pMaxX then
self.pMaxX=pX
end
if pY>self.pMaxY then
self.pMaxY=pY
end
if pZ>self.pMaxZ then
self.pMaxZ=pZ
end
end
function colldet_walk(sf, ppos, level, parts)
local partCnt=sf:getRefCount()
for i=1,partCnt do
local part=sf:getRef(i):getSubfile()
-- print('-- part ', part:getDescription())
local pos=sf:getRef(i):getPosOri()
pos:mulBA(ppos)
-- print('-- pos ', pos)
local data=part:getRenderData()
if data:isLinked() then
local mpart=Part:new(part, level)
table.insert(parts, mpart)
for g=1,data:getGroupCount() do
local grp=data:getGroup(g)
if grp:isTriangle() then
local cnt=grp:getCount()
-- print('triangle strip contains ', cnt, ' entries')
for j=1,cnt do
local a,b,c=grp:getPositionIndices(j)
-- print('triPosRel: ', data:getPosition(a), '; ', data:getPosition(b), '; ', data:getPosition(c))
-- print('triPosAbs: ', data:getPosition(a):getTransformed(pos), '; ', data:getPosition(b):getTransformed(pos), '; ', data:getPosition(c):getTransformed(pos))
mpart:add(Triangle:new(data:getPosition(a):getTransformed(pos), data:getPosition(b):getTransformed(pos), data:getPosition(c):getTransformed(pos)))
end
elseif grp:isQuad() then
local cnt=grp:getCount()
-- print('quad strip contains ', cnt, ' entries')
for j=1,cnt do
local a,b,c,d=grp:getPositionIndices(j)
-- print('quadPosRel: ', data:getPosition(a), '; ', data:getPosition(b), '; ', data:getPosition(c), '; ', data:getPosition(d))
-- print('quadPosAbs: ', data:getPosition(a):getTransformed(pos), '; ', data:getPosition(b):getTransformed(pos), '; ', data:getPosition(c):getTransformed(pos), '; ', data:getPosition(d):getTransformed(pos))
mpart:add(Triangle:new(data:getPosition(a):getTransformed(pos), data:getPosition(b):getTransformed(pos), data:getPosition(c):getTransformed(pos)))
mpart:add(Triangle:new(data:getPosition(c):getTransformed(pos), data:getPosition(d):getTransformed(pos), data:getPosition(a):getTransformed(pos)))
end
end
end
else
colldet_walk(part, pos, level+1, parts)
end
end
end
function colldet_collect()
local sf=ldc.subfile()
local pos=ldc.matrix()
--identity is set by default: pos:setIdentity()
local parts={}
colldet_walk(sf, pos, 0, parts)
print('found ',#parts,' parts:')
for i=1,#parts do
print(i, ': ', parts[i]:info())
end
end
function colldet_floor()
local sf=ldc.subfile()
local pos=ldc.matrix()
pos:setIdentity()
local parts={}
colldet_walk(sf, pos, 0, parts)
if #parts==0 then
print('no part found')
else
local max=parts[1].pMaxY
for i=2,#parts do
if max<parts[i].pMaxY then
max=parts[i].pMaxY
end
end
local partCnt=sf:getRefCount()
for i=1,partCnt do
local ref=sf:getRef(i)
local x,y,z=ref:getPos():get()
ref:setPos(ldc.vector(x,y-max,z))
end
end
end
function colldet_check()
print('not implemented yet!')
local sf=ldc.subfile()
local pos=ldc.matrix()
pos:setIdentity()
local parts={}
colldet_walk(sf, pos, 0, parts)
for i=2,#parts do
for j=1,i-1 do
local p1=parts[i]
local p2=parts[j]
print('todo: checking ', i, ' (', p1:info(), ') against ', j, ' (', p2:info(), ')')
-- todo
end
end
end
function register()
local macro=ldc.macro('CD: 1 collect parts')
macro:setHint('Collision Detection: collect parts')
macro:setEvent('run', 'colldet_collect')
local macro=ldc.macro('CD: 2 move to floor')
macro:setHint('Collision Detection: move bottom point down/up to 0')
macro:setEvent('run', 'colldet_floor')
local macro=ldc.macro('CD: 3 check collision')
macro:setHint('Collision Detection: check part collision')
macro:setEvent('run', 'colldet_check')
end
register()