Category: Programming

基于OpenCV的简单图片特征匹配

{0 Comments}

前两个月闲的蛋疼做了一个图片匹配的东东,原本以为直接用OpenCV就可以用图像特征的方式来做了,结果发现OpenCV只是封装了比较低层的特征识别算法,并没有高级的端口。这里把特征匹配的实现思路记录如下:

1. 计算出descriptor:

1
2
3
4
5
Mat img = imread(filename);
cv::Feature2D *detector = new cv::OrbFeatureDetector();
cv::Feature2D *extractor = new cv::OrbDescriptorExtractor();
detector->detect(img, kpImg);
extractor->compute(img, kpImg, descImg);

其中的detector负责提取图片中的特征点,并由extractor提取出descriptor,最终保存到descImg中。

2. 两张图片的descriptor进行对比

1
2
3
4
5
6
7
8
BruteForceMatcher< L2 > matcher;
matcher.knnMatch(desc1, desc2, matches1, 2);
matcher.knnMatch(desc2, desc1, matches2, 2);
 
ratioTest(matches1);
ratioTest(matches2);
 
symmetryTest(matches1, matches2, symMatches);

首先会使用BruteForceMatcher找出两个descriptor到对方的配对点,然后使用ratioTest和SymmetryTest来剔除不靠谱的匹配特征点,也就是下面所要说的剔除匹配点的操作。

3. 剔除低质量匹配点
ratioTest用来剔除距离比例相差过大的配对点,配对点之间的距离相差越大,能匹配上的概率也就越小。这里使用一个参数ratio来控制剔除距离相差在一定范围之外的特征点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
int ratioTest( VVecMatch &matches )
{
  float ratio = 0.8f;
  int removed=0;
  // for all matches
  for (std::vector<std::vector>::iterator
    matchIterator= matches.begin();
    matchIterator!= matches.end(); ++matchIterator) 
  {
    // if 2 NN has been identified
    if (matchIterator->size() > 1) 
    {
      // check distance ratio
      if ((*matchIterator)[0].distance/
        (*matchIterator)[1].distance > ratio) {
          matchIterator->clear(); // remove match
          removed++;
      }
    } else { // does not have 2 neighbours
      matchIterator->clear(); // remove match
      removed++;
    }
  }
  return removed;
}

symmetryTest用来判断两个图像间的特征点匹配是否是一一映射,对于不是的点则剔除掉。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
void symmetryTest( const VVecMatch matches1, 
                               const VVecMatch matches2, 
                               VecMatch& symMatches )
{
  // for all matches image 1 -> image 2
  for (std::vector<std::vector>::const_iterator matchIterator1= matches1.begin();
    matchIterator1!= matches1.end(); ++matchIterator1) 
  {
    // ignore deleted matches
    if (matchIterator1->size() < 2)       
      continue;
 
    // for all matches image 2 -> image 1
    for (std::vector<std::vector>::const_iterator matchIterator2= matches2.begin();
      matchIterator2!= matches2.end();
      ++matchIterator2) 
    {
      // ignore deleted matches
      if (matchIterator2->size() < 2)         
        continue;       
      // Match symmetry test
      if ((*matchIterator1)[0].queryIdx ==
         (*matchIterator2)[0].trainIdx &&
         (*matchIterator2)[0].queryIdx ==
         (*matchIterator1)[0].trainIdx)
        {
         // add symmetrical match
         symMatches.push_back(
           cv::DMatch((*matchIterator1)[0].queryIdx,
           (*matchIterator1)[0].trainIdx,
           (*matchIterator1)[0].distance));
         break; // next match in image 1 -> image 2
      }
    }
  }
}

4. 判断是否匹配上

经过上面的特征匹配和剔除,剩下来的应该都是比较靠谱的匹配点了。如果用一个图片来和一个模板集合来进行比对,那么通过简单的比较能够匹配到的特征点数目就能够判断出是否匹配上了。

