Search
Duplicate

Python으로 모델링 정리 2. 배 밧줄

요약
폴리곤으로 제작된 와이어 모델링을 스플라인으로 변환
분류
script
태그
python
작성일
2020/08/11
1 more property

목표. Goal

샘플의 와이어는 실린더나 박스형태의 폴리곤으로 되어있습니다. 이것을 spline으로 변경하고 Sweep을 이용한 구조로 수정합니다.

샘플 출처. Sample link

파일 정리. Organize unnecessary content

FBX를 불러오면 모델링 하나와 많은 null 이 있습니다. 모델링만 남기도 다 지워주세요.
모델링은 Polygon Group to Object 로 분리하고 JS Random Display Color 를 이용해서 색을 변경해주세요. 이중 와이어 정리를 해볼 것이기에 와이어만 골라내 주세요.
wire_only.zip
121.4KB

스크립트 실행 방법. 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
스플라인으로 변환한 와이어는 스플라인 다이나믹을 이용하기도 쉽고 모델링을 관리하는 측면에서도 유리한 면이 습니다.

스크립트 시연. Demo