九天雁翎的博客
如果你想在软件业获得成功,就使用你知道的最强大的语言,用它解决你知道的最难的问题,并且等待竞争对手的经理做出自甘平庸的选择。 -- Paul Graham

编程世界中惯性的力量

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    今天下载lua的Windows二进制发布luabinaries
的时候,发现luabinaries的发布包含两个dll,lua5.1.dll,lua51.dll,其中lua5.1.dll大小合适,lua51.dll只有11K,感觉不像是个有用的lua dll,对此比较不解,查看了luabinaries的文档,

文档中如此解释:

The LuaBinaries DLL packages have a dll proxy called "lua51.dll". It can be used to replace other "lua51.dll" released by other distributions. It will simply forward calls to the "lua5.1.dll". There is no compiled source code involved in the forwarding.

也就是说,lua51.dll完全是一个lua5.1.dll的代理类,没有任何实际的代码,仅仅是做一个到lua5.1.dll的forwarding。

    不过文档解释了lua51.dll是什么,却没有解释为啥会需要一个这样的东西呢?我还从来没有见过类似的情况。要说有一点点类似的情况的话,也是以前做反外挂的时候,知道可以通过替换现有的dll,并且完全模拟原来dll的接口,并将不需要hack的函数全部forwarding到原来的dll中。对于lua怎么会需要这样的功能呢?

于是我google了一下,发现了原因:
The standard DLL name "lua51.dll" has been selected more than

three years ago. Around twice every year someone comes along and

thinks "Oh, we absolutely need a dot in the DLL version number".

Alas, Windows does not like an extra dot in there. Many things

break when you have an extra dot in DLL names. Depends on the

version of the OS, on the specific system call, on the library or

tool used ... it's hopeless. So please let's forget about it.

--Mike

来自lua-users.org的一个帖子

原来是3年前有人确定了一个lua51.dll名字的动态库,并且,有人觉得我们非常需要在5和1之间加一个点,不然lua的5.1版岂不是看成lua的51版了?

对此,有人进一步提出了疑问
,认为这个问题怎么这么久了竟然没有人修复?

接着有人回答了:
But who's duty is to resolve the issue?


It's clearly not an issue of Lua as a language. It's just a consequence of (a very popular) LuaBinaries once releasing lua5.1.dll that became a de facto binary standard, then authors of many third-party Lua libraries were releasing binary packages compatible with LuaBinaries.

Note: I'm not blaming LuaBinaries; that was just one unfortunate decision that is difficult to be undone. --

Shmuel


我们知道了:

LuaBinaries做出了一个错误的决定,但是已经发布了,很多第3方的库也发布了,并且依赖于LuaBinaries的这个lua51.dll,于是:
that was just one unfortunate decision that is difficult to be undone.

那仅仅是一个过去做下,现在难以撤销的不幸决定。。。。。。。。。。。。


    有的东西存在了,即使是不合理的存在,因为它存在了一段时间了,因为惯性,它还会存在在那里。编程中,这种情况经常出现。突然让我想起上个项目中,大家经常对项目中蹩脚代码存在原因的解释:“历史原因”。

    对于代码来说,即使大家都知道可以重构,但是重构是有代价的,很多时候大家就妥协在历史原因当中。

    对于语言来说,C++就是对历史进行最大妥协而产生的语言,大家都承认,假如当年C++不兼容C的话,C++根本就得不到现在这样的流行程度,也都承认,因为C++兼容C,(常常被称为历史的包袱)C++在语言的优美程度上损失了太多。

    想起国内某个大牛有过类似的感慨,“现在每做一个设计决定的时候都非常小心,因为那可能会被使用非常非常长的时间,当它还能正常工作的时候,甚至不会有人想要去重写它“

    呵呵,仅仅将这个有趣的事件作为编程中的一个轶事来看吧。

 

 

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

数据/配置 的存储方式 Lua篇

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

    在《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》《数据/配置 的存储方式 Json篇 以Cocos2D For Iphone+TouchJson为例
》中我总结了一下怎么使用Json作为配置,但是,其实Json虽然语法格式简单,同时解析库的使用也非常简单,但是,因为这种简单性,缺少有的时候的确非常需要的功能。比如配置文件中对另一行配置的引用,比如配置段的继承等,这些可以使用XML来解决,另外,有的时候还会需要在配置文件中进行简单的运算,比如我就常常会有想将某个图片缩放到目前分辨率的几分之几这样的需求,此时,用一个完整的可以进行运算的语言来做配置的诱惑实在太大了。。。。。。
    Lua就是为此而生的。。。。。。虽然现在Lua的功能已经远远不止这样了,但是最初设计Lua的目的就是一个简单的配置语言。见Lua的历史
。虽然其实用Python作为配置也完全可行,但是Lua有着速度快的优点,而且,Lua实在比Python小太多了,很适合嵌入到程序中去。

对于使用Lua作为配置,事实上就相当于在C++中嵌入Lua,只不过不用其脚本语言的一些复杂特性,仅仅将其作为一个配置文件来看。

这里还是以原来《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》文中开始的SDL工程作为例子。

首先搭建可以内嵌使用Lua的C++编译环境,下载一个luabinaries
是简单的办法,现在我使用的是lua5_1_4_Win32_dll8_lib.zip
。这里我甚至不需要一个可以运行的Lua交互环境。。。。。

简单示例

首先建立一个简单的Lua文件,命名为picture.lua
name="dragon.png"

rotation=180



虽然只有简单的两行,也是个合法的Lua程序。

然后在C++程序中读取出来,这个过程实际上是使用了Lua的C API,具体的Lua的C API这里就不详细讲解了,那比较复杂,可以参考《Programming in Lua》,中文版也已经有了。这里仅仅作为一个例子提供参考吧。
现在可以开始完成C++的程序了:
首先,包含必要的头文件:
extern "C" {
#include "lua/lua.h"
#include "lua/lauxlib.h"
#include "lua/lualib.h"
}
因为lua的头文件完全是只考虑C语言的情况,所以在C++中使用的时候需要自己添加extern "C"。这是典型的对C++不屑的态度,因为现在大部分的纯C语言写的库,都会很自然的通过宏判断来自动使用extern "C"语句。

然后:
using
 namespace
 std;
struct
  PictureInfo {
  string name;
  float
 rotation;
}gPictureInfo;

void
  PictureInit() {
  lua_State *L = luaL_newstate();
  if
( luaL_dofile(L, "picture.lua"
) != 0
) {
    printf("Error happen."
);
    // handle the error.

    exit(1
);
  }

  lua_getglobal(L, "name"
);
  assert(lua_isstring(L, -1
) == 1
);

  gPictureInfo.name = lua_tostring(L, -1
);

  lua_getglobal(L, "rotation"
);
  gPictureInfo.rotation = (float
)lua_tonumber(L, -1
);

  lua_close(L);
}

这里我们使用的所有变量全部都是global的变量,所以调用起来非常方便。上面的主要内容就是对Lua C API的使用,由于其本身较为复杂,这里一下也讲不清楚。
基本流程是创建新的Lua state,然后用luaL_dofile
执行我们需要的配置文件,然后调用lua_getglobal
加适当的转换来获取我们需要的配置值。这里的例子是一个字符串和一个浮点数。
具体显示的效果和其他代码参考《数据/配置 的存储方式 Json篇 以JsonCpp库使用为例
》就好了,这里不重复描述了。

数组

    在Lua中数组也通过table的形式来表示,这里我仅仅介绍怎么从lua文件中获取配置,具体的显示等东西也不再累述了。
    这里,我们建立一个包含数组的lua文件作为配置:

data = {

    {
 name="dragon.png"
, rotation=180
 }
,
    {
 name="dragon.png"
, rotation=0
 }

}

下面看怎么将配置读取出来:

using
 namespace
 std;
struct
  PictureInfo {
  string name;
  float
 rotation;
};

vector gPictureInfoVec;

void
  PictureInit() {
  lua_State *L = luaL_newstate();
  if
( luaL_dofile(L, "picture.lua"
) != 0
) {
    printf("Error happen."
);
    // handle the error.

    exit(1
);
  }

  // get the table

  lua_getglobal(L, "data"
);
  assert(lua_istable(L, -1
) == 1
);

  /*
 table is in the stack at index 't'
*/

  lua_pushnil(L);  /*
 first key
*/

  while
 (lua_next(L, -2
) != 0
) {
    PictureInfo info;
    /*
'key' (at index -2) and 'value' (at index -1)
*/

    // push the key to stack for getting the value

    lua_pushstring(L, "name"
);

    // now the table is in the -2 and key in the top(-1)

    lua_gettable(L, -2
);
    assert(lua_isstring(L, -1
));

    info.name = lua_tostring(L, -1
);

    lua_pop(L, 1
);

    // push the key to stack for getting the value

    lua_pushstring(L, "rotation"
);

    // now the table is in the -2 and key in the top(-1)

    lua_gettable(L, -2
);
    assert(lua_isnumber(L, -1
));

    info.rotation = lua_tonumber(L, -1
);

    gPictureInfoVec.push_back(info);
    /*
 removes the key we pushed and the 'value' of the global table ;   keeps 'key' for next iteration
*/

    lua_pop(L, 2
);
  }

  lua_close(L);
}


代码中的注释解释的已经够详细了,但是因为Lua 的API的确不是太容易理解,这里也无法一下讲清楚,所以还是先了解Lua 的 API为好,具体的API的意义可以参考参考手册


上面仅仅使用了Lua API遍历数组以及从table中获取元素的方法。
假如仅仅只有上面这些,是看不出用lua作为配置的好处的,用lua做配置的好处在于可以利用lua的特性实现配置段之间的继承以及完整的运算功能。
比如说,完全不用修改上面的读取配置的代码,我们仅仅修改配置,看看在Lua中使用上述功能:

data1 = {
 name="dragon.png"
, rotation=180
 }

data2 = {
 name = data1.name, rotation = data1.rotation / 2
}

data = {

    data1, data2
}

此时,data2的数据完全依赖于data1的数据,当有任何东西需要修改的时候,你只需要修改一个地方,对于配置来说,don't repeat yourself也是很有意义的。最最重要的的是,在配置中能够进行计算那是非常强大,即使完全不用lua的函数功能与代码的交互,仅仅通过lua的计算,也可以完成游戏中所有sprite的布局。。。。。。

小结

    相对于使用XML,Json,使用Lua作为配置绝对是最最强大的。。。。。。作为一个完整的语言,它拥有你想在配置中实现的一切功能。但是缺点也是很明显的:
    首先,速度上,Lua需要解释运行,可能明显跟不上XML或者Json的解析速度。但是,配置的解析读取可以都放在初始化阶段,所以配置不是太多的时候,也不算太过难以接受,另外,还可以将lua的配置完全作为开发期的一种机制使用,在发布后完全转化为2进制数据。
    其次,Lua的配置读取需要手动调用Lua的API来完成,相对于XML,Json那种有很方便的库的情况,使用上还是麻烦一些,特别是Lua的API的使用并不是那么简单易懂,不算太直观。但是,这也不是不可以克服的,完全可以自己写个小型的库将Lua的API封装起来,形成一个类似JsonCpp那样的库,用Map来表示一切。这个可能需要限制一些Lua语法的时候,或者在解析的时候进行取舍,比如函数啥的可能需要过滤掉,不然那就不太像将Lua作为配置使用了。
    还有,Lua的配置的生成更加是没有XML,Json那样有完善库支持来的方便,这点暂时没有办法克服,写一个自动生成Lua配置文件的库感觉并不是太过容易的事情。而且,自动生成时,lua的优势其实并没有余地发挥出来,那样的话,还不如使用Json。
    总的来说,假如是没有工具,需要大量手写配置的话,对Lua的API进行一层简单的封装,然后使用Lua来做配置,那还是件很爽的事情,需要手写的时候,你才会感觉到一个完整语言作为配置给你带来的好处。

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

