2016.4.15

 • 

说来好笑,在去渡鸦和陈叔一起做项目的前一天,我刚刚因为之前没去上课被叫去训话。然后第二天我飞去北京,变本加厉地敲了两周课,算是非常过瘾的体验 —— 毕竟,不是每个学生都有正经理由逃学的。
因为之前并没有在公司正经干活的经验
1 ,所以我也无从对比,但在渡鸦感觉挺轻松,而且做的项目非常有趣,等到真正做完可以展示的时候,倒是可以写一写踩过的坑。
所以我现在回到了学校,准备继续远程工作 —— 对于一个不是纯代码的项目,远程工作同步进度一直很困难

2

。能做的只是尽量把所有原本可以机器布局的内容用代码手动布局,除此以外,要忍受糟糕的网络同步环境和奇怪的因为不完全同步产生的软件问题,bug,crash,以及随之带来的烦躁和信心丧失。

  1. “Quadera, inc 因为办公场合在咖啡厅和别人家里面 并没有真正身处公司的感觉”

  1. 即使是把素材文件用文本的方式序列化,想用 git 来处理这些大文件也是几乎不可能的

Remote access to leap motion data

 • 

总结:迷一样的 leap motion

服务器端设置

开启允许远程连接设置

参考:https://developer.leapmotion.com

其中关于websockets_allow_remote

Key string websockets_allow_remote
Value type boolean
Default value false
Purpose Allows non-localhost clients to connect

就是说,要去把默认关掉的这个参数改为开启,于是要去改 leap motion 的 config 文件,config.json的文件路径:

Linux:  
~/.Leap Motion/config.json
Mac:
~/Library/Application Support/Leap Motion/config.json
Windows: 
%AppData%\Leap Motion\config.json

改完后记得重启 leap motion service 才能生效,重启方法:Mac / Windows

测试是否开启成功

leap motion 安装的时候自带了一个名为 SDK Samples 的文件夹,我已经忘了 Mac 版是不是默认安装到这个目录了,不过还是写下,至少便于自己以后访问这个文件夹:

/Developer/LeapSDK/samples

修改JSONViewer.html第28行,从

function init() {
    // Create and open the socket
    ws = new WebSocket("ws://localhost:6437/v6.json");

改为

function init() {
    // Create and open the socket
    ws = new WebSocket("ws://你的目前的内网 IP 地址:6437/v6.json");

然后测试是否能够从内网 IP 访问,测试时请确保连接上 leap motion,control panel 里打开了 tracking 功能,没有开启任何 proxy 软件,若有数据刷新,则成功。
此部分参考:https://community.leapmotion.com

客户端设置

var controller = new Leap.Controller({host:'xxx.xxx.xxx.xxx', port:6437});
controller.setBackground(true);

以上是 javascript SDK 的 Controller constructor,可以直接定从哪个地址读取数据,但是 Unity/C# 版本目前貌似没有这个功能。

Cetacea

 • 

Cetacea 设计图
Cetacea 是一个最近正在写的 iOS markdown 客户端,起因是我发现目前没有一个适合的在手机上使用的编辑器,主要是这么几个点:

  • 主题提供的太少,而且大部分都不是很好看
  • Ulysses 提供了很多好看的主题(包括编辑器内的高亮主题),但是手机版略贵
  • 很多桌面编辑器上都有的一个功能就是同步预览(scroll sync),但是在 iOS 的设备上没有看到类似的功能,虽然有可能是有人认为这个是伪需求 —— 不过我觉得我倒是挺需要这个功能的

于是开始设计&写这么一个东西,目前只能说实现了基本的功能,慢慢写着。
demo gif

给 Ghost 加上 Bigfoot 支持

 • 

Bigfoot.js 是一个很漂亮的 footnote 脚注插件。我之前在 Xhacker 的网站看到过(比如他的这篇文章),当时就觉得非常优雅,但一直不知道这个东西叫啥,今天才知道叫 Bigfoot,并且集成到我目前在用的 Vno 主题里面,效果就是这段话右边的气泡按钮1 .

1.下载所需文件

Bigfoot 依赖 jquery,所以需要下载以下内容:
Bigfoot.js
jquery
(当然,大部分 Ghost 主题应该都有 jquery 了,只要保证版本号高于 1.8 即可)

2.移动文件

Ghost 的主题结构应该是这个样子:

将下载得到的 bigfoot.min.jsjquery-1.12.2.min.js(如果你的主题没有 jquery ) 放入 js 文件夹,
将下载得到的 bigfoot-default.css 放入 css 文件夹。

3.修改文件

打开 css 文件夹里面的主文件,比如在我使用的 vno 主题里面是 vno.css,根据不同主题会有变化
添加:

@import url(../css/bigfoot-default.css);

然后打开主题根目录的 default.hbs ,添加以下内容(一般在 {{ghost_foot}} 后面):

{{! 注释:jquery 请根据情况导入 }}
    <script type="text/javascript" src="{{asset "js/jquery-1.12.2.min.js"}}"></script>  
    <script type="text/javascript" src="{{asset "js/bigfoot.min.js"}}"></script>
    <script type="{{asset "text/javascript"}}">
        $.bigfoot();
    </script>
    <script type="text/javascript">
        var bigfoot = $.bigfoot(
            {
                deleteOnUnhover: false,
                preventPageScroll: false,
                hoverDelay: 250
            }
        );
    </script>

保存,push 到服务器里去。

4.使用 Bigfoot

Bigfoot 的使用是靠直接写 html 的,使用如下:
在 Ghost 的 Markdown 编辑器内:
文章需要显示气泡处,添加:

<sup id="fnref:1"><a href="#fn:1" rel="footnote">1</a> </sup>.  

其中数字 1 代表脚注序号。
文章末尾处,添加:

<div class="footnotes"><ol>  
    <li class="footnote" id="fn:1">
        <p>footnote 里面要显示的内容<a href="#fnref:1" title="footnote 在文末的标题"> ↩</a><p>
    </li>
</ol></div>

序号要对应。
好了,这个时候更新页面,应该会出现了,如果显示不对,请刷新浏览器缓存。

  1. 真的是非常漂亮,又优雅的脚注方式

2016.3.23

 • 

今天我妈还在打电话和我说一定要拿毕业证书的事情。
我给她说,有一个人和我是同一年上大学。然后他大一后就休学了,现在在阿里巴巴实习。照这样算下去,等到大部分人大四按部就班又啥也不会地毕业,他估计都已经有两年的工作经验了,这个差距该有多大。
我妈说,那是特例。
我就觉得很好笑的一点是,小孩子还小的时候,家长抢着要让赢在起跑线上,结果长大了,孩子想比别人先走一步,家长却怕的不能行。
还有就是特例这个词,我很烦这个词,因为一般都带有宿命论的意味:某某人技术很厉害,简直是天才,那就是天生的有才华。然后一众人心安理得继续碌碌无为下去,该打游戏打游戏,毕竟 —— 人家是天生的啊,怎么能赶得上呢。
事实是,这些人懒罢了。互联网行业是个很幸运的行业,没有这么多阶层固化,至少是相对传统行业,向上流动的机会大了很多。就这样学校里面还有很多人整天懒得不行,口口声声想做项目然后啥也不学,API 文档都不愿意花时间查,那还指望什么呢。

Virtual Terrain Project

 • 

网站地址
Virtual Terrain Project 是一个有关星球在计算机图形上的集合:从地形生成/LOD,大气散射模拟,海洋模拟到过程生成城市布局,应有尽有。
很多东西在我做 Epoch 的时候提供了很多思路,虽然我暂时没能力按照 Paper 直接手动实现一遍。
挑几个有趣的:

Spherical Textures Mapping

http://vterrain.org/Textures/spherical.html

Terrain LOD: Runtime Regular-Grid Approaches

http://vterrain.org/LOD/Implementations/

Sky / Atmospheric Rendering

http://vterrain.org/Atmosphere/

Clouds

http://vterrain.org/Atmosphere/Clouds/

Water

http://vterrain.org/Water/

Procedural Building Implementations

http://vterrain.org/Culture/BldCity/Proc/

Epoch Dev Blog 4.5

 • 

Unity 的 编辑器 bug 仍在继续,5.3.4f1 及 5.4.0 beta 确认。
所以我没有对核心的东西继续开发(因为一旦调试 Editor 就崩溃啊摔),而是做两件事情。
一个是写 SceneKit 版本的 Epoch,Github 地址 https://github.com/FinGameWorks/Epoch-Remastered,当然这个东西是不可能很漂亮的,毕竟 SceneKit 本身没有提供很有力的工具,不像 Unity 那么容易做出漂亮的效果,只是作为没法继续 Unity 的无聊的替代品。

一个是给 Unity 的 Epoch 添加了 Cardboard 支持,效果真的非常震撼。
以下是两段在 iPhone 6S 上录制的视频
https://www.youtube.com/watch?v=W-R63Cd05bM
https://www.youtube.com/watch?v=NEUoIn9SnlQ

如果有人想要尝试一番的话,请 Twitter/E-mail 联系我告知我您的 Apple ID 邮箱,我会把你加入 Epoch 的 testflight public beta program。
以上。

2016.3.16

 • 


新学期新气象 重修科目要缴费,一个学分八十块,一门课四点五个学分,立马就是几百块。
Apple Developer 账号开始提醒我续期了,这么算下来又是几百块。
手机 Home 按键坏了,换了手机,这么算下来....
穷。以后就是为了省钱,也是要去听听课的....
上周 去看了 疯狂动物城,Zootopia --> Utopia,很明显是有一些寓意的,不同人看的内容和观点也不一样,不细说了。
昨天给 Epoch 加了 Cardboard 支持,感觉很不一样 —— 以往很难表现出太空船的体积,但在 Cardboard 里面转动下头,整个空间感就出来了。

2016.3.13

 • 

....我才发现原来 WWDC Scholarship 是不需要写什么特殊的 app 的,之前没注意看 Guidelines,现在才发现原来就是要写一个“个人简介”的软件。
WTF?

最近的情况大致就是,Epoch 在 Unity Editor 里 Crash 的问题仍然严重,从 5.1.3f1 到 5.3.3p3 都试了,没有一个可用,只能说要么是 Unity 的这个问题太隐蔽了,要么就是我的项目文件彻底地 corrupt 了
(顺便 敬请关注推特账号 @Unity 今天崩溃了么,欢迎 DM 投稿)

于是我闲的无聊,开始把 Epoch 的一些东西往 SceneKit 上搬。大致就是 用 libnoise 生成各种图,Height,Normal,Color 一类的,然后做一个可以根据 Height Map 更新 Mesh 的星球,过程中槽点挺多:

  • SceneKit 作为一个游戏引擎,竟然基本的运算符重载都不给,比如 SCNVector3 * float 都不行
  • WWDC 里面一个 Session 工程师说的大致意思就是 Model I/O 和 SceneKit 无缝结合,但是实际情况是可以互相转化,但是基本没用,因为该不支持的还是不支持,比如带相机全屏特效的 MDLCamera 转换成 SCNCamera 就没特效了,PBR 管道的 MDLMaterial 转换成 SCNMaterial 还是只能用非 PBR 的 Light Model
  • GLSL to SCNProgram / shaderModifier 这块文档略少,我还本来就不会写 Shader,更是看不懂(摊手

然后除去 这些事情 最近还做了

  • 投了腾讯的实习申请
  • 睡觉

Create a Cube Sphere in SceneKit

 • 

前言

Cube Sphere 是一种奇特的球体,不同于 Geodesic Sphere 和一般 Sphere,更像是一个 Cube 通过某种变换得到的 Sphere。

Cube Sphere 有个好处,就是更适合来做过程生成的星球,因为每个面都可以用 QuadTree 无限细分(详见:https://acko.net/blog/making-worlds-4-the-devils-in-the-details/

过程

1.新建一个 SCNBox
2.对其修改顶点(vertices)
3.重新计算 Normal
4.刷新 SCNode 的 Geometry

1.新建 SCNBox

SCNBox *SCNBoxToSphereMapping = [SCNBox boxWithWidth:60.0f height:60.0f length:60.0f chamferRadius:0.0f];
    SCNBoxToSphereMapping.widthSegmentCount = 16;
    SCNBoxToSphereMapping.heightSegmentCount = 16;
    SCNBoxToSphereMapping.lengthSegmentCount = 16;
    
    SCNNode *PlanetNode = [SCNNode nodeWithGeometry:SCNBoxToSphereMapping];
    [PlanetSceneKitView.scene.rootNode addChildNode:PlanetNode];
    
    [SCNTransaction flush];
  • SegmentCount 是 2 的 n 次方 因为后面(当然不是这篇文章)要做 QuadTree
  • [SCNTransaction flush]; 是很关键的一步,(详见http://stackoverflow.com/questions/17760275/geometry-from-scenekit-primitives?lq=1 简单说来,就是不 flush 的话,获取的 geometry data 就是 SegmentCount = 1 的默认 SCNBox 数据,而默认的 SCNBox 顶点只有八个,不足以变换成球体)

2.对其修改顶点

// Get the vertex sources
    NSArray *vertexSources = [PlanetNode.geometry geometrySourcesForSemantic:SCNGeometrySourceSemanticVertex];
    
    // Get the first source
    SCNGeometrySource *vertexSource = vertexSources[0]; // TODO: Parse all the sources
    
    long stride = vertexSource.dataStride; // in bytes
    long offset = vertexSource.dataOffset; // in bytes
    
    long componentsPerVector = vertexSource.componentsPerVector;
    long bytesPerVector = componentsPerVector * vertexSource.bytesPerComponent;
    long vectorCount = (long)vertexSource.vectorCount;
    
    
    SCNVector3 vertices[vectorCount]; // A new array for vertices
    
    // for each vector, read the bytes
    for (long i=0; i<vectorCount; i++)
    {
        
        // Assuming that bytes per component is 4 (a float)
        // If it was 8 then it would be a double (aka CGFloat)
        
        //xyz 3 componet
        float vectorData[componentsPerVector];
        
        // The range of bytes for this vector
        NSRange byteRange = NSMakeRange(i*stride + offset, // Start at current stride + offset
                                        bytesPerVector);   // and read the lenght of one vector
        
        // Read into the vector data buffer
        [vertexSource.data getBytes:&vectorData range:byteRange];
        
        // At this point you can read the data from the float array
        
        //
        float x = vectorData[0] / SCNBoxToSphereMapping.width * 2.0f;
        float y = vectorData[1] / SCNBoxToSphereMapping.width * 2.0f;
        float z = vectorData[2] / SCNBoxToSphereMapping.width * 2.0f;
        
        float SphereX = x*sqrt(1-pow(y,2)/2.0f-pow(z,2)/2.0f + pow(y*z,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;
        float SphereY = y*sqrt(1-pow(z,2)/2.0f-pow(x,2)/2.0f + pow(x*z,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;

        float SphereZ = z*sqrt(1-pow(x,2)/2.0f-pow(y,2)/2.0f + pow(y*x,2)/3.0f) * SCNBoxToSphereMapping.width / 2.0f;

        
        // ... Maybe even save it as an SCNVector3 for later use ...
        vertices[i] = SCNVector3Make(SphereX, SphereY, SphereZ);
        
        // ... or just log it
        NSLog(@"x:%f, y:%f, z:%f", x, y, z);
        NSLog(@"SphereX:%f, SphereY:%f, SphereX:%f", SphereX, SphereY, SphereZ);
    }

3.重新计算 Normal

SCNGeometrySource *DeformedGeometrySource = [SCNGeometrySource geometrySourceWithVertices:vertices count:vectorCount];
    NSArray *SCNGeometrySourceArray = [NSArray arrayWithObject:DeformedGeometrySource];
    NSArray *DeformGeometryElement = [PlanetNode.geometry geometryElements];
    SCNGeometry *DeformedGeometry = [SCNGeometry geometryWithSources:SCNGeometrySourceArray elements:DeformGeometryElement];
    
    
    MDLMesh *DeformedGeometryUsingMDL = [MDLMesh meshWithSCNGeometry:DeformedGeometry];
    [DeformedGeometryUsingMDL addNormalsWithAttributeNamed:MDLVertexAttributeNormal creaseThreshold:1.0f];
  • 一个自定义 SCNGeometry 需要两样东西,SCNGeometrySource 和 SCNGeometryElements,SCNGeometryElements 在我的理解中就是用来描述顶点之间连接成三角形的顺序。当然实际肯定并非如此简单,只是便于理解我们只是修改了顶点的位置,但连接顺序并没有改变,因此 SCNBox 的 SCNGeometryElements 是可以继续用在新的 Sphere 上的
  • MDLMesh 是 Model I/O 的东西,用的时候和 SceneKit 一起记得导入以下头文件:
#import <SceneKit/SceneKit.h>
#import <ModelIO/ModelIO.h>
#import <SceneKit/ModelIO.h>

4.刷新 Geometry

DeformedGeometry = [SCNGeometry geometryWithMDLMesh:DeformedGeometryUsingMDL];
    PlanetNode.geometry = DeformedGeometry;

记得打开 Debug 的线框预览功能

PlanetSceneKitView.debugOptions = SCNDebugOptionShowWireframe;

效果如下: