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;

效果如下:

Build libnoise on iOS!

 • 

libnoise 是一个 Cpp 的噪声库 写了一个简易的教程 怎么把这个东西做成一个可以 iOS 用的 lib

1.Download libnoise from http://libnoise.sourceforge.net/
2.Navigate to libnoisesrc-1.0.0/noise/src, and select files like this:

No makefile, just .h and .cpp files, and don't select win32 folder, you are building in Xcode.

3.On your current Xcode project menu, Select File/New/Target/iOS/Frameworks & Library/Cocoa Touch Static Library, hit next and name it "libnoise" or whatever :-)
4.Now, Drag files you selected in Finder to Xcode like this:

Things to remember to check:

  • Copy items if needed
  • Create Groups
  • Add to targets, just select the new library we have created.

And now our project will looks like this: (I made a group from these files so you will see a top folder named libnoise)

5.Switch your target to libnoise (or whatever you named it when creating library target), we will check something.

6.Navigate to Build Phases/Compile Sources, see if all .cpp files are in there.

7.Jump back the target to your app, make sure Build Phases/Compile Sources have these:

  • Xcode's target are on your app
  • Target Dependencies have libnoise
  • Link Binary With Libraries have libnoise.a

8.Add libc++.tbd in General Tab, or in Build Phases.
9.Create a UIViewController (like NoiseDebuggerViewController), and change NoiseDebuggerViewController.m to NoiseDebuggerViewController.mm
10.In NoiseDebuggerViewController.mm:

  • import "noise.h"

  • paste these codes to viewDidLoad():
noise::module::Perlin myModule;
double value = myModule.GetValue(1.25, 0.75, 0.5);
NSLog(@"Value : %f",value);

and Press Run, make sure you have real iDevice plugged in instead of Simulator.
See the Log!

Value : 0.686347

Now you have libnoise on iOS! We are ready to use it for procedural planet generation on iOS!

Epoch Dev Blog 4

 • 


👆用来提交崩溃的 Bug Report 都崩溃

Unity 关于 PrepareShadowMaps 还有 Occlusion Culling 崩溃的 bug 在5.3的最新 Patch 版本上仍然存在 让我的进度延缓了不少 因为没办法实时 Editor 预览了 只能自己写一堆东西 试着 build 到 iPhone 上看
所以最近没做什么事情 大致有这几个
1.加了God Ray(Sun Shafts)

2.让游戏根据不同机型调整分辨率 比如 iPad mini 2 这种 A7+大屏Retina 的组合 就运行在 0.75倍率原始分辨率上 然后更老的机型就使用Non-Retina
3.改进了SpeedTree的树木,准备了很多花花草草还有大树

4.研究了下 Noise 图生成 HeightMap 后怎么在 Runtime 生成 Splatmap 留给地面贴图用
5.试着重写树木生成的脚本(但失败了),等脑子清醒并且 Unity 不会那么容易崩溃的时候再试着重写一遍 主要是准备直接从 HeightMap 中 GetPixels 后 把根据 Alpha 值随机出的树木样式数据固化到某个地方 然后做一个 Pool Manager 来管理 Spawn 和 Destroy

多说几句 因为 Unity 工作不正常的原因 最近又看了会 WWDC 学习用了下 ModelIO里面的 Voxel 感觉可以试着用 ModelIO 和 SceneKit 写一个类似的东西(根据噪声的过程星球生成) 如果能写出来的话倒是有个东西可以提交 WWDC 奖学金了

晚安