战魂小筑

讨论群:309800774 知乎关注:http://zhihu.com/people/sunicdavy 开源项目:https://github.com/davyxu

   :: 首页 :: 新随笔 :: 联系 :: 聚合  :: 管理 ::
  257 随笔 :: 0 文章 :: 506 评论 :: 0 Trackbacks

#

本人的设计图可以方便在文本编辑器里查看(>为派生 +为包含)

MAX插件导出的模型资源分:Mesh/Skin, Skeleton, Animation

不同动作按照不同的动画文件保存

 

ResourceHandle 包含基本id
        >NamedResourceHandle 带有名称资源,包含hashA, hashB, 使用暴雪hash函数生成
        >ModelHandle       
        >MeshHandle           
        >SkeletonHandle       
        >AnimationHandle

获取资源,如果资源不存在时,自动加载
ModelMaster.ManualCache( &ModelHandle )

直接资源访问:
RawSkeleton = ModelMaster.ManualCache( &SkeletonHandle("a.skl") )
RawSkeleton->GetMarker(...)

异步资源加载,异步id保存于ModelHandle中,在callback中根据id确认
ModelMaster.AsyncCache( &ModelHandle )

获取资源指针,未加载时,返回空
ModelResource = ModelMaster.Fetch( &ModelHandle )

模型资源加载器
    在模型句柄中绑定对应加载器
    可以自行编写带换装的ModelLoader,通过读取自己的配置文件,自行加载资源后生成RawModel
ModelLoader
    ModelHandle         ---mapping--->    ModelLoader         ---generate--> RawModel
    MeshHandle          ---mapping--->    MeshLoader             ---generate--> RawMesh
    SkeletonHandle     ---mapping--->    SkeletonLoader      ---generate--> RawSkeleton
    AnimationHandle    ---mapping--->    AnimationLoader   ---generate--> RawAnimation

引用计数类   
ModelResource
    >RawModel
    >RawMesh
    >RawSkeleton
    >RawAnimation

RawModel中不保留Handle,只保留指针
RawModel
    +MeshVB    从模型文件中直接读取顶点格式,包含 静态,GPU,CPU类型顶点
        +RawMesh
        +MeshIB        一次性填充IB
        >GPUMeshVB    一次性填充VB
        >CPUMeshVB    每帧计算
    +Animation            运行期数据(时间/帧)
        +RawAnimation    原始动画内容
    +Skeleton
        +RawSkeleton

posted @ 2010-03-12 16:13 战魂小筑 阅读(1973) | 评论 (0)编辑 收藏

1. System::String 转换到 const wchar_t*

 
   1:  const wchar_t* ToUnmanagedUnicode( System::String^ str )
   2:  {
   3:      pin_ptr<const WCHAR> nativeString1  = PtrToStringChars( str );
   4:      return (const wchar_t*)nativeString1;
   5:  }

 

2.  const wchar_t* / const char* 转换到 System::String

   1:  const wchar_t* p= L"hello";
   2:   
   3:  System::String( p ).ToString();

 

3.  C++数值类型转CLR数值类型

   1:  int a;
   2:   
   3:  System::Int32 b = System::Int32( a );

 

4. HWND 转为IWin32Window

   1:  public ref class WindowWrapper : System::Windows::Forms::IWin32Window
   2:  {
   3:      public: WindowWrapper(IntPtr handle)
   4:      {
   5:          _hwnd = handle;
   6:      }
   7:   
   8:  public:
   9:      property IntPtr Handle
  10:      {
  11:          virtual IntPtr get(void){return _hwnd;};
  12:      };
  13:   
  14:      private: IntPtr _hwnd;
  15:  };
  16:   
  17:  HWND nativehwnd;
  18:  IWin32Window^ w = gcnew Managed::WindowWrapper( System::IntPtr( nativehwnd ) );
posted @ 2010-03-11 09:37 战魂小筑 阅读(3286) | 评论 (0)编辑 收藏

Direct3D中的Shader是这样的:

1. ASM Shader是最元老的也是DX8主要使用Shader

