목표. Goal
샘플의 와이어는 실린더나 박스형태의 폴리곤으로 되어있습니다. 이것을 spline으로 변경하고 Sweep을 이용한 구조로 수정합니다.
샘플 출처. Sample link
파일 정리. Organize unnecessary content
FBX를 불러오면 모델링 하나와 많은 null 이 있습니다. 모델링만 남기도 다 지워주세요.
모델링은 Polygon Group to Object 로 분리하고 JS Random Display Color 를 이용해서 색을 변경해주세요. 이중 와이어 정리를 해볼 것이기에 와이어만 골라내 주세요.
스크립트 실행 방법. How to run script.
스크립트 실행 과정. Process
1.
오브젝트의 한 포인트를 기준으로 다른 포인트들과의 거리를 구합니다.
(0과1의 거리), (0과2의 거리), (0과3의 거리)...
2.
경우의 수만큼 두 점간의 거리를 비교하여 0.5cm 보다 작다면 하나의 그룹으로 판단할 수 있습니다.
[0,1,2,3,4], [5,6,7,8,9]...
3.
그렇게 찾아진 그룹들의 포인트 위치값을 모두 더하고 포인트 수로 나누면 중심위치값이 나옵니다.
포인트 포지션은 c4d.Vector(0,0,0) 타입이고 더하고 나누는 과정에서 같은 성분끼리 계산합니다.
위 그림으로 보면 총 5개의 그룹을 찾을수 있고, 5개의 중심점의 위치를 찾습니다.
4.
찾은 값은 Z 값을 기준으로 정렬하고, X 값을 기준으로 다시 정리합니다. 정렬을 하지 않으면 스플라인 생성시 꼬인 모양이 됩니다. 이 정렬로 완벽히 해결되진 않지만, 대부분 정상적인 스플라인이 생성됩니다.
5.
찾은 중심점의 수와 위치값을 기반으로 Spline Object 를 생성합니다.
6.
1~5까지의 과정을 와이어 오브젝트 수 만큼 반복시킵니다.
7.
꼬여있는 스플라인을 찾아 다시한번 정렬합니다.
정리 코드. Final code
줄 오브젝트를 선택하고 스크립트를 실행해야합니다.
import c4d
def main():
# 선택한 오브젝트들 검색, 하위 검색은 하지 않습니다.
sels = doc.GetActiveObjects(1)
# 선택이 없다면 중지합니다.
if sels == []:return
# 언두 기록을 시작합니다.
doc.StartUndo()
# 스플라인을 묶어줄 널을 생성합니다.
null = c4d.BaseObject(c4d.Onull)
null.SetName('wire')
doc.InsertObject(null)
# 널 생성을 언두에 기록합니다. Add 언두로 언두할 내용을 기록해야 합니다.
doc.AddUndo(c4d.UNDOTYPE_NEW, null)
# 가까운 점을 검색->중심->스프라인생성->널에 차일드
# 선택한 오브젝트를 하나씩 sel 에 넣어주고 아래 코드를 실행합니다.
for sel in sels:
# 폴리곤 오브젝트가 아니라면 아래 코드를 스킵합니다. 널은 스킵
# continue 가 아래로 계속 코드를 실행하는것이 아닙니다. 패스의 의미입니다.
if sel.GetType() != c4d.Opolygon:
continue
# 폴리곤 오브젝트면 아래코드들이 실행됩니다.
# 모든 포인트 위치값을 ps 에 리스트로 담습니다.
ps = sel.GetAllPoints()
# 무한 반복을 막기위해 반복 회수를 카운트합니다. 시작은 0으로 합니다.
loop = 0
# 그룹으로 찾을 포지션을 담을 빈 리스트입니다.
pos = []
# 반복을 시작합니다. ps 에 내용이 있을때는 True, ps = [] 비어있으면 False
while ps:
# 30번 이상 반복중이라면 중지합니다.
if loop > 30:
return
# 비교대상인 포지션 하나를 first 로 지정합니다.
first = ps.pop(0)
# first 를 포함한 그룹을 담을 리스트를 만듭니다.
find_near = [first]
# i 에 역으로 숫자를 집어넣어 pop 이용시 오류가 없도록 만든다.
# pop 은 리스트에서 해당 값을 뽑아내고 리스트에서 제거한다.
# 따라서 뒤에서 앞으로 검사를 하고 뽑아내면 아직 비교하지 않은 앞의 값에는 영향이 없다.
for i in reversed(range(len(ps))):
# 두점 사이의 거리
dis = (first-ps[i]).GetLength()
# 적당한 거리 값을 찾아 비교값으로 사용한다.
if dis < 0.5:
# 거리가 맞으면 리스트에 담아주면서 ps에서 제거한다.
find_near.append(ps.pop(i))
# 찾은 점들의 위치를 평균내면 점들의 중심 찾을 수 있다.
pos.append(sum(find_near)/len(find_near))
# 실행값을 1 올려 기록한다.
loop += 1
# 비슷한 위치의 점들이 한번 걸러진 상태이고 ps 에는 그외의 것들만 남아있다.
# 다시 위의 과정을 반복하면서 ps 의 값들이 전부 빠지면 반복을 중지하게 된다.
# 중심값들을 정렬한다.
pos.sort(key=lambda x : (x[2], x[0]))
# 한 오브젝트에서 찾은 중심위치 값을 기반으로 스플라인 을 생성한다.
spline = c4d.SplineObject(len(pos), 1)
spline.ResizeObject(len(pos),1)
spline.SetSegment(0, len(pos), False)
spline.SetAllPoints(pos)
spline[c4d.SPLINEOBJECT_TYPE] = 0
spline.Message(c4d.MSG_UPDATE)
spline.InsertUnder(null)
spline.SetMg(sel.GetMg())
# 추가된 스플라인을 언두로 추가한다.
doc.AddUndo(c4d.UNDOTYPE_NEW, spline)
# 언두를 닫아준다. 언두는 많은 AddUndo도 Start부터 End까지가 한번의 언두이다.
doc.EndUndo()
c4d.EventAdd()
if __name__ == "__main__":
main()
Python
복사
생성된 와이어 스플라인
꼬여있는 일부 스플라인
꼬인 스플라인 수정 코드. Align point index order
꼬여있는 스플라인 오브젝트만 선택하고 실행합니다. X 위치값으로 포인트 인덱스를 수정합니다.
import c4d
def main():
sel = doc.GetActiveObjects(1)
for s in sel:
pos = s.GetAllPoints()
pos.sort(key=lambda x : x[0])
s.SetAllPoints(pos)
s.Message(c4d.MSG_UPDATE)
c4d.EventAdd()
if __name__ == "__main__":
main()
Python
복사
스플라인으로 변환한 와이어는 스플라인 다이나믹을 이용하기도 쉽고 모델링을 관리하는 측면에서도 유리한 면이 습니다.