![2335ce91401cb60cf7714060c438dc66.png](https://i-blog.csdnimg.cn/blog_migrate/56c3aa4b335d825b422f62906e77928a.jpeg)
考试周终于结束辣, 复习的时候见缝插针的给Sakura写了下场景部分以及编辑器反射的部分, 这篇文章大致写一下已经实现的这些东西。
![c364d237c2d71afc9c476afaa0be4b9e.png](https://i-blog.csdnimg.cn/blog_migrate/02ab9609b23a6af42f1a44059a217d92.jpeg)
照例放仓库地址, 目前最新的提交在SakuraScene分支。
Sakuragithub.com场景部分
场景部分采用了轻量级引擎普遍采用的Node方案, 提供了带Transform的Node以及NodeProperty两个小型结构:
![a8832ec64f199f8733bafc11119cd538.png](https://i-blog.csdnimg.cn/blog_migrate/1d8c943a66669371ac109c242749e8b1.png)
场景整体组成一棵树, 每个节点均带有Transform, 这也方便对场景进行组合性的导入/导出, 通过树结构可以让序列化等工作变得非常清晰。
![1f0124699d3ce8b768583a0651b04069.png](https://i-blog.csdnimg.cn/blog_migrate/1d335a8ae261b938e60fcd2569ec7336.png)
Scene作为一种特殊的Node, 存储着导出/入场景时的一些附加信息, 导出时会将主node包被成为Scene, 在导入时再进行向下的转换, 并链接入场景树。
在场景和渲染模块的对接上, 建立了一块黑板RenderScene(没错从ue来的)。所有的VB和IB以及材质等一系列资源由上文完成的ResourceManager创建并保持。
需要渲染的node持有资源引用, 将对应的引用提交到RenderScene中创建出RenderItem, 再由GraphicsManager进行收集, 即可进行绘制。
![1b0a48b3587e137049247ec2002c8b5c.png](https://i-blog.csdnimg.cn/blog_migrate/823a979ddf01413750039bfa64247077.jpeg)
这样在场景导入导出上也十分方便, 用assimp读通用格式文件, 再进行节点信息的收集以及mesh Geometry的导入即可。
编辑器反射
由于C++20反射还没有纳入标准, 甚至连实验性特性也没有纳入, 所以目前只能使用动态反射的方案了。我个人还是有些洁癖, 但只在编辑器下用用非侵入性的动态反射还算是可以接受。反射实现起来倒是不难, 但因为已经有非常成熟的开源库, 这里就不再重复造轮了, 直接用了rttr:
RTTRgithub.com支持继承, 也可以添加meta, 用来写编辑器绰绰有余了。引擎本身不会依赖反射, 保持相对静态的结构就可以了。
![5534f48b20ed09309d0504c64aec070f.png](https://i-blog.csdnimg.cn/blog_migrate/8363ebfed1a441c8b6659bd939516439.png)
RTTR在使用上也比较简单漂亮, 不需要像侵入式方案一样写大量扭曲的宏, 在.cpp进行注册就可以辣:
![536e52938e429269841e47476b81c80a.png](https://i-blog.csdnimg.cn/blog_migrate/f2e60fec7aa5c0751976e0d4bde51e6f.jpeg)
由于RTTR支持反射, 我们只需要利用继承虚表把注册宏的类名存进类成员里, 这样又可以很方便的通过接口拿到对象的终极类名, 如此就能轻松获取并遍历属性字段了。
由于要拉编辑器, 并且需要跨dll进行交接, 因此还是需要确定下基本类型。大致如下分类:
- bool, float, int, uint, string, vector3/4, color等作为基础类型, 需要拉对应的控件;
- vector, map等容器;
- 嵌套的反射类对象。
属性字段
![8951edaa34e9e7190f98a824f9a799ec.png](https://i-blog.csdnimg.cn/blog_migrate/26f0795b84e5583f638182ce1081edab.png)
包含属性字段的一些信息, 方便处理。PropertyData涵盖上面的所有类型, 也可以通过里面的obj指针进行成员嵌套。
属性字段遍历
![397bb166f973f02dce8b17ed20f7f9de.png](https://i-blog.csdnimg.cn/blog_migrate/070352c30f6629bac5e227900229b9fe.jpeg)
有了反射库基本没什么好说, 遍历属性字段并且把属性数组导出就好了。比较坑的是std::string不能像float一样, 没有公共语言运行时支持, 也不能直接传char*出去, 需要写个wrapper来包一下。
![fd524cd266bad49a9054c24d95612779.png](https://i-blog.csdnimg.cn/blog_migrate/4c7f635dfaf6079e483d0bf078a5d897.jpeg)
C#端用同样的wrapper接住:
![65821ce426b6c8937f35aced19927cdf.png](https://i-blog.csdnimg.cn/blog_migrate/808beb14235b10c429a8fe27ea904226.png)
再直接new string(sbyte*)就可以完成交接了。还要注意要确实的在传入的C#引用上直接操作, 这样才能让C#的GC顺利接盘, 不然很容易泄露。不过不得不说C++配C#还是很舒爽, 写起来完全没有违和感。
基本类型/容器/对象嵌套
![7c8410c1d548b548eab716c879405961.png](https://i-blog.csdnimg.cn/blog_migrate/98c96b17a1544b9cec9fbe9773ad03ee.jpeg)
基本类型需要写全套的set和get接口并导出dll。C#端遍历对象的属性字段并按类型进行dll调用就可以进行get/set了。这些类型的控件也需要每样拉一个并提供一个公共接口。
![653003707737dd9839257945b1e8d038.png](https://i-blog.csdnimg.cn/blog_migrate/5265bcbe6fba9a67d147f7b9cd5c5c12.jpeg)
容器和对象嵌套上也是同样的道理, 直接解析容器返回字段数组/对象就可以嘞。完成之后的编辑器界面基本像下面这个样子:
![7f95f2d8961788a03105cde91b0dfa58.png](https://i-blog.csdnimg.cn/blog_migrate/c29214e900c855d0056c7ced0eaec768.png)
可扩展管线的方案
因为下一步要写多着色模型, 所以构思了一下管线扩展的方案, 大致有这样两种:
- shaderlab式的shadergraph, 组合pass进行多样化的绘制, 即好多好多种shader。坏处是需要写套shader graph和方便的parameter收集机制, 不然需要写很大量的重复hlsl和srv绑定代码;
- 为renderer指定固定的shadingmodel, 通过扩展shadingmodel来获取多样性。坏处是比较呆不够灵活, 只能通过在管线上开天窗(类如ue的材质编辑器, 好多好多种材质)或者硬写新管线来完成扩展;
- 我全都要...
现在还是考虑用第二种思路, 因为已经写了FG了, 只需要把FG露出来就可以很方便的加pass和注册新的着色模型了。
2020年拉, 大家新年快乐~