对于引擎开发者

代码样式

这款引擎采用结构化程序。该代码由模块组织。没有用OOP方法都,没有定义的类,不进行继承等。

使用 K&R风格 除了对于一个组合操作的括号开口被放置在同一行上,例如:

function foo_bar() {
    // ...
}

if (a > b) {
    // ...
}

4个空格用于缩进(不允许tab)。

例子

下划线符号在函数和变量名称中使用:

var foo_bar = 123;  // correct
var fooBar = 123;   // wrong

所有的全局变量以下划线开头:

var _foo_bar = null;

这些常量用大写字母表示,从不以下划线开头:

var FOO_BAR = 100;

外部API方法和属性的名称均在点之后写的。为了避免混淆他们必须用 @ cc_externs 标签字段:

exports.FOO_BAR = 123;

exports.foo_bar = function() {

}

/**
 * Set properties.
 * @method module:properties.set_props
 * @param {Object} foo Foo object
 * @cc_externs props_1 props_2
 * @cc_externs props_3 props_4
 */
exports.set_props = function(foo) {

    var bar_1 = foo.props_1;
    var bar_2 = foo.props_2;
    var bar_3 = foo.props_3;
    var bar_4 = foo.props_4;

    ...
}

注释仅用英语。注释风格 - JSDoc。

构建引擎

构建之前,请确保您的系统已安装所有需要的依赖关系(参见 table).

要编译引擎,并纳入SDK,请执行以下命令(在SDK根目录)的应用程序:

make compile

完整的构建,包括将资源(纹理,声音和视频),编辑和转换的文档,可以用下面的命令执行:

make build

构建发布档案:

make dist

所有上述操作可以用一个命令来执行:

make all

构建插件

二进制的Blend4Web插件版本是可用于以下平台:Linux的X32 / 64,macOS的 x64,Windows X32 / 64。同时用户可以自行编译插件。

要做到这一点,Python 3.X(如果它是跟blender相同的版本更好)和C编译器是必需的。在Linux下,只需安装python3-dev 和 build-essential 的程序包。

路径相对于库根:
  • 构建脚本:csrc/b4w_bin/build.py
  • Blend4Web插件: addons/blend4web/

该构建过程开始于以下方式:

python3 ./csrc/b4w_bin/build.py

由于构建的结果,你会得到一个名为二进制文件:

b4w_bin_[PLATFORM]_[ARCHITECTURE].[STANDARD_EXTENSION],

和插件位于同一目录中。例如:b4w_bin_Linux_64.so。在此之后,插件已经准备好这个平台下使用。

依赖

所有依赖于下表中列出的重要性顺序递减。

名称 Ubuntu的16.04包 用途
Bash 默认情况下包含 脚本解释器
Python 3 默认情况下包含 脚本解释器
NodeJS nodejs 着色器编译
Java 默认的JRE 编译和混淆引擎模块
ImageMagick imagemagick 转换纹理
NVIDIA纹理工具 libnvtt-bin 转换纹理
Libav libav-tools 转换资源
NVIDIA CG工具包 NVIDIA-CG-工具包 着色器的调试
OptiPNG optipng 优化 PNG 文件
Emscripten from EMSDK source 代码 构建Uranium
Gnuplot gnuplot 偵錯
Graphviz graphviz 偵錯
xsel xsel 偵錯
Sphinx python3-sphinx 构建手册
sphinx-intl 使用 PIP v3 安装(pip3 install sphinx-intl) 构建手册(国际)
TeX Live texlive texlive-latex-extra texlive-lang-cyrillic texlive-lang-chinese texlive-xetex 构建说明书(PDF版)
JSDoc 3 使用 NPM 进行安装 (npm install -g jsdoc) 构建API文档
PEG.js 从PEG.js源代码 着色器预处理

命名函数和变量

当创建新的函数和变量,建议使用以下前缀和后缀。

init_
创建抽象物体
create_
创建实际的物体
update_
更新现有物体的状态
attach_/detach_
添加/删除临时物体属性
append_/remove_
添加/删除临时属性同类的现有属性
insert_/pop_
添加/删除数组元素(按索引访问)
switch_
切换标志的二进制值
apply_/clear_
操作标志,二进制值或任意参数的操作
set_/get_
设置/获取属性/变量值
_tmp
全局变量 - 缓存在一个简单的物体的形式(数组,向量)
_cache
全局变量 - 缓存在一个复杂的物体的形式

调试中

引擎调试在 debug.js 模块方法进行。

当前的渲染图的结构可以用 b4w.debug.scenegraph_to_dot() 调用,例如,在浏览器的控制台被保存在DOT格式。调用此方法后,保存控制台输出与.gv扩展名的文件。为了得到在视觉形式图表中的 graphviz的 是必需的工具。转换为SVG格式是使用命令执行:

> dot -Tsvg graph.gv -o graph.svg

其中,graph.gv 是保存的图形文件的名称。

着色器编译

在引擎中使用的所有着色器由编译器处理。编译器执行以下三个主要步骤:

  • shader代码的验证,
  • 它的混淆处理,
  • 优化

为了编译运行,在SDK根处键入以下命令执行:

> make compile_shaders
> make verify_shaders
  • make compile_shaders - 执行验证,混淆,优化和最后,编译着色器的导出,
  • make verify_shaders - 只执行验证,混淆和优化。

在编译期间首先进行着色文本的语法分析(解析)。相应的解析器自动创建基于语法,使用 PEG.js 引擎。然后将由着色器验证,混淆和优化根据解析器数据,之后该着色器中抽象语法树(AST),用于在引擎直接装载的形式输出。

资源库中的主要文件的位置:

  • 初步的语法 - glsl_utils / pegjs / glsl_parser.pegjs
  • 解析器生成脚本 - glsl_utils / pegjs / gen_nodejs.sh
  • 解析器 - glsl_utils /编译器/ glsl_parser.js

验证

编译器执行与着色器代码验证以下程序:

  • 报告有关未使用的变量和函数(死码)
  • 检查着色器的语法,
  • 检查着色器的一致性导入/导出机制,
  • 去除奇数或重复的标记:空格,endlines和分号。

混淆

混淆minifies的GLSL代码,使得它很难理解它。到目前为止以下过程实现:

  • 具有较短的单符号(与支持导入/导出机制的)替换用户定义的标识符,两符号等的名称。

优化

优化构成以下程序:

  • 除去大括号,不能在除了创建本地作用域任何方式有用(这一功能是用于处理节点/灯指示),
  • 内部功能的优化 - 创建共享局部变量,以取代原来由程序员创建的。

删除未使用大括号的一个例子:替换下面的代码

void function(){
    int a;
    {
        a = 1;
    }
}

与这些代码

void function(){
    int a;
    a = 1;
}

临时局部变量的低数量是由在不同情况下重复使用它们来实现的。例如,下面的代码

int function(){
    int a = 1;
    int b = a + 3;
    return b;
}

Blender内置渲染引擎的纹理绘图类型已被其它三个模块所取代:

int function(){
    int _int_tmp0 = 1;
    _int_tmp0 = _int_tmp0 + 3;
    return _int_tmp0;
}

注解

对于结构和数组的局部变量未优化这种方式。

导入/导出指令

导入/导出指令用于组织,结构和增加着色器代码的可读性中包含文件。它们在文件的开头指定,应该看起来大约是这样的:

#import u_frame_factor u_quatsb u_quatsa u_transb u_transa a_influence
#import qrot

#export skin

#import 指令定义了一组声明的包含文件之外,但可以从里面被访问的ID。这里有一个限制,但:这样的id必然是上述地方,其中包括文件链接的地方声明。

#export 指令定义了一组可以从该文件外部访问的ID。这样的id必然在这个文件中声明。

因此,它使用的着色器包含文件必须拥有所有必要的连接位置之前,进口的声明,以后它可以使用导出的标识。

IDS既可以是变量名和函数名。如果没有导入/导出命令中,它在默认情况下认为,包括文件不使用外部声明和内部的人的使用不允许。

建议和限制

由于以下几个原因:预处理,处理多个着色器和包括文件和由于编译器的功能的需要 - 其可能保证只有当一些规则和限制方面的着色源推崇的输出代码的工作码:

  1. 为了描述这是由引擎在运行中定义的常量,有必要使用``#var``特殊指令。例如:
#var AU_QUALIFIER uniform
AU_QUALIFIER float a;

这里的语法是类似的#define指令。所述#var指令的一点是,它定义了值允许解析初始着色。它是不相关的这将是什么(在上面的例子如“制服”或“属性”),因为在这个水平上它是未知反正。不过,最好是指定一个或多或少适当的描述,不是随心所欲。

注解

该#var指令没有必要不是在着色的代码,但在预处理器表达式中使用的常数。

  1. 在需要的时候使用导入/导出指令。
  2. 内置的功能,不能超载 - 只有用户的。
  3. 变量不应与的内置函数名称,或者主要(即使它不导致错误)中声明。
  4. 的#var和#define指令不能被用于以这样运营商更换单个符号:“ ”,“ - ”,“* =”,“/ =”,“ =”,“ - =” ,“==”,“<=”,“> =”,“!=”,“&&”,“||”,“^^”。

例如:

#var EQUAL =
...
a *EQUAL b;
...
  1. #include指令的使用不应该包含文件的混淆中产生歧义。当多个着色器纳入相同的文件,上面定义的指令(如#var或者将#define)可以对任何人的影响,可能发生这种情况。此外,最好不要使用未声明的函数和变量中包含文件。
  2. 多级包括或相同的多个纳入到包括不支持相同的着色器。
  3. 着色器的故障也可以通过使用非平凡的预处理,例如,创建一个无效的GLSL代码引起的:
#if TYPE
void function1() {
#else
void function1(int i) {
#endif
    ...
}
  1. 不要声明与这样的名字变量 node_ [NODE_NAME] _var_ [IN_OUT_NODE],其中 NODE_NAME —一些节点的名称,IN_OUT_NODE —输入或输出的名称节点。
  2. 重复使用 #nodes_main#nodes_global#lamps_main 指令是不是一个单一的着色器内禁止。
  3. #nodes_main#nodes_global#lamps_main 指令,建议在该文件中使用,包含这些着色器节点的说明,例如,在同一个包含文件。这是必要的正确的着色器验证。

WebGL的扩展

如果他们以某种方式影响着色语言所使用的编译可能取决于WebGL的扩展。目前以下扩展名由编译器的支持:

  • OES_standard_derivatives

编译错误

在发生错误时,编译器会在控制台输出相应的消息。

可能发生的错误的表:

错误信息 原因
错误!含糊不清的包含文件“FILE_NAME”。 在“FILE_NAME”含糊不清包含文件。
错误!扩展名是在混淆器不支持。文件:“FILE_NAME”。 在FILE_NAME文件中使用的名称WebGL的扩展不被混淆器支持。
错误!包括“FILE_NAME找不到。 该FILE_NAME包含文件找不到。
Error! Undeclared TYPE: 'NAME'. File: 'FILE_NAME'. FILE_NAME文件中错误。类型TYPE(变量,函数,结构等)的未声明标识符名称。
Error! Undeclared TYPE: 'NAME'. Importing data missed. File: 'FILE_NAME'. 类型TYPE(变量,函数,结构等)的未声明标识符名称。声明中缺少在FILE_NAME所需的标识符包括根据``#import``指令文件。
Error! Undeclared TYPE: 'NAME'. Possibly exporting needed in include file 'INCLUDE_NAME'. File: 'FILE_NAME'. 在FILE_NAME文件有错误。类型TYPE(变量,函数,结构等)的未声明标识符名称。可能出口到INCLUDE_NAME include文件应该被允许。
Error! Undeclared TYPE: 'NAME'. Possibly importing needed. File: 'FILE_NAME'. 类型TYPE(变量,函数,结构等)的未声明标识符名称。也许应指定为FILE_NAME进口包含文件。
Error! Unused export token 'NAME' in include file 'FILE_NAME'. 未声明的标识符名称是允许在出口FILE_NAME包含文件。

Error! Using reserved word in TYPE 'NAME'. File: 'FILE_NAME'. 错误FILE_NAME文件。的预留ID用于声明类型TYPE(变量,函数,结构等)的标识符名称。
Error! 'all' extension cannot have BEHAVIOR_TYPE behavior. File: 'FILE_NAME'. 对于指定的 #extension 指令 在FILE_NAME文件 all WebGL的扩展不支持的行为 BEHAVIOR_TYPE。
Syntax Error. ERROR_MESSAGE. File: FILE_NAME, line: LINE_NUMBER, column: COL_NUMBER. 解析FILE_NAME着色器在线路LINE_NUMBER列COL_NUMBER语法错误。最初的错误描述在ERROR_MESSAGE是引用。代码清单来自各地的相应线路连接到邮件采取的(注意哪些指定哪个才是真正的错误后,一点点就行pegjs解析器的特点。
Warning! Function 'NAME' is declared in [include ]file FILE_NAME, but never used. 未使用的函数名在FILE_NAME文件中声明。
Warning! Include file 'FILE_NAME' not used in any shader, would be omitted! 该FILE_NAME包含文件未在任何着色器的使用,所以它会从模糊版本被排除在外。
Warning! Unused import token 'NAME' in include file 'FILE_NAME'. 未使用的ID名称在FILE_NAME导入包含文件。
Warning! Variable 'NAME' is declared in include file FILE_NAME, but never used. 未使用的变量名称在FILE_NAME文件中声明。

更新插件翻译

如果您需要更新现有的所有.po文件,运行不带参数的SDK / scripts目录中的脚本 translator.py

> python3 translator.py

为了更新现有的.po文件,以支持的语言代码作为参数运行脚本:

> python3 translator.py ru_RU

为了查看支持的语言列表,运行脚本如下:

> python3 translator.py help

在任何情况下,该文件 empty.po 将在运行脚本进行更新。

更新后的.po文件可以如常编辑/转换。