今年万圣节来的很快。 去年我用这个树莓派项目享受一夜的娱乐。 今年我使用了 JMP 和一个阿杜诺纳米. 有关 nano 的更多信息,请参见下文。
因为我一直在玩将地球的图片包裹到一个球体上,我想知道我是否可以找到类似的头骨纹理来代替包裹。 我发现了更好的方法:这个地点有3D模型。 该项目使用链接指向的模型。 感谢有人使用这个名字可打印模型。
附件是完整的 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 sizeTexture map for skull, 1/4 sizeTexture map for skull, 1/4 sizeTexture map for skull, 1/4 size头骨纹理贴图,1/4 尺寸
OBJ 文件包含其余数据:3D 点及其 2D 纹理贴图坐标。 有文件格式的解释这里. 该文件包含v- 3D坐标的部分, vt-纹理贴图坐标的部分, vn-法向量的部分, F-部分面(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、空白行、一个带三个点的三角形面和一堆四边形面,以及一些我将忽略的 g 和 s 记录。
JSL 可以通过模式匹配有效地读取这些内容。 首先,将 OBJ 转化为字符串:
obj=za<<READ></READ>
String( "# 3ds Max ... 6171025 个字符 ...lygons
") 分配。
然后解析它。 设置一些列表以在解析时保存数据值,并制作一些辅助模式:
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},
...Faces 列表分为三角形和四边形。 三角形列表也是列表的列表:
{{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列表分段有四个项目,每个项目都包含一个片段名称(头骨、下颌、上牙、下牙)和起始位置的列表三角形和四边形列出。
此 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 windowHalloween early evening, model in windowHalloween early evening, model in windowHalloween early evening, model in window万圣节傍晚,橱窗里的模特
看起来眼睛是不是跟着摄影师走? 窗口底部的缝隙是怎么回事?
Arduino Nano 和 PIR 传感器
为了让眼睛跟踪人,需要某种传感器。被动红外传感器有一个单比特接口(有些东西被感动了!)和响应时间的一些限制。 考虑到万圣节的低投票率,他们是正确的选择。 Raspberry PI 相机可以提供更好的性能。 窗口下方的间隙允许五个 PIR 传感器与准直器观察人们可能行走的区域。 这是设置:
They do like like eyes. Those collimator tubes are re-purposed.They do like like eyes. Those collimator tubes are re-purposed.They do like like eyes. Those collimator tubes are re-purposed.They do like like eyes. Those collimator tubes are re-purposed.他们确实喜欢眼睛。 那些准直管被重新利用。
这是组装件的样子,用胶带粘好,用热胶粘好,并用跳线连接到 Arduino Nano。 纳米很小。
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 on left under zip-ties. PIR sensors on right hot-glued to collimators. There are two orange adjustments on each PIR.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 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 has wires soldered on, orange is +5, green is ground, purple/gray/blue are data linesNano has wires soldered on, orange is +5, green is ground, purple/gray/blue are data linesNano 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 JMPArduino IDE showing code to read sensors and send to JMPArduino IDE showing code to read sensors and send to JMPArduino IDE showing code to read sensors and send to JMPArduino IDE 显示读取传感器并发送到 JMP 的代码
安装例程运行一次;循环例程被一次又一次地调用,直到永远。 Delay(100) 在循环再次运行之前等待 1/10 秒。 2、3、4、5、6 是连接到传感器的引脚。 JMP 可以使用 DataFeed 读取打印到串行端口的任何内容。 这是 JSL 的另一个片段,使用单词() 函数从数据线上获取五个传感器值:
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 逻辑所做的。 其他一切只是保持传感器位如果传感器位打开则矩阵通过递增更新,如果传感器位关闭则通过衰减更新。 传感器保持开启状态至少三秒钟,计数上升到大约 10,然后很快回落到几乎为零。
仅此而已; JSL(附件)中有一个循环,靠近末尾,可以旋转眼睛和模型以响应传感器……并检测头骨何时感到无聊并运行一些动画来吸引捣蛋鬼。 即使没有任何连接,JSL 也可能会与数据馈送一起运行;它可能对 com 端口不满意,如果运行第二次可能真的不满意。 应该很容易注释掉 com 端口。
跟踪眼球确实有效,但有几秒钟的滞后。 它有助于了解站在哪里。 动画让观众很难知道他们正在被观看。
此外,JMP 目前并不真正支持纹理映射 (JMP 15),因此速度有点慢。 使用较旧的笔记本电脑,我仍然可以获得 15 FPS,这对于万圣节来说已经足够好了。
8pm, about to rain. Candy Corn half gone, glow sticks popular, vampire teeth and bubbles too!8pm, about to rain. Candy Corn half gone, glow sticks popular, vampire teeth and bubbles too!8pm, about to rain. Candy Corn half gone, glow sticks popular, vampire teeth and bubbles too!8pm, about to rain. Candy Corn half gone, glow sticks popular, vampire teeth and bubbles too!晚上八点,要下雨了。 玉米糖不见了一半,荧光棒很受欢迎,吸血鬼牙齿和泡泡也很受欢迎!
那台投影仪很亮! 这是特殊的投影仪支架,投影到半透明的窗帘上:
Not recommended if you have a cat.Not recommended if you have a cat.Not recommended if you have a cat.Not recommended if you have a cat.养猫的话不推荐。
和,以前的万圣节三部曲帖子。
short loop of animation possibilityshort loop of animation possibilityshort loop of animation possibilityshort loop of animation possibility动画可能性的短循环