我感觉自己照抄视频中的脚本还是有点慢了
Entrypoint
启动点
Applicationlayer
Windowslayer
Input
Event
Renderer
RenderAPIabstract
Debuggingsupport
Scriptinglanguage
Memorysystem
ECS
Physics
FileIO,virtualfilesystem(VFS)
Buildsystem
buildcustomformatofdataoffline
创建github仓库
创建VisualStudio空项目
在sln文件的目录下gitclone仓库
现在我们的解决方案中只有Engine一个项目
配置管理器-项目上下文-平台-编辑中删去Win32
在Engine项目的属性页配置改为所有配置
在Engine项目的属性页-配置属性-常规-常规属性-配置类型改为dll
现在这个项目会构建成dll而不是exe
在Engine项目的属性页-配置属性-常规-常规属性-输出目录改为$(SolutionDir)bin\$(Configuration)-$(Platform)\$(ProjectName)\
中间目录改为$(SolutionDir)bin-int\$(Configuration)-$(Platform)\$(ProjectName)\
这就表示bin-int是一个可以随时删除的文件夹
这就链接到了Engine项目产生的dll
在两个项目下创建src文件夹
在Engine项目下面新建一个测试用的
Test.h
#pragmaoncenamespaceMeowEngine{__declspec(dllexport)voidPrint();}Test.cpp
然后在Sandbox里面创建
Application.cpp
在Engine的src文件夹中新建一个Engine文件夹
创建Application类
#pragmaonce#ifdefME_PLATFORM_WINDOWS#ifdefME_BUILD_DLL#defineME_API__declspec(dllexport)#else#defineME_API__declspec(dllimport)#endif//ME_BUILD_DLL#else#errorMeowEngineonlysupportWindows!#endifME_PLATFORM_WINDOWS表示对Windows构建
通过ME_BUILD_DLL我们实现了使用一个ME_API就能处理dll的导入导出
之前定义的Application类中declspec也可以替换成ME_API了
对Sandbox同样如此
对Engine再添加一个预处理器ME_BUILD_DLL表示这是用来构建dll的项目
把Sandbox的Application.cpp改名为SandboxApp.cpp与Engine中的Application.cpp区分开
在Engine项目的src文件夹中创建一个头文件作为中介
MeowEngine.h
也就是添加了Engine中的一个路径
SandboxApp.cpp
还是得看之后写了什么吧
之后它把main函数的位置改了
他在Engine项目的src/Engine下面创建了一个EntryPoint.h把main放到了这里
Log.h
platforms的作用相当于另一种configurations只是方便之后区别不同情况
define是定义一个preprocessor预编译头
就是之前ME_PLATFORM_WINDOWS之类
kind表示构建类型
language表示构建语言
targetdir表示构建位置
objdir设置构建项目时应放置对象和其他中间文件的目录。
include表示附加的头文件目录
具体有哪些内建变量可以看wiki
之后的
讲的还挺清楚的
AsthisisaDLL,theproblemmightlieindifferentheapsusedforallocationanddeallocation(trytobuildthelibrarystaticallyandcheckifthatwillwork).Theproblemis,thatDLLsandtemplatesdonotagreetogetherverywell.Ingeneral,dependingonthelinkageoftheMSVCruntime,itmightbeproblemifthememoryisallocatedintheexecutableanddeallocatedintheDLLandviceversa(becausetheymighthavedifferentheaps).Andthatcanhappenwithtemplatesveryeasily,forexample:youpush_back()tothevectorinsidetheremoveWhiteSpaces()intheDLL,sothevectormemoryisallocatedinsidetheDLL.Thenyouusetheoutputvectorintheexecutableandonceitgetsoutofscope,itisdeallocated,butinsidetheexecutablewhoseheapdoesn’tknowanythingabouttheheapithasbeenallocatedfrom.Bang,you’redead.
那么需要设置VS中的RuntimeLibrary选项
/MTMulti-threaded/MTdMulti-threadedDebug/MDMulti-threadedDLL/MDdMulti-threadedDebugDLL用带DLL的选项就可以了
最终我遇到了类似这样的问题
这里返回的类型实际上是一个enum变量
而每一个函数对这个虚函数的继承都需要根据自己的情况来重写一遍
那么我们用一个宏定义来实现继承虚函数返回自己的类型enum的这一步
gitsubmoduleadd出错时怎么清理这个命令的痕迹
虚析构和纯虚析构共性:
都需要有具体的含函数实现
它目前实现创建派生窗口类对象的方式还是比较神奇的
所以需要一个函数来创建一个Window类的派生类的对象
虽然原来的
//case1newTunique_ptr
他这个触发事件的流程分为若干个步骤
每一个引擎窗口有自定义的窗口数据结构体
每一个窗口派生类的对象有一个glfw窗口对象GLFWwindow成员m_Window与自定义的窗口数据结构体WindowData成员m_Data
窗口绑定事件
m_Data具有一个std::function
也就是在初始化的时候给m_Data中的事件回调函数赋值
glfwMakeContextCurrent之后再gladLoadGLLoader
可能需要修改一些头文件的包含
初始化包含ImGui::CreateContext();ImGui_ImplOpenGL3_Init()这两个函数
...//StarttheDearImGuiframeImGui_ImplOpenGL3_NewFrame();ImGui_ImplGlfw_NewFrame();ImGui::NewFrame();...//RenderingImGui::Render();...ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData());然后就把这些写到自己的ImguiLayer的Update中
之前premake中我们设置将构建结果复制到指定目录下
于是glfw提供了根据键的序号查询键的状态的函数
这个类应该在任何地方都可以访问
单例还要把类的拷贝构造函数和赋值运算符重载函数设置成delete
例如glfw中#defineHZ_KEY_TAB258但是Windows中TAB是9
这些ImGui中的源文件就会被编译到工程中
原因是我们在E:\Hazel-master\Hazel\Sandbox\src\SandboxApp.cpp中调用了ImGui的API
例如在Engine项目的Core.h中添加这些定义
在Engine项目的Core.h中添加这些定义
#ifdefHZ_PLATFORM_WINDOWS#ifdefHZ_BUILD_DLL#defineHAZEL_API_declspec(dllexport)//#defineIMGUI_API_declspec(dllexport)//添加导出这一行不要了#else#defineHAZEL_API_declspec(dllimport)#defineIMGUI_API_declspec(dllimport)//添加导入#endif//HZ_BUILD_DLL#endif而在ImGui的premake5.lua中设置
我们不能直接再submodule里面添加文件
hotswappingcode
EasytoLink
不用担心dll的版本与使用引擎的代码不匹配的问题
改成了静态库之后会修复一部分
解决方法就是删掉多余的a文件
LOD
Animation
m_Context.SwapBuffers();m_Context.GetSwapChain().Flush();之类的
官方Shader示例
方案1
一个Shader
BeginScene:负责每帧渲染前的环境设置
BeginScene
Submit
EndScene
把使用相同的材质的物体合并到一起(Batch)
把在Frustum外部的物体Cull掉
根据位置进行排序
Render
相机的位置
没什么好说的
原视频没什么好说的
固定dt等于某一值
这里也会有死亡螺旋问题
比如写成
做了一个缩写
还有多线程的问题
使用weak_ptr来打破循环引用
用make_shared来生成shared_ptr
用enable_shared_from_this来使一个类能获取自身的shared_ptr
利用ifstream来读取文件
TextureAtlas的支持
SpriteAnimation系统
Transform组件
Renderer组件
从glfw的resize事件唤起引擎自己设置的resize事件
重新设置opengl的framebuffer
通知摄像机跟随变化
现在是这么写
因为m_AspectRatio的定义是依赖于某一个轴的
同时这个proj矩阵也影响了世界中的一个单位是否是对应屏幕上的一个像素单位……
整理了一下文件
将2d部分的VAO摄像机渲染提交放到了一个Sandbox2D层中
Startingour2DRenderer
emmm我感觉2d不需要shader有点不对劲吧
在OpenGLRendererAPI::Init()中设置了深度测试
其实就是一直写成采样*main_color的形式
他这个计时类的写法就很骚
你还可以自己用花括号做一个作用域出来
按照一定的格式把计时器记录的信息写成json
然后谷歌浏览器有一个内置的功能读取这个json输出时序图chrome://tracing
为DrawQuad函数添加了缩放比例和旋转角度这两个参数
shader也对应地更改
同样这里也没有做从存储了这32个Texture的map中删除元素的逻辑
可以使用flatv_TexIndex解决z-fighting
根据commit5e94d7da514829d69c22e93202319ade63f29d67
但是仍然没有删除某一个长方形的功能
用ImGui把Stats绘制出来
Debug模式和Release模式差距还挺大的
根据
OpenGL只能使用intuniform作为arrayuniform的index
采样精灵表
摄像机的OnResize函数是重新设置AspectRatio和Proj矩阵
所以要达成相同的目的还要这么写……
不知道为啥我没出现这个问题
原来是这么写的
程序主循环是这样的
马后炮只能分析到这里了
我们相当于是
绑定了一个framebuffer
绘制
除了OpenGL其他的渲染API是根本没有VertexArray这个概念所以VertexArray这个类要大改因为它不是一个Render跨平台通用的概念
介绍了一下entt的用法
总之我很混乱
空的结构体在使用的时候会出现问题
比如这里是在MeshComponent没有成员的时候出现的问题
之前他已经写过了一个OrthographicCamera
现在不知道为什么又写了一个Camera类
他应该是为了ECS里面的CameraComponent
我觉得这有点麻烦了说实话
为Camera创建了一个派生类SceneCamera
然后把OrthographicCamera中的一些东西搬到SceneCamera中
仍然觉得挺冗余
只是为了配合CameraComponent吧
应该只是为了演示这个思路才这么做的把
这个代码变得更奇怪了
emmmm感觉有点怪
感觉不像Script
类似
在ImGui各个按钮绘制之间设置了间隔
yaml-cpp的用法看一下代码里面是怎么写的就理解了
保存文件同理
ImGuizmo的使用方法
把原本用作EditorCamera的OrthographicCamera和OrthographicCameraController重命名为EditorCamera和EditorCameraController类
但是一般的shader的输出只有一个fragcolor
在创建纹理的时候提供一个RED_INTEGER的选项
他这个计算鼠标在视口中的0到1的坐标的做法可能需要看一下
左上角是0,0右下角是bound
添加了一个清理FBO的颜色附件的函数
通过VertexShader传入
通过Uniform传入
所以还是要通过顶点属性传入
介绍SPIR-V(可以把shader翻译为跨平台中间语言的编译器)
接入shaderc项目到Hazel里
给调用的exe添加命令行参数
uniform本质上是告诉GPU在这次着色程序渲染的时候创建一个缓冲区
所以可以把uniform的设置抽象出来
这里就是创建了一个UniformBuffer基类
OpenGL的派生类里面是UBO的内容
GLSL->glslang->SPIR-VTools
需要的lib可见仓库的premake的lua文件
structTransform{mat4Transform;}uniformTransformu_RendererUniforms;但是Vulkan不会识别到
Vulkan的写法是
yaml-cpp.lib(stream.obj):errorLNK2038:检测到“RuntimeLibrary”的不匹配项:值“MTd_StaticDebug”不匹配值“MDd_DynamicDebug”(EditorLayer.obj中)于是我把Editor和Engine的运行时库都改成Mtd
结果还是有错
所以我就把EditorEngineyaml-cpp的运行时库的设置都改成了/MDd就好了
需要创建shaderc的Compiler和CompileOptions对象
shaderc::Compilercompiler;shaderc::CompileOptionsoptions;options中设置了要编译到Vulkan
编译的话就用到compiler.CompileGlslToSpv
shaderc::Compilercompiler;shaderc::CompileOptionsoptions;options中设置了要编译到OpenGL
那么现在我们有vulkan的shader的二进制数据m_VulkanSPIRV我们想要opengl的shader的二进制数据m_OpenGLSPIRV
但是spirv没有直接在两个平台的二进制数据之间转换的功能
spirv_cross的使用也很简单
这应该算反射把……
比如uniformbuffers有多少啊之类的
直接用glShaderBinary和glSpecializeShader替换glShaderSource和glCompileShader
就是说可能出现版本不一致的问题
果然还是需要meta
层级显示
在这之前实现了图标显示
变为判断图片是否被点击if(ImGui::IsItemHovered()&&ImGui::IsMouseDoubleClicked(ImGuiMouseButton_Left))
其他的没啥了
然后在别的地方应该调用if(ImGui::BeginDragDropTarget())
其中添加一个获得payload的逻辑
经过之间鼠标拖拽到viewport的操作
现在我们希望鼠标拖拽到组件属性中的一个地方就能设置纹理
可视化编程系统
2D的物理引擎
Callbacks系统
本地化
stripping代码剥离删除未使用或无法访问的代码例如将Editor代码从发布的游戏中剥离
联网
音频
Scene里的Update函数分为EditorUpdate和RuntimeUpdate函数
这个ImGui的dockingspace分支果然还是有点问题
可以显示cppinclude了哪些头文件
使用Box2D进行物理模拟
创建了全局唯一ID组件
他是在切换到播放模式OnScenePlay的时候从m_EditorScene复制一份场景到m_ActiveScene
所以还是在内存中一直存着m_EditorScene比较合理
添加了一个画圆的功能
同上
添加一个Component的流程……感觉很多都是重复的
其实EndScene就是一次提交
多做了一个物理模拟的模式
他这个写法还挺有意思的
那么逗号表达式的第一个表达式是一个生命周期仅存在于逗号表达式的lambda
终于不用我每次拉取的时候都手动把Dependencies里面的VulkanSDKDebug等等修改了
GLFW只调用GLFW_PRESSRELEASE
GL_REPEAT是在回调函数中的……
有点没懂
将引擎内的头文件放在最前面
我很好奇什么类型的size会为0
我记得空结构的size至少为1才对
他把std::filesystem::relative放到了点击事件中
这样就不用每一个循环中都使用一次这个函数
噢……涉及到OnImGuiRender的这种Update函数确实要小心
JetBrainDotPeek可以查看.NET的DLL中包含的代码
下载Mono
这里获得的是Mono要用到的.NET的库文件
克隆mono
gitcheckout到最近一次Release的commit
打开克隆下来的mono源码中的msvc文件夹中的sln
可以看到这个解决方案中有很多项目
编译libmono-static项目
在需要使用到Mono的项目的premakelua文件中添加link
那么我们在初始化Mono的代码中可以写
然后是创建应用域……就不再死复制教程了
感觉像一个独立的沙盒
这个函数的内容可以看教程
也就是说Hazel依赖于Hazel-ScriptCore
在Dependencies.lua中定义一些Windows的lib然后在引擎中link
具体可以看仓库
ScriptEngine.cpp定义的静态变量staticScriptEngineData*s_Data在VisualStudio的监视面板中会被识别为Renderer2D.cpp静态变量staticRenderer2DDatas_Data
我自己写的时候避免这个全局变量重名的情况就好了
嗯……就很神奇
教程已经很清楚了
借助Mono的InternalCall
怎么传struct或class的数组、怎么传普通primitive的数组
通过Copy值类型进行
这就没什么好说了
现在我们要序列化这个fileMap
反序列化也是一样的
现在我们要在一个C#的Entity的派生类的实例中获取另一个Entity的派生类的实例
在Unload某个AppDomain之间先mono_domain_set当前的RootDomain为false
这个commit还没有添加文件检测
这些都是可以做的……
使用了一个filewatch库来检测文件是否发生更改
//Application.hstd::mutexm_MainThreadQueueMutex;//Application.cppvoidApplication::SubmitToMainThread(conststd::function