PS:对于OpenCV中的所有特征匹配算法都可以用这个办法来做,比如SIFT, SURF等等。只需要简单的替换第一步中的extractor和detector就可以了

处理Emacs中的行号和空白字符

{2 Comments}

做过开源项目的童鞋可能会知道,在很多开源项目中会有专门的脚本来检查代码风格。其中包括行尾是否有多余的空白字符,是否使用使用tab,每一行是否超过了指定的宽度等等。这些问题通常在敲代码的时候并不容易看出来,等到要提交代码的时候才发现问题。稍微搜索了一下发现在emacs中有一个很有用的whitespace插件可以解决这个问题。

whitespace可以针对设置的规则对上面提到的问题进行高亮显示,效果如下:

WhiteSpaceOnLight

whitespace具体可以找到的问题包括tab, 空格混用;行尾多余的空白字符;文件结束前的空白字符;超过指定长度的行等等。可以通过设置whitespace-style来指定需要提示哪些问题。在我的配置中使用了如下配置:

1
2
3
4
5
6
7
;; deal with white spaces
(require 'whitespace)
(global-whitespace-mode)
(setq whitespace-style
      '(face trailing tabs lines lines-tail empty
        space-after-tab space-before-tab))
(add-hook 'before-save-hook 'delete-trailing-whitespace)

默认的whitespace-style把整个buffer被搞得有点乱糟糟的,因此我把不是很需要的部分都去掉了,只剩下检测行尾空白、tab等少数几个问题。对于行尾的多余字符而言,一个个删除也是很麻烦的事情。所以我直接用delete-trailing-whitespace在保存的时候把它们都干掉了。

不过在这里whitespace会和行号有一点点冲突。通常为了显示行号都会打开linum-mode,行号会是右边对其的数字。whitespace会认为行号前面的空白字符是问题字符而高亮显示,搞得非常难看。为了解决这个问题可以用下面代码对linum进行设置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
;; Show line number
(require 'linum)
(global-linum-mode t)
(setq column-number-mode t)
(setq line-number-node t)
(setq linum-format "%5d ")
(add-hook 'linum-before-numbering-hook
          (lambda ()
            (let ((w (+
                      (length
                       (number-to-string
                        (count-lines (point-min) (point-max))
                        ))
                      2)
                     ))
              (setq linum-format
                    `(lambda (line)
                       (propertize (concat
                                    (truncate-string-to-width
                                     "" (- ,w (length (number-to-string line)))
                                     nil ?x2007)
                                    (number-to-string line))
                                   'face 'linum))))))

这是从emacs wiki上面抄过来的一段代码,用了一个特殊的unicode字符x2007来设置行号的格式,既能达到右端对其的效果又不会被whitespace误杀。

Emacs中处理日志文件

{0 Comments}

最近写的东西会将信息输出到一个日志文件中去,每次调试的时候log文件内容都会刷新。每次都手动revert-buffer比较烦人,查了一下发现有一个auto-revert-mode可以自动刷新。下面的配置可以实现对于.log文件调用auto-revert-mode:

1
2
3
4
5
6
7
8
9
10
11
;; auto revert log files
(add-hook 'find-file-hook
          (lambda ()
            (if (string-equal
                 ".log"
                 (substring (buffer-file-name)
                            (search "." (buffer-file-name))))
                (progn
                  (setq auto-revert-mode t)
                  (message "Enabling auto-revert-mode for log file")))
            ))

如果是比较长的日志文件,并且日志使用追加内容的方式添加上去的话,可以将上面的(setq auto-revert-mode t)换成(setq auto-revert-tail-mode t)以提高效率。

STL中的红黑树

{2 Comments}

平时用STL通常只是顺手把vector, map之类常用的类拿来用用就完了,也没有想到去仔细研究里面都有些什么东西。这两天有一个算法程序需要将一堆对象排序,并且会非常频繁的插入删除,对效率的要求极高。原本想用priority queue,但是发现它是对一个数组进行堆排序的方式来操作,效率并不高。在STL的文档里面找了找发现居然没有红黑树,google了一下才知道set, map底层都是用红黑树实现的,只是这个红黑树没有暴露出来给人用。

也不知道是出于什么考虑,性能优秀的红黑树虽然标准库里面有,但是却遮遮掩掩不直接提供给大家用。值得注意的是,因为红黑树是内部使用的类,各个STL实现中的红黑树还不一样。

在VC的实现中红黑树是在xtree文件中的_Tree类,用法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#include 
#include 
#include 
 
using namespace std;
 
typedef _Tree< _Tset_traits, allocator, false > > rb_tree;
 
int main()
{
    rb_tree* tree = new rb_tree( less(), allocator() );
    for(int i=35; iinsert(i);
 
    printf("output: %d ", *tree->begin() );
}

gcc中的实现则是在bits/stl_tree.h文件中,而且类的名字和用法都不一样:

1
2
3
4
5
6
7
8
9
10
11
12
#include 
 
typedef _Rb_tree,
                 less > rb_tree;
typedef rb_tree::iterator rb_tree_iter;
 
int main()
{
    rb_tree queue;
    queue._M_insert_unique( int );
    printf("size: %d", queue.size() );
}

尝试给开源项目打补丁

{2 Comments}

最近开始尝试给开源项目打补丁。首先是跑到Gnome Love上面去逛了一圈。GnomeLove是Gnome官方为了降低开源开发者加入的门槛而搞的一个页面,上面有一些帮助入门的文档链接,从GTK开发到git的使用都有。然后再到Bugzilla上面用GnomeLove作为关键词,能够搜索出一堆比较容易改的bug。
我抓了Empathy的一个bug开始改。Empathy有一个问题就是,它的聊天窗口不会记忆联系人边栏的位置,每次打开一个窗口都需要自己重新拖动它。修改这个bug也很简单,在聊天窗口创建的时候加入一个事件响应函数。然后再在窗口边栏位置变化的时候把这个位置保存在GConf里面,下次窗口打开的时候再把这个数读出来就好了。上次Jesse给Gnome给数独游戏修改bug的时候对方一个多月以后才有反应,不知道这个bug多久以后会有人理我?

3ds Max中导出Cal3D骨骼动画

{1 Comment}

最近要做一个3d引擎的动画性能测试,所以得为各个引擎导出骨骼动画,其中就包括使用了Cal3D的Crystal Space和OSG。其中Crystal Space本身就可以直接读取Cal3D的骨骼动画,而OSG则是得是用一个名为OsgCal的插件才可以。此外在网上还看到有人在Ogre中使用Cal3D来渲染骨骼动画。

虽然Cal3D用的也算是挺多的,但是很奇怪的是这个项目已经有两三年没有更新了。同时官方文档的描述也很简单,唯一一个官方的教程居然绝大部分篇幅是将如何在3ds max中制作骨骼动画,关键部分如何使用Cal3D插件只有简单的两段话。对于第一次使用Cal3D的人来说肯定会被它搞得一头雾水,而且各个步骤中有一个地方错了都会导致最终的骨骼动画不正确。这里把我试验正确的d导出步骤贴出来:

  1. 导出骨骼
    a. 选择骨骼根节点bip01
    b. 打开Figure Mode
    c. File->Export Cal3D Skeleton
    d. 选择所有骨骼,通常默认即可
  2. 导出材质
    a. 所有纹理必须使用tga格式
    b. 选择骨骼根节点bip01
    c. 打开Figure Mode
    d. 打开材质球,将所有材质命名为material[0], material[1]这样的名称
    e. File->Export->Cal3d Materials
    f. 选择导出的材质(每一种材质分别需要导出一次)
  3. 导出模型
    a. 打开Figure Mode
    b. 选择需要被导出的模型
    c. File->Export Cal3d Mesh
  4. 导出动作
    a. 关闭Figure Mode
    b. File->Export Cal3D Animation
    c. 选择之前导出的谷歌文件
    d. 选择骨骼,默认即可
    e. 选择导出动作的起始结束帧号,帧速和displacement(不知道有什么用?)
  5. 创建xxx.cfg文件,格式如下:

scale=0.1

skeleton=skeleton.csf

animation=running.caf

material=material.crf

注意事项:

  1. Cal3D的导出插件只能支持3ds max 8。如果你是用的9或者更高版本,虽然你已经看了不少内容但是你也只能放弃了:(
  2. 导出时必须严格按照上面所说的顺序依次导出骨骼、材质、模型和动作
  3. 最后创建的.cfg文件中等号左右不要留空格,否则在某些解析程序中(比如说OsgCal中)会导致错误。这应该算是OsgCal的一个小bug。另外OsgCal要求配置文件中scale, skeleton等的顺序不能变,否则也会出错。

RockyRay第一个Demo即将完成

{3 Comments}

之前由于准备考研复试、毕业答辩和吃散伙饭忙活了好一阵子,现在总算是有点喘息的时间来做点其他事情了。这第一件事情就是继续RockyRay的开发。貌似我以前还没有在blog中提到过RockyRay。

所谓RockyRay是我和小强精力过剩蛋疼无聊的时候满怀着拯救世界解放他人并把爱与和平带给全人类的信念做的一个RPG游戏。恩恩,这也是小强的毕设。因为明天就是小强答辩的日子,结果他在deadline到之前终于完成了第一个场景模型、男主角的模型,以及一些设定图和其他一些CG。第一个游戏场景的Demo已经编译出来,不过还没有打包。明天我就会把整个Demo打包提供下载。整个程序使用Ogre+OgreMax+OpenAL+LuaPlus+Boost,从三月份开始弄到现在(其实中间有将近2个月没有更新)。


感兴趣的童鞋可以先在下面的网址看看小强为RockyRay做的一些2D作品:

http://code.google.com/p/rockyray/



探险
看!灰机!
主角们
排排站,吃果果

配置Emacs下的Python开发环境

{0 Comments}

特性

在Linux论坛上总有人问Python用什么IDE比较好,然后总会有人回答说Emacs。最近开始学Python,也花了点时间研究怎么配置Emacs,发现没有想象中的那么麻烦。这篇文章大致上来自于Lei Chen博客文章的翻译,完成以后的Emacs具有以下特性:

  • 自动完成同一个文件内的变量、函数
  • 自动完成python库中的名称
  • 代码重构
  • 模板展开功能
  • 在线帮助系统
  • 即时语法检测

其他特性还包括自动缩进,括号匹配,语法高亮,代码折叠等等。其中最有用的莫过于自动完成了,貌似很少有python编辑器可以做到这一点。而即时语法检测让emacs下的python代码书写变得像Eclipse一样,一旦有错误立刻就会高亮标记出来。

如何安装

首先你得在home目录下有一个.emacs配置文件,并且有一个用来放插件的文件夹(比如说~/.emacs.d/)

  1. 下载auto-completion.el到.emacs.d,并且在.emacs中添加如下几行:
    (require ‘auto-complete)
    (global-auto-complete-mode t)
    
  2. 下载yasnippet到.emacs.d并且编辑.emacs:
    (require ‘yasnippet) (yas/initialize) (yas/load-directory “~/.emacs.d/snippets”)
  3. 下载python-mode.el并且放到.emacs.d中。我们会在之后的配置中用到它
  4. 设置Rope, Ropemacs 和 Pymacs

    我们需要使用最新的development版的rope和ropemacs,否则在emacs中不能找到rope-completion函数。通过如下步骤安装:

    sudo apt-get install mercurial
    mkdir /tmp/rope && cd /tmp/rope
    hg clone http://bitbucket.org/agr/rope
    hg clone http://bitbucket.org/agr/ropemacs
    hg clone http://bitbucket.org/agr/ropemode
    sudo easy_install rope
    ln -s ../ropemode/ropemode ropemacs/
    sudo easy_install ropemacs
  5. 安装pyflacks以进行自动语法检查:

    sudo apt-get install pyflakes
  6. 把所有东西放到一块儿
  7. 在你的.emacs.d中创建一个init_python.el,并且加入以下内容:

    (autoload 'python-mode "python-mode" "Python Mode." t)
    (add-to-list 'auto-mode-alist '(".py'" . python-mode))
    (add-to-list 'interpreter-mode-alist '("python" . python-mode))
    (require 'python-mode)
    (add-hook 'python-mode-hook
          (lambda ()
    	(set-variable 'py-indent-offset 4)
    	;(set-variable 'py-smart-indentation nil)
    	(set-variable 'indent-tabs-mode nil)
    	(define-key py-mode-map (kbd "RET") 'newline-and-indent)
    	;(define-key py-mode-map [tab] 'yas/expand)
    	;(setq yas/after-exit-snippet-hook 'indent-according-to-mode)
    	(smart-operator-mode-on)
    	))
    ;; pymacs
    (autoload 'pymacs-apply "pymacs")
    (autoload 'pymacs-call "pymacs")
    (autoload 'pymacs-eval "pymacs" nil t)
    (autoload 'pymacs-exec "pymacs" nil t)
    (autoload 'pymacs-load "pymacs" nil t)
    ;;(eval-after-load "pymacs"
    ;;  '(add-to-list 'pymacs-load-path YOUR-PYMACS-DIRECTORY"))
    (pymacs-load "ropemacs" "rope-")
    (setq ropemacs-enable-autoimport t)
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; Auto-completion
    ;;;  Integrates:
    ;;;   1) Rope
    ;;;   2) Yasnippet
    ;;;   all with AutoComplete.el
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    (defun prefix-list-elements (list prefix)
      (let (value)
        (nreverse
         (dolist (element list value)
          (setq value (cons (format "%s%s" prefix element) value))))))
    (defvar ac-source-rope
      '((candidates
         . (lambda ()
             (prefix-list-elements (rope-completions) ac-target))))
      "Source for Rope")
    (defun ac-python-find ()
      "Python `ac-find-function'."
      (require 'thingatpt)
      (let ((symbol (car-safe (bounds-of-thing-at-point 'symbol))))
        (if (null symbol)
            (if (string= "." (buffer-substring (- (point) 1) (point)))
                (point)
              nil)
          symbol)))
    (defun ac-python-candidate ()
      "Python `ac-candidates-function'"
      (let (candidates)
        (dolist (source ac-sources)
          (if (symbolp source)
              (setq source (symbol-value source)))
          (let* ((ac-limit (or (cdr-safe (assq 'limit source)) ac-limit))
                 (requires (cdr-safe (assq 'requires source)))
                 cand)
            (if (or (null requires)
                    (>= (length ac-target) requires))
                (setq cand
                      (delq nil
                            (mapcar (lambda (candidate)
                                      (propertize candidate 'source source))
                                    (funcall (cdr (assq 'candidates source)))))))
            (if (and (> ac-limit 1)
                     (> (length cand) ac-limit))
                (setcdr (nthcdr (1- ac-limit) cand) nil))
            (setq candidates (append candidates cand))))
        (delete-dups candidates)))
    (add-hook 'python-mode-hook
              (lambda ()
                     (auto-complete-mode 1)
                     (set (make-local-variable 'ac-sources)
                          (append ac-sources '(ac-source-rope) '(ac-source-yasnippet)))
                     (set (make-local-variable 'ac-find-function) 'ac-python-find)
                     (set (make-local-variable 'ac-candidate-function) 'ac-python-candidate)
                     (set (make-local-variable 'ac-auto-start) nil)))
    ;;Ryan's python specific tab completion
    (defun ryan-python-tab ()
      ; Try the following:
      ; 1) Do a yasnippet expansion
      ; 2) Do a Rope code completion
      ; 3) Do an indent
      (interactive)
      (if (eql (ac-start) 0)
          (indent-for-tab-command)))
    (defadvice ac-start (before advice-turn-on-auto-start activate)
      (set (make-local-variable 'ac-auto-start) t))
    (defadvice ac-cleanup (after advice-turn-off-auto-start activate)
      (set (make-local-variable 'ac-auto-start) nil))
    (define-key py-mode-map "t" 'ryan-python-tab)
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;;; End Auto Completion
    ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
    ;; Auto Syntax Error Hightlight
    (when (load "flymake" t)
      (defun flymake-pyflakes-init ()
        (let* ((temp-file (flymake-init-create-temp-buffer-copy
    		       'flymake-create-temp-inplace))
    	   (local-file (file-relative-name
    			temp-file
    			(file-name-directory buffer-file-name))))
          (list "pyflakes" (list local-file))))
      (add-to-list 'flymake-allowed-file-name-masks
    	       '(".py'" flymake-pyflakes-init)))
    (add-hook 'find-file-hook 'flymake-find-file-hook)
    (provide 'init_python)
    
  8. 在你的.emacs文件中添加:(load-library “init_python”)

使用说明

  1. 代码自动展开。比如说在编辑C/C++头文件的时候键入once然后按tab,则会自动展开#ifndef __FILE_H__这样的宏。这个功能是YASnippet提供的,在emacs的菜单中可以看到它的所有宏。展开以后再按tab键可以在已展开的代码之间切换位置。
  2. 自动完成代码。函数名输入到一半按tab键会调用Rope和Ropemacs的功能自动完成库文件中的函数、变量名
  3. C-c d 显示python的doc string
  4. C-c C-c 运行当前的文件
  5. C-c ! 打开python shell

事实上init_python.el做的事情是通过使用auto_complete.el中的功能,把YASnippet和Rope的功能结合到了一块儿,实现了类似TexMate的自动完成等功能。其中YASnippet可以自定义模板,很是实用。Rope的功能还在研究中。

OgreOpcode碰撞检测使用概述

{3 Comments}


OgreOpcode几乎是不使用物理引擎时Ogre下的唯一选择,但是这个天杀的库文档实在是太少了,约等于没有。下面把这两天使用它的经验整理一下。

下载&编译

OgreOpcode没有任何Release,获取它的唯一办法是从SVN中下载源代码进行编译,SVN地址如下:
https://ogreconglo.svn.sourceforge.net/svnroot/ogreconglo/ogreopcode/trunk
在script文件夹找到自己用的编译器的项目文件,直接编译就可以了。需要注意的是,SVN中的exmples是无法正确编译的–这些例子运行所需要的一些文件没有在SVN里面!另外就是doc目录中的Doxygen配置文件中居然用的是绝对路径,不经修改是无法运行的。此外我在使用VS2005的时候发现,它的项目文件中少包含了一个叫OgreCapsuleMeshCollisionShape.cpp的文件,导致编译无法通过。需要自己把它加到OgreOpcode项目中才可以编译成功。另外在项目的“生成后事件”中会执行一个拷贝的命令,但是在svn的文件夹结构中目标文件夹是不存在的!这也会导致编译失败。
实在是被这个项目的维护人员打败了…这么多纰漏…
我知道是个人都会觉得从svn里面编译很麻烦,所以我吧它编译好了上传上来了,点这里下载ogreopcodebin。里面包括编译好的debug和release版的dll。

使用

要使用OgreOpcode,基本上可以分为这么几步:

  1. 创建一个Manager
  2. 创建一个Context
  3. 加入参加碰撞的物体
  4. 碰撞检测

初始化

    // 创建Manager。这个Manager使用Singleton实现
    CollisionManager* mCollisionMgr = new CollisionManager(sceneMgr);
    // 创建context 不同的context中可以添加不同的物体,发生不同的碰撞关系
    mCollisionContext = mCollisionMgr->createContext( "test" );

    // 创建碰撞类,并且设置不同类型之间碰撞的检测方式
    mCollisionMgr->addCollClass("charactor");
    mCollisionMgr->addCollClass("scene");
    // COLLTYPE_QUICK 表示两个charactor之间的碰撞使用简单的球体进行检测
    mCollisionMgr->addCollType("charactor", "charactor", COLLTYPE_QUICK);
    // COLLTYPE_IGNORE 表示忽略场景物体之间的碰撞
    mCollisionMgr->addCollType("scene", "scene", COLLTYPE_IGNORE);
    // COLLTYPE_EXACT 表示计算charactor和scene碰撞时的所有交点
    mCollisionMgr->addCollType("charactor", "scene", COLLTYPE_EXACT);

加入物体

OgreOpcode中的物体主要有以下几种:Box, Capsule, Sphere, Terrain, Mesh, Entity, Ptr,调用CollisionManager::createXXXXCollisionShape函数来创建。从函这些物体的名称大体上可以才到它的用法。其中Ptr是从一个三角形数组中得到一个碰撞物体的形状。

    // 根据一个entity的网格模型信息来创建碰撞形状
    EntityCollisionShape *shape = CollisionManager::getSingletonPtr()->
        createEntityCollisionShape(ent->getName());
    // 制定该shape所关联的entity
    shape->load(ent);
    // 创建一个碰撞物体
    CollisionObject* pCollisionObject = mCollisionContext->createObject( ent->getName() );
    // 设置该物体的类型,这里应该是第一步中设置的"charactor","scene"之一
    pCollisionObject->setCollClass( type.c_str() );
    // 设置object的形状
    pCollisionObject->setShape( shape );
    pCollisionObject->setForcedUpdate(forceupdate);
    // 把这个object加到我们创建的CollisionContext中去
    mCollisionContext->addObject( pCollisionObject );

其他设置

在每一帧都需要调用CollisionContext::Collide(time)来更新状态,否则是无法检测到碰撞的。

另外在所有物体都已经添加到CollisionContext中了以后需要调用一次CollisionContext::reset()。

碰撞检测

OgreOpcode中的碰撞检测包括射线(rayCheck)、球体(sphereCheck)、移动球体(sweptSphereCheck)三种方式。rayCheck就是传入一个Ray对象,检查射线上有没有和其他物体相交。sphereCheck则是创建一个虚拟的球体,检查这个球体有没有碰撞。sweptSphereCheck则是创建一个虚拟球体,并且让它沿着某个方向移动,检查在这个过程中是否有交点。

下面给出一个rayCheck的例子:

    Ogre::Ray ray(Vector(0,30,0), Vector(0,-1,0));
    Ogre::Real dist = 100;
    std::vector vectorList;
    CollisionPair** collisionPair = NULL;
    if( mCollisionContext->rayCheck(ray, dist ,
            COLLTYPE_EXACT, COLLTYPE_ALWAYS_EXACT, collisionPair ) > 0)
    {
        int count= mCollisionContext->getNumCollisions();
        for( int i = 0; i < count; i ++ )
        {
            vectorList.push_back( collisionPair [i]->contact );
        }
    }

其他

其实OgreOpcode的接口并不是那么那么的复杂,但是没有文档使得使用它变成了一个比较头疼的事情。官方网站也仅仅是一个并不活跃的论坛,让人很是怀疑这个库是不是值得去深入学习。其它的碰撞检测库如GIMPACT虽然文档上好很多,但是却没有Ogre下的wrapper。说不是什么时候我心血来潮了就会写一个这样的wrapper?

被OgreOpcode折磨中

{0 Comments}

用了很长时间的开源软件,大多是时候都用的是开发时间很长很成熟的代码。这次因为要在Ogre里面作碰撞检测,但是又不想用物理引擎这么重量级的东西,所以选择了OgreOpcode。但是没有想到的是,整个项目没有任何Release,没有文档,论坛里面门可罗雀,运行Doxygen生成的还是没有多少有价值的东西。想看看samples里面是怎么做的,但是缺少场景、模型等文件,不能运行…
google搜索OgreOpcode,唯一有价值的东西还是Azure在blog里面写的一个很简略的介绍,其他的基本上都是转载的这个。

这真的是Ogre引擎下用的最广泛的碰撞检测库么…?