2. fxc编译器可以同时编译ASM,HLSL和fx脚本,其中HLSL和fx可以查看编译后的GPU汇编代码

3. D3D9中,fx是HLSL的一种渲染脚本,简化了HLSL设置及常量绑定,并且附带RenderStateBlock及设置

但只能用于制作简单的Shader

4. DirectXSDK中有一个概念混淆:C++例子中的BasicHLSL使用的其实还是fx,HLSLwithoutEffects例子才是真正的纯HLSL

5.fx与HLSL程序鉴别:

使用fx程序必定含有:D3DXCreateEffectXXX 系列函数, ID3DXEffect对象,渲染中能看到SetTechnique,BeginPass,EndPass之类的字眼

使用纯HLSL程序含有:D3DXCompileShader,ID3DXConstantTable对象,GetConstantByName,GetConstantDesc之类的字眼

6. 在fx中包含有 VertexShader,PixelShader代码及profile,entry,RenderState设置及简单的绘制过程(pass)。一次编译后,VS,PS,Texture,Sampler及常量都是在ID3DXEffect对象中自动完成,无需手动设置。

7. HLSL可以将VS及PS代码写入1个.hlsl文件。注意,以下这种代码可以在HLSL中编译过,但实际没有任何效果

   1:  sampler_state
   2:  {
   3:      Texture = <tex>;
   4:      MipFilter = LINEAR;
   5:      MinFilter = LINEAR;
   6:      MagFilter = LINEAR;
   7:  };

这点可以参考AMD RenderMonkey中只在shader中使用sampler而忽略texture。

8. fx中往shader设置纹理使用的是ID3DXBaseEffect::SetTexture下的这个函数

   1:  HRESULT SetTexture(
   2:    D3DXHANDLE hParameter,
   3:    LPDIRECT3DBASETEXTURE9 pTexture
   4:  );

但是在HLSL中,这点就变得很麻烦,需要手动设置,可以参考这篇文章

Shader代码片段:

   1:  sampler Samp0 = sampler_state
   2:  {
   3:      Texture = <Tex0>;
   4:      MipFilter = LINEAR;
   5:      MinFilter = LINEAR;
   6:      MagFilter = LINEAR;
   7:  };

编译HLSL代码后得到ConstantTable,然后取出句柄:

   1:  ScalarHandle = pixelConstTable->GetConstantByName(0, "Scalar");
   2:   
   3:  Samp0Handle = pixelConstTable->GetConstantByName(0, "Samp0");
   4:   
   5:  Samp1Handle = pixelConstTable->GetConstantByName(0, "Samp1");

再从句柄取出symbol的描述:

   1:  UINT count;
   2:   
   3:  pixelConstTable->GetConstantDesc(Samp0Handle, & Samp0Desc, &count);
   4:   
   5:  pixelConstTable->GetConstantDesc(Samp1Handle, & Samp1Desc, &count);

通过上面的描述,将纹理变量的寄存器偏移作为纹理的stage

8.  优化常量设置速度的方法一般就是根据字符串取出句柄,以后每次渲染时,只通过句柄设置。但ID3DXConstantTable最后还是通过

IDirect3DDevice9::SetPixelShaderConstantX 系列函数来实现的

   1:  HRESULT SetPixelShaderConstantF(
   2:    UINT StartRegister,
   3:    CONST float * pConstantData,
   4:    UINT Vector4fCount
   5:  );

9. HLSL将一段包含VS和PS代码编译完成后,将得到VS和PS两个单独的ID3DXConstantTable

posted @ 2010-03-05 14:26 战魂小筑 阅读(3260) | 评论 (0)编辑 收藏

 

最近在写D3D9模拟D3D10接口的渲染系统中碰到大量的渲染状态对象,不仅成员多,枚举也多的要命。

 

    struct CORE_API RasterizerState : ResourceHandle            
    {
        eFillMode            mFillMode;        
        eCullMode            mCullMode;
        bool                mFrontFaceCCW;
        float                mDepthBias;
        float                mSlopeScaledDepthBias;
        bool                mDepthClipEnable;
        bool                mScissorEnable;
        bool                mMultisampleEnable;

        RasterizerState();
    };

而要从配置文件中读取数据并填充到这个结构体,对于C++来说完全就是吃力不讨好的,写出来的代码也是极为过程,修改和扩展极为麻烦的。

因此决定使用反射的方法来填充数据,先总结一下我的C++反射系统

class RTTIObject // 动态类型识别对象基类,对象通过一些宏后可以很方便的通过字符串创建出类实例,并且可以查询注册时的类型和其他绑定信息
class NameRef  // 名字表,类似于虚幻中的FName,可以定义Const和普通Name,比较和拷贝只是一个dword耗费的时间
value_parse,value_tostring,value_typename // 一系列类型模板函数,提供对类型的ToString,Parse及类型名查询

 

首先需要处理的是枚举查询,这里将枚举通过宏做成一个个枚举对象,并可以通过名字创建实例

#define DECLARE_ENUMOBJECT( TEnum ) \
    struct EnumObject_##TEnum : EnumObject\
    {\
    DECLARE_RTTIOBJECT( EnumObject_##TEnum );\
    EnumObject_##TEnum( );\
    };


#define IMPLEMENT_ENUMOBJECT_BEGIN( TEnum, TEnum_prefixoffset, TMember_prefixoffset ) \
    IMPLEMENT_RTTIOBJECT_STRING( EnumObject_##TEnum, #TEnum + TEnum_prefixoffset, #TEnum + TEnum_prefixoffset, "EnumObject" )\
    EnumObject_##TEnum::EnumObject_##TEnum(){ const int member_prefixoffset = TMember_prefixoffset;

#define ENUMOBJECT_ADD( enumkey ) AddMember( #enumkey + member_prefixoffset, (dword)enumkey );

#define IMPLEMENT_ENUMOBJECT_END }

#define ENUMOBJECT_STATICINIT( TEnum ) EnumObject_##TEnum::StaticInit();

EnumObject 中通过宏将枚举的名称和值保存在这个对象中

IMPLEMENT_ENUMOBJECT_BEGIN( eFillMode, 1, 3 )  // 这里的1,3是将eFillMode及FM_Point转成字符串后去掉前缀
    ENUMOBJECT_ADD( FM_Point )
    ENUMOBJECT_ADD( FM_Line )
    ENUMOBJECT_ADD( FM_Fill )
IMPLEMENT_ENUMOBJECT_END

// 注册到RTTIObject系统

ENUMOBJECT_STATICINIT( eFillMode )

 

// 通过枚举对象可以查找到字符串对应的值
dword v;
EnumObject::GetEnumValue( "FillMode", "Point", v )



下一步是将结构体成员信息记录

    void SettingObject::BindMember( const NameRef& objname, void* instancePtr, void* dataPtr, SettingProxy* proxy )
    {
        proxy->mOffset = dword(dataPtr) - dword(instancePtr);

        MemberList& memberlist = mSettingMap[ objname ];
        memberlist[ proxy->mName ] = proxy;
    }

这里记录的是结构体成员的内存偏移

使用大量的宏,可以让结构体绑定变得漂亮

#define BIND_SETTINGOBJECT_BEGIN( TClass ) \
    { const NameRef& soname = TClass::StaticGetClassInfo()->mClassName;TClass soobj;

#define BIND_SO_MEMBER( TMemberType, TMember ) \
    so.BindMember( soname, &soobj, &soobj.TMember, new TSettingElement<TMemberType>(#TMember + 1 ) );

#define BIND_SO_MEMBER_NAME( TMemberType, TMember, TName ) \
    so.BindMember( soname, &soobj, &soobj.TMember, new TSettingElement<TMemberType>(TName) );

#define BIND_SO_ENUM( TEnumType, TMember ) \
    so.BindMember( soname, &soobj, &soobj.TMember, new TSettingEnum(#TMember + 1, #TEnumType + 1) );

#define BIND_SO_ENUM_NAME( TEnumType, TMember, TName ) \
    so.BindMember( soname, &soobj, &soobj.TMember, new TSettingEnum(TName, #TEnumType + 1) );

#define BIND_SETTINGOBJECT_END }

绑定代码如下

        BIND_SETTINGOBJECT_BEGIN( RasterizerState )
            BIND_SO_ENUM    ( eFillMode    , mFillMode )
            BIND_SO_ENUM    ( eCullMode    , mCullMode )
            BIND_SO_MEMBER    ( bool        , mFrontFaceCCW )
            BIND_SO_MEMBER    ( float        , mDepthBias )
            BIND_SO_MEMBER    ( float        , mSlopeScaledDepthBias)
            BIND_SO_MEMBER    ( bool        , mDepthClipEnable)
            BIND_SO_MEMBER    ( bool        , mScissorEnable)
            BIND_SO_MEMBER    ( bool        , mMultisampleEnable)
        BIND_SETTINGOBJECT_END

 

所有结构体的信息被记录在SettingObject中,读取配置文件填充结构体的任务就变得异常的简单了

    SettingObject settings;
// 将所有的结构体信息记录
    InitRenderStateObjectSetting( settings );

    const NameRef& rzname = DepthStencilState::StaticGetClassInfo()->mClassName;

    DepthStencilState a;
 // 这里就是将配置文件的信息填充到结构体
    settings.SetMember( rzname, &a, "BackFace.StencilFunc", "Equal" );
 
posted @ 2010-02-26 17:58 战魂小筑 阅读(2517) | 评论 (0)编辑 收藏

今天在写一个宏时始终报

fatal error C1075: end of file found before the left brace '{' at 'd:\xxx.cpp(49)' was matched

检查排除代码,去除了宏,留下了一句

{__asm int 3;}

将大括号去掉,能编译成功。去掉;也能编译成功

我就奇了怪了,查了MSDN文档,里面是这么写的:

__asm assembly-instruction [ ; ]

__asm { assembly-instruction-list } [ ; ]

asm-statement:

__asm assembly-instruction ;opt

__asm { assembly-instruction-list };opt

assembly-instruction-list:

assembly-instruction;opt

assembly-instruction;assembly-instruction-list;opt

也就是说 分号是可选的,这个类似于lua

不加分号也可以这样写

__asm mov al, 2   __asm mov dx, 0xD007   __asm out dx, al
 
于是乎,我想这成了一个VC的bug
 
编译环境vs2008 c/c++
posted @ 2010-02-24 17:11 战魂小筑 阅读(4224) | 评论 (3)编辑 收藏

最近翻出很早之前自己写的一套使用Direct3D 9  Effect系统的纯shader 3d引擎,打算使用最新技术重写。重写的主要修改在于:

1. 去掉Effect系统,改用HLSL + 渲染脚本

2. 优化渲染接口,使用材质统一shader和渲染状态

随即参考了DirectX SDK的Graphics部分文档:DirectX9 时代的Effect系统纯粹只是一个HLSL的简单渲染脚本实现,除了DXUT,FXComposer等极少程序使用这套东西外,大型的引擎很少使用这种半成品系统。到了DirectX10甚至11,因为架构更改,去掉固定管线,因此Effect成为较为高效和便捷的渲染脚本,如果不是要求较高的3d引擎,一般的游戏使用DirectX10的Effect渲染脚本还是很不错的。

DX10的fx脚本与DX9的差异在于

渲染状态,采样器状态等都变为对象,并与API高度统一

在脚本与API中均可以设置

DepthStencilState EnableDepth
{
    DepthEnable = TRUE;
    DepthWriteMask = ALL;
    DepthFunc = LESS_EQUAL;
};

BlendState NoBlending
{
    AlphaToCoverageEnable = FALSE;
    BlendEnable[0] = FALSE;
};

technique10 Render
{
    pass P0
    {

        SetDepthStencilState( EnableDepth, 0 );
        SetBlendState( NoBlending, float4( 0.0f, 0.0f, 0.0f, 0.0f ), 0xFFFFFFFF );
    }
}

本人觉得,这样的设计让图形API更为敏捷与归类化,另外,也便于StateManager或者自己做渲染状态转移及差异比较时更为高效

 

Vista操作系统推出很久后,DX10的显卡也占有了大量的市场份额。但是由于DX10仍然是一个过渡API(类似于DX8),因此,很多3D游戏要么仍然支持DX9,要不然即支持DX9也支持DX10,甚至DX11.

   看博客上有达人组团编写类似DX10接口和系统的软渲染,DX10的设计是优秀的。因此,在DX9 HLSL基础上,结合自己编写的渲染脚本会是非常好的选择。

    渲染脚本我的设计思路是这样的:

1. 只是一种预处理脚本,并非实时运行脚本。

编译器将文本解析后,转化为一些运行指令,比如:本pass使用一块小纹理,下一pass的target是这个纹理,并且开启哪些渲染状态。

2. 自定义格式的解析脚本。

使用lua,python等脚本其实也是可以的。但是在出现错误时,报出的错可能会让不熟悉这个脚本语言的人莫名其妙。

使用松鼠sq脚本语言?可惜其在lua基础上,对table的slot初次赋值时必须使用<-而不是统一使用=,因此会让你的脚本稀奇古怪

XML脚本? XML可以避免复杂的语法检查,写完就是归整的,但也是罗嗦的,本来Texture[2]可以表达完毕的,非要<Texture index = 2/>来罗嗦下。

OGRE的compositor脚本和材质脚本就是自己解析的,不过出乎预料的使用了BNF范式这类较为正规的方法。这就是说,需要先解析BNF表达式,然后再输入脚本解析,编写过程和系统复杂度会变得异常复杂。

最终选择还是使用自己解析的脚本,使用一些具体代码结构来替代BNF这类高深的东西

决定以后,下一步需要制定渲染脚本具体各部分及制作过程

1. 基本lexer

   从文本得到各种token

1. 渲染状态对象

   照着DX10抄就好

2. Shader导入口

     shader文件来自于何处,入口怎样定义

3. 渲染脚本VM及指令

  决定一个纹理怎样设置,RenderTarget怎样使用等的指令

posted @ 2010-02-15 23:18 战魂小筑 阅读(2399) | 评论 (0)编辑 收藏

Lemon是本人开发的一套适用于2D游戏动画和高级游戏框架及配套编辑器(LemonComposer)

Lemon系统特性:

1. 支持 Canvas,Sprite,ImageSet(图片帧存储于一张图片,等大小)

2. ImageSetEx(自由摆放的图片帧于一张图片)*

3. 支持对象无关键帧时使用静态属性进行设置,类似于HGE里的精灵

4. 每个对象均可成为Container,并拥有Child Node

5. 动画关键帧类型支持:缩放,旋转,位移,颜色,动画帧,锚点,音效*

6. 支持拾取

7. 基于XML存储的文件格式

8. 基于Squirrel松鼠脚本的高速面向对象脚本*

9. 图形系统Graphics抽象,适用于任何渲染设备

10. 控件系统*

11. 视频回放*

所有对象均由RTTI创建,枚举均有NamePool+Hash,并由于PropertySet的反射系统

 

LemonComposer编辑器特性

界面基于我去年开发的MotionUI,lua

8+1控制点点对象调节属性

类Adobe Flash的帧编辑。

类3DS Max的分轨道关键帧编辑,有助于优化art assert

自由调节对象层级及父子关系

全功能无限制自由Redo,Undo

 

*将在未来版本支持

image

 

 

2D游戏是独立游戏的主流,也是创意,投入比最小的一个维度。但是基于2D的大多是一些类似于HGE,IndieLib等开源免费2D引擎。但是面对游戏中大量的动画而言,开发者大多是使用图片帧来制作,虽然效果很好,但是设备资源好用和制作难度也是很难控制的。

流行于去年的植物对僵尸和2004年发行的RO Offline经过资源分析,就是使用类似于Lemon系统,或者说Flash的系统制作而成,因此效果和扩展性非常好。

这就是Lemon存在的理由。

Flash面向的是GDI+Web,那么Lemon就是针对游戏专有的,基于硬件加速的游戏框架

Flash Action Script 对应的就是Lemon的Squirrel脚本

FlashIDE 对应的就是Lemon Composer

Lemon的目标就是让2D游戏开发更简单,让游戏中充满更多的动画, 让游戏开发难度降低

farming-gold-in-pvz[1]

posted @ 2010-02-05 14:58 战魂小筑 阅读(2626) | 评论 (4)编辑 收藏

for /r 路径 %%i in (匹配文件名) do 指令

 

例子:

for /r publish\ui\ %%i in (*.lua) do luac -o %%i %%i

posted @ 2010-02-04 17:27 战魂小筑 阅读(1583) | 评论 (0)编辑 收藏

前面一片文章中lua出现的bug,其实是lua本身结构问题导致的:

lua中,数值使用double来存储,包含整形和double。而解析出来的整形也是被强转为double进行存储,这样就会出问题。

举一个简单的例子:

double f = (double)0xffffffff;
int a = int(f);

a肯定不是-1

这里的文章说明这个类型转换问题的缘由。

 

Squirrel脚本中就不会有这个问题

local a = 0xffffffff

print( a )

结果为-1

查看其源代码:

typedef union tagSQObjectValue
{
    struct SQTable *pTable;
    struct SQArray *pArray;
    struct SQClosure *pClosure;
    struct SQGenerator *pGenerator;
    struct SQNativeClosure *pNativeClosure;
    struct SQString *pString;
    struct SQUserData *pUserData;
    SQInteger nInteger;
    SQFloat fFloat;
    SQUserPointer pUserPointer;
    struct SQFunctionProto *pFunctionProto;
    struct SQRefCounted *pRefCounted;
    struct SQDelegable *pDelegable;
    struct SQVM *pThread;
    struct SQClass *pClass;
    struct SQInstance *pInstance;
    struct SQWeakRef *pWeakRef;
    SQRawObjectVal raw;
}SQObjectValue;

 

可以看到

SQInteger nInteger;
SQFloat fFloat;

是分开存储的,因此就不会有这个问题

 

lua解决方法:

1. 将十六进制换为10进制存储

2. 等待大侠或者官方修改代码,做出patch

posted @ 2010-02-04 12:49 战魂小筑 阅读(7922) | 评论 (9)编辑 收藏

最近将自己的UI工程在Release版下编译,发现有部分控件的颜色居然偏黄,想必液晶也不至于老化的那么厉害,随机开始将工程加入调试信息进行跟踪,排除UI工程,及自己写的lua封装问题,将问题缩小为:

lua代码:

t = {}

t.FillColor = 0xFFFFFFFF

foo( t )

在C++中注册一个foo函数,然后获取table t中的FillColor成员

发现取得的值居然为0x80000000

 

使用lua c api测试代码

lua_newtable( L );
lua_setglobal( L, "t");

lua_getglobal( L, "t");
lua_pushstring(L,"FillColor");
lua_pushinteger( L, 0xffffffff );
lua_pushinteger( L, -1 );
lua_settable( L, -3 );

lua_getglobal( L, "t");
lua_pushstring( L, "FillColor");
lua_gettable( L, -2 );
int t = lua_tointeger( L, -1 );

结果t也是0x80000000

 

然后将十六进制的0xFFFFFFFF换成十进制的4294967295,测试结果正确显示-1

再使用几个样本测试

0xFF5F5F5F = 4284440415 会被改成0xFF5F5F00

 

这个bug倒是很好解决,将代码中的十六进制数全换为10进制即可。但是为什么只在Release版本发生呢?

本人使用的是lua 5.1.4 原生

posted @ 2010-02-04 10:59 战魂小筑 阅读(1803) | 评论 (2)编辑 收藏

仅列出标题
共26页: First 15 16 17 18 19 20 21 22 23 Last