metahook吧 关注:756贴子:11,755
  • 12回复贴,共1

如何在HL引擎中制作高清贴花?

只看楼主收藏回复

一楼不能放东西


IP属地:广东1楼2018-01-21 00:12回复
    你是不是想过要做自定义的子弹孔、喷漆或者一些自定义的痕迹呢?
    如果按照HL引擎的基本FA,首先要在decals.wad里加入我们自定义的纹理,然后利用gEngfuncs.pEfxAPI->R_DecalShoot或者服务端发送TE_DECAL消息来使用。
    这种方法有许多限制,首先你不能往一个WAD文件加太多纹理,其次,WAD只能保存索引色的纹理(图像质量非常LOW)。如果我们要更进一步定制,比方说做个会动的喷漆,那么WAD就不行了。
    首先我要声明一下名词:
    Decal 即 贴花,指贴在地图表面的图像。
    这篇文章给出一种方法让你能够使用自己加载的纹理进行贴花,还可以做到实时更新贴花,做出动态效果。
    首先我们认识一下引擎提供的一个贴花函数 R_DecalShoot ,它的原型如下:
    void R_DecalShoot( int textureIndex, int entity, int modelIndex, vec3_t position, int flags )
    第1个参数 textureIndex 通常你必须这样来获得: pEfxAPI->Draw_DecalIndex( pEfxAPI->Draw_DecalIndexFromName("{yblood6") ) ,这个名称必须是decals.wad里存在的,否则函数将会失败。
    第2个参数 entity 贴花将会贴到该实体的模型上,该实体的模型必须是 mod_brush ,你必须提供这个实体,否则函数将会失败。
    第3个参数 modelIndex ,如果 entity 参数提供的实体没有设置模型,将会使用此参数指定的模型 ,否则忽略此参数。
    第4个参数 position 贴花的坐标。世界坐标。
    第5个参数 flags 该参数可以指定为 FDECAL_ 相关的标志。
    我们并不想通过这个 textureIndex 来指定纹理,我们需要自己改造一个 R_DecalShoot 。非常幸运的是,HL引擎的写法让我们可以简单地实现。
    我们来看看 R_DecalShoot 内部是怎么实现的:

    可以看到,它首先通过调用一个名为 Draw_DecalTexture 的函数获得一个 texture_t 的指针,然后将该指针传给一个名为 R_DecalShoot_ 的函数(注意,后面有个下划线)。
    可以猜测,我们自己创建一个 texture_t 然后调用 R_DecalShoot_ 应该是没问题的。
    所以我先提供这个结构体的定义:
    typedef struct texture_s
    {
    char name[16];
    unsigned int width, height;
    unsigned int gl_texturenum;
    void *texturechain;
    int anim_total;
    int anim_min, anim_max;
    void *anim_next;
    void *alternate_anims;
    unsigned int offsets[4];
    void *pPal;
    } texture_t;
    看似很复杂,其实只需要指定其中的 width、height、gl_texturenum 这3个即可,其它的全部设为0。
    既然我们要调用 R_DecalShoot_ ,那么就得知道它的原型:
    void R_DecalShoot_( texture_t *ptexture, int textureIndex, int entity, int modelIndex, float *position, int flags, float scale )
    可以看出这个函数和 R_DecalShoot 非常像,但聪明的你可能已经发现了问题,既然都有一个 ptexture 来指定纹理了,干嘛还需要一个 textureIndex 呢,不是多此一举吗?
    解释如下:引擎为了节省内存占用,时不时会将一些不经常访问的资源丢弃掉,这时 ptexture 就可能会被丢掉(释放掉),所以引擎只为贴花存储一个 textureIndex 当引擎绘制贴花时,
    会使用 textureIndex 来查找一个 texture_t ,如果这个 texture_t 已经被丢弃了,那么引擎会重新载入它。
    可能你还有疑问,既然传 textureIndex 一本万利,干嘛要传 ptexture 呢。那是因为 R_DecalShoot_ 需要 texture_t 里的 width 和 height 。
    但是,根据上面的解释,引擎在绘制贴花时才通过 textureIndex 来查找 texture_t ,所以我们需要Hook那个查找函数,返回我们自己的 texture_t 才行。
    那个函数上面已经出现过了,它就是 Draw_DecalTexture ,它的原型如下:
    texture_t * Draw_DecalTexture( int textureIndex )
    你可以看到它只有一个 int 参数,当引擎需要绘制一个贴花时,会调用这个函数来获取一个 texture_t ,而参数的 textureIndex 正是我们传入 R_DecalShoot_ 的那个。
    现在尝试一下,首先我们要自己造一个 R_DecalShoot ,例如:
    void R_My_DecalShoot( texture_t *ptexture, int textureIndex, int entity, int modelIndex, float *position, int flags ){ R_DecalShoot_( ptexture, textureIndex, entity, modelIndex, position, flags, 1.0f );}
    然后还要Hook Draw_DecalTexture 函数,例如:
    texture_t * Draw_DecalTexture( int textureIndex )
    {
    if ( textureIndex == 999 )
    {
    return &g_my_texture;
    }
    return g_callback_Draw_DecalTexture( textureIndex );
    }
    我这里判断 textureIndex 如果是999那么返回我们自己创建的一个 texture_t
    然后调用 R_My_DecalShoot ,例如:
    void Decal_Test( void )
    {
    R_My_DecalShoot( &g_my_texture, 999, ent, 0, tr.endpos, 0 );
    }
    这里我给 textureIndex 传了 999 这个数值,如果贴花创建成功,引擎会把这个值存储到引擎内部的神秘数组里。
    等到引擎需要绘制这个贴花时,便会调用 Draw_DecalTexture 来获取 texture_t 用于绘制。这时我们检查 textureIndex 是不是我们定义的,
    如果是我们定义的,那就返回我们自己创建的 texture_t 给引擎绘制。
    大功告成!
    附上一张效果图:

    附加:
    你可能想立刻删掉一个贴花,那么你可以使用如下方法:
    gEngfuncs.pEfxAPI->R_DecalRemoveAll( textureIndex )
    注意:该方法会删除所有 textureIndex 为指定的值的贴花,所以建议给 textureIndex 再包装一层,使得每个贴花拥有独立的索引,而不是靠纹理索引来区分。
    核能注意: textureIndex 的取值范围只允许 -32768 ~ 32767
    函数 R_DecalShoot_ 的地址:
    0x01D4B530 //32660x01D49750 //7561
    函数 Draw_DecalTexture 的地址:
    0x01D321F0 //32660x01D2EB30 //7561


    IP属地:广东2楼2018-01-21 00:14
    收起回复
      实例代码我就不放了,如果你实现过程中遇到问题,回帖提问即可。


      IP属地:广东3楼2018-01-21 00:20
      回复


        IP属地:上海4楼2018-01-21 00:35
        收起回复
          好像很清晰的样子,原版的感觉贼鸡儿模糊


          IP属地:广东来自Android客户端5楼2018-01-21 01:09
          回复
            我去,动态,厉害了!


            来自Android客户端6楼2018-01-21 09:15
            回复
              把老婆放到墙上


              IP属地:重庆来自Android客户端7楼2018-01-21 09:32
              回复
                大佬厉害,佩服


                来自Android客户端8楼2018-01-22 00:55
                回复
                  射惠,射惠


                  IP属地:江苏9楼2018-01-31 14:35
                  回复
                    期待已久的教程call


                    IP属地:广东来自Android客户端10楼2018-01-31 23:39
                    回复
                      大佬 我问下 文理索引 和 模型索引是什么意思


                      IP属地:江苏11楼2018-08-08 22:19
                      回复