올해는 할로윈이 빨리 찾아왔습니다. 작년에 내가 사용한 이 라즈베리 파이 프로젝트 하룻밤의 엔터테인먼트를 즐기기 위해. 올해 저는 JMP와 아두이노 나노 . 아래에서 나노에 대해 자세히 알아보세요.
가지고 놀다보니 지구 그림을 구체에 감싸기 , 대신 감쌀 비슷한 해골 질감을 찾을 수 있을지 궁금했습니다. 더 나은 방법을 찾았습니다. 대지 3D 모델이 있습니다. 이 프로젝트는 링크가 가리키는 모델을 사용합니다. 이름을 사용하는 사람 덕분에 printable_models.
첨부 파일은 완전한 JSL입니다. 이 블로그의 스니펫은 불완전하며 완전히 일치하지 않을 수 있습니다.
zip 파일 내부(열려면 JMP의 zip 아카이브 사용):
</CODE></DIR></PRE><P><STRONG>{"Skull_v3_L2.123c1407fc1e-ea5c-4cb9-9072-d28b8aba4c36/",</STRONG><BR /><STRONG> "Skull_v3_L2.123c1407fc1e-ea5c-4cb9-9072-d28b8aba4c36/Skull.jpg",</STRONG><BR /><STRONG> "Skull_v3_L2.123c1407fc1e-ea5c-4cb9-9072-d28b8aba4c36/12140_Skull_v3_L2.obj",</STRONG><BR /><STRONG> "Skull_v3_L2.123c1407fc1e-ea5c-4cb9-9072-d28b8aba4c36/12140_Skull_v3_L2.mtl"}</STRONG></P><P> </P><P>그것은 세 개의 파일과 그것들을 보관할 디렉토리입니다. .mtl 파일이 무엇을 하는지 잘 모르겠습니다. 이 게시물은 대부분 .obj 파일에 관한 것입니다. .jpg는 정말 흥미롭고 나중에 사용되며 다음과 같이 보입니다.</P><PRE><CODE class=" language-jsl">jpgblob=za<<READ></READ>za = open("e:/Skull_v3_L2.123c1407fc1e-ea5c-4cb9-9072-d28b8aba4c36.zip",zip);
members = za<
Texture map for skull, 1/4 size해골 텍스처 맵, 1/4 크기
OBJ 파일에는 나머지 데이터인 3D 점과 해당 2D 텍스처 맵 좌표가 포함되어 있습니다. 파일 형식에 대한 설명이 있습니다. 여기 . 파일에는 다음이 포함됩니다. V- 3D 좌표 섹션, vt- 텍스처 맵 좌표 섹션, vn- 법선 벡터의 섹션, 에프- 면 섹션(3면 또는 4면 폴리곤) 및 기타 몇 가지 항목.
# 3ds Max Wavefront OBJ Exporter v0.97b - (c)2007 guruware
# File Created: 21.12.2011 14:29:19
mtllib 12140_Skull_v3_L2.mtl
#
# object 12140_Skull_v3_jaw
#
v -1.6458 -14.7959 1.0976
v -1.2548 -14.9644 1.0679
...
v 0.8637 -15.1231 0.7422
v 0.4355 -15.2145 0.7175
# 6114 vertices
vn -0.4113 -0.8998 0.1457
vn -0.3415 -0.9275 0.1520
...
vn 0.2578 -0.9662 0.0017
vn 0.1450 -0.9890 -0.0288
# 6114 vertex normals
vt 0.2309 0.7002 0.0000
vt 0.2363 0.7000 0.0000
...
vt 0.2632 0.6958 0.0000
vt 0.2578 0.6955 0.0000
# 6434 texture coords
g 12140_Skull_v3_jaw
usemtl 12140_Skull_v3
s 2
f 1/1/1 2/2/2 3/3/3 4/4/4
f 2/2/2 5/5/5 6/6/6 3/3/3
f 7/7/7 8/8/8 3/3/3 6/6/6
f 8/8/8 9/9/9 4/4/4 3/3/3
f 5/5/5 10/10/10 11/11/11 6/6/6
f 10/10/10 12/12/12 13/13/13 11/11/11
f 14/14/14 15/15/15 11/11/11 13/13/13
...
f 6109/6429/6109 6108/6428/6108 6113/6433/6113 6114/6434/6114
f 3086/3247/3086 3097/3258/3097 6114/6434/6114 6113/6433/6113
f 3097/3258/3097 12/12/12 3072/3233/3072 6114/6434/6114
# 6112 polygons
#
# object 12140_Skull_v3_teeth
#
...
위의 파일에는 #comments, 빈 줄, 3개의 점이 있는 삼각형 면, 여러 개의 쿼드 면, 무시할 g 및 s 레코드가 표시됩니다.
JSL은 패턴 일치를 사용하여 효율적으로 읽을 수 있습니다. 먼저 OBJ를 문자열로 가져옵니다.
obj=za<<READ></READ>
String( "# 3ds Max ... 총 6171025자 ...라이곤
" ) 할당된.
그런 다음 구문 분석합니다. 구문 분석될 때 데이터 값을 보유하도록 일부 목록을 설정하고 몇 가지 도우미 패턴을 만듭니다.
vertex = {};
normal = {};
texture = {};
quad = {};
triangle = {};
seg={};
number=patspan("0123456789-.");
threenums = number>>n1+" "+number>>n2+" "+number>>n3+patspan(" \!n\!r");
integer = patspan("0123456789");
threetriples =
integer>>n1+"/"+integer>>n2+"/"+integer>>n3+patspan(" ")+
integer>>n4+"/"+integer>>n5+"/"+integer>>n6+patspan(" ")+
integer>>n7+"/"+integer>>n8+"/"+integer>>n9;
fourthtriple =
integer>>n10+"/"+integer>>n11+"/"+integer>>n12;면 중 일부는 삼각형이고 일부는 사각형입니다. 그만큼 쓰리트리플 그리고 네 번째 트리플 쿼드 또는 트라이앵글과 같은 적절한 목록에서 얼굴을 효율적으로 구문 분석하고 저장하기 위해 아래에서 사용됩니다.
objpat =
("f"+patspan(" ")+
// in this file, the "f" records all look like one of these:
// f 10714/11419/10714 10713/11418/10713 10658/11363/10658 <<< trailing space
// or
// f 39694/42282/39694 39692/42679/39692 40060/42677/40060 39722/42283/39722 <<< trailing space
threetriples+
(
(patspan(" ")+fourthtriple+patspan(" \!n\!r")+ //
pattest(insertinto(quad,evallist({evallist({num(n1),num(n2),num(n3),num(n4),num(n5),num(n6),num(n7),num(n8),num(n9),num(n10),num(n11),num(n12)})}));1))
| //
(patspan(" \!n\!r")+ //
pattest(insertinto(triangle,evallist({evallist({num(n1),num(n2),num(n3),num(n4),num(n5),num(n6),num(n7),num(n8),num(n9)})}));1))
)
) | // in this file, the v, vt, vn records have x,y,z values
("v"+patspan(" ")+threenums+pattest(insertinto(vertex,evallist({evallist({num(n1),num(n2),num(n3)})}));1))|
("vt"+patspan(" ")+threenums+pattest(insertinto(texture,evallist({evallist({num(n1),num(n2),num(n3)})}));1))|
("vn"+patspan(" ")+threenums+pattest(insertinto(normal,evallist({evallist({num(n1),num(n2),num(n3)})}));1))|
("g"+patspan(" ")+
patbreak("\!n\!r")>>SegName+
pattest(insertinto(seg,// 2 is quads, 3 is triangles, 1 is name (mostly unused)
evallist({evallist({segname,nitems(quad),nitems(triangle)})}));
1)+
patspan("\!n\!r")
) |
("#"+patbreak("\!n\!r")+patspan("\!n\!r")) |
(patbreak("\!n\!r")>>log("unused data")+patspan("\!n\!r"));오브제팟 위에서 정의한 은 패턴을 보유합니다. 아직 파싱된 것이 없습니다. 오브제팟 패턴 매처(아래)에게 OBJ 파일의 한 줄을 일치시키는 방법을 알려줍니다. 다음은 패턴 일치자에 대한 호출입니다. 일치자가 파일의 모든 레코드를 반복하도록 만들기 위해 패턴 일치 JSL이 조금 더 추가되었습니다.
이전에 로드된 텍스트를 사용하여 객체 , 왼쪽 위치 0에서 시작하여 반복적으로 사용 오브제팟 오른쪽 위치 0까지 패턴. 성공할 때마다 오브제팟 , JMP가 추가 기록 보관을 수행할 필요가 없도록 이미 완료된 작업을 차단합니다.
rc = patmatch(obj,
patpos(0)+
patrepeat(objpat+patfence())+
patrpos(0)
);
show(rc);
99(미사용 데이터) mtllib 12140_Skull_v3_L2.mtl
487834(미사용 데이터) usemtl 12140_Skull_v3
487857(미사용 데이터) s 2
1305132(미사용 데이터) usemtl 12140_Skull_v3
1305155(미사용 데이터) s 1
2038681(미사용 데이터) usemtl 12140_Skull_v3
2038704(미사용 데이터) s 1
4326305(미사용 데이터) usemtl 12140_Skull_v3
4326328(미사용 데이터) s 2
rc = 1;
rc==1은 좋은 것입니다. 처음부터 끝까지 일치하는 패턴입니다. log() 함수는 오브제팟 사용하지 않는 데이터에 대한 라인을 만들고 있습니다. 해당 데이터 라인이 유용하지 않을 것 같습니다. 그리고 그 과정에서 매처는 각각에 대해 이 JSL을 실행했습니다. vn 레코드(다른 레코드도 유사):
pattest(insertinto(normal,evallist({evallist({num(n1),num(n2),num(n3)})}));1)JSL 목록을 채운 정상 모든 정점 법선을 사용합니다. (일반 벡터는 빛이 표면에서 반사되는 방향을 OpenGL에 알려줍니다.) 끝에 있는 ;1은 일치자가 계속 앞으로 이동하도록 성공해야 한다고 pattest()에 알려줍니다.
정점 목록은 3개 항목 목록의 목록입니다. 항목은 x, y, z 좌표입니다.
{{-1.6458, -14.7959, 1.0976},
{-1.2548, -14.9644, 1.0679},
{-1.2202, -14.8974, 1.4306},
{-1.6069, -14.7373, 1.4505},
{-0.8477, -15.1013, 1.036},
{-0.8223, -15.0288, 1.403},
{-0.7966, -14.9354, 1.8162},
{-1.1896, -14.811, 1.8424},
{-1.5761, -14.6593, 1.8511},
{-0.4272, -15.1961, 1.01},
{-0.4138, -15.1209, 1.3778},
{-0.0001, -15.2327, 0.9984},
{-0.0001, -15.1568, 1.3658},
{0, -15.0543, 1.7793},
{-0.3993, -15.0212, 1.7914},
...얼굴 목록은 삼각형과 사각형으로 구분됩니다. 삼각형 목록은 목록의 목록이기도 합니다.
{{6148, 6468, 6148, 6150, 6470, 6150, 6151, 6471, 6151},
{6149, 6469, 6149, 6148, 6468, 6148, 6151, 6471, 6151},
{6174, 6494, 6174, 6175, 6495, 6175, 6151, 6471, 6151},
{6150, 6470, 6150, 6174, 6494, 6174, 6151, 6471, 6151},
{6198, 6518, 6198, 6199, 6519, 6199, 6151, 6471, 6151},
{6175, 6495, 6175, 6198, 6518, 6198, 6151, 6471, 6151}, 이 세 개의 트리플은 정점, 텍스처 및 일반 목록에 대한 인덱스입니다. 텍스처 목록은 0..1 좌표를 사용하여 위의 텍스처 맵에서 색상을 선택합니다. JSL 목록 세그먼트 4개의 항목이 있으며 각 목록에는 세그먼트 이름(두개골, 턱, 윗니, 아랫니)과 시작 위치가 포함되어 있습니다. 삼각형 그리고 쿼드 기울기.
이 JSL은 데이터를 JSL로 로드합니다. 장면 표시 목록 각각에 대해 세그먼트 항목:
// load model into OpenGL
// seg describes 4 segments (jaw, top teeth, bot teeth, skull)
// for Halloween I want two segments, skull and jaw, with the
// teeth welded on already.
// so, load the segs, then recombine them...
displaylists = {};
for(iDisplayList=1,iDisplayList<=nitems(seg),iDisplayList +=1,
partial = Scene Display List();
partial << BEGIN( QUADS );
// the seg notes its startinq quad/triangle, use the next seg
// to find this segs end. or the rest, for the last seg.
// 2 is quads, 3 is triangles
lastquad = if(iDisplayList < nitems(seg),seg[iDisplayList+1][2],nitems(quad));
for(i=seg[iDisplayList][2]+1,i<=lastquad,i+=1,
for(q=0,q<12,q+=3,
partial << Normal( normal[quad[i][q+3]][1], normal[quad[i][q+3]][2], normal[quad[i][q+3]][3] );
tx=texture[quad[i][q+2]][1];
ty=texture[quad[i][q+2]][2];
c=jpg[(1-ty)*(nrows(jpg)-1)+1,(tx)*(ncols(jpg)-1)+1];
partial << Color( c );
partial << Vertex( vertex[quad[i][q+1]][1], vertex[quad[i][q+1]][2], vertex[quad[i][q+1]][3] );
);
);
partial << END;
partial << BEGIN( TRIANGLES );
lasttriangle = if(iDisplayList < nitems(seg),seg[iDisplayList+1][3],nitems(triangle));
for(i=seg[iDisplayList][3]+1,i<=lasttriangle,i+=1,
for(t=0,t<9,t+=3,
partial << Normal( normal[triangle[i][t+3]][1], normal[triangle[i][t+3]][2], normal[triangle[i][t+3]][3] );
tx=texture[quad[i][t+2]][1];
ty=texture[quad[i][t+2]][2];
c=jpg[(1-ty)*(nrows(jpg)-1)+1,(tx)*(ncols(jpg)-1)+1];
partial << Color( c );
partial << Vertex( vertex[triangle[i][t+1]][1], vertex[triangle[i][t+1]][2], vertex[triangle[i][t+1]][3] );
);
);
partial << END;
displaylists[iDisplayList] = partial;
);그것은 동일한 논리입니다. 두 번, 한 번은 삼각형을 만들고, 한 번은 쿼드를 만듭니다.
그 JSL은 네 부분에서 네 개의 디스플레이 목록을 만들었습니다. 첨부된 파일은 그것들을 결합하고 애니메이션하며 할로윈 스타일의 안구를 추가합니다. 사진 찍을 시간...
Halloween early evening, model in window할로윈 이른 저녁, 창가의 모델
시선이 포토그래퍼를 따라가는 것처럼 보이나요? 창 하단의 틈은 무엇입니까?
Arduino Nano 및 PIR 센서
시선이 사람을 추적하게 하려면 일종의 센서가 필요합니다.PIR 센서 단일 비트 인터페이스(뭔가 움직였습니다!)와 응답 시간에 대한 몇 가지 제한이 있습니다. 할로윈에 낮은 투표율을 감안할 때 그들은 올바른 선택이었습니다. Raspberry PI 카메라는 더 나은 성능을 제공할 수 있습니다. 창 아래의 간격은 5개의 PIR 센서를 허용합니다. 콜리메이터 사람들이 걸을 수 있는 지역을 관찰하기 위해. 설정은 다음과 같습니다.
They do like like eyes. Those collimator tubes are re-purposed.그들은 눈을 좋아합니다. 이러한 콜리메이터 튜브는 용도가 변경됩니다.
아두이노 나노에 테이핑, 핫글루, 점퍼 와이어로 연결된 조립품의 모양은 다음과 같습니다. 나노는 작습니다.
Arduino Nano on left under zip-ties. PIR sensors on right hot-glued to collimators. There are two orange adjustments on each PIR.집타이 아래 왼쪽에 있는 Arduino Nano. 콜리메이터에 핫 접착된 오른쪽의 PIR 센서. 각 PIR에는 두 개의 주황색 조정이 있습니다.
Nano 클론은 납땜할 수 있는 헤더 핀과 함께 제공되거나 와이어를 Nano에 납땜할 수 있습니다. 점퍼선을 반으로 자르고 한쪽 끝을 납땜하면 떨어져 나갈 가능성이 조금 줄어들고 다른 프로젝트에서 재사용될 가능성이 조금 더 높아집니다.
Nano has wires soldered on, orange is +5, green is ground, purple/gray/blue are data linesNano에는 와이어가 납땜되어 있고 주황색은 +5, 녹색은 접지, 보라색/회색/파란색은 데이터 라인입니다.
다음은 Nano에서 실행되는 코드입니다. Arduino IDE를 사용하여 USB 연결을 통해 컴파일하고 다운로드합니다. IDE를 사용하면 USB 포트에 할당된 COM 포트 번호를 쉽게 찾을 수 있습니다. JSL에서도 필요합니다. 첨부 파일에는 추가 .txt 확장자가 있으며 .ino 확장자를 갖도록 이름을 바꿉니다.
Arduino IDE showing code to read sensors and send to JMP센서를 읽고 JMP로 보내는 코드를 보여주는 Arduino IDE
설정 루틴은 한 번 실행됩니다. 루프 루틴은 계속해서 호출됩니다. Delay(100)는 루프가 다시 실행되기 전에 1/10초를 기다립니다. 2,3,4,5,6은 센서에 연결된 핀입니다. 직렬 포트에 인쇄된 모든 항목은 DataFeed를 사용하여 JMP에서 읽을 수 있습니다. 다음은 이를 위한 JSL의 또 다른 스니펫입니다. 단어 () 데이터 라인에서 5개의 센서 값을 가져오는 함수:
sensorBits = [0, 0, 0, 0, 0];
feed = Open Datafeed(
Connect( Port( "com4" ), Baud rate( 115200 ), Parity( none ), DataBits( 8 ) ),
Set Script(
Local( {i, txt, temp = sensorBits},
txt = feed << getLines;
for(iline=1,iline<=nitems(txt),iline+=1,
For( i = 1, i <= N Rows( sensorBits ), i += 1,
temp[i] += Num( Word( i, txt[iline], "," ) )
);
If( Any( Is Missing( temp ) ),
Show( temp,chartoblob(txt[iline]) ),
sensorBits = temp * .9;
try(tb<<SETTEXT></SETTEXT>데이터 피드는 올바르게 닫아야 합니다. 그렇지 않으면 다음에 포트가 여전히 사용 중임을 알게 됩니다. 그것이 OnClose 로직이 하는 일입니다. 나머지는 그냥 유지 센서비트 센서 비트가 켜져 있으면 증분하고 꺼져 있으면 페이드하여 매트릭스를 업데이트합니다. 센서는 최소 3초 동안 켜져 있고 카운트가 약 10까지 올라가고 상당히 빠르게 거의 0으로 되돌아갑니다.
그게 전부입니다. 끝에 있는 JSL(첨부 파일)에는 센서에 대한 응답으로 눈과 모델을 회전시키는 루프가 있습니다...두개골이 지루할 때를 감지하고 트릭 오어 트리터를 유인하기 위해 일부 애니메이션을 실행합니다. JSL은 아무 것도 연결되지 않은 상태에서도 데이터 피드와 함께 실행될 것입니다. com 포트에 불만이 있을 수 있으며 두 번째로 실행하면 정말 불만일 수 있습니다. COM 포트를 주석 처리하기 쉬워야 합니다.
추적 안구는 몇 초의 지연과 함께 작동합니다. 어디에 서야 하는지 아는 것이 도움이 됩니다. 애니메이션은 관객이 자신이 보고 있다는 것을 알기 어렵게 만들었습니다.
또한 JMP는 현재 텍스처 매핑을 실제로 지원하지 않기 때문에(JMP 15) 약간 느립니다. 구형 노트북을 사용해도 여전히 15FPS를 얻었고, 이는 할로윈에 충분했습니다.
8pm, about to rain. Candy Corn half gone, glow sticks popular, vampire teeth and bubbles too!오후 8시, 비가 내리기 직전. 반쯤 사라진 캔디콘, 야광봉 인기, 뱀파이어 이빨과 비눗방울도!
그리고 그 프로젝터는 밝습니다! 다음은 반투명 창 가리개에 투사되는 특수 영사기 스탠드입니다.
Not recommended if you have a cat.고양이가 있다면 추천하지 않습니다.
그리고, 이전 할로윈 3부작 포스트.
short loop of animation possibility애니메이션 가능성의 짧은 루프