数据/配置 的存储方式 Json篇 以JsonCpp库使用为例

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

配置的好处

    我不知道我需不需要用一节内容来向大家讲解这个公认的事实。现在公认的事实实际上比单纯所谓的配置还要走的远,一般的看法,游戏中最好的用法是数据驱动而不是代码驱动。那样才能满足游戏需要灵活改变的特点,并且易于编辑。在开发期可以使用文本配置,以易于调试,XML是大多时候的选择。发布时通过资源处理,将文本配置转换为二进制配置以加快读取速度,减少解析时间。(假如效率可以接受的话,这一步甚至可以省略)配置对于C++这样编译时间超长的语言更加是非常必要(偏偏游戏编程大部分是用C++),当某个属性需要修改的时候只需要改动配置而不是文件,可以极大的减少开发期在编译中消耗的时间,特别是原来的宏定义的方式,假如很多地方需要而被放在了头文件中,每次的更改对于大型工程来说简直就是程序员休息上网的好时间。。。。。。。

JSon介绍

JSON
(JavaScript Object Notation) 是一种轻量级的数据交换格式。 易于人阅读和编写。同时也易于机器解析和生成。 它基于JavaScript Programming Language
, Standard ECMA-262 3rd Edition - December 1999
的一个子集。 JSON采用完全独立于语言的文本格式,但是也使用了类似于C语言家族的习惯(包括C, C++, C#, Java, JavaScript, Perl, Python等)。 这些特性使JSON成为理想的数据交换语言。

JSON建构于两种结构:

  • “名称/值”对的集合(A collection of name/value pairs)。不同的语言中,它被理解为对象 (object)
    ,纪录(record),结构(struct),字典(dictionary),哈希表(hash table),有键列表(keyed list),或者关联数组 (associative array)。
  • 值的有序列表(An ordered list of values)。在大部分语言中,它被理解为数组(array)。

这些都是常见的数据结构。事实上大部分现代计算机语言都以某种形式支持它们。这使得一种数据格式在同样基于这些结构的编程语言之间交换成为可能。

JSON具有以下这些形式:

对象是一个无序的“‘名称/值’对”集合。一个对象以“{”(左括号)开始,“}”(右括号)结束。每个“名称”后跟一个“:”(冒号);“‘名称 /值’ 对”之间使用“,”(逗号)分隔。

数组是值(value)的有序集合。一个数组以“[”(左中括号)开始,“]”(右中括号)结束。值之间使用“,”(逗号)分隔。

值(value
)可以是双引号括起来的字符串(string
)、数值(number)、true
false
null
、对象(object)或者数组(array)。这些结构可以嵌套。

字符串(string
)是由双引号包围的任意数量Unicode字符的集合,使用反斜线转义。一个字符(character)即 一个单独的字符串(character string)。

字符串(string
)与C或者Java的字符串非常相似。

数值(number
)也与C或者Java的数值非常相似。除去未曾使用的八进制与十六进制格式。除去一些编码细节。

空白可以加入到任何符号之间。 以下描述了完整的语言。

介绍部分完全完全copy自Json的中文网页(http://www.json.org/json-zh.html)。

下面才是我自己的内容:

为什么要使用Json

    Json的格式更加简单,同样的内容,用Json来表示比用XML表示时字符少的多,没有一大堆的标记及尖括号,决定了Json更加容易手写。同样的,字符少,信息量大,所以我认为也更加易读。而且,不常进行网页开发的我,非常讨厌尖括号!我不想去写那些重复的尖括号标记。。。。。。。。。。让人手写太多重复的东西简直就是犯罪。

    在使用Json以前其实我都不是太喜欢配置,因为使用太麻烦,写起来浪费时间,虽然说配置可以节省编译时间,但是有那功夫写读写配置的代码,都编译N次了。特别是以前用Excel或者SQL的时候,读写数据/配置,都是非常非常麻烦的事情。此时我想起一句LUA之父说的话,只有当配置的使用足够简单灵活,人们才会使用它。

Json的格式到底有多简单,其实上面的介绍很多其实归根于两句话:

  • “名称/值”对的集合(A collection of name/value pairs)。
  • 值的有序列表(An ordered list of values)。

仅此而已。

一种就是

{x : y}

形式的名称/值对,说成key-value对可能更多人更好理解。

一种就是

{x : [1,2,3,4] }

或者 [1,2,3,4]

形式的有序列表。也就是数组。第二个例子说明数组也可以作为根对象。

另加上明白一个{}构成一个json对象,然后,因为上面的3种形式,可以任意嵌套,也就可以满足任意复杂的需求了。

缺点:

    简单有简单的优势,但是也有简单带来的缺点,那就是缺乏高级特性,比如继承,引用这些很好用的特性在Json中就都没有,相对应的,XML中有类似的概念。另外,作为手工编写的配置,常常会需要用到运算,特别是引用+运算,当然,这些XML都很难做到,那得指望Lua了。事实上,我的确准备在以后尝试使用Lua做配置,因为以一个完整的语言来做配置的吸引力实在是太大了。。。。。。。。。。。另外,Json最后不支持可有可无的逗号(如C++ enum的语法格式那样),有的就必须有,没有的地方坚决不能没有,这个导致很容易出现问题,(主要是手写Json的时候),需要特别注意,一般可以通过工具验证一下自己的Json文件。比如Jslint
,jsonlint
.我个人常用jslint,报错非常详细,甚至有对不良语法的警告

Json有多流行?

看看有多少语言的解析库
,有多少语言有多个解析库就能一窥一二。对于JS来说,使用多方便就不用说了,像php,python事实上都已经原生支持Json的解析了。(通过函数或者标准库)

实例说明

    得益于简单的格式,所以解析及创建都可以做的很简单的,特别是JSONCPP这个库,在使用的时候简直就像获取到JS中的Object一样,直接通过 [] 操作完成索引及建立操作,使用起来非常简单。这里以JsonCPP + SDL + OpenGL 为例,来记录一下Json的使用。之所以选用SDL ,仅仅是因为我了解并知道怎么它们,作为了解Json来说,下面的例子中,假如对SDL不熟悉的话,知道那些大量冗余的部分都是用来实际绘图和控制程序流程的,只关心与Json相关的部分即可。本例实现的功能与图像有关,需要用图形想关的东西属于迫不得已。至于为啥举与图像有关的例子,仅仅因为那样更酷^^难道我以读取配置然后正常printf读取的配置为例子吗?

JsonCPP + SDL + OpenGL

JsonCPP
介绍

JsonCpp是我本人非常喜欢的一个Json解析库,有读写模块,实现具有很强的移植性,当时在公司项目中嵌入JsonCpp时,仅仅通过修改了一个读文件的接口就完美的集成了进去,无论是在Windows平台还是在IPhone平台上都运行良好,当时我还当心在IPhone上会碰到什么问题,但是结果是没有碰到任何问题。。。。。。。。。对此,我印象非常深刻。JsonCpp中带有完整的测试套件,对于这样一个底层库,有测试套件存在,让人使用的时候心里放心很多。另外,JsonCpp使用C++的Map特性,(得益于Json语法的简单)可以非常方便的查找需要的任何Json数据,并且效率不低,当然,因为JsonCpp使用了一种DOM方式解析Json文档(参考XML的描述),一次将全部文档都解析了以后,然后再查询使用,使用虽然方便,但是效率上还是逊于SAX方式,但是,对于小规模应用,这应该不是问题。jsoncpp的文件比较少,其实全部拷贝进自己的工程都完全没有问题。jsoncpp的协议是公有领域,也就是说作者完全放弃了版权,你可以随便使用,连版权协议都不用带。需要include的头文件就是json.h,但是需要将include/json下的头文件都拷贝到需要的地方。例子中我为了方便还是用了静态库,将json附带的VS工程的Runtime library改成Multi-threaded Debug DLL (/MDd),(个人习惯使用dll方式进行开发,这个需要和正在进行的工程一致)编译后,生成静态库。

这里我以某些参数显示某个图片为例,介绍json/jsoncpp的用法。

首先,原来的例子:

#include
<stdlib.h>

#include
<stdio.h>

#include
<tchar.h>

#include
"SDL.h"

#include
"SDL_opengl.h"

#include
"SDL_image.h"

#define WINDOW_WIDTH
300

#define WINDOW_HEIGHT
300

GLuint gTexName;
//OpenGL初始化开始

void
 SceneInit(int
  w,int
  h)

{

  gluOrtho2D(-1.0
, 1.0
, -1.0
, 1.0
);

  glShadeModel(GL_FLAT);

  SDL_Surface *surface = IMG_Load("dragon.png"
);

  if
 (!surface)

  {

    printf("Load the picture failed."
);

    exit(1
);

  }

  GLenum texture_format;

  // get the number of channels in the SDL surface

  GLint nOfColors = surface->format->BytesPerPixel;

  if
 (nOfColors == 4
)     // contains an alpha channel

  {

    if
 (surface->format->Rmask == 0x000000ff
)

      texture_format = GL_RGBA;

    else

      texture_format = GL_BGRA;

  } else
 if
 (nOfColors == 3
)     // no alpha channel

  {

    if
 (surface->format->Rmask == 0x000000ff
)

      texture_format = GL_RGB;

    else

      texture_format = GL_BGR;

  } else
 {

    printf("warning: the image is not truecolor..  this will probably break
/n
"
);

    exit(1
);

  }

  glGenTextures(1
  , &gTexName);

  glBindTexture(GL_TEXTURE_2D, gTexName);

  // Specify filtering and edge actions

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

  glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

  glTexImage2D( GL_TEXTURE_2D, 0
, nOfColors, surface->w, surface->h, 0
,

    texture_format, GL_UNSIGNED_BYTE, surface->pixels );

  glEnable(GL_DEPTH_TEST);

  glEnable( GL_TEXTURE_2D );

  //Free the loaded image

  SDL_FreeSurface( surface );

}

// display

void
  SceneShow(GLvoid) {

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBindTexture(GL_TEXTURE_2D, gTexName);

  glBegin(GL_QUADS);

  glTexCoord2f(0.0
 , 1.0
 ); glVertex3f(-1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 1.0
 ); glVertex3f(1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 0.0
 ); glVertex3f(1.0
 , 1.0
 , 0.0
 );

  glTexCoord2f(0.0
 , 0.0
 ); glVertex3f(-1.0
 , 1.0
 , 0.0
 );

  glEnd();

}  

int
 _tmain(int
 argc, _TCHAR* argv[])

{

  if
 ( SDL_Init(SDL_INIT_VIDEO) < 0
 )

  {

    printf("Unable to initialize SDL:
%s
/n
"
, SDL_GetError());

    exit(1
);

  }

  atexit(SDL_Quit);

  if
  (IMG_Init(IMG_INIT_PNG) == 0
  ) {

    printf("Unable to initialize SDL_image"
 );

    exit(1
);

  }

  // use these two lines instead of the commented one

  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1
 ); // *new*

  SDL_Surface* screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 16
, SDL_OPENGL); // *changed*

  SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

  // main loop

  bool
 running = true
;

  while
 (running) {

    //While there's an event to handle

    SDL_Event event;

    while
( SDL_PollEvent( &event ) ) {

      if
 (event.type == SDL_QUIT) {

        running = false
;

      }

    }

    SceneShow();

    //Update Screen

    SDL_GL_SwapBuffers();

    // delay, 50 for simple

    SDL_Delay( 50
 );

  }

    return
 1
;

}

代码主要就是SDL+OpenGL,很浅显易懂,可以参考原来的文章。《GLFW 简单入门学习
》,《SDL 简单入门学习
》,《在SDL 中使用OpenGL
》。

显示如下的图片:(为了减少截图提及,上面我特意将窗口创建成300*300这样比较小的体积了)


 

这是原图,现在我希望将其显示加入一定的属性配置,以达到我的显示要求, 这里将配置放到Json中完成。

构建一段最简单的Json文件:

{

    "name"
 : "dragon.png"
,

    "rotation"
 : 180
}



里面只有图片名字和旋转度数。顺便以此例解释下Json的语法,前面提到过Json的格式总的来说就是Key : value.如上所示,所有的key都是字符串,value可以是各种值,包括整数,boolean,字符串,数组,甚至是一个{ }表示的object。上例中,name表示图片的名字,rotation表示图片旋转的度数,这里的度数按照OpenGL的规范,以逆时针为正。

JsonCpp的使用就非常简单了。比如我用下列代码来解析上述Json文件:

struct
 PictureInfo {

  string name;

  float
rotation;

}gPictureInfo;

void
 PictureInit()

{

  Json::Reader reader;

  ifstream file("picture.json"
);

  assert(file.is_open());

  Json::Value root;

  if
 (!reader.parse(file, root, false
)) {

    printf("Parse error"
);

    exit(1
);

  }

  gPictureInfo.name = root["name"
].asString();

  gPictureInfo.rotation = root["rotation"
].asDouble();

}

虽然已经如此简单,但还是解释一下:

reader是用于parse Json文件的jsoncpp类,传入打开的ifstream文件对象即可完成parse。Json::Value是一个包罗万象的类,可以存储一个Json 对象,如上所示,整个Json文件就是一个Json对象,(以{}表示)在JsonCpp中表示为root,在parse函数中传入,打开的文件,根Value,parse后,root就包含了解析后的Json文件信息。对value的检索都是使用类似于C++中map的方式,直接以字符串为key索引,索引返回的还是一个Json::Value的对象,同样也可以有包罗万象的内容。(与Json文件格式本身对应)从Json::Value到C++的静态类型的转换在JsonCpp中通过Json::Value对象的asXXX函数来完成。上例中,asStrint表示返回一个C++的String对象,asDouble表示返回一个浮点数,以此类推。

通过上述方式,获得了图片名字,图片的rotation。以此来完成新的图片的显示。

将SceneShow部分改成如下代码:

// display

void
  SceneShow(GLvoid) {

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBindTexture(GL_TEXTURE_2D, gTexName);

  glMatrixMode(GL_MODELVIEW);

  glPushMatrix();

  glRotatef(gPictureInfo.rotation, 0.0
, 0.0
, 1.0
);

  glBegin(GL_QUADS);

  glTexCoord2f(0.0
 , 1.0
 ); glVertex3f(-1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 1.0
 ); glVertex3f(1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 0.0
 ); glVertex3f(1.0
 , 1.0
 , 0.0
 );

  glTexCoord2f(0.0
 , 0.0
 ); glVertex3f(-1.0
 , 1.0
 , 0.0
 );

  glEnd();

  glPopMatrix();

}  

添加了 

glPushMatrix();

glRotatef(gPictureInfo.rotation, 0.0
, 0.0
, 1.0
);

glPopMatrix();

3句来进行模型矩阵变换,来完成旋转。

因为上面是180度,所以显示效果如下:


在游戏编程古老的岁月中,大家都全靠图片来堆积游戏,仅仅是一个这样的旋转,都不会使用。下图是某游戏同一个武器不同方向的图片。事实上,完全没有必要。

即使在今天,人物的4向行走用4组图片也不是什么稀罕事。。。。虽然其实是那么的没有必要。

这里再添加一些其他的要素,比如scale,position。(这里没有使用一般2D引擎中用的屏幕坐标,还是用OpenGL坐标)

Json配置:

{

    "name"
 : "dragon.png"
,

    "rotation"
 : 0,

    "positionX"
 : -0.5,

    "positionY"
 : 0.0,

    "scaleX"
 : 0.3,

    "scaleY"
 : 1.0
}



代码改动部分:

struct
 PictureInfo {

  string name;

  float
 rotation;

  float
 positionX;

  float
 positionY;

  float
 scaleX;

  float
 scaleY;

  // can't read from config,read from surface

  int
 width;

  int
 height;

}gPictureInfo;

void
 ReadPictureInfo() {

  Json::Reader reader;

  ifstream file("picture.json"
);

  assert(file.is_open());

  Json::Value root;

  if
 (!reader.parse(file, root, false
)) {

    printf("Parse error"
);

    exit(1
);

  }

  gPictureInfo.name = root["name"
].asString();

  gPictureInfo.rotation = (float
)root["rotation"
].asDouble();

  gPictureInfo.positionX = (float
)root["positionX"
].asDouble();

  gPictureInfo.positionY = (float
)root["positionY"
].asDouble();

  gPictureInfo.scaleX = (float
)root["scaleX"
].asDouble();

  gPictureInfo.scaleY = (float
)root["scaleY"
].asDouble();

}

void
 PictureInit() {

  ReadPictureInfo();

}



// display

void
  SceneShow(GLvoid) {

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  glBindTexture(GL_TEXTURE_2D, gTexName);

  glPushMatrix();

  glMatrixMode(GL_MODELVIEW);

  glTranslatef(gPictureInfo.positionX, gPictureInfo.positionY, 0
);

  glScalef(gPictureInfo.scaleX, gPictureInfo.scaleY, 0.0
);

  glRotatef(gPictureInfo.rotation, 0
, 0
, 1.0
);

  glBegin(GL_QUADS);

  glTexCoord2f(0.0
 , 1.0
 ); glVertex3f(-1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 1.0
 ); glVertex3f(1.0
 , -1.0
 , 0.0
 );

  glTexCoord2f(1.0
 , 0.0
 ); glVertex3f(1.0
 , 1.0
 , 0.0
 );

  glTexCoord2f(0.0
 , 0.0
 ); glVertex3f(-1.0
 , 1.0
 , 0.0
 );

  glEnd();

  glPopMatrix();

}  

添加了相应的读取代码而已,并在模型矩阵变换的时候添加了scale和rotate相关的代码,其实都没有什么新鲜的。


这里的乐趣在于,你可以不改变任何代码,通过配置实现很多改变了,虽然仅仅是这么一幅图。比如:

{

    "name"
 : "dragon.png"
,

    "rotation"
 : 0,

    "positionX"
 : 0.5,

    "positionY"
 : 0.0,

    "scaleX"
 : -0.3,

    "scaleY"
 : 1.0
}



会得到如下的图:


Json数组

    其实讲了上面的两个例子,我是想说明怎么样同时显示上述两幅图,都通过配置。这需要用到Json中的数组的概念,当然,懂一点编程的人对此都不会太陌生。配置如下:

[


{

    "name"
 : "dragon.png"
,

        "rotation"
 : 0,

        "positionX"
 : -0.5,

        "positionY"
 : 0.0,

        "scaleX"
 : 0.3,

        "scaleY"
 : 1.0
}

,
{

    "name"
 : "dragon.png"
,

    "rotation"
 : 0,

    "positionX"
 : 0.5,

    "positionY"
 : 0.0,

    "scaleX"
 : -0.3,

    "scaleY"
 : 1.0
}


]

同时,代码的改动可能有些大,因为需要在任意地方都将原来的全局对象改为全局vector.实际的新内容除了解析Json那一部分,倒是几乎完全没有。解析Json数组时,与用普通的字符串key来索引value的差别仅仅在于此时只需要用整数来索引即可。

如下:



using
 namespace
 std;
struct
 PictureInfo {

  string name;

  float
 rotation;

  float
 positionX;

  float
 positionY;

  float
 scaleX;

  float
 scaleY;

  // can't read from config,read from surface

  int
 width;

  int
 height;

  GLuint texName;

};

// didn't care about efficiency too much as a demo

vector<PictureInfo> gPictureInfoVec;

void
 ReadPictureInfo() {

  Json::Reader reader;

  ifstream file("picture.json"
);

  assert(file.is_open());

  Json::Value root;

  if
 (!reader.parse(file, root, false
)) {

    printf("Parse error"
);

    exit(1
);

  }

  assert(root.isArray());

  PictureInfo info;

  int
 size = root.size();

  for
 (int
 i = 0
; i < size; ++i) {

    Json::Value &current = root[i];

    info.name = current["name"
].asString();

    info.rotation = (float
)current["rotation"
].asDouble();

    info.positionX = (float
)current["positionX"
].asDouble();

    info.positionY = (float
)current["positionY"
].asDouble();

    info.scaleX = (float
)current["scaleX"
].asDouble();

    info.scaleY = (float
)current["scaleY"
].asDouble();

    gPictureInfoVec.push_back(info);

  }

}

上面这段for循环中的内容,root[i]的使用方式就是JsonCpp中索引Json数组的方式,很简单是吧?

于是,添加相应的显示代码后(其实也没有新内容,就是原来的东西变成数组)

//OpenGL初始化开始

void
 SceneInit(int
  w,int
  h)

{

  glClearColor (1.0f
 , 1.0f
 , 1.0f
 , 0.0
 );

  glViewport(0
, 0
, w, h);

  glShadeModel(GL_FLAT);

  for
 (vector<PictureInfo>::iterator it = gPictureInfoVec.begin();

    it != gPictureInfoVec.end();

    ++it) {

      PictureInfo& info = *it;

      SDL_Surface *surface = IMG_Load(info.name.c_str());

      if
 (!surface)

      {

        printf("Load the picture failed:
%s
"
,info.name.c_str());

        exit(1
);

      }

      info.width = surface->w;

      info.height = surface->h;

      GLenum texture_format;

      // get the number of channels in the SDL surface

      GLint nOfColors = surface->format->BytesPerPixel;

      if
 (nOfColors == 4
)     // contains an alpha channel

      {

        if
 (surface->format->Rmask == 0x000000ff
)

          texture_format = GL_RGBA;

        else

          texture_format = GL_BGRA;

      } else
 if
 (nOfColors == 3
)     // no alpha channel

      {

        if
 (surface->format->Rmask == 0x000000ff
)

          texture_format = GL_RGB;

        else

          texture_format = GL_BGR;

      } else
 {

        printf("warning: the image is not truecolor..  this will probably break
/n
"
);

        exit(1
);

      }

      glGenTextures(1
  , &info.texName);

      glBindTexture(GL_TEXTURE_2D, info.texName);

      glTexImage2D( GL_TEXTURE_2D, 0
, nOfColors, surface->w, surface->h, 0
,

        texture_format, GL_UNSIGNED_BYTE, surface->pixels );

      // Specify filtering and edge actions

      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);

      glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);

      //Free the loaded image

      SDL_FreeSurface( surface );

  }

  glEnable(GL_DEPTH_TEST);

  glEnable( GL_TEXTURE_2D );

}

// display

void
  SceneShow(GLvoid) {

  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

  for
 (vector<PictureInfo>::const_iterator it = gPictureInfoVec.begin();

    it != gPictureInfoVec.end();

    ++it) {

      const
 PictureInfo& info = *it;

      glBindTexture(GL_TEXTURE_2D, info.texName);

      glPushMatrix();

      glMatrixMode(GL_MODELVIEW);

      glTranslatef(info.positionX, info.positionY, 0
);

      glScalef(info.scaleX, info.scaleY, 0.0
);

      glRotatef(info.rotation, 0
, 0
, 1.0
);

      glBegin(GL_QUADS);

      glTexCoord2f(0.0
 , 1.0
 ); glVertex3f(-1.0
 , -1.0
 , 0.0
 );

      glTexCoord2f(1.0
 , 1.0
 ); glVertex3f(1.0
 , -1.0
 , 0.0
 );

      glTexCoord2f(1.0
 , 0.0
 ); glVertex3f(1.0
 , 1.0
 , 0.0
 );

      glTexCoord2f(0.0
 , 0.0
 ); glVertex3f(-1.0
 , 1.0
 , 0.0
 );

      glEnd();

      glPopMatrix();

  }

}  

可以看到一个很有意思的图^^当然,中间要是再加个球那就更好了。




因为此时已经有点数据驱动的意思了,需要添加新龙的时候改改配置就好了。

其实这个例子还可以进一步发挥,在每个数组值之间进行插值,实现类似flash的关键帧动画^^让上图中左边的龙渐变成右边的龙,其实本例已经与此相差不远了。。。。。。限于精力有限,暂时就不进一步发挥了。

 

 

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

数据/配置 的存储方式 Json篇 以Cocos2D For Iphone+TouchJson为例

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

前言

配置的好处,JSon介绍,及为什么要使用Json都参看原来JsonCpp篇
的内容。本文仅针对TouchJson稍微进行一些讲解。

 

Cocos2D For IPhone
+ TouchJson

    Cocos2D For IPhone是我见过的开源2D引擎中特性最完备的一个,即使速度上不算最快的。因为其只支持IPhone平台,所以能够在这个平台上做的很出众,现在新版的Cocos2D For IPhone已经支持iOS4和IPhone4,其工程模板的安装使用也是非常方便。新版甚至将原来的LGPL协议改为现在的MIT协议了,使用更加灵活自由。。。。
    TouchJson的使用属于不想在一个比较完全的Objective C环境中添加C++代码,(事实上使用Box2D的话还是避免不了)所以不使用JsonCpp来配合Cocos2D,何况Cocos2D的模板工程中本来就带有TouchJson了,将其删掉再插进JsonCpp也太不人道了。。。。呵呵。另外,因为TouchJson用Objective C完成,解析后也是个NSDictionary的对象所以与Objecitve C的对象组合使用会更加自然一些,使用苹果并为苹果开发,我是几乎已经习惯一整套都是用apple平台专有的东西了,唉。。。。对比当年简单的因为C#完全掌握在MS手中而不想学习,这也算是一种悲哀,因为Objective C比C#更加封闭,而且,起码C#还是这个世界上语法最漂亮,最先进语言的代表。

首先,利用Cocos2D的模板,创建一个新的工程,此时默认的效果是显示一个Hello World。如下:

这里,我们就不用其他图了,看看怎么配置这个Hello World。

最最基础的流程:
建立一个Json文件,仅仅有两行配置,一行表示显示的文字,一行表示文字的旋转
{


  "text"
  : "Don't Hello World"
,
   "rotation"
 : 20
}



然后将此Json文件放入工程的Resources目录,我这里命名为picture.json。

然后可以开始着手解析这个Json文件了。
整个解析过程又分几步,首先,#import "CJSONDeserializer.h"
然后,获取到编译打包后在Resources目录文件的位置:
NSString *path  = [[NSBundle mainBundle]pathForResource:@"picture" ofType:@"json"];

获取文件路径后,从文件中读取数据:
NSData *jsonData = [[NSFileManager defaultManager] contentsAtPath:path];

获取文件数据后,解析Json文件:
// Parse JSON results with TouchJSON.  It converts it into a dictionary.
CJSONDeserializer *jsonDeserializer = [CJSONDeserializer deserializer];
NSError *error = nil;
NSDictionary *jsonDict = [jsonDeserializer deserializeAsDictionary:jsonData error:&error];
if (error) {
//handle Error, didn't have here.
}


此时jsonDict保存的就是解析后的Json数据了。
(以上代码都添加在HelloWorldScene的init中)

下面来看使用:
首先,text改变HelloWorld显示的文字:
    NSString *text = [jsonDict valueForKey:@"text"];
        // create and initialize a Label
        CCLabel* label = [CCLabel labelWithString:text fontName:@"Marker Felt" fontSize:64];
这里就已经是cocoa中NSDictionary
怎么使用的问题了。

然后,rotation改变旋转:
    NSNumber *rotation = [jsonDict valueForKey:@"rotation"];
    NSAssert(rotation, @"Didn't have a key named rotation");
    label.rotation = [rotation floatValue];

一切就绪,看效果:

上面的流程已经基本完整了,作为补充,还是添加一个Json数组使用的例子。
在TouchJson中,作者不推荐将根对象设定为数组(参见这里
,TouchJson的作者自己说的),事实上也就不那么做就好了。我们随便用一个key来指定这个数组即可。
所以,定义Json文件如下:

{


  "result"
 :
  [


    {


      "text"
  : "Don't Hello World"
,

      "rotation"
 : 20

    }

,
    {


      "text"
  : "Just Hello World"
,
      "rotation"
 : -20
    }


  ]


}

然后,读取的时候还是先读取出一个NSDictionary对象,但是我们随后从中取出数组:
NSArray *dictArray = [jsonDict valueForKey:@"result"];

然后再遍历数组,此时数组中的每个对象又是NSDictionary对象

for (NSDictionary *dict in dictArray) {}

此时获取到NSDictionary的对象就与原来的字典对象很像了,直接通过valueForKey取对应的配置使用即可。较完整的循环代码如下:

    for (NSDictionary *dict in dictArray) {
      NSString *text = [dict valueForKey:@"text"];
      // create and initialize a Label
      CCLabel* label = [CCLabel labelWithString:text fontName:@"Marker Felt" fontSize:64];
      
      NSNumber *rotation = [dict valueForKey:@"rotation"];
      NSAssert(rotation, @"Didn't have a key named rotation");
      label.rotation = [rotation floatValue];
      
      // ask director the the window size
      CGSize size = [[CCDirector sharedDirector] winSize];
      
      // position the label on the center of the screen
      label.position =  ccp( size.width /2 , size.height/2 );
      
      // add the label as a child to this Layer
      [self addChild: label];
    }

此时可以看到同时显示多个文字的效果:

小结:
在使用了JsonCpp和TouchJson后,可以发现由于Json的数据结构主要就是一个Key:Value的映射加数组,所以无论在C++中还是在Objective C中,总是能用语言的原生结构很好的表示,(在C++中是map,在Objective C中是NSDictionary和NSArray)所以使用会非常方便,对比XML的强大并且复杂,简单的Json在保持概念非常简单的情况下完成了配置任务。

 

 

 

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

SDL与OpenGL配合使用时,OpenGL的纹理的UV坐标是上下颠倒的

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

同样的一段程序,在SDL with OpenGL时是颠倒的,而在GLFW和完全使用Windows API加OpenGL时又是正确的。
如下:
  glBegin(GL_QUADS);
  glTexCoord2f(0.0 , 0.0 ); glVertex3f(-1.0 , -1.0 , 0.0 );
  glTexCoord2f(1.0 , 0.0 ); glVertex3f(1.0 , -1.0 , 0.0 );
  glTexCoord2f(1.0 , 1.0 ); glVertex3f(1.0 , 1.0 , 0.0 );
  glTexCoord2f(0.0 , 1.0 ); glVertex3f(-1.0 , 1.0 , 0.0 );
  glEnd();

在默认情况下, 纹理的UV坐标的原点是是左下角,在SDL中默认定在了左上角,所以需要将上面的纹理的坐标Y轴倒过来,这样显示就没有问题了。就像下面这样:
  glBegin(GL_QUADS);
  glTexCoord2f(0.0 , 1.0 ); glVertex3f(-1.0 , -1.0 , 0.0 );
  glTexCoord2f(1.0 , 1.0 ); glVertex3f(1.0 , -1.0 , 0.0 );
  glTexCoord2f(1.0 , 0.0 ); glVertex3f(1.0 , 1.0 , 0.0 );
  glTexCoord2f(0.0 , 0.0 ); glVertex3f(-1.0 , 1.0 , 0.0 );
  glEnd();

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

Orx 1.2正式发布

经历了长达两个月的多次延期,历经iarwain的1984次更新,发布前的多次通宵奋斗,Orx 1.2终于正式发布了。。。。。。。。。。

 

作者的发布宣言在这里:http://orx-project.org/component/content/article/1-orx/65-orx-v12-has-been-released

 

新版本的下载地址在这里:http://sourceforge.net/projects/orx/files/orx/orx%20-%201.2%20%281984%29/

 

说实话,因为Orx目前没有好的工程文件管理系统,而又需要同时支持如此多的平台,iarwain光是为各个平台打包都花费了较长的时间。真希望有精通CMake的人能够帮他一把。将整个过程实现自动化。

 

新版本的Orx已经支持了目前所有的流行平台,添加了custom font和unicode支持,已经可以很好的显示中文及各类工具生成美化过的英文,iarwain为unicode的显示还自己写了一个字体工具,并且以zlib这样一个非常自由的协议发布。Have Fun!

 

贴一段Orx的新的Notes:

Orx - Portable Game Engine  (Version 1.2)

=============================================================================

Email iarwain [at] orx-project.org to contact the author; or better, check
orx's homepage at http://orx-project.org for the most up-to-date contact information.

This engine is licensed under the zlib license, see the LICENSE file for details.

Intro
-----

Orx is a 2D-oriented, data-driven, portable game engine focused primarily on extreme ease of use.

This is the final release for orx v1.2. If you find any bugs,
please report them at http://forum.orx-project.org, on the "Bug report - Feature request" board,
or via orx's SourceForge page/tracker.

The current features of orx engine are:
- hardware-accelerated rendering that provides: translation, anisotropic scale, rotation,
  transparency (alpha blending), different blending modes, coloring, tiling and mirroring
- automatic differential scrolling and depth scaling upon request
- animation engine (including a chaining graph & custom animation events for synchronization)
- fragment (pixel) shader support
- collision handling and rigid body physics
- generic input system that abstracts keyboard, mouse, joystick, touch and accelerometer inputs
- powerful config system that makes orx data-driven and provides an easy to use load/save system
- powerful localization module
- camera/viewport scheme allowing multiple views displayed with camera translation, zoom and rotation
- visual FXs based on curve combinations
- spawners (provides an easy way to create particles or projectiles)
- 3D positioning using "scene nodes"
- custom bitmap font support
- music and spatialized sound support
- clock system that provides time consistency and allows time stretching + high precision timers
- event manager
- unicode support with UTF-8 encoding
- plugin system
- screenshot capture tool (supports bmp, png, jpg, tga and dds)

See ./doc/html directory for the doxygen documentation of orx's API.

You can find details about the tutorials (as well as community-made ones) and the data-driven side of orx
(ie. how to use the config system) on orx's wiki at: http://wiki.orx-project.org

The English version is the most complete one, Chinese and Spanish translations are being written by community members.

Supported Platforms
-------------------

The engine compiles and is tested for:
- Linux (x86/x86-64 with codelite)
- Win32 (msvs2005, msvs2008, msvs2010 and mingw32 with code::blocks or codelite)
- MacOS X (ppc/x86 with xcode), version 10.5+ for GLFW plugins (default version) and 10.4+ for SFML ones (no joystick support).
- iPhone/iPod Touch/iPad (simulator & device with xcode)

The GP2X target is in debug stage and is not currently actively developed/maintained.

Versions
--------

Those are not revision versions but link/run versions.
Orx library can be compiled as a static or a dynamic library.
In addition to these modes, orx can be compiled as an embedded version or not.

Orx's core is basically platform-independent. All the platform/OS-dependent features
are implemented via plugins. These plugins can be loaded at runtime (hotplug) or
they can be embedded at linktime.

If you use the non-embedded versions, you'll have to specify which plugins to use.
This is more flexible but also requires additional files (the plugins themselves).
The embedded version will store everything in orx's library, meaning you won't be able
to choose which plugin to use at runtime, but will also be more compact. This will
also make orx run considerably faster.

From the download page you'll find precompiled binaries for Win32, Linux (x86) and MacOS X (ppc/x86)
using the dynamic embedded versions only.
If you want to use the non-embedded versions (to use with your own plugins)
or the static ones, you'll need to compile orx yourself from the source.
Everything compiles out-of-the-box for the hardware platforms cited above.

The embedded versions currently use:
- GLFW-based (+SOIL) plugins for display, joystick, keyboard and mouse for all non-iPhone platforms
- OpenAL-based (+libsndfile/stb_vorbis) plugins for sound
- Box2D-based plugin for physics
- homemade plugin for 2D rendering

- OpenGL/OpenAL-based plugins for display and sound on iPhone
- Touch/MultiTouch-based plugin for mouse on iPhone
- Accelerometer-based plugin for joystick on iPhone

Packages
--------

You can download all the packages from sourceforge (http://orx.sf.net).
Here is a list with a small description for each package.

- orx-doc-1.2.zip: orx's API doxygen documentation + PDF version of the English wiki (detailed tutorials and config system)

- orx-src-1.2.zip: orx's source code including build projects for
  . code::blocks (Win32 only, non-embedded versions)
  . codelite (Win32 and Linux, all versions)
  . msvs2005, msvs2008 & msvs2010 (Win32 Visual Studio, all versions)
  . xcode (2.4+, for MacOS X, non-static versions, for iPhone/iPod Touch, static embedded versions)
NB: You'll need orx-extern-1.2.zip (orx plugins' external dependencies) if you intend to compile orx yourself.

- orx-extern-1.2.zip: orx's external dependencies. You will only *NEED* these if you
use orx-src-1.2.zip and you plan on compiling orx yourself. They are not neeeded otherwise.
The external libraries are usually modified versions of the original ones.
VERY IMPORTANT: If you want to compile orx yourself, you'll need these versions and not the official ones.
Warning for slow connections: this package is ~67MB.

- orx-tools-1.2.zip: orx's additional set of tools (Win32, Linux and MacOS X) containing:
  . orxCrypt  : command line tool to encrypt/decrypt/merge multiple config files
  . orxFontGen: command line tool based on FreeType2 for generating custom bitmap fonts (.tga texture & .ini config file) from TrueType fonts

- orx-dev-linux-1.2.tar.bz2: orx's dynamic embedded precompiled binaries for Linux (x86), release and debug.
- orx-dev-mac-1.2.zip      : orx's dynamic embedded precompiled binaries for MacOS X (ppc/x86), release and debug.
- orx-dev-mingw-1.2.zip    : orx's dynamic embedded precompiled binaries for Win32 (mingw), release and debug.
- orx-dev-msvs2005-1.2.zip : orx's dynamic embedded precompiled binaries for Win32 (Visual Studio 2005), release and debug.
- orx-dev-msvs2008-1.2.zip : orx's dynamic embedded precompiled binaries for Win32 (Visual Studio 2008), release and debug.
- orx-dev-msvs2010-1.2.zip : orx's dynamic embedded precompiled binaries for Win32 (Visual Studio 2010), release and debug.

- orx-full-iphone-1.2.zip  : orx's static embedded precompiled binaries for iPhone/iPod Touch/iPad, release/debug, simulator/device + doc + source + XCode project file.

- orx-tutorial-linux-1.2.tar.bz2: orx's precompiled tutorial for Linux (x86), release only.
- orx-tutorial-mac-1.2.zip      : orx's precompiled tutorial for MacOS X (ppc/x86), release only.
- orx-tutorial-mingw-1.2.zip    : orx's precompiled tutorial for Win32 (mingw), release only.
- orx-tutorial-msvs2005-1.2.zip : orx's precompiled tutorial for Win32 (Visual Studio 2005), release only.
- orx-tutorial-msvs2008-1.2.zip : orx's precompiled tutorial for Win32 (Visual Studio 2008), release only.
- orx-tutorial-msvs2010-1.2.zip : orx's precompiled tutorial for Win32 (Visual Studio 2010), release only.

All the *-dev-* packages above include:
 . orx release/debug libraries used for linking
 . runtime release/debug orx libraries, launchers and config en/de-crypting tool
 . external runtime librairies needed on some platforms
 . headers to include at compile time
 . Template files exposing wich properties can be accessed using the config system (for user reference only, not needed by orx)

All the *-tutorial-* packages above include:
 . heavily commented source code for 11 basic and advanced tutorials
 . precompiled binaries (orx link library, orx runtime library and launcher, external runtime library)
 . headers to include at compile time
 . Template files exposing wich properties can be accessed using the config system (for user reference only, not needed by orx)
 . project files (xcode (MacOS X), codelite (Win32 mingw & Linux x86), msvs2005 & msvs2008)

NB: Most of the binaries have been packed using UPX (http://upx.sf.net) for the sake of bandwidth.
You can unpack them (upx -d) if you feel like it, but it shouldn't affect their execution anyway.

Compiling
---------
 
The easiest way to learn how to compile your project using orx for a given platform
is to look at the tutorial build project files. If you want to use another version
(tutorials use the dynamic embedded versions of orx), please look at
orx's launcher (main executable) build project file.

The debug version is far slower than the release one, but will output all the warning
and error messages useful for debugging.

Here's a quick list of the available compile preprocessor flags:
- __orxDEBUG__    : used to compile and link against the debug versions of orx library
                    (liborxd.a / orxd.lib / liborxd.dylib), if not specified it refers to
                    the release versions (liborx.a / orx.lib / liborx.dylib).
                    NB: If you want to link against the debug versions of orx library, you need
                    to specify it to your compiler!

- __orxSTATIC__   : used to compile and link against the static versions of orx library.
                    NB: If you want to link against the static versions of orx library, you need
                    to specify it to your compiler!

- __orxEMBEDDED__ : used to compile the embedded versions of orx library.
                    NB: this flag is *ONLY* needed when compiling orx library, not when linking
                    against it.

- __orxFREEBASIC__: used to compile and link the freebasic version of orx, still WIP.

There are other preprocessor flags used when compiling the orx library,
but those should be easy enough to decipher.
However, you might want to specify them manually for cross-compiling or
use them so that your code will behave differently depending on the architecture
for which you're compiling. Here's a quick list of these flags:
- __orxPPC__          : orx is being compiled for a PowerPC architecture
- __orxX86_64__       : orx is being compiled for a x86-64 architecture
- __orxLITTLE_ENDIAN  : orx is being compiled for a little endian architecture
- __orxBIG_ENDIAN     : orx is being compiled for a big endian architecture
- __orxGCC__          : orx is being compiled with gcc
- __orxMSVC__         : orx is being compiled with visual studio C/C++
- __orxWINDOWS__      : orx is being compiled for Win32
- __orxLINUX__        : orx is being compiled for Linux (x86)
- __orxMAC__          : orx is being compiled for MacOS X (ppc/x86)
- __orxIPHONE__       : orx is being compiled for iPhone/iPod Touch
- __orxWII__          : orx is being compiled for Wii
- __orxGP2X__         : orx is being compiled for GP2X (gcc-arm)
- __orxCPP__          : orx is being compiled with a C++ compiler
- __orxOBJC__         : orx is being compiled with an Objective-C compiler
- __orxFREEBASIC__    : orx is being compiled for FreeBasic
- __orxPLUGIN__       : a plugin for orx is being compiled
- __orxEXTERN__       : code using orx's library is being compiled

Comments
--------

If you have any questions, comments, ideas or reviews, feel free to post them
on orx's forum (http://forum.orx-project.org) or send them directly by mail to iarwain [at] orx-project.org

Enjoy!

阅读全文....

Orx 字体图片生成工具完成 支持unicode,当然包括中文

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

工具产生的历史

    很正常又让非英语/拉丁语国家恼怒的事情就是,绝大多数的美国/欧洲人编写的软件从来没有考虑到世界上还有用其他语言的人,在以前,可以说是编码上考虑多语言难度比较大,为了简单才这样,但是在使用Unicode编码已经如此简单的时候,还是有很多软件的作者从来没有考虑过这个世界上还有用其他语言的人。作为非英语国家,甚至连拉丁语系都不是的国家,而且还拥有者世界上最复杂最多字符语言(之一)的中国受害最深,但是,情况正在改善。

    当iarwain因为Orx的中国使用者越来越多,开始为Orx加入了custom font和unicode支持的时候,他碰到了很大的问题,现在能够找到的字体图片生成工具就没有一个很好的支持Unicode的。(或者工具本身就不够好)我也尝试了几款,有很多很酷的字体工具,但是,很遗憾的,最多的支持也就是拉丁语系。于是iarwain决定自己写一个这样的工具。

    勤奋的iarwain让我印象深刻,整个周末都在忙着完成这个字体生成工具。从蒙特利尔时间晚上两点,iarwain完成第一版,因为stb_truetype的一些问题,导致一些字体的中文标点显示不正常,一直到蒙特利尔时间第二天早上7点半,iarwain将工具完整的迁移到更加稳定强大的freetype2上,我测试过新的工具,完整,可用,不知道还有谁会在工作之余如此的拼命,也许只有code for fun才能这样吧,我只能说I服了you了。

    事实上,虽然iarwain是为了Orx写了这样一个工具,但是,如我所言,现在网络上很难找到一个类似的支持unicode的字体图片生成工具,所以这样的工具其实填补了很大的空白,因为其开源,所以应该可以被其他任何游戏引擎所借鉴和使用。因为,显示一个自定义字体的原理都是一样的,事实上,为了高效的完成字体的显示,原理也只能是这样。当然,对于需要完整字库(比如需要聊天功能的网游)的游戏,这样的原理可能并不适用。假如还有什么不足的话,那就是这个工具目前仅仅是完成了文字到字体图片的转换功能,还不能添加更多很炫很酷的效果。

工具介绍

    这里,介绍一下新的工具。下载地址

    也能从Orx的SVN中获取。SVN地址:https://orx.svn.sourceforge.net/svnroot/orx。

    工具是个命令行工具。

    使用方法:

    orxFontGen.exe -s "字体大小" -f "字体文件名" -o “生成对象名" -t "输入的所有文字"

比如,我希望使用微软雅黑,来完成上面一段工具的历史的输出。我首先将上述文字保存成一个文件,这里命名为toolHistroy.txt,(需要特别注意的是,保存的文字一定要用utf-8进行编码,这点可以用notepad++或者ultraEditor之类的工具进行。并将其拷贝到工具所在的目录。然后将微软雅黑的字体文件msyh.ttf(从windows/fonts目录)拷贝到工具所在的目录。我希望将生成的对象命名为msyhFont,于是,我用下列命令执行工具:

orxFontGen.exe -s 32 -f msyh.ttf -o msyhFont -t toolHistory.txt

然后,我们得到两个东西,其一,4通道的tag文件。

 

其实这个图片可以被任何支持类似文字显示的引擎使用。

其二,一段Orx的配置。

[msyhFont]

CharacterList = " ()/27IOU_abcdefimnoprstuwxy。一丁上不且世两个中为么义之也了事二于些产人什仅从他以件任会似但体何余作使候借假入其具写决况几利到刻前功加勤半单印历原又受只可史吧周命和善因国图在填复多够大天奋好如始字完定实害家导将尔尝就工己已常应度开引强当很忙怒恼情憾戏成我或所才找拉拥拼持换擎支改效数整文新早时是显晚更最有服末本杂来果标样欧款正此比没洲测深添游源炫点然片版特现理甚生用界白的目直着知码碰示移稳空符第简类系经络绝编网美考者而能自至致英蒙虑虽补被言让许试话该语说谁象越足身转软较迁过还这连道遗那都酷鉴问间难非题高(),"

CharacterSize = (32, 32, 0)

Texture = msyhFont.tga

如此,显示这段文字,那就是很简单的事情了。(因为窗口大小问题,只显示了上面文字中的一段)

另外,很明显可以看到一点,虽然文字本身生成的时候是白色的,但是,可以由Orx的定点色进行颜色的改变。

对于还不知道怎么使用Orx显示文字的,可以参考Orx的教程
,或者我以前的文章

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

在SDL中使用OpenGL

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

    以前使用过SDL和GLFW,发现SDL的API设计,第3方库,以及社区支持都好过GLFW,但是SDL是对显示做过封装的,所以使用2D是方便,但是GLFW没有对显示进行封装,完全使用OpenGL,所以使用OpenGL非常方便,这里,我不禁想到,要是SDL也能很好的使用OpenGL那么就完美了,在网上也的确查到了相关的资料。在试用以后发现比我想的还要简单,因为SDL在Windows中默认使用D3D渲染,我还以为会需要加宏重新编译SDL或者啥的,但是,一切比我想象的要简单,并且,找到的教程质量非常高,该讲的都讲了,该注意的都提到了,通俗易懂,明白无误,这是一个开源产品的最大优势,SDL就是这样一个产品!本来我以为需要大书特书的东西,在这样的教程下,显得非常多余。。。。呵呵,还是贴一些自己的代码吧。

#include
#include
#include
#include "SDL.h"
#include "SDL_opengl.h"

#define WINDOW_WIDTH 640
#define WINDOW_HEIGHT 480
//OpenGL初始化开始
void SceneInit(int  w,int  h)
{
  glClearColor(0.0f , 0.0f , 0.0f , 0.0f );      // 黑色背景
  glColor3f(1.0f , 1.0f , 1.0f );

  glShadeModel(GL_FLAT);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-50.0f , 50.0f , -50.0f , 50.0f , -1.0f , 1.0f );
}

//这里进行所有的绘图工作
void  SceneShow(GLvoid) {
  // 旋转角度

  static  float  fSpin = 0.0f ;

  fSpin += 2.0f ;

  if (fSpin > 360.0f ) {
    fSpin -= 360.0f ;
  }

  glClear(GL_COLOR_BUFFER_BIT);
  glPushMatrix();

  // 旋转矩形的主要函数
  glRotatef(fSpin, 0.0f , 0.0f , 1.0f );
  glRectf(-25.0 , -25.0 , 25.0 , 25.0 );
  glPopMatrix();
}  

int _tmain(int argc, _TCHAR* argv[])
{
  if ( SDL_Init(SDL_INIT_VIDEO) < 0 )
  {
    printf("Unable to initialize SDL: %sn", SDL_GetError());

    exit(1);
  }
  atexit(SDL_Quit);

  // use these two lines instead of the commented one
  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
  SDL_Surface* screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_OPENGL); // *changed*
  //SDL_Surface *screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_DOUBLEBUF);

  SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

  // main loop
  bool running = true;
  while (running) {
    //While there's an event to handle
    SDL_Event event;
    while( SDL_PollEvent( &event; ) ) {
      if (event.type == SDL_QUIT) {
        running = false;
      }
    }

    SceneShow();
    //Update Screen
    // use this line instead of the commented one
    SDL_GL_SwapBuffers();
    //SDL_Flip( screen );

    // delay, 50 for simple
    SDL_Delay( 50 );

  }

    return 1;
}

以前写的使用SDL本身封装的渲染方式使用的方法比较一下,发现仅仅改变了几行代码,注释中都有说明。
一是创建窗口和使用双缓冲的代码:
  // use these two lines instead of the commented one
  SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 ); // *new*
  SDL_Surface* screen = SDL_SetVideoMode( WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_OPENGL); // *changed*
  //SDL_Surface *screen = SDL_SetVideoMode(WINDOW_WIDTH, WINDOW_HEIGHT, 16, SDL_DOUBLEBUF);

一是flip缓冲的代码:
    SDL_GL_SwapBuffers();
    //SDL_Flip( screen );

仅此两处而已,其他地方,就可以完全使用openGL了,如上面代码所示,与GLFW的文章比较一下,发现SDL假如能够如此容易的支持OpenGL的话,即使是3D的东西,用SDL来管理一些跨平台的东西,然后使用OpenGL来渲染,其实也是非常不错的选择。
效果:

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

SDL 简单入门学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

概要

实际学习使用SDL创建窗口,并绘制图形。

前言   

    今天想要做一个简单的demo,因为一部分需要使用objective C,所以还需要跨平台,我才发现,我了解的东西还真没有一个适合做这样事情的,Cocos2D For IPhone仅仅能在IPhone下跑,HGE仅仅能在Windows下跑,Orx虽然能够跨平台,但是很显然,用于做简单的demo太麻烦了,因为我需要的仅仅是一个简单的DrawBmp函数而已,Orx那种一使用就使用一套的做法不太适合,还能想到的就是OpenGL了,但是用OpenGL做跨平台应用全靠自己那就挺麻烦了,还是找个框架吧。
    其实我有3个选择,glut/free glut, SDL, GLFW。其中glut虽然在学习OpenGL的时候用过一些,但是因为该项目已经死了,我不想再为其投入更多的学习时间,了解到可以看懂其代码的水平(其实使用也很简单),已经够了。SDL是很多人推荐的选择,我以前找工作的时候,国内的一家公司竟然直接提到过,说明其在国内也算是有人用了。我在那以后也看过SDL的API,感觉还算简单,与HGE一样,图形显示上,都是用了类似DirectDraw的抽象。(应该也是最通吃的抽象方式了)我对GLFW的感兴趣是因为最近Orx的作者iarwain提到过,并且给予了很高的评价,他说对GLFW的轻量级印象非常深刻,在最新的Orx版本中,GLFW是Orx的默认插件,并且就iarwain的测试,比SDL快5%左右(虽然不算太多),最重要的是,GLFW的封装都很简单,以直接使用OpenGL为主,借这个契机,我也顺便复习一下OpenGL,最近老是用库,我都快忘了该怎么用了。
    两相比较,我发现我不知道该用SDL还是GLFW,按我的习惯,两个一起用先,尝试一下再下结论。本文先看看SDL。

实际使用   

    其实我的需求很简单,创建窗口,在制定的地方绘图。简单的说也就是类似于CreateWindows和DrawBmp的两个函数而已。

    SDL其实真的算挺出名的了,也有人提到过,即使不真的准备使用SDL,但是想想一个库,能够被移植到这么多平台,抽象封装的方式和源代码起码都值得研究研究。因为这个,我也稍微看一下,虽然真的不打算长期SDL。SDL的协议是LGPL的(也有商业协议),还算可以接受。
    环境的搭建还算简单,Windows版本的SDL需要D3D SDK支持。
    简单的参考了一下教程,显示BMP图片的过程还算简单:
 1

 2
#include


 3
#include


 4
#include


 5
#include
"SDL.h"

 6

 7
int
 _tmain(int
 argc, _TCHAR* argv[])
 8
{
 9
  if
 ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0
 )
10
  {
11
    printf("Unable to initialize SDL:
%s
n
"
, SDL_GetError());
12

13
    exit(1
);
14
  }
15
  atexit(SDL_Quit);
16

17
  //Load image

18
  SDL_Surface* picture = SDL_LoadBMP( "dragon.bmp"
 );
19

20
  SDL_Surface *screen = SDL_SetVideoMode(640
, 480
, 16
, SDL_DOUBLEBUF);
21
  if
 ( screen == NULL
 )
22
  {
23
    printf("Unable to set video mode:
%s
n
"
, SDL_GetError());
24

25
    exit(1
);
26
  }
27

28
  //Apply image to screen

29
  SDL_BlitSurface( picture, NULL
, screen, NULL
 );
30

31
  //Update Screen

32
  SDL_Flip( screen );
33

34
  //Pause

35
  SDL_Delay( 2000
 );
36

37
  //Free the loaded image

38
  SDL_FreeSurface( picture );
39

40
    return
 1
;
41
}

这里,可以看到,SDL没有管理主循环,同时我没有使用自己的主循环,那得牵涉到SDL的事件系统,所以,这个演示里,用了SDL_Delay,才能看到图片的显示。这里的显示没有指定大小,没有指定alpha值,所以图片原大显示。SDL_BlitSurface
函数的使用很像Windows API的对应函数。其他也没有什么好说的,看注释及函数名就知道在干什么了。

然后,通过下述方式来设置想要的透明色(在没有alpha通道的bmp中,也只能使用这样蹩脚的color key方式了)
1

2
  Uint32 colorKey = SDL_MapRGB(picture->format, 0xFF
, 0xFF
, 0xFF
);
3
  SDL_SetColorKey(picture, SDL_SRCCOLORKEY, colorKey);


其中0xFF,0xFF,0xFF分别是想要设定的颜色的R,G,B,这里都是0xFF,那就是白色了。
于是,原图:

在黑色背景下,周围的白色都透明了,显示出下列的效果:

说实话,用SDL的API还算比较简单,使用的时候有点感觉时空穿越,有点回到当年学习使用Win32 API来做类似事情的时候。接口的概念都差不多,也许SDL的优势比起当年的Win32 API仅在于速度和跨平台了。
显示两个图只需要再blit一次即可:
  //Apply image to screen

  SDL_BlitSurface( picture, NULL
, screen, NULL
 );

  SDL_Rect dest;
  dest.x = picture->w;
  dest.y = 0
;

  //Apply image to screen again and move it to right

  SDL_BlitSurface( picture, NULL
, screen, &dest );


然后,加入SDL对主循环的控制。

  // main loop

  bool
 running = true
;
  while
 (running) {
    //Update Screen

    SDL_Flip( screen );

    // delay, 50 for simple

    SDL_Delay( 50
 );

    //While there's an event to handle

    SDL_Event event;
    while
( SDL_PollEvent( &event ) ) {
      if
 (event.type == SDL_QUIT) {
        running = false
;
      }
    }
  }


SDL_PollEvent用于轮询SDL的事件。
于是,现在SDL创建的窗口可以被拖动,也可以点击关闭了。
想说的是,SDL真的很简单,我看教程的时候基本上只需要看源代码即可,就能了解大部分的意思。

    下面到关键的部分了,PNG的显示,我看到,虽然SDL本身仅仅支持BMP,但是已经有人做了一个名叫SDL_Image的库,可以支持其他格式。(考验一个开源库好不好,有没有良好的第3方支持是很重要的方面,SDL这方面明显很不错)于是,我们就不用直接使用libpng了,个人不是很喜欢libpng的接口。。。。。
    不知道是不是秉承了SDL简单的优良传统,SDL_Image的使用也非常简单,编译好后,将里面的动态库(因为需要支持PNG,所以有libpng和zlib的动态库)都拷贝到运行目录里面,然后包含"SDL_image.h"就可以了。从bmp到png的距离只有几行代码。。。。。从我发现SDL_Image到真的加载显示PNG图片,也就过了不到10分钟。。。。
全部源代码:

#include


#include


#include


#include
"SDL.h"

#include
"SDL_image.h"

int
 _tmain(int
 argc, _TCHAR* argv[])
{
  if
 ( SDL_Init(SDL_INIT_AUDIO|SDL_INIT_VIDEO) < 0
 )
  {
    printf("Unable to initialize SDL:
%s
n
"
, SDL_GetError());

    exit(1
);
  }
  if
 (IMG_Init(IMG_INIT_PNG) == 0
 ) {
    printf("Unable to initialize SDL_image"
);

    exit(1
);
  }
  atexit(SDL_Quit);

  //Load image

  //SDL_Surface* picture = SDL_LoadBMP( "dragon.bmp" );

  SDL_Surface* picture = IMG_Load("dragon.png"
);

  SDL_Surface *screen = SDL_SetVideoMode(640
, 480
, 16
, SDL_DOUBLEBUF);
  if
 ( screen == NULL
 )
  {
    printf("Unable to set video mode:
%s
n
"
, SDL_GetError());

    exit(1
);
  }

  // because we use png with alpha now

  //Uint32 colorKey = SDL_MapRGB(picture->format, 0xFF, 0xFF, 0xFF);

  //SDL_SetColorKey(picture, SDL_SRCCOLORKEY, colorKey);

  //Apply image to screen

  SDL_BlitSurface( picture, NULL
, screen, NULL
 );

  SDL_Rect dest;
  dest.x = picture->w;
  dest.y = 0
;

  //Apply image to screen again and move it to right

  SDL_BlitSurface( picture, NULL
, screen, &dest );

  // main loop

  bool
 running = true
;
  while
 (running) {
    //Update Screen

    SDL_Flip( screen );

    // delay, 50 for simple

    SDL_Delay( 50
 );

    //While there's an event to handle

    SDL_Event event;
    while
( SDL_PollEvent( &event ) ) {
      if
 (event.type == SDL_QUIT) {
        running = false
;
      }
    }
  }

  //Free the loaded image

  SDL_FreeSurface( picture );

    return
 1
;
}


仅仅只有几行修改,在代码中看的很清楚。

 

小结

    简单,还是简单,这是我学习SDL的最大感受,API设计的简单,相关概念也简单,SDL无愧于Simple DirectMedia Layer中的Simple一次。要想真的从OpenGL学习起,然后调用libpng来加载PNG图像并显示,你得看到红宝书的第十几章,直到纹理贴图的学习后你才能做到,但是,在SDL中做这些事情实在是太简单了。即使与当年的Win32 API相比,也少了很多Windows特定的消息循环原理等东西的学习,创建窗口的API比较起来,就会感觉MS那一帮人都是废物,设计的窗口创建API,竟然需要用几十行的代码去创建一个窗口,还需要注册窗口类。。。。。。。。。。。事实上,只需要一行代码。。。。。。

    一般来说,一个比较流行的开源库都是比较好的,因为好,才流行,因为流行而变的更好,其中,最最重要的关键在于,使用要简单,太复杂难用的库,无论设计的多么精巧,都很难吸引到使用者,也就难以进入这个良性的循环,不得不说,SDL在这方面,做的是非常好了。

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....

GLFW 简单入门学习

write by 九天雁翎(JTianLing) -- www.jtianling.com

讨论新闻组及文件

概要

    实际学习使用GLFW创建窗口,并绘制图形。作为比较,可以参考一篇关于SDL的类似文章

前言

    使用SDL时,对其使用的简单印象非常深刻,假如没有效率上的原因,(SDL据说效率也不差)我想,即使是做一般的游戏引擎都可以考虑用SDL来实现。现在尝试使用一下GLFW(
http://www.glfw.org/),GLFW在国内并不是很出名,第一次听说也是从Orx的作者iarwain那里,SDL那篇文章已经说过了,因为SDL给我的感觉非常好,我很难想象GLFW会超过它,但是,平时想真的自己使用OpenGL的时候,有个框架可以使用也挺不错的,而且可以不使用glut,使得真的需要进一步做成产品时,不至于像使用glut那样受到限制。另外,GLFW使用的是zlib的协议,比SDL更加自由。

实际的使用

    现在的最新版GLFW是2.6,我下载的是源码包,下载后,support目录有VS的工程,有两个projects,一个是编译成lib,(因为GLFW使用的是zlib协议,所以这也是允许的)一个是编译成dll。我为了减少编译和链接的时间,使用dll。编译成库以后,使用时,只用包含一个头文件即可,只需要一个头文件。。。。。
    其实,假如你原因,GLFW的文件并不是很多,全部包含进自己的工程也可以。(这也不违反协议)而且,我注意到support目录下有很多有意思的东西,包括通过lua,basic,汇编语言来使用GLFW和OpenGL的例子,也算是很有意思了。因为GLFW的接口比较少,所以,事实上,做一个语言的绑定工作的工作量不是太大,而且提供的都是C接口,这让语言的绑定更加简单,只要该语言支持C语言接口,而且有了OpenGL的绑定。虽然GLFW没有提供Python绑定,不过我感觉也不难做。呵呵,这些都是题外话了。
    编译后创建工程,这些都没有什么好说的了,不过第一次编译一个使用了glfwInit函数的小工程,竟然报链接错误,我很奇怪,网上查了后,发现需要自己定义一个GLFW_DLL的宏,这点倒是比较奇怪。见此贴
。心理有点不快,这点在官方的user guide中也没有提及。

创建窗口

    一直记得在Windows下使用OpenGL有多么的麻烦,见《Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤
》,那甚至值得用一篇论文来专门描述。使用GLFW呢?
    也还算简单,主要是调用glfwOpenWindow这个函数,参数比较多,但是还算容易理解。当我按照网上少有的教程
,(这点也体现了越流行的东西越好用的道理,SDL的教程太丰富了,其中很好的也很多,我很容易就上手了。GLFW就没有那么好了,我联想到自己用Orx的经历,更加发现如此。)开始尝试运行起一个程序时,我发现一个问题,我建立自己的主循环时,(GLFW也没有内部为你控制)尽管加入了对键盘的响应,但是还是没有用,窗口仍然死在那里。
类似这样:
//错误示范,切勿模仿
int
 _tmain(int
 argc, _TCHAR* argv[])
{
  if
(!glfwInit()) {
    printf("GLFW init error."
);
  }

  if
 (!glfwOpenWindow(800
, 600
, 6
, 6
, 6
, 0
, 32
, 0
, GLFW_WINDOW) ) {
    glfwTerminate();
    exit(1
);
  }
  glfwSetWindowTitle("The GLFW Window"
);

  // this just loops as long as the program runs

  while
(true
) {
    if
 (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
      break
;
    }

    glfwSleep(0.05
);
  }

  glfwTerminate();

    return
 0
;
}


我不得其解,后来感觉主循环可能需要加入事件的轮询吧,而且还发现了GLFW中有类似的机制,pollevent的函数也在那里,但是这个教程为什么没有用却可以呢?后来在GLFW的文档中找到这么一句:

 If it is not desirable that glfwPollEvents is called implicitly from glfwSwapBuffers, call glfwDisable
with the argument GLFW_AUTO_POLL_EVENTS.

    我就晕了,glfwSwapBuffers竟然隐含着调用了poolEvents。。。。。。。。。。我无语。因为我暂时没有绘制图形,所以从网上的教程中去掉了此句,这正是问题所在,在glfwSleep(0.05
);

前添加glfwSwapBuffers后,问题解决。再次感叹,好的接口容易让人用对,坏的接口反之。。。。。。。不要去说是用某个东西前需要将文档全看了话,那是狡辩,好的语言,API设计,应该让人仅仅看了一部分,也能正常的工作和使用,即使是想当然的撞大运编程方式,也应该让人撞对才对,这才符合最小惊讶原则。。。。。。

    用到这里,我得感叹一句,GLFW的user guide写的真的不咋地,和reference没有区别,狂列举API,却缺少实际的例子,这和reference有啥区别,user guide应该就是写来让人快速掌握的。

 

OpenGL使用

有了上述的东西,基本上OpenGL的环境已经搭好了,可以尝试用OpenGL干点什么了。

这里将原来学OpenGL中的教程的例子《Win32 OpenGL 编程(1)Win32下的OpenGL编程必须步骤
》拉过来改改,先尝试一下。


#include
"stdafx.h"

#include
"stdlib.h"

#include
"gl/glfw.h"

#define WINDOW_WIDTH (
800
)

#define WINDOW_HEIGHT (
600
)

//OpenGL初始化开始

void
 SceneInit(int
 w,int
 h)
{
  glClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);      // 黑色背景

  glColor3f(1.0f
, 1.0f
, 1.0f
);

  glShadeModel(GL_FLAT);
  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();
  glOrtho(-50.0f
, 50.0f
, -50.0f
, 50.0f
, -1.0f
, 1.0f
);
}

//这里进行所有的绘图工作

void
 SceneShow(GLvoid) {
  // 旋转角度

  static
 float
 fSpin = 0.0f
;

  fSpin += 2.0f
;

  if
(fSpin > 360.0f
) {
    fSpin -= 360.0f
;
  }

  glClear(GL_COLOR_BUFFER_BIT);
  glPushMatrix();

  // 旋转矩形的主要函数

  glRotatef(fSpin, 0.0f
, 0.0f
, 1.0f
);
  glRectf(-25.0
, -25.0
, 25.0
, 25.0
);
  glPopMatrix();

  // 交换缓冲区

  glfwSwapBuffers();
}  

int
 _tmain(int
 argc, _TCHAR* argv[])
{
  if
(!glfwInit()) {
    printf("GLFW init error."
);
  }

  if
 (!glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 6
, 6
, 6
, 0
, 32
, 0
, GLFW_WINDOW) ) {
    glfwTerminate();
    exit(1
);
  }
  glfwSetWindowTitle("The GLFW Window"
);

  SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

  // this just loops as long as the program runs

  while
(true
) {
    if
 (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
      break
;
    }

    SceneShow();
    glfwSleep(0.05
);
  }

  glfwTerminate();

    return
 0
;
}

 

运行正常,总的来说在GLFW中搭建一个OpenGL的环境还是很简单的吧,起码比Windows下使用Win32 API来使用OpenGL要简单的多,并且,这还是跨平台的。(知道和很多人说跨平台就是废话)

真的用GLFW使用过OpenGL后,发现其实还算是比较简单,我的那些话也有些苛责了。虽然我一向很喜欢真的了解底层,比如Win32 API,比如cocoa,但是,能够有个跨平台的库,为我将这些东西都管理起来,就算我要写什么,那也会简单很多。另外,虽然SDL也能做到这些,但是相对于GLFW对于OpenGL的直接支持,(SDL中其实也可以)我感觉用起来还是更加亲切一些。

图形的显示

    这才是最终的目的。
真正的位图的显示,在OpenGL中都不是那么容易的,需要掌握一堆的东西。见《Win32 OpenGL编程(15) 位图显示
》《Win32 OpenGL编程(16) 纹理贴图
》,那是因为OpenGL中实际完全对图形的显示没有直接的支持。听起来有些奇怪。。。实际的意思就是,OpenGL的API完全不理解位图,png图的含义。(虽然在上述15中提到一些bmp的操作接口,但是很遗憾的,实际的使用中都是使用纹理贴图,即16中提到的东西) 在GLFW中呢?我看到GLFW有对图形操作的接口。可是遗憾的是仅支持TGA,连BMP都不支持,不知道这种取舍是为啥,一般而言,我感觉,支持bmp的话,是最基础的。
这里还是用《SDL 简单入门学习
》中的那个龙图。
其中,图形文件操作的接口分两种,这里只看OpenGL常用的使用纹理贴图的方式。用到的API名字叫glfwLoadTexture2D。先显示个tga试一下。
代码的主要部分还是OpenGL,所以可以参考《Win32 OpenGL编程(16) 纹理贴图
》中的代码,仅仅借用了glfw的glfwLoadTexture2D函数而已。主要代码如下:

#include
"stdafx.h"

#include
"stdlib.h"

#include
"gl/glfw.h"

#define WINDOW_WIDTH (
800
)

#define WINDOW_HEIGHT (
600
)

GLuint gTexName;
//OpenGL初始化开始

void
 SceneInit(int
 w,int
 h) {
  glClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);      // 黑色背景

  glColor3f(1.0f
, 1.0f
, 1.0f
);

  glShadeModel(GL_FLAT);

  glGenTextures(1
 , &gTexName);
  glBindTexture(GL_TEXTURE_2D, gTexName);

  if
 ( !glfwLoadTexture2D("dragon.tga"
, GLFW_BUILD_MIPMAPS_BIT) ) {
    printf("glfw load the file failed."
);
  }

  // Use trilinear interpolation for minification

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    GL_LINEAR_MIPMAP_LINEAR );
  // Use bilinear interpolation for magnification

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
    GL_LINEAR );

  // Enable texturing

  glEnable( GL_TEXTURE_2D );
}

//这里进行所有的绘图工作

void
 SceneShow(GLvoid) {
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glBindTexture(GL_TEXTURE_2D, gTexName);

  glBegin(GL_QUADS);
  glTexCoord2f(0.0
 , 0.0
 ); glVertex3f(-1.0
 , -1.0
 , 0.0
 );
  glTexCoord2f(1.0
 , 0.0
 ); glVertex3f(1.0
 , -1.0
 , 0.0
 );
  glTexCoord2f(1.0
 , 1.0
 ); glVertex3f(1.0
 , 1.0
 , 0.0
 );
  glTexCoord2f(0.0
 , 1.0
 ); glVertex3f(-1.0
 , 1.0
 , 0.0
 );

  glEnd();

  // 交换缓冲区

  glfwSwapBuffers();
}  

int
 _tmain(int
 argc, _TCHAR* argv[])
{
  if
(!glfwInit()) {
    printf("GLFW init error."
);
  }

  if
 (!glfwOpenWindow(WINDOW_WIDTH, WINDOW_HEIGHT, 6
, 6
, 6
, 0
, 32
, 0
, GLFW_WINDOW) ) {
    glfwTerminate();
    exit(1
);
  }
  glfwSetWindowTitle("The GLFW Window"
);

  SceneInit(WINDOW_WIDTH, WINDOW_HEIGHT);

  // this just loops as long as the program runs

  while
(true
) {
    if
 (glfwGetKey(GLFW_KEY_ESC) == GLFW_PRESS) {
      break
;
    }

    SceneShow();
    glfwSleep(0.05
);
  }

  glfwTerminate();

    return
 0
;
}


感觉相对于图像显示来说,glfw并没有如SDL那般省事,因为毕竟还是全程使用OpenGL,对比原来OpenGL中显示位图的代码来说,仅仅是没有调用Windows API了而已,仅仅是多了跨平台的特性而已,并没有简化工作,而且,glfw同样的,也没有内置png图形的支持。虽然说tga在3D中用的非常多,主要是因为无损压缩,但是2D中,我还是喜欢使用png,因为小的多。当然,一旦使用png,无可避免的会需要使用libpng/zlib,所以GLFW为了保持自身的简单,没有做这样的工作吧,相对来说tga的解压就要简单太多了。
另外,GLFW还有glfwReadImage函数可以将tga图直接读入内存,然后获取到图形的相关信息的办法(上面就没有办法获取到图形的宽高)。但是使用上都已经差不多了。

都到了这个地步了,不显示一下GLFW对OpenGL的强力支持,所以做3D比较方便都不像话了。这里套用原来的代码。见《Win32 OpenGL编程(16) 纹理贴图
》。

//OpenGL初始化开始

void
 SceneInit(int
 w,int
 h) {
  glClearColor(0.0f
, 0.0f
, 0.0f
, 0.0f
);      // 黑色背景

  glColor3f(1.0f
, 1.0f
, 1.0f
);

  glViewport(0
 ,0
 ,WINDOW_WIDTH,WINDOW_HEIGHT);                      // Reset The Current Viewport

  glMatrixMode(GL_PROJECTION);                        // Select The Projection Matrix

  glLoadIdentity();                                   // Reset The Projection Matrix

  // Calculate The Aspect Ratio Of The Window

  gluPerspective(45.0f
 ,(GLfloat)WINDOW_WIDTH/(GLfloat)WINDOW_HEIGHT,0.1f
 ,100.0f
 );

  glMatrixMode(GL_MODELVIEW);                         // Select The Modelview Matrix

  glLoadIdentity();                                   // Reset The Modelview Matrix

  glGenTextures(1
 , &gTexName);
  glBindTexture(GL_TEXTURE_2D, gTexName);

  if
 ( !glfwLoadTexture2D("dragon.tga"
, GLFW_BUILD_MIPMAPS_BIT) ) {
    printf("glfw load the file failed."
);
  }

  // Use trilinear interpolation for minification

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
    GL_LINEAR_MIPMAP_LINEAR );
  // Use bilinear interpolation for magnification

  glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
    GL_LINEAR );

  glEnable(GL_DEPTH_TEST);
  // Enable texturing

  glEnable( GL_TEXTURE_2D );
}

//这里进行所有的绘图工作

void
 SceneShow(GLvoid) {
  static
 float
 xrot = 0.0f
,yrot = 0.0f
,zrot = 0.0f
;
  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
  glLoadIdentity();                                   // Reset The View

  glTranslatef(0.0f
 ,0.0f
 ,-5.0f
 );

  glRotatef(xrot,1.0f
 ,0.0f
 ,0.0f
 );
  glRotatef(yrot,0.0f
 ,1.0f
 ,0.0f
 );
  glRotatef(zrot,0.0f
 ,0.0f
 ,1.0f
 );

  glBindTexture(GL_TEXTURE_2D, gTexName);

  glBegin(GL_QUADS);
  // Front Face

  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f(-1.0f
 , -1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f( 1.0f
 , -1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 ,  1.0f
 );
  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 ,  1.0f
 );
  // Back Face

  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f(-1.0f
 , -1.0f
 , -1.0f
 );
  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f( 1.0f
 , -1.0f
 , -1.0f
 );
  // Top Face

  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 , -1.0f
 );
  // Bottom Face

  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f(-1.0f
 , -1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f( 1.0f
 , -1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f( 1.0f
 , -1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f(-1.0f
 , -1.0f
 ,  1.0f
 );
  // Right face

  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f( 1.0f
 , -1.0f
 , -1.0f
 );
  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 , -1.0f
 );
  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f( 1.0f
 ,  1.0f
 ,  1.0f
 );
  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f( 1.0f
 , -1.0f
 ,  1.0f
 );
  // Left Face

  glTexCoord2f(0.0f
 , 0.0f
 ); glVertex3f(-1.0f
 , -1.0f
 , -1.0f
 );
  glTexCoord2f(1.0f
 , 0.0f
 ); glVertex3f(-1.0f
 , -1.0f
 ,  1.0f
 );
  glTexCoord2f(1.0f
 , 1.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 ,  1.0f
 );
  glTexCoord2f(0.0f
 , 1.0f
 ); glVertex3f(-1.0f
 ,  1.0f
 , -1.0f
 );
  glEnd();

  xrot+=0.3f
 ;
  yrot+=0.2f
 ;
  zrot+=0.4f
 ;

  // 交换缓冲区

  glfwSwapBuffers();
}  


效果:

其实最后一个例子已经与介绍GLFW没有关系了,新添加的部分纯粹属于OpenGL的内容,这里参看原来的文章,这里不多加解释了。仅仅用于演示,当使用了OpenGL后,3D图形的使用的方便。

小结

    GLFW无愧于其号称的lightweight的OpenGL框架,的确是除了跨平台必要做的事情都没有做,所以一个头文件,很少量的API,就完成了任务。GLFW的开发目的是用于替代glut的,从代码和功能上来看,我想它已经完全的完成了任务,(虽然从历史原因上考虑还没有,毕竟红宝书都还是用glut。。。)并且glfw还在持续的开发当中,虽然作者总说他很忙。
    作为与SDL(参考《SDL 简单入门学习》
)比较而写的两篇文章。这里也做个小结。相对来说,SDL真的将API做的很简单,而且因为使用的人比较多,所以第3方扩展也做的很好,并且,碰到问题,你比较容易找到答案。假如是做2D应用,SDL真的已经非常不错了。GLFW作为一个跨平台的OpenGL框架,也出色的完成了任务,不过因为定位不同,封装较少,所以在做一些基本任务的时候,因为OpenGL本身的复杂性,会复杂一些。同时,资料也少的多,所以我写本文花费的时间比SDL那篇就要长了很多,说明学习周期也会长一些。。。。(但是个人感觉类似GLFW,SDL这样的库,学习周期基本为0。。。。比起编写图形或者游戏的其他方面来说可以忽略不计)但是,同时的,GLFW封装的少,也带来更大的灵活性,我们可以还是自由的用OpenGL完成工作,也可以完成自己的进一步封装,相当于GLFW将跨平台的一些脏活累活都干了,我们就剩最核心的渲染去自由发挥了。碰到一个任务,该怎么在两者之间选择呢?出于简单考虑,2D方面的东西用SDL做实在再合适和简单不过了,但是假如想要学习OpenGL或者是做3D应用,GLFW的确是不错的选择。绝对比在glut上的投入要值,当然,其实因为glut实在太过简单,其实出于教学/学习目的去看一下也实在没有什么投入,实际用OpenGL开发东西的时候,还是选择用GLFW做吧。
    也许,下一步,我可以看看怎么在SDL中使用OpenGL。在Windows下,SDL的默认渲染API使用的是D3D,不知道是否可以更改,并且完全使用OpenGL来工作呢?

 

 

原创文章作者保留版权 转载请注明原作者 并给出链接

write by 九天雁翎(JTianLing) -- www.jtianling.com

阅读全文....