33 Commits

Author SHA1 Message Date
57700da217 now using deferred rendering instead of forward rendering; some little problem: grid not render, shadow not impl 2026-03-18 14:28:02 +08:00
f1de5df4de add bloom;some treaks 2026-03-17 16:20:22 +08:00
28d9a7dfb6 add Preetham sky; add point light and spot light; 2026-03-16 01:44:23 +08:00
3f56a6878d remove some useless include; add include <filesystem> for entrypoint 2026-03-15 00:49:10 +08:00
a6fb4bdcea add args for entry Application; using efsw to replace custom filewatcher; remove some useless code; move FileOpenSelector from Application to FileSystem;some tweaks 2026-03-15 00:46:47 +08:00
5cb9b04ab0 add compute shader to auto calculate screen exposure, add SSBO impl 2026-03-12 23:42:52 +08:00
8ba00467fd add README.md 2026-03-11 17:31:32 +08:00
79e6631c50 add Physics2D class; For Physics2D add DistanceJoint RevoluteJoint PrismaticJoint WeldJoint Component and serialize/deserialize code; use template to Copy Component from entity to entity; remove useless code, fix some error code 2026-03-11 16:13:38 +08:00
a265e71e1a add AnimationComponent, Skeleton, AnimationClip; add single Renderer3D class, remove some useless code; problem now renderer2D cannot blend with Renderer3D 2026-03-11 09:19:05 +08:00
eb0a463370 fix environment texture drag to viewport didn't create SkylightComponent; fix PhysicsWrappers::CreateConvexMesh collier worng scale;some tweaks 2026-03-09 01:03:35 +08:00
c1bb8f9fba readd Sprite Renderer render and panel edit; in renderer scene, add icon for camera and light; improve a user-friendly EditorCamera, is more like UE Editor Camera; fix woring build m_ProjectionMatrix for SceneCamera::Orthographic 2026-03-08 19:27:00 +08:00
79f56b60a0 replace mesh load texture from native create to AssetsManager::GetAsset; renderer.h add Submit::pFunc->~FuncT(); fix PhysicsWrappers::CreateTriamgleMesh() error collider collect; remove unused ChildrenComponent; AssetEditorPanel now will auto save(serialize) when the window closed 2026-03-08 00:26:39 +08:00
57e14bc3d2 add ImViewzmo, add floating window for QWER(select, translate, rotate, scale) , and add new debug button to floating (Experimental) 2026-03-02 17:10:03 +08:00
a0086020c1 add infiniteGrid, rewrote the shadow , now the shadow is a simple hardware shadow 2026-03-01 17:03:40 +08:00
56da5ebef7 add auto exposure 2026-02-26 18:17:12 +08:00
99bbf1eb5a fix the child position incorrect while parenting node; add camera focus func; treat icons to the asset and using AssetsManager::GetAsset to load it 2026-02-17 21:38:43 +08:00
2bbe332532 lots of asset manager changes. using meta to manage asset, add twoside material flag for grid 2026-02-15 16:17:23 +08:00
896d3c7f97 add file watcher using widows api for windows platform, using handles to import and manager assets, some little tweaks 2026-01-21 23:25:41 +08:00
cf3a46bae1 add trace for imgui.ini, add assets manager system and objects manager system, add some icons, add entity tree node for scene Hierachy's entities, add convert blend file process(but not actual use it, just implement) BUGS: file copy not work 2026-01-21 12:30:47 +08:00
2bfde2dfb0 add circle2d collider renderer, fixed a bug for renderer 2 value properties 2026-01-20 02:04:16 +08:00
323d646611 default mesh collider are generated by default mesh, add camera info serialization, using error info instead of assert to process missing filepath when deserializing scene, move cullface into internal renderer api, fixed dynamic vertex buffer bug 2026-01-19 13:05:24 +08:00
81b52abf0f update cursor load 2026-01-12 19:18:07 +08:00
bed57f18d3 add serialize for RigidbodyComponent linear/angular drag and gravity, change the physx API for c++ and c#, fixed rotate bug(using radians to storage), some little code tweaks 2026-01-12 18:58:11 +08:00
f857d8e791 move imgui property code and Pysics Actor code to a single file 2026-01-10 14:48:17 +08:00
9e1474e643 add directional light component and skylight component, add PCSS and hard shadow 2026-01-02 22:46:29 +08:00
abf0a65bd6 update transformComponent style, and change the position of button 'Add Component' 2026-01-01 00:57:23 +08:00
faecfe21ab fixed layer problem, remove 'default' layer to serialize, remove Transform class, using translation rotation scale instead 2025-12-31 23:56:49 +08:00
960eeaf94b add transform class, some code tweaks 2025-12-29 23:11:14 +08:00
ce41e348f8 add physX settings through the setting window for editor 2025-12-29 13:44:00 +08:00
9a44dd8d79 add overlapBox/Sphere/Capsule function, fixed OnWake/OnSleep not work 2025-12-28 22:40:48 +08:00
d0eed3a33d add c# debug (mono debug) 2025-12-25 17:39:50 +08:00
f747db4e27 add physX colliders ,add trigger, colliders now can visible, some rotation for cs and native cpp connection 2025-12-24 10:10:24 +08:00
00d3993a77 add PhysX SDK and some tweaks 2025-12-17 12:39:20 +08:00
202 changed files with 17347 additions and 3768 deletions

9
.gitmodules vendored
View File

@ -26,3 +26,12 @@
[submodule "Prism/vendor/Box2D"]
path = Prism/vendor/Box2D
url = https://github.com/erincatto/box2d.git
[submodule "Prism/vendor/PhysX"]
path = Prism/vendor/PhysX
url = https://github.com/NVIDIA-Omniverse/PhysX.git
[submodule "Prism/vendor/ImViewGuizmo"]
path = Prism/vendor/ImViewGuizmo
url = https://github.com/Ka1serM/ImViewGuizmo
[submodule "Prism/vendor/efsw"]
path = Prism/vendor/efsw
url = https://github.com/SpartanJ/efsw

View File

@ -10,7 +10,7 @@ set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin)
# set MSVC output directory
if(MSVC)
# config
string(REPLACE "/showIncludes" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
# temp config
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /wd4251")

View File

@ -2,9 +2,13 @@ project(PrismEditor)
set(CMAKE_BINARY_DIR ${CMAKE_BINARY_DIR}/bin)
file(GLOB ASSETS assets)
file(GLOB ASSETS "assets")
file(COPY ${ASSETS} DESTINATION ${CMAKE_BINARY_DIR})
# imgui.ini file
file(GLOB IMGUI_INI imgui.ini)
file(COPY ${IMGUI_INI} DESTINATION ${CMAKE_BINARY_DIR})
file(GLOB DOTNET_LIBRARY library)
file(COPY ${DOTNET_LIBRARY} DESTINATION ${CMAKE_BINARY_DIR})
@ -12,7 +16,7 @@ file(GLOB_RECURSE SRC_SOURCE ./**.cpp)
add_executable(${PROJECT_NAME} ${SRC_SOURCE})
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-shared)
target_link_libraries(${PROJECT_NAME} PRIVATE Prism-static)
# Enable ImGui Docking space
target_compile_definitions(${PROJECT_NAME} PRIVATE ENABLE_DOCKSPACE)

View File

@ -23,7 +23,7 @@ public:
};
Prism::Application* Prism::CreateApplication()
Prism::Application* Prism::CreateApplication(const CommandArgs args)
{
return new Editor({"hello wrold", 1920, 1080});
return new Editor({"hello world", 1920, 1080, args});
}

File diff suppressed because it is too large Load Diff

View File

@ -6,6 +6,8 @@
#define EDITORLAYER_H
#include "Prism.h"
#include "Prism/Editor/ContentBrowserPanel.h"
#include "Prism/Editor/ObjectsPanel.h"
#include "Prism/Editor/SceneHierachyPanel.h"
namespace Prism
@ -26,7 +28,7 @@ namespace Prism
bool OnKeyPressedEvent(KeyPressedEvent& e);
bool OnMouseButtonPressedEvent(MouseButtonPressedEvent& e);
void ShowBoundingBoxes(bool show, bool onTop = false);
void ShowBoundingBoxes(bool show);
void SelectEntity(Entity entity);
void UpdateWindowTitle(const std::string& sceneName);
@ -44,18 +46,22 @@ namespace Prism
void OnEntityDeleted(Entity e);
Ray CastMouseRay();
void NewScene();
void OpenScene();
void OpenScene(const std::string& filepath);
void SaveScene();
void SaveSceneAs();
void OnScenePlay();
void OnSceneStop();
float GetSnapValue();
float GetSnapValue() const;
private:
Scope<SceneHierarchyPanel> m_SceneHierarchyPanel;
Scope<ContentBrowserPanel> m_ContentBrowserPanel;
Scope<ObjectsPanel> m_ObjectsPanel;
Ref<Scene> m_ActiveScene;
Ref<Scene> m_CurrentScene;
Ref<Scene> m_RuntimeScene, m_EditorScene;
std::string m_SceneFilePath;
@ -112,7 +118,7 @@ namespace Prism
struct RoughnessInput
{
float Value = 0.5f;
float Value = 1.0f;
Ref<Texture2D> TextureMap;
bool UseTexture = false;
};
@ -120,22 +126,14 @@ namespace Prism
// PBR params
bool m_RadiancePrefilter = false;
float m_EnvMapRotation = 0.0f;
// Editor resources
Ref<Texture2D> m_CheckerboardTex;
Ref<Texture2D> m_PlayButtonTex;
Ref<Texture2D> m_PlayButtonTex, m_StopButtonTex, m_PauseButtonTex;
// configure button
bool m_AllowViewportCameraEvents = false;
bool m_DrawOnTopBoundingBoxes = false;
bool m_UIShowBoundingBoxes = false;
bool m_UIShowBoundingBoxesOnTop = false;
enum class SceneType : uint32_t
{
@ -143,8 +141,9 @@ namespace Prism
};
SceneType m_SceneType;
bool m_ViewportPanelMouseOver = false;
bool m_ViewportPanelHovered = false;
bool m_ViewportPanelFocused = false;
bool m_ShowPhysicsSettings = false;
enum class SceneState
{

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 115 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 21 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -1,155 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr
Light:
Direction: [-0.787, -0.73299998, 1]
Radiance: [1, 1, 1]
Multiplier: 0.514999986
Entities:
- Entity: 12498244675852797835
TagComponent:
Tag: Box
TransformComponent:
Position: [-12.0348625, 6.59647179, 9.60061925e-07]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [-21.7406311, 9.70659542, 15]
Rotation: [0.999910355, -0.0133911213, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 1289165777996378215
TagComponent:
Tag: Cube
TransformComponent:
Position: [500, 0, 0]
Rotation: [1, 0, 0, 0]
Scale: [1200, 1, 5]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [600, 0.5]
Density: 1
Friction: 2
- Entity: 14057422478420564497
TagComponent:
Tag: Player
TransformComponent:
Position: [-23.6932545, 1.59184527, -1.96369365e-06]
Rotation: [1, 0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.PlayerCube
StoredFields:
- Name: HorizontalForce
Type: 1
Data: 0.5
- Name: MaxSpeed
Type: 5
Data: [7, 10]
- Name: JumpForce
Type: 1
Data: 3
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 29.2000008
CircleCollider2DComponent:
Offset: [0, 0]
Radius: 0.5
Density: 1
Friction: 1
- Entity: 1352995477042327524
TagComponent:
Tag: Box
TransformComponent:
Position: [-29.6808929, 29.7597198, 0]
Rotation: [0.707106769, 0, 0, 0.707106769]
Scale: [58.4179001, 4.47999144, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 3
BoxCollider2DComponent:
Offset: [0, 0]
Size: [29.7000008, 2.24000001]
Density: 1
Friction: 1
- Entity: 15223077898852293773
TagComponent:
Tag: Box
TransformComponent:
Position: [6.12674046, 45.5617676, 0]
Rotation: [0.977883637, 0, 0, -0.209149584]
Scale: [4.47999668, 4.47999668, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
Density: 1
Friction: 1
- Entity: 5421735812495444456
TagComponent:
Tag: Box
TransformComponent:
Position: [-20.766222, 2.29431438, 0]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1
- Entity: 2842299641876190180
TagComponent:
Tag: Box
TransformComponent:
Position: [-16.6143265, 4.39151001, 6.43359499e-09]
Rotation: [1, 0, 0, 0]
Scale: [3.00000024, 0.300000012, 1]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1.5, 0.150000006]
Density: 1
Friction: 1

View File

@ -0,0 +1,49 @@
Scene: Scene Name
Environment:
AssetHandle: 10549690553241162923
Light:
Direction: [-0.314, -0.941, -0.209]
Radiance: [0, 0, 0]
Multiplier: 1
Entities:
- Entity: 17803125207910630398
Parent: 0
Children:
[]
TagComponent:
Tag: Directional Light
TransformComponent:
Position: [0, 0, 0]
Rotation: [-0.4810984, -0.20606127, 2.9545484]
Scale: [1.0000023, 1.0000007, 0.9999998]
DirectionalLightComponent:
Radiance: [1, 1, 1]
CastShadows: true
SoftShadows: true
LightSize: 0.5
- Entity: 4315886439647742331
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 2.048974, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
MeshComponent:
AssetID: 3580169978473467053
- Entity: 16992665426857995732
Parent: 0
Children:
[]
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [50, 1, 50]
MeshComponent:
AssetID: 3580169978473467053
PhysicsLayers:
[]

View File

@ -1,174 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/pink_sunrise_4k.hdr
Light:
Direction: [-0.787, -0.73299998, 1]
Radiance: [1, 1, 1]
Multiplier: 0.514999986
Entities:
- Entity: 15861629587505754
TagComponent:
Tag: Box
TransformComponent:
Position: [-18.2095661, 39.2518234, 0]
Rotation: [0.967056513, 0, 0, -0.254561812]
Scale: [4.47999525, 4.47999525, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 15223077898852293773
TagComponent:
Tag: Box
TransformComponent:
Position: [5.37119865, 43.8762894, 0]
Rotation: [0.977883637, 0, 0, -0.209149718]
Scale: [4.47999668, 4.47999668, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 2157107598622182863
TagComponent:
Tag: Box
TransformComponent:
Position: [-7.60411549, 44.1442184, 0]
Rotation: [0.989285827, 0, 0, 0.145991713]
Scale: [4.47999287, 4.47999287, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 0.5
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 8080964283681139153
TagComponent:
Tag: Box
TransformComponent:
Position: [-0.739211679, 37.7653275, 0]
Rotation: [0.956475914, 0, 0, -0.291811317]
Scale: [5, 2, 2]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 0.25
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.5, 1]
- Entity: 1352995477042327524
TagComponent:
Tag: Box
TransformComponent:
Position: [-8.32969856, 30.4078159, 0]
Rotation: [0.781595349, 0, 0, 0.623785794]
Scale: [14.000001, 4.47999334, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 3
BoxCollider2DComponent:
Offset: [0, 0]
Size: [7, 2.24000001]
- Entity: 935615878363259513
TagComponent:
Tag: Box
TransformComponent:
Position: [6.88031197, 31.942337, 0]
Rotation: [0.986578286, 0, 0, 0.163288936]
Scale: [4.47999954, 4.47999954, 4.48000002]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [2.24000001, 2.24000001]
- Entity: 14057422478420564497
TagComponent:
Tag: Player
TransformComponent:
Position: [0, 22.774044, 0]
Rotation: [0.942591429, 0, 0, -0.333948225]
Scale: [6.00000048, 6.00000048, 4.48000002]
ScriptComponent:
ModuleName: Example.PlayerCube
StoredFields:
- Name: HorizontalForce
Type: 1
Data: 10
- Name: VerticalForce
Type: 1
Data: 10
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
CircleCollider2DComponent:
Offset: [0, 0]
Radius: 3
- Entity: 1289165777996378215
TagComponent:
Tag: Cube
TransformComponent:
Position: [0, 0, 0]
Rotation: [1, 0, 0, 0]
Scale: [50, 1, 50]
ScriptComponent:
ModuleName: Example.Sink
StoredFields:
- Name: SinkSpeed
Type: 1
Data: 0
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 0
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [25, 0.5]
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [0, 25, 79.75]
Rotation: [0.995602965, -0.0936739072, 0, 0]
Scale: [1, 0.999999821, 0.999999821]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 3948844418381294888
TagComponent:
Tag: Box
TransformComponent:
Position: [-1.48028564, 49.5945244, -2.38418579e-07]
Rotation: [0.977883637, 0, 0, -0.209149733]
Scale: [1.99999976, 1.99999976, 2]
MeshComponent:
AssetPath: assets/meshes/Cube1m.fbx
RigidBody2DComponent:
BodyType: 1
Mass: 1
BoxCollider2DComponent:
Offset: [0, 0]
Size: [1, 1]

View File

@ -1,66 +0,0 @@
Scene: Scene Name
Environment:
AssetPath: assets/env/birchwood_4k.hdr
Light:
Direction: [-0.5, -0.5, 1]
Radiance: [1, 1, 1]
Multiplier: 1
Entities:
- Entity: 1289165777996378215
TagComponent:
Tag: Sphere
TransformComponent:
Position: [0, 21.9805069, -1.64006281]
Rotation: [1, 0, 0, 0]
Scale: [0.100000024, 0.100000024, 0.100000024]
ScriptComponent:
ModuleName: Example.Sink
StoredFields:
- Name: SinkSpeed
Type: 1
Data: 5
MeshComponent:
AssetPath: assets/meshes/Sphere1m.fbx
- Entity: 5178862374589434728
TagComponent:
Tag: Camera
TransformComponent:
Position: [0, 14.75, 79.75]
Rotation: [0.995602965, -0.0936739072, 0, 0]
Scale: [1, 0.999999821, 0.999999821]
ScriptComponent:
ModuleName: Example.BasicController
StoredFields:
- Name: Speed
Type: 1
Data: 12
CameraComponent:
Camera: some camera data...
Primary: true
- Entity: 9095450049242347594
TagComponent:
Tag: Test Entity
TransformComponent:
Position: [0.248109579, -1.90734863e-06, -0.268640995]
Rotation: [1, 0, 0, 0]
Scale: [1, 1, 1]
ScriptComponent:
ModuleName: Example.Script
StoredFields:
- Name: VerticalSpeed
Type: 1
Data: 0
- Name: SinkRate
Type: 1
Data: 0
- Name: Speed
Type: 1
Data: 1
- Name: Rotation
Type: 1
Data: 0
- Name: Velocity
Type: 6
Data: [0, 0, 0]
MeshComponent:
AssetPath: assets/meshes/TestScene.fbx

View File

@ -0,0 +1,24 @@
Scene: Scene Name
Environment:
AssetHandle: 17073147362577408906
Light:
Direction: [-0.314, -0.941, -0.209]
Radiance: [0, 0, 0]
Multiplier: 1
Entities:
- Entity: 3696833073589069488
Parent: 0
Children:
[]
TagComponent:
Tag: venice_dawn_1_4k
TransformComponent:
Position: [0, 0, 0]
Rotation: [0, 0, 0]
Scale: [1, 1, 1]
SkyLightComponent:
EnvironmentMap: 17073147362577408906
Intensity: 1
Angle: 0
PhysicsLayers:
[]

View File

@ -0,0 +1,63 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_SceneTexture;
uniform sampler2D u_BloomTexture;
uniform float u_Exposure;
uniform bool u_EnableBloom;
void main()
{
#if 1
const float gamma = 2.2;
const float pureWhite = 1.0;
// Tonemapping
vec3 color = texture(u_SceneTexture, v_TexCoord).rgb;
if (u_EnableBloom)
{
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
color += bloomColor;
}
// Reinhard tonemapping
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance);
// Scale color by ratio of average luminances.
vec3 mappedColor = (mappedLuminance / luminance) * color* u_Exposure;
// Gamma correction.
o_Color = vec4(mappedColor, 1.0);
#else
const float gamma = 2.2;
vec3 hdrColor = texture(u_SceneTexture, v_TexCoord).rgb;
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
hdrColor += bloomColor; // additive blending
// tone mapping
vec3 result = vec3(1.0) - exp(-hdrColor * u_Exposure);
// also gamma correct while we're at it
result = pow(result, vec3(1.0 / gamma));
o_Color = vec4(result, 1.0);
#endif
}

View File

@ -0,0 +1,84 @@
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
vec4 position = vec4(a_Position.xy, 0.0, 1.0);
v_TexCoord = a_TexCoord;
gl_Position = position;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2D u_Texture;
uniform bool u_Horizontal; // 未使用,可保留或移除
uniform bool u_FirstPass; // 是否进行阈值处理
uniform float u_Threshold; // 亮度阈值
uniform int u_Quality;
uniform float u_Directions; // 模糊方向数
uniform float u_Size; // 模糊半径
void main()
{
float Pi = 6.28318530718; // 2*PI
vec2 Radius = u_Size / textureSize(u_Texture, 0);
// 中心像素采样
vec3 centerColor = texture(u_Texture, v_TexCoord).rgb;
float centerLum = dot(centerColor, vec3(0.2126, 0.7152, 0.0722));
// 如果启用第一次处理且中心像素亮度低于阈值,则直接输出黑色(不进行模糊)
if (u_FirstPass && centerLum <= u_Threshold)
{
o_Color = vec4(0.0, 0.0, 0.0, 1.0);
return;
}
vec3 result = centerColor; // 先累加中心像素
float totalSamples = 1.0; // 有效采样计数(中心像素已计入)
// 周围像素采样
for (float d = 0.0; d < Pi; d += Pi / u_Directions)
{
for (float i = 1.0 / u_Quality; i <= 1.0; i += 1.0 / u_Quality)
{
vec2 offset = vec2(cos(d), sin(d)) * Radius * i;
vec3 sampleColor = texture(u_Texture, v_TexCoord + offset).rgb;
if (u_FirstPass)
{
float lum = dot(sampleColor, vec3(0.2126, 0.7152, 0.0722));
if (lum <= u_Threshold)
{
// 低于阈值则贡献黑色,但采样点仍计入分母?这里选择不计入有效采样数
// 若希望保持模糊能量,可以 continue 跳过累加,但需调整分母
// 为简单起见,此处设为黑色并计入计数(分母不变),也可选择跳过
sampleColor = vec3(0.0);
// 如果希望忽略该采样点,可以 continue 并减少 totalSamples
// 但为了效果平滑,这里保留为黑色并计入计数
}
}
result += sampleColor;
totalSamples += 1.0;
}
}
// 归一化:除以总采样数(包括中心像素)
// 若之前选择忽略低于阈值的采样点continue则需相应调整 totalSamples
result /= totalSamples;
o_Color = vec4(result, 1.0);
}

View File

@ -0,0 +1,24 @@
// Collider Shader
#type vertex
#version 450
layout(location = 0) in vec3 a_Position;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
void main()
{
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
}
#type fragment
#version 450
layout(location = 0) out vec4 color;
void main()
{
color = vec4(0.1, 1.0, 0.1, 1.0);
}

View File

@ -0,0 +1,63 @@
#type compute
#version 460 core
layout(local_size_x = 1, local_size_y = 1) in;
layout(binding = 0, std430) buffer Histogram {
uint bins[64];
};
layout(binding = 1, std430) buffer Exposure {
float exposure;
};
uniform float u_SpeedUp;
uniform float u_SpeedDown;
uniform float u_Key;
uniform float u_LowPercent;
uniform float u_HighPercent;
uniform float u_MinExposure;
uniform float u_MaxExposure;
uniform float u_DeltaTime;
uniform float u_LogMin;
uniform float u_LogMax;
void main() {
float currentExposure = exposure;
uint total = 0;
uint prefix[64];
for (int i = 0; i < 64; i++) {
total += bins[i];
prefix[i] = total;
}
float lowCount = u_LowPercent * 0.01 * total;
float highCount = u_HighPercent * 0.01 * total;
int lowBin = 0, highBin = 63;
for (int i = 0; i < 64; i++) {
if (prefix[i] < lowCount) lowBin = i + 1;
if (prefix[i] < highCount) highBin = i + 1;
}
lowBin = clamp(lowBin, 0, 63);
highBin = clamp(highBin, 0, 63);
float sumLum = 0.0;
uint count = 0;
for (int i = lowBin; i <= highBin; i++) {
float t = (float(i) + 0.5) / 64.0;
float logLum = u_LogMin + t * (u_LogMax - u_LogMin);
float lum = exp2(logLum);
sumLum += lum * float(bins[i]);
count += bins[i];
}
float avgLum = count > 0 ? sumLum / count : 0.18;
float targetExposure = u_Key / max(avgLum, 0.0001);
targetExposure = clamp(targetExposure, u_MinExposure, u_MaxExposure);
float speed = (targetExposure > currentExposure) ? u_SpeedUp : u_SpeedDown;
float adaptFactor = 1.0 - exp(-speed * u_DeltaTime);
float newExposure = mix(currentExposure, targetExposure, adaptFactor);
newExposure = clamp(newExposure, u_MinExposure, u_MaxExposure);
exposure = newExposure;
}

View File

@ -1,45 +0,0 @@
// Grid Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
out vec2 v_TexCoord;
void main()
{
vec4 position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);
gl_Position = position;
v_TexCoord = a_TexCoord;
}
#type fragment
#version 430
layout(location = 0) out vec4 color;
uniform float u_Scale;
uniform float u_Res;
in vec2 v_TexCoord;
float grid(vec2 st, float res)
{
vec2 grid = fract(st);
return step(res, grid.x) * step(res, grid.y);
}
void main()
{
float scale = u_Scale;
float resolution = u_Res;
float x = grid(v_TexCoord * scale, resolution);
color = vec4(vec3(0.2), 0.5) * (1.0 - x);
}

View File

@ -0,0 +1,26 @@
#type compute
#version 460 core
layout(local_size_x = 16, local_size_y = 16) in;
layout(binding = 0) uniform sampler2D u_SceneColor;
layout(binding = 1, std430) buffer Histogram {
uint bins[64];
};
uniform float u_LogMin;
uniform float u_LogMax;
void main() {
ivec2 texel = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = textureSize(u_SceneColor, 0);
if (texel.x >= size.x || texel.y >= size.y) return;
vec3 color = texelFetch(u_SceneColor, texel, 0).rgb;
float lum = max(dot(color, vec3(0.2126, 0.7152, 0.0722)), 0.0001);
float logLum = log2(lum);
float invLogRange = 1.0 / (u_LogMax - u_LogMin);
float t = (logLum - u_LogMin) * invLogRange;
int bin = int(clamp(t * 64.0, 0.0, 63.0));
atomicAdd(bins[bin], 1u);
}

View File

@ -0,0 +1,224 @@
// Infinite Grid Shader
// Based on "The Best Darn Grid Shader (Yet)" by Ben Golus
#type vertex
#version 450 core
layout(location = 0) in vec3 a_Position;
// camera
uniform mat4 u_View;
uniform mat4 u_Projection;
uniform vec3 u_CameraPosition;
out CameraData{
mat4 ViewProjection;
vec3 Position;
}CameraOutput;
out vec3 v_NearPoint;
out vec3 v_FarPoint;
vec3 unprojectPoint(float x, float y, float z) {
mat4 viewInv = inverse(u_View);
mat4 projInv = inverse(u_Projection);
vec4 unprojectedPoint = viewInv * projInv * vec4(x, y, z, 1.0);
return unprojectedPoint.xyz / unprojectedPoint.w;
}
void main() {
v_NearPoint = unprojectPoint(a_Position.x, a_Position.y, 0.0);
v_FarPoint = unprojectPoint(a_Position.x, a_Position.y, 1.0);
CameraOutput.ViewProjection = u_Projection * u_View;
CameraOutput.Position = u_CameraPosition;
gl_Position = vec4(a_Position, 1.0);
}
#type fragment
#version 450 core
layout(location = 0) out vec4 o_Color;
in vec3 v_NearPoint;
in vec3 v_FarPoint;
in CameraData{
mat4 ViewProjection;
vec3 Position;
}CameraInput;
// Grid plane: 0 = XZ (Y up), 1 = XY (Z forward), 2 = YZ (X right)
uniform int u_GridPlane;
uniform float u_GridScale;
uniform vec4 u_GridColorThin;
uniform vec4 u_GridColorThick;
uniform vec4 u_AxisColorX;
uniform vec4 u_AxisColorZ;
uniform float u_FadeDistance;
float computeDepth(vec3 pos) {
vec4 clipSpacePos = CameraInput.ViewProjection * vec4(pos, 1.0);
return clipSpacePos.z / clipSpacePos.w;
}
// Get the plane normal based on grid plane type
vec3 getPlaneNormal() {
if (u_GridPlane == 1) return vec3(0.0, 0.0, 1.0); // XY plane, Z normal
if (u_GridPlane == 2) return vec3(1.0, 0.0, 0.0); // YZ plane, X normal
return vec3(0.0, 1.0, 0.0); // XZ plane, Y normal (default)
}
// Get 2D coordinates on the plane
vec2 getPlaneCoords(vec3 pos) {
if (u_GridPlane == 1) return pos.xy; // XY plane
if (u_GridPlane == 2) return pos.yz; // YZ plane
return pos.xz; // XZ plane (default)
}
// Get the component perpendicular to the plane (for axis drawing)
vec2 getAxisCoords(vec3 pos) {
// Returns the two coordinates used for drawing axis lines
// First component -> first axis color, Second component -> second axis color
if (u_GridPlane == 1) return vec2(pos.x, pos.y); // XY: X-axis and Y-axis
if (u_GridPlane == 2) return vec2(pos.y, pos.z); // YZ: Y-axis and Z-axis
return vec2(pos.x, pos.z); // XZ: X-axis and Z-axis
}
// Calculate t for ray-plane intersection
float rayPlaneIntersection(vec3 nearPoint, vec3 farPoint) {
vec3 rayDir = farPoint - nearPoint;
if (u_GridPlane == 1) {
// XY plane (z = 0)
if (abs(rayDir.z) < 0.0001) return -1.0;
return -nearPoint.z / rayDir.z;
}
if (u_GridPlane == 2) {
// YZ plane (x = 0)
if (abs(rayDir.x) < 0.0001) return -1.0;
return -nearPoint.x / rayDir.x;
}
// XZ plane (y = 0) - default
if (abs(rayDir.y) < 0.0001) return -1.0;
return -nearPoint.y / rayDir.y;
}
// Get view angle component for normal fade
float getViewAngleComponent(vec3 viewDir) {
if (u_GridPlane == 1) return abs(viewDir.z); // XY plane
if (u_GridPlane == 2) return abs(viewDir.x); // YZ plane
return abs(viewDir.y); // XZ plane
}
// Pristine grid - single pixel line with proper AA
float pristineGridLine(vec2 uv) {
vec2 dudv = fwidth(uv);
vec2 uvMod = fract(uv);
vec2 uvDist = min(uvMod, 1.0 - uvMod);
vec2 distInPixels = uvDist / dudv;
vec2 lineAlpha = 1.0 - smoothstep(0.0, 1.0, distInPixels);
float alpha = max(lineAlpha.x, lineAlpha.y);
float density = max(dudv.x, dudv.y);
float densityFade = 1.0 - smoothstep(0.5, 1.0, density);
return alpha * densityFade;
}
// Axis line - single pixel wide
float axisLineAA(float coord, float dudv) {
float distInPixels = abs(coord) / dudv;
return 1.0 - smoothstep(0.0, 1.5, distInPixels);
}
void main() {
float t = rayPlaneIntersection(v_NearPoint, v_FarPoint);
if (t < 0.0) {
discard;
}
vec3 fragPos3D = v_NearPoint + t * (v_FarPoint - v_NearPoint);
float depth = computeDepth(fragPos3D);
if (depth > 1.0 || depth < -1.0) {
discard;
}
vec2 worldPos = getPlaneCoords(fragPos3D);
// === Fading ===
// Radial fade
float dist = length(fragPos3D - CameraInput.Position);
float radialFade = 1.0 - smoothstep(u_FadeDistance * 0.3, u_FadeDistance, dist);
// Normal fade (view angle)
vec3 viewDir = normalize(fragPos3D - CameraInput.Position);
float viewAngle = getViewAngleComponent(viewDir);
float normalFade = smoothstep(0.0, 0.15, viewAngle);
float fadeFactor = radialFade * normalFade;
if (fadeFactor < 0.001) {
discard;
}
// === Grid calculation ===
vec2 gridCoord1 = worldPos / u_GridScale;
vec2 gridCoord10 = worldPos / (u_GridScale * 10.0);
float grid1 = pristineGridLine(gridCoord1);
float grid10 = pristineGridLine(gridCoord10);
// LOD blend
vec2 deriv1 = fwidth(gridCoord1);
float lodFactor = smoothstep(20.0, 200.0, dist);
// float lodFactor = smoothstep(0.2, 0.5, max(deriv1.x, deriv1.y));
// Combine grids
float gridIntensity = mix(max(grid1, grid10 * 0.7), grid10, lodFactor);
// Grid color
vec3 gridColor = mix(u_GridColorThin.rgb, u_GridColorThick.rgb, lodFactor);
float baseAlpha = mix(u_GridColorThin.a, u_GridColorThick.a, lodFactor);
float gridAlpha = baseAlpha * gridIntensity * fadeFactor;
// === Axis lines ===
vec2 axisCoords = getAxisCoords(fragPos3D);
vec2 worldDeriv = fwidth(worldPos);
// First axis (uses AxisColorX - typically red)
float axis1Alpha = axisLineAA(axisCoords.y, worldDeriv.y) * fadeFactor;
// Second axis (uses AxisColorZ - typically blue)
float axis2Alpha = axisLineAA(axisCoords.x, worldDeriv.x) * fadeFactor;
// === Final composition ===
vec3 finalColor = gridColor;
float finalAlpha = gridAlpha;
// Blend axis colors
if (axis2Alpha > 0.001) {
float blend = axis2Alpha * u_AxisColorZ.a;
finalColor = mix(finalColor, u_AxisColorZ.rgb, blend);
finalAlpha = max(finalAlpha, blend);
}
if (axis1Alpha > 0.001) {
float blend = axis1Alpha * u_AxisColorX.a;
finalColor = mix(finalColor, u_AxisColorX.rgb, blend);
finalAlpha = max(finalAlpha, blend);
}
if (finalAlpha < 0.001) {
discard;
}
gl_FragDepth = depth * 0.5 + 0.5;
o_Color = vec4(finalColor, finalAlpha);
}

View File

@ -0,0 +1,348 @@
#type vertex
#version 430 core
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
out vec2 v_TexCoord;
void main()
{
v_TexCoord = a_TexCoord;
gl_Position = vec4(a_Position.xy, 0.0, 1.0);
}
#type fragment
#version 430 core
// ==================== 输入 ====================
in vec2 v_TexCoord;
// G-buffer 纹理
uniform sampler2D u_AlbedoMetallic; // RGB: albedo, A: metallic
uniform sampler2D u_NormalRoughness; // RGB: normal (encoded), A: roughness
uniform sampler2D u_EmissiveAO; // RGB: emissive, A: AO
uniform sampler2D u_Depth; // depth
// 相机参数
uniform mat4 u_InvViewProj; // 逆视图投影矩阵,用于重建世界坐标
uniform vec3 u_CameraPosition;
// 光源结构体与你的PBR着色器一致
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
uniform DirectionalLight u_DirectionalLights; // 仅一个方向光
uniform int u_PointLightCount;
uniform PointLight u_PointLights[16]; // 假设最多16个点光源
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights[16]; // 最多16个聚光源
// IBL 相关
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
uniform sampler2D u_BRDFLUTTexture;
uniform float u_IBLContribution;
uniform float u_EnvMapRotation;
// 阴影相关
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform int u_ShadowEnabled;
uniform float u_ShadowIntensity; // 阴影强度0-1
uniform mat4 u_LightSpaceMatrix; // 方向光光源空间矩阵
// 天空盒(可选)
uniform samplerCube u_Skybox; // 如果深度为1.0则采样天空盒
uniform float u_SkyIntensity;
uniform float u_SkyTextureLod;
// 输出
layout(location = 0) out vec4 o_Color;
// ==================== 常量 ====================
const float PI = 3.14159265359;
const float Epsilon = 0.00001;
const vec3 Fdielectric = vec3(0.04);
// ==================== 工具函数 ====================
// 从深度重建世界坐标
vec3 worldPosFromDepth(vec2 uv, float depth) {
vec4 clipPos = vec4(uv * 2.0 - 1.0, depth * 2.0 - 1.0, 1.0);
vec4 worldPos = u_InvViewProj * clipPos;
return worldPos.xyz / worldPos.w;
}
// 从深度重建世界空间方向(用于天空盒采样)
vec3 worldDirFromUV(vec2 uv) {
// 假设深度为1.0时,得到远平面方向
vec4 clipPos = vec4(uv * 2.0 - 1.0, 1.0, 1.0);
vec4 worldPos = u_InvViewProj * clipPos;
return normalize(worldPos.xyz / worldPos.w);
}
// 旋转向量绕Y轴
vec3 RotateVectorAboutY(float angle, vec3 vec) {
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
// ==================== PBR 函数(复用你的代码) ====================
float ndfGGX(float cosLh, float roughness) {
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness) {
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnelSchlick(vec3 F0, float cosTheta) {
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness) {
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------- 方向光 ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic) {
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
// ---------- 点光源 ----------
vec3 ComputePointLight(PointLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic, vec3 worldPos) {
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
// ---------- 聚光源 ----------
vec3 ComputeSpotLight(SpotLight light, vec3 F0, vec3 N, vec3 V, float NdotV, vec3 albedo, float roughness, float metallic, vec3 worldPos) {
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
float cosAngle = dot(-L, normalize(light.Direction));
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 Lh = normalize(L + V);
float cosLi = max(dot(N, L), 0.0);
float cosLh = max(dot(N, Lh), 0.0);
vec3 F = fresnelSchlick(F0, max(dot(Lh, V), 0.0));
float D = ndfGGX(cosLh, roughness);
float G = GeometrySmith(N, V, L, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseBRDF = kd * albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
// ---------- IBL ----------
vec3 IBL(vec3 F0, vec3 N, vec3 V, float NdotV, float roughness, float metallic, vec3 albedo) {
vec3 irradiance = texture(u_EnvIrradianceTex, N).rgb;
vec3 F = fresnelSchlickRoughness(F0, NdotV, roughness);
vec3 kd = (1.0 - F) * (1.0 - metallic);
vec3 diffuseIBL = albedo * irradiance;
vec3 R = 2.0 * NdotV * N - V; // 反射向量
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, R),
roughness * u_EnvRadianceTexLevels
).rgb;
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(NdotV, 1.0 - roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
// ---------- 阴影 ----------
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir) {
if (u_ShadowEnabled == 0) return 0.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for (int x = -pcfRange; x <= pcfRange; ++x) {
for (int y = -pcfRange; y <= pcfRange; ++y) {
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += (currentDepth - bias > pcfDepth) ? 1.0 : 0.0;
sampleCount++;
}
}
shadow /= float(sampleCount);
return shadow * u_ShadowIntensity; // 应用阴影强度
}
// ==================== 主函数 ====================
void main() {
vec2 uv = v_TexCoord;
float depth = texture(u_Depth, uv).r;
if (depth >= 1.0) {
vec3 dir = worldDirFromUV(uv);
vec3 skyColor = textureLod(u_Skybox, dir, u_SkyTextureLod).rgb * u_SkyIntensity;
o_Color = vec4(skyColor, 1.0);
return;
}
vec4 albedoMetal = texture(u_AlbedoMetallic, uv);
vec4 normalRough = texture(u_NormalRoughness, uv);
vec4 emissiveAO = texture(u_EmissiveAO, uv);
vec3 albedo = albedoMetal.rgb;
float metallic = albedoMetal.a;
vec3 normal = normalRough.rgb * 2.0 - 1.0;
float roughness = normalRough.a;
vec3 emissive = emissiveAO.rgb;
float ao = emissiveAO.a;
vec3 worldPos = worldPosFromDepth(uv, depth);
vec3 V = normalize(u_CameraPosition - worldPos);
float NdotV = clamp(dot(normal, V), 0.0, 1.0);
vec3 F0 = mix(Fdielectric, albedo, metallic);
vec3 Lo = vec3(0.0);
// Direction Light
if (u_DirectionalLights.Intensity > 0.0) {
Lo += ComputeDirectionalLight(u_DirectionalLights, F0, normal, V, NdotV, albedo, roughness, metallic);
}
// Point Light
for (int i = 0; i < u_PointLightCount; ++i) {
Lo += ComputePointLight(u_PointLights[i], F0, normal, V, NdotV, albedo, roughness, metallic, worldPos);
}
// Spot light
for (int i = 0; i < u_SpotLightCount; ++i) {
Lo += ComputeSpotLight(u_SpotLights[i], F0, normal, V, NdotV, albedo, roughness, metallic, worldPos);
}
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0 && u_DirectionalLights.CastShadows && u_DirectionalLights.Intensity > 0.0) {
vec4 fragPosLightSpace = u_LightSpaceMatrix * vec4(worldPos, 1.0);
float shadow = calculateShadow(fragPosLightSpace, normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
Lo *= shadowFactor;
// 计算 IBL
vec3 ibl = IBL(F0, normal, V, NdotV, roughness, metallic, albedo) * u_IBLContribution;
vec3 finalColor = Lo + ibl + emissive;
o_Color = vec4(finalColor, 1.0);
}

View File

@ -4,13 +4,10 @@
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec2 a_TexCoord;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
out vec2 v_TexCoord;
void main()
{
gl_Position = u_ViewProjection * u_Transform * vec4(a_Position, 1.0);

View File

@ -0,0 +1,36 @@
// Outline Shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjection;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
void main()
{
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjection * u_Transform * localPosition;
}
#type fragment
#version 430
layout(location = 0) out vec4 color;
void main()
{
color = vec4(1.0, 0.5, 0.0, 1.0);
}

View File

@ -1,5 +1,5 @@
// -----------------------------
// -- From Hazel Engine PBR shader --
// -- Based on Hazel PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
@ -21,19 +21,25 @@ layout(location = 4) in vec2 a_TexCoord;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform;
uniform mat4 u_LightSpaceMatrix;
out VertexOutput
{
vec3 WorldPosition;
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
} vs_Output;
void main()
@ -49,8 +55,14 @@ void main()
vs_Output.Normal = mat3(u_Transform) * mat3(boneTransform) * a_Normal;
vs_Output.TexCoord = vec2(a_TexCoord.x, 1.0 - a_TexCoord.y);
vs_Output.WorldNormals = mat3(u_Transform) * mat3(a_Tangent, a_Binormal, a_Normal);
vs_Output.Binormal = mat3(boneTransform) * a_Binormal;
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * localPosition;
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
// gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
gl_Position = u_ViewProjectionMatrix * u_Transform * localPosition;
}
@ -61,14 +73,32 @@ const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Multiplier;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
in VertexOutput
@ -77,46 +107,72 @@ in VertexOutput
vec3 Normal;
vec2 TexCoord;
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
} vs_Input;
layout(location = 0) out vec4 color;
layout(location = 1) out vec4 o_BloomColor;
uniform Light lights;
uniform DirectionalLight u_DirectionalLights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
uniform int u_PointLightCount;
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights;
// PBR
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
// environment
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform float u_IBLContribution;
uniform float u_BloomThreshold;
uniform float u_EnvMapRotation;
// baseColor
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
// textureToggle
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
// shadow
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform float u_ShadowIntensity;
uniform int u_ShadowEnabled;
// Emissive
uniform sampler2D u_EmissiveTexture;
uniform float u_EmissiveTexToggle;
uniform vec3 u_EmissiveColor;
uniform float u_EmissiveIntensity;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
@ -124,39 +180,21 @@ struct PBRParameters
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
// ---------- PBR param func ----------
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
@ -166,11 +204,9 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
@ -181,97 +217,117 @@ vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
// ---------- direction light ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters params)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
vec2 Hammersley(uint i, uint N)
vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减:通常使用平方衰减,但为避免分母为零,加一个小值
float attenuation = 1.0 / (dist * dist + 0.0001);
// 可选:范围平滑衰减
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor; // 平滑
attenuation *= rangeFactor;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
// 角度衰减(聚光锥)
float cosAngle = dot(-L, normalize(light.Direction)); // 光方向指向外,所以用 -L
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
float TotalWeight = 0.0;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// ---------------------------------------------------------------------------------------------------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
}
vec3 Lighting(vec3 F0)
{
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance * lights.Multiplier;
vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Intensity;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
@ -279,6 +335,18 @@ vec3 Lighting(vec3 F0)
return result;
}
// ---------- IBL ----------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
@ -287,26 +355,98 @@ vec3 IBL(vec3 F0, vec3 Lr)
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
// shadow
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
// Perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// If outside shadow map bounds, assume no shadow
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
// Get closest depth value from light's perspective
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
// Calculate bias based on surface angle
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
// PCF (Percentage Closer Filtering) for soft shadows
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for(int x = -pcfRange; x <= pcfRange; ++x)
{
for(int y = -pcfRange; y <= pcfRange; ++y)
{
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
sampleCount++;
}
}
shadow /= float(sampleCount);
return shadow;
}
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
{
if (u_ShadowEnabled == 0) return 1.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0) return 1.0;
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
float alpha = 1.0;
if (u_AlbedoTexToggle > 0.5) {
vec4 albedoWithAlpha = texture(u_AlbedoTexture, vs_Input.TexCoord);
m_Params.Albedo = albedoWithAlpha.rgb;
alpha = albedoWithAlpha.a;
} else {
m_Params.Albedo = u_AlbedoColor;
alpha = 1.0;
}
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
m_Params.Roughness = max(m_Params.Roughness, 0.05);
// Normals (either from vertex or map)
// normal
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
@ -317,14 +457,40 @@ void main()
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 emissive = u_EmissiveColor;
if (u_EmissiveTexToggle > 0.5) {
emissive = texture(u_EmissiveTexture, vs_Input.TexCoord).rgb;
}
emissive *= u_EmissiveIntensity;
vec3 finalRGB = lightContribution + iblContribution + emissive;
vec4 finalColor = vec4(finalRGB, alpha);
color = finalColor;
// Bloom
float brightness = dot(color.rgb, vec3(0.2126, 0.7152, 0.0722));
o_BloomColor = brightness > u_BloomThreshold ? color : vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@ -1,5 +1,5 @@
// -----------------------------
// -- From Hazel Engine PBR shader --
// -- Based on Hazel PBR shader --
// -----------------------------
// Note: this shader is still very much in progress. There are likely many bugs and future additions that will go in.
// Currently heavily updated.
@ -19,8 +19,11 @@ layout(location = 3) in vec3 a_Binormal;
layout(location = 4) in vec2 a_TexCoord;
uniform mat4 u_ViewProjectionMatrix;
uniform mat4 u_ViewMatrix;
uniform mat4 u_Transform;
uniform mat4 u_LightSpaceMatrix;
out VertexOutput
{
vec3 WorldPosition;
@ -29,6 +32,8 @@ out VertexOutput
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
} vs_Output;
void main()
@ -40,26 +45,55 @@ void main()
vs_Output.WorldTransform = mat3(u_Transform);
vs_Output.Binormal = a_Binormal;
vs_Output.FragPosLightSpace = u_LightSpaceMatrix * u_Transform * vec4(a_Position, 1.0);
vs_Output.ViewPosition = vec3(u_ViewMatrix * vec4(vs_Output.WorldPosition, 1.0));
gl_Position = u_ViewProjectionMatrix * u_Transform * vec4(a_Position, 1.0);
}
#type fragment
#version 430 core
layout(location = 0) out vec4 outAlbedoMetal;
layout(location = 1) out vec4 outNormalRoughness;
layout(location = 2) out vec4 outEmissiveAO;
layout(location = 3) out vec4 outColor;
layout(location = 4) out vec4 outBloomColor;
const float PI = 3.141592;
const float Epsilon = 0.00001;
const int LightCount = 1;
// Constant normal incidence Fresnel factor for all dielectrics.
const vec3 Fdielectric = vec3(0.04);
struct Light {
struct DirectionalLight {
vec3 Direction;
vec3 Radiance;
float Multiplier;
float Intensity;
bool CastShadows;
};
struct PointLight {
vec3 Position;
vec3 Radiance;
float Intensity;
float Range;
bool CastShadows;
};
struct SpotLight {
vec3 Position;
vec3 Direction;
vec3 Radiance;
float Intensity;
float Range;
float InnerConeCos;
float OuterConeCos;
bool CastShadows;
};
in VertexOutput
{
vec3 WorldPosition;
@ -68,45 +102,70 @@ in VertexOutput
mat3 WorldNormals;
mat3 WorldTransform;
vec3 Binormal;
vec3 ViewPosition;
vec4 FragPosLightSpace;
} vs_Input;
layout(location = 0) out vec4 color;
uniform Light lights;
uniform bool u_GBufferMode;
uniform DirectionalLight u_DirectionalLights;
uniform int u_PointLightCount;
uniform PointLight u_PointLights;
uniform int u_SpotLightCount;
uniform SpotLight u_SpotLights;
uniform vec3 u_CameraPosition;
// PBR texture inputs
// PBR
uniform sampler2D u_AlbedoTexture;
uniform sampler2D u_NormalTexture;
uniform sampler2D u_MetalnessTexture;
uniform sampler2D u_RoughnessTexture;
// Environment maps
// environment
uniform samplerCube u_EnvRadianceTex;
uniform samplerCube u_EnvIrradianceTex;
// BRDF LUT
uniform sampler2D u_BRDFLUTTexture;
uniform float u_IBLContribution;
uniform float u_BloomThreshold;
uniform float u_EnvMapRotation;
// baseColor
uniform vec3 u_AlbedoColor;
uniform float u_Metalness;
uniform float u_Roughness;
uniform float u_EnvMapRotation;
// Toggles
uniform float u_RadiancePrefilter;
// textureToggle
uniform float u_AlbedoTexToggle;
uniform float u_NormalTexToggle;
uniform float u_MetalnessTexToggle;
uniform float u_RoughnessTexToggle;
// shadow
uniform sampler2D u_ShadowMap;
uniform float u_ShadowBias;
uniform float u_ShadowSoftness;
uniform float u_ShadowIntensity;
uniform int u_ShadowEnabled;
// Emissive
uniform sampler2D u_EmissiveTexture;
uniform float u_EmissiveTexToggle;
uniform vec3 u_EmissiveColor;
uniform float u_EmissiveIntensity;
struct PBRParameters
{
vec3 Albedo;
float Roughness;
float Metalness;
vec3 Normal;
vec3 View;
float NdotV;
@ -114,39 +173,21 @@ struct PBRParameters
PBRParameters m_Params;
// GGX/Towbridge-Reitz normal distribution function.
// Uses Disney's reparametrization of alpha = roughness^2
// ---------- PBR param func ----------
float ndfGGX(float cosLh, float roughness)
{
float alpha = roughness * roughness;
float alphaSq = alpha * alpha;
float denom = (cosLh * cosLh) * (alphaSq - 1.0) + 1.0;
return alphaSq / (PI * denom * denom);
}
// Single term for separable Schlick-GGX below.
float gaSchlickG1(float cosTheta, float k)
{
return cosTheta / (cosTheta * (1.0 - k) + k);
}
// Schlick-GGX approximation of geometric attenuation function using Smith's method.
float gaSchlickGGX(float cosLi, float NdotV, float roughness)
{
float r = roughness + 1.0;
float k = (r * r) / 8.0; // Epic suggests using this roughness remapping for analytic lights.
return gaSchlickG1(cosLi, k) * gaSchlickG1(NdotV, k);
}
float GeometrySchlickGGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
@ -156,11 +197,9 @@ float GeometrySmith(vec3 N, vec3 V, vec3 L, float roughness)
float NdotL = max(dot(N, L), 0.0);
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Shlick's approximation of the Fresnel factor.
vec3 fresnelSchlick(vec3 F0, float cosTheta)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
@ -171,74 +210,94 @@ vec3 fresnelSchlickRoughness(vec3 F0, float cosTheta, float roughness)
return F0 + (max(vec3(1.0 - roughness), F0) - F0) * pow(1.0 - cosTheta, 5.0);
}
// ---------------------------------------------------------------------------------------------------
// The following code (from Unreal Engine 4's paper) shows how to filter the environment map
// for different roughnesses. This is mean to be computed offline and stored in cube map mips,
// so turning this on online will cause poor performance
float RadicalInverse_VdC(uint bits)
// ---------- direction light ----------
vec3 ComputeDirectionalLight(DirectionalLight light, vec3 F0, PBRParameters params)
{
bits = (bits << 16u) | (bits >> 16u);
bits = ((bits & 0x55555555u) << 1u) | ((bits & 0xAAAAAAAAu) >> 1u);
bits = ((bits & 0x33333333u) << 2u) | ((bits & 0xCCCCCCCCu) >> 2u);
bits = ((bits & 0x0F0F0F0Fu) << 4u) | ((bits & 0xF0F0F0F0u) >> 4u);
bits = ((bits & 0x00FF00FFu) << 8u) | ((bits & 0xFF00FF00u) >> 8u);
return float(bits) * 2.3283064365386963e-10; // / 0x100000000
vec3 L = normalize(-light.Direction);
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
}
vec2 Hammersley(uint i, uint N)
vec3 ComputePointLight(PointLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
return vec2(float(i)/float(N), RadicalInverse_VdC(i));
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
// 距离衰减:通常使用平方衰减,但为避免分母为零,加一个小值
float attenuation = 1.0 / (dist * dist + 0.0001);
// 可选:范围平滑衰减
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor; // 平滑
attenuation *= rangeFactor;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 ImportanceSampleGGX(vec2 Xi, float Roughness, vec3 N)
vec3 ComputeSpotLight(SpotLight light, vec3 F0, PBRParameters params, vec3 worldPos)
{
float a = Roughness * Roughness;
float Phi = 2 * PI * Xi.x;
float CosTheta = sqrt( (1 - Xi.y) / ( 1 + (a*a - 1) * Xi.y ) );
float SinTheta = sqrt( 1 - CosTheta * CosTheta );
vec3 H;
H.x = SinTheta * cos( Phi );
H.y = SinTheta * sin( Phi );
H.z = CosTheta;
vec3 UpVector = abs(N.z) < 0.999 ? vec3(0,0,1) : vec3(1,0,0);
vec3 TangentX = normalize( cross( UpVector, N ) );
vec3 TangentY = cross( N, TangentX );
// Tangent to world space
return TangentX * H.x + TangentY * H.y + N * H.z;
}
vec3 lightVec = light.Position - worldPos;
float dist = length(lightVec);
if (dist > light.Range) return vec3(0.0);
float TotalWeight = 0.0;
vec3 L = lightVec / dist;
vec3 Lradiance = light.Radiance * light.Intensity;
vec3 PrefilterEnvMap(float Roughness, vec3 R)
{
vec3 N = R;
vec3 V = R;
vec3 PrefilteredColor = vec3(0.0);
int NumSamples = 1024;
for(int i = 0; i < NumSamples; i++)
{
vec2 Xi = Hammersley(i, NumSamples);
vec3 H = ImportanceSampleGGX(Xi, Roughness, N);
vec3 L = 2 * dot(V, H) * H - V;
float NoL = clamp(dot(N, L), 0.0, 1.0);
if (NoL > 0)
{
PrefilteredColor += texture(u_EnvRadianceTex, L).rgb * NoL;
TotalWeight += NoL;
}
}
return PrefilteredColor / TotalWeight;
}
// 距离衰减
float attenuation = 1.0 / (dist * dist + 0.0001);
float rangeFactor = clamp(1.0 - (dist / light.Range), 0.0, 1.0);
rangeFactor = rangeFactor * rangeFactor;
attenuation *= rangeFactor;
// ---------------------------------------------------------------------------------------------------
// 角度衰减(聚光锥)
float cosAngle = dot(-L, normalize(light.Direction)); // 光方向指向外,所以用 -L
if (cosAngle < light.OuterConeCos) return vec3(0.0);
float angleFalloff = (cosAngle - light.OuterConeCos) / (light.InnerConeCos - light.OuterConeCos);
angleFalloff = clamp(angleFalloff, 0.0, 1.0);
attenuation *= angleFalloff;
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3x3 rotationMatrix ={vec3(cos(angle),0.0,sin(angle)),
vec3(0.0,1.0,0.0),
vec3(-sin(angle),0.0,cos(angle))};
return rotationMatrix * vec;
vec3 Lh = normalize(L + params.View);
float cosLi = max(0.0, dot(params.Normal, L));
float cosLh = max(0.0, dot(params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, params.View)));
float D = ndfGGX(cosLh, params.Roughness);
float G = GeometrySmith(params.Normal, params.View, L, params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - params.Metalness);
vec3 diffuseBRDF = kd * params.Albedo;
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * params.NdotV);
return (diffuseBRDF + specularBRDF) * Lradiance * cosLi * attenuation;
}
vec3 Lighting(vec3 F0)
@ -246,22 +305,20 @@ vec3 Lighting(vec3 F0)
vec3 result = vec3(0.0);
for(int i = 0; i < LightCount; i++)
{
vec3 Li = -lights.Direction;
vec3 Lradiance = lights.Radiance * lights.Multiplier;
vec3 Li = u_DirectionalLights.Direction;
vec3 Lradiance = u_DirectionalLights.Radiance * u_DirectionalLights.Intensity;
vec3 Lh = normalize(Li + m_Params.View);
// Calculate angles between surface normal and various light vectors.
float cosLi = max(0.0, dot(m_Params.Normal, Li));
float cosLh = max(0.0, dot(m_Params.Normal, Lh));
vec3 F = fresnelSchlick(F0, max(0.0, dot(Lh, m_Params.View)));
float D = ndfGGX(cosLh, m_Params.Roughness);
float G = gaSchlickGGX(cosLi, m_Params.NdotV, m_Params.Roughness);
float G = GeometrySmith(m_Params.Normal, m_Params.View, Li, m_Params.Roughness);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseBRDF = kd * m_Params.Albedo;
// Cook-Torrance
vec3 specularBRDF = (F * D * G) / max(Epsilon, 4.0 * cosLi * m_Params.NdotV);
result += (diffuseBRDF + specularBRDF) * Lradiance * cosLi;
@ -269,54 +326,172 @@ vec3 Lighting(vec3 F0)
return result;
}
// ---------- IBL ----------
vec3 RotateVectorAboutY(float angle, vec3 vec)
{
angle = radians(angle);
mat3 rotationMatrix = mat3(
vec3(cos(angle), 0.0, sin(angle)),
vec3(0.0, 1.0, 0.0),
vec3(-sin(angle), 0.0, cos(angle))
);
return rotationMatrix * vec;
}
vec3 IBL(vec3 F0, vec3 Lr)
{
vec3 irradiance = texture(u_EnvIrradianceTex, m_Params.Normal).rgb;
vec3 F = fresnelSchlickRoughness(F0, m_Params.NdotV, m_Params.Roughness);
// vec3 F = fresnelSchlickR(F0, m_Params.NdotV);
vec3 kd = (1.0 - F) * (1.0 - m_Params.Metalness);
vec3 diffuseIBL = m_Params.Albedo * irradiance;
int u_EnvRadianceTexLevels = textureQueryLevels(u_EnvRadianceTex);
float NoV = clamp(m_Params.NdotV, 0.0, 1.0);
vec3 R = 2.0 * dot(m_Params.View, m_Params.Normal) * m_Params.Normal - m_Params.View;
vec3 specularIrradiance = textureLod(u_EnvRadianceTex, RotateVectorAboutY(u_EnvMapRotation, Lr), (m_Params.Roughness) * u_EnvRadianceTexLevels).rgb;
vec3 specularIrradiance = textureLod(
u_EnvRadianceTex,
RotateVectorAboutY(u_EnvMapRotation, Lr),
m_Params.Roughness * u_EnvRadianceTexLevels
).rgb;
// Sample BRDF Lut, 1.0 - roughness for y-coord because texture was generated (in Sparky) for gloss model
vec2 specularBRDF = texture(u_BRDFLUTTexture, vec2(m_Params.NdotV, 1.0 - m_Params.Roughness)).rg;
vec3 specularIBL = specularIrradiance * (F * specularBRDF.x + specularBRDF.y);
return kd * diffuseIBL + specularIBL;
}
void main()
{
// Standard PBR inputs
m_Params.Albedo = u_AlbedoTexToggle > 0.5 ? texture(u_AlbedoTexture, vs_Input.TexCoord).rgb : u_AlbedoColor;
m_Params.Metalness = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
m_Params.Roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
m_Params.Roughness = max(m_Params.Roughness, 0.05); // Minimum roughness of 0.05 to keep specular highlight
// shadow
// Normals (either from vertex or map)
m_Params.Normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
float calculateShadow(vec4 fragPosLightSpace, vec3 normal, vec3 lightDir)
{
m_Params.Normal = normalize(2.0 * texture(u_NormalTexture, vs_Input.TexCoord).rgb - 1.0);
m_Params.Normal = normalize(vs_Input.WorldNormals * m_Params.Normal);
// Perspective divide
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
// Transform to [0,1] range
projCoords = projCoords * 0.5 + 0.5;
// If outside shadow map bounds, assume no shadow
if(projCoords.z > 1.0 || projCoords.x < 0.0 || projCoords.x > 1.0 || projCoords.y < 0.0 || projCoords.y > 1.0)
return 0.0;
// Get closest depth value from light's perspective
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
// Calculate bias based on surface angle
float bias = max(u_ShadowBias * (1.0 - dot(normal, lightDir)), u_ShadowBias * 0.1);
// PCF (Percentage Closer Filtering) for soft shadows
float shadow = 0.0;
vec2 texelSize = 1.0 / textureSize(u_ShadowMap, 0);
int pcfRange = int(u_ShadowSoftness);
int sampleCount = 0;
for(int x = -pcfRange; x <= pcfRange; ++x)
{
for(int y = -pcfRange; y <= pcfRange; ++y)
{
float pcfDepth = texture(u_ShadowMap, projCoords.xy + vec2(x, y) * texelSize).r;
shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0;
sampleCount++;
}
}
shadow /= float(sampleCount);
return shadow;
}
float ComputeShadow(vec4 fragPosLightSpace, float NdotL)
{
if (u_ShadowEnabled == 0) return 1.0;
vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w;
projCoords = projCoords * 0.5 + 0.5;
if (projCoords.x < 0.0 || projCoords.x > 1.0 ||
projCoords.y < 0.0 || projCoords.y > 1.0 ||
projCoords.z > 1.0) return 1.0;
float closestDepth = texture(u_ShadowMap, projCoords.xy).r;
float currentDepth = projCoords.z;
float bias = max(u_ShadowBias * (1.0 - NdotL), u_ShadowBias * 0.5);
float shadow = (currentDepth - bias) > closestDepth ? 1.0 : 0.0;
return mix(1.0, 1.0 - u_ShadowIntensity, shadow);
}
void main()
{
// === 1. 采样基础属性(所有模式都需要) ===
vec4 albedoWithAlpha = texture(u_AlbedoTexture, vs_Input.TexCoord);
vec3 albedo = u_AlbedoTexToggle > 0.5 ? albedoWithAlpha.rgb : u_AlbedoColor;
float alpha = u_AlbedoTexToggle > 0.5 ? albedoWithAlpha.a : 1.0;
float metallic = u_MetalnessTexToggle > 0.5 ? texture(u_MetalnessTexture, vs_Input.TexCoord).r : u_Metalness;
float roughness = u_RoughnessTexToggle > 0.5 ? texture(u_RoughnessTexture, vs_Input.TexCoord).r : u_Roughness;
roughness = max(roughness, 0.05);
// === 2. 法线计算(世界空间) ===
vec3 normal = normalize(vs_Input.Normal);
if (u_NormalTexToggle > 0.5)
{
vec3 tangentNormal = texture(u_NormalTexture, vs_Input.TexCoord).rgb * 2.0 - 1.0;
normal = normalize(vs_Input.WorldNormals * tangentNormal);
}
// === 3. 自发光计算 ===
vec3 emissive = u_EmissiveColor;
if (u_EmissiveTexToggle > 0.5)
emissive = texture(u_EmissiveTexture, vs_Input.TexCoord).rgb;
emissive *= u_EmissiveIntensity;
// === 4. GBuffer 模式:直接输出到多个目标 ===
if (u_GBufferMode)
{
outAlbedoMetal = vec4(albedo, metallic);
outNormalRoughness = vec4(normal * 0.5 + 0.5, roughness);
outEmissiveAO = vec4(emissive, 1.0); // AO 暂设为 1.0
return; // 提前结束
}
// === 5. 非 GBuffer 模式:继续 PBR 光照计算 ===
// 填充 PBRParameters
m_Params.Albedo = albedo;
m_Params.Metalness = metallic;
m_Params.Roughness = roughness;
m_Params.Normal = normal;
m_Params.View = normalize(u_CameraPosition - vs_Input.WorldPosition);
m_Params.NdotV = max(dot(m_Params.Normal, m_Params.View), 0.0);
// Specular reflection vector
vec3 Lr = 2.0 * m_Params.NdotV * m_Params.Normal - m_Params.View;
// Fresnel reflectance, metals use albedo
vec3 F0 = mix(Fdielectric, m_Params.Albedo, m_Params.Metalness);
vec3 lightContribution = Lighting(F0);
vec3 iblContribution = IBL(F0, Lr);
color = vec4(lightContribution + iblContribution, 1.0);
// color = vec4(iblContribution, 1.0);
// Shadow
float shadowFactor = 1.0;
if (u_ShadowEnabled > 0.5) {
float shadow = calculateShadow(vs_Input.FragPosLightSpace, m_Params.Normal, u_DirectionalLights.Direction);
shadowFactor = 1.0 - shadow;
}
// directional light with with shadow
vec3 lightContribution = u_DirectionalLights.Intensity > 0.0 ? Lighting(F0) * shadowFactor : vec3(0.0);
if(u_PointLightCount > 0)
lightContribution += ComputePointLight(u_PointLights, F0, m_Params, vs_Input.WorldPosition);
if(u_SpotLightCount > 0)
lightContribution += ComputeSpotLight(u_SpotLights, F0, m_Params, vs_Input.WorldPosition);
vec3 iblContribution = IBL(F0, Lr) * u_IBLContribution;
vec3 finalRGB = lightContribution + iblContribution + emissive;
vec4 finalColor = vec4(finalRGB, alpha);
outColor = finalColor;
// Bloom
float brightness = dot(finalColor.rgb, vec3(0.2126, 0.7152, 0.0722));
outBloomColor = brightness > u_BloomThreshold ? finalColor : vec4(0.0, 0.0, 0.0, 1.0);
}

View File

@ -0,0 +1,159 @@
#type compute
#version 450 core
const float PI = 3.141592;
layout(binding = 0, rgba32f) restrict writeonly uniform imageCube o_CubeMap;
uniform vec3 u_TurbidityAzimuthInclination;
#define PI 3.14159265359
vec3 GetCubeMapTexCoord()
{
vec2 st = gl_GlobalInvocationID.xy / vec2(imageSize(o_CubeMap));
vec2 uv = 2.0 * vec2(st.x, 1.0 - st.y) - vec2(1.0);
vec3 ret;
if (gl_GlobalInvocationID.z == 0) ret = vec3( 1.0, uv.y, -uv.x);
else if (gl_GlobalInvocationID.z == 1) ret = vec3( -1.0, uv.y, uv.x);
else if (gl_GlobalInvocationID.z == 2) ret = vec3( uv.x, 1.0, -uv.y);
else if (gl_GlobalInvocationID.z == 3) ret = vec3( uv.x, -1.0, uv.y);
else if (gl_GlobalInvocationID.z == 4) ret = vec3( uv.x, uv.y, 1.0);
else if (gl_GlobalInvocationID.z == 5) ret = vec3(-uv.x, uv.y, -1.0);
return normalize(ret);
}
float saturatedDot( in vec3 a, in vec3 b )
{
return clamp(dot(a, b), 0.0, 1.0);
}
vec3 YxyToXYZ( in vec3 Yxy )
{
float Y = Yxy.r;
float x = Yxy.g;
float y = Yxy.b;
float X = x * ( Y / y );
float Z = ( 1.0 - x - y ) * ( Y / y );
return vec3(X,Y,Z);
}
vec3 XYZToRGB( in vec3 XYZ )
{
// CIE/E
mat3 M = mat3
(
2.3706743, -0.9000405, -0.4706338,
-0.5138850, 1.4253036, 0.0885814,
0.0052982, -0.0146949, 1.0093968
);
return XYZ * M;
}
vec3 YxyToRGB( in vec3 Yxy )
{
vec3 XYZ = YxyToXYZ( Yxy );
vec3 RGB = XYZToRGB( XYZ );
return RGB;
}
void calculatePerezDistribution( in float t, out vec3 A, out vec3 B, out vec3 C, out vec3 D, out vec3 E )
{
A = vec3( 0.1787 * t - 1.4630, -0.0193 * t - 0.2592, -0.0167 * t - 0.2608 );
B = vec3( -0.3554 * t + 0.4275, -0.0665 * t + 0.0008, -0.0950 * t + 0.0092 );
C = vec3( -0.0227 * t + 5.3251, -0.0004 * t + 0.2125, -0.0079 * t + 0.2102 );
D = vec3( 0.1206 * t - 2.5771, -0.0641 * t - 0.8989, -0.0441 * t - 1.6537 );
E = vec3( -0.0670 * t + 0.3703, -0.0033 * t + 0.0452, -0.0109 * t + 0.0529 );
}
vec3 calculateZenithLuminanceYxy( in float t, in float thetaS )
{
float chi = ( 4.0 / 9.0 - t / 120.0 ) * ( PI - 2.0 * thetaS );
float Yz = ( 4.0453 * t - 4.9710 ) * tan( chi ) - 0.2155 * t + 2.4192;
float theta2 = thetaS * thetaS;
float theta3 = theta2 * thetaS;
float T = t;
float T2 = t * t;
float xz =
( 0.00165 * theta3 - 0.00375 * theta2 + 0.00209 * thetaS + 0.0) * T2 +
(-0.02903 * theta3 + 0.06377 * theta2 - 0.03202 * thetaS + 0.00394) * T +
( 0.11693 * theta3 - 0.21196 * theta2 + 0.06052 * thetaS + 0.25886);
float yz =
( 0.00275 * theta3 - 0.00610 * theta2 + 0.00317 * thetaS + 0.0) * T2 +
(-0.04214 * theta3 + 0.08970 * theta2 - 0.04153 * thetaS + 0.00516) * T +
( 0.15346 * theta3 - 0.26756 * theta2 + 0.06670 * thetaS + 0.26688);
return vec3( Yz, xz, yz );
}
vec3 calculatePerezLuminanceYxy( in float theta, in float gamma, in vec3 A, in vec3 B, in vec3 C, in vec3 D, in vec3 E )
{
float cosTheta = max(cos(theta), 1e-6);
return ( 1.0 + A * exp( B / cos( theta ) ) ) * ( 1.0 + C * exp( D * gamma ) + E * cos( gamma ) * cos( gamma ) );
}
vec3 calculateSkyLuminanceRGB( in vec3 s, in vec3 e, in float t )
{
vec3 A, B, C, D, E;
calculatePerezDistribution( t, A, B, C, D, E );
float thetaS = acos(clamp(dot(s, vec3(0,1,0)), 0.0, 1.0));
float thetaE = acos(clamp(dot(e, vec3(0,1,0)), 0.0, 1.0));
float gammaE = acos( saturatedDot( s, e ) );
vec3 Yz = calculateZenithLuminanceYxy( t, thetaS );
vec3 fThetaGamma = calculatePerezLuminanceYxy( thetaE, gammaE, A, B, C, D, E );
vec3 fZeroThetaS = calculatePerezLuminanceYxy( 0.0, thetaS, A, B, C, D, E );
vec3 Yp = Yz * ( fThetaGamma / fZeroThetaS );
return YxyToRGB( Yp );
}
layout(local_size_x = 32, local_size_y = 32, local_size_z = 1) in;
void main()
{
vec3 cubeTC = GetCubeMapTexCoord();
float turbidity = u_TurbidityAzimuthInclination.x;
float azimuth = u_TurbidityAzimuthInclination.y;;
float inclination = u_TurbidityAzimuthInclination.z;
vec3 sunDir = normalize( vec3( sin(inclination) * cos(azimuth), cos(inclination), sin(inclination) * sin(azimuth) ) );
vec3 viewDir = cubeTC;
const float SUN_ANGULAR_RADIUS = 0.03465;
const float SUN_INTENSITY = 100.0;
vec3 skyLuminance;
if (viewDir.y < 0.0) {
skyLuminance = vec3(0.02);
} else {
skyLuminance = calculateSkyLuminanceRGB(sunDir, viewDir, turbidity);
}
float cosAngle = dot(viewDir, sunDir);
float angle = acos(cosAngle);
if (angle < SUN_ANGULAR_RADIUS) {
skyLuminance = vec3(SUN_INTENSITY);
} else {
float haloWidth = 0.1;
if (angle < SUN_ANGULAR_RADIUS + haloWidth) {
float t = (angle - SUN_ANGULAR_RADIUS) / haloWidth;
float haloFactor = 1.0 - smoothstep(0.0, 1.0, t);
skyLuminance += vec3(SUN_INTENSITY * 0.1 * haloFactor);
}
}
vec4 color = vec4(skyLuminance * 0.05, 1.0);
imageStore(o_CubeMap, ivec3(gl_GlobalInvocationID), color);
}

View File

@ -6,14 +6,14 @@
layout(location = 0) in vec3 a_Position;
layout(location = 1) in vec4 a_Color;
layout(location = 2) in vec2 a_TexCoord;
layout(location = 3) in float a_TexIndex;
layout(location = 3) in int a_TexIndex;
layout(location = 4) in float a_TilingFactor;
uniform mat4 u_ViewProjection;
out vec4 v_Color;
out vec2 v_TexCoord;
out float v_TexIndex;
flat out int v_TexIndex;
out float v_TilingFactor;
void main()
@ -32,12 +32,17 @@ layout(location = 0) out vec4 color;
in vec4 v_Color;
in vec2 v_TexCoord;
in float v_TexIndex;
flat in int v_TexIndex;
in float v_TilingFactor;
uniform sampler2D u_Textures[32];
void main()
{
color = texture(u_Textures[int(v_TexIndex)], v_TexCoord * v_TilingFactor) * v_Color;
vec4 texColor = texture(u_Textures[v_TexIndex], v_TexCoord * v_TilingFactor) * v_Color;
if(texColor.a < 0.1)
discard;
color = texColor;
}

View File

@ -0,0 +1,44 @@
// Basic Texture Shader
#type vertex
#version 430 core
layout(location = 0) in vec3 a_WorldPosition;
layout(location = 1) in float a_Thickness;
layout(location = 2) in vec2 a_LocalPosition;
layout(location = 3) in vec4 a_Color;
uniform mat4 u_ViewProjection;
out vec2 v_LocalPosition;
out float v_Thickness;
out vec4 v_Color;
void main()
{
v_LocalPosition = a_LocalPosition;
v_Thickness = a_Thickness;
v_Color = a_Color;
gl_Position = u_ViewProjection * vec4(a_WorldPosition, 1.0);
}
#type fragment
#version 430 core
layout(location = 0) out vec4 color;
in vec2 v_LocalPosition;
in float v_Thickness;
in vec4 v_Color;
void main()
{
float fade = 0.01;
float dist = sqrt(dot(v_LocalPosition, v_LocalPosition));
if (dist > 1.0 || dist < 1.0 - v_Thickness - fade)
discard;
float alpha = 1.0 - smoothstep(1.0f - fade, 1.0f, dist);
alpha *= smoothstep(1.0 - v_Thickness - fade, 1.0 - v_Thickness, dist);
color = v_Color;
color.a = alpha;
}

View File

@ -20,38 +20,46 @@ layout(location = 0) out vec4 o_Color;
in vec2 v_TexCoord;
uniform sampler2DMS u_Texture;
uniform float u_Exposure;
uniform int u_TextureSamples;
uniform sampler2D u_HDRTexture; // 来自 LightingPass 的 HDR 颜色
uniform sampler2D u_BloomTexture; // 来自 BloomBlendPass 的 Bloom 纹理
vec4 MultiSampleTexture(sampler2DMS tex, ivec2 texCoord, int samples)
uniform bool u_EnableAutoExposure;
uniform float u_ManualExposure;
layout(std430, binding = 2) buffer Exposure
{
vec4 result = vec4(0.0);
for (int i = 0; i < samples; i++)
result += texelFetch(tex, texCoord, i);
float u_Exposure;
};
result /= float(samples);
return result;
}
uniform bool u_EnableBloom;
void main()
{
const float gamma = 2.2;
const float pureWhite = 1.0;
ivec2 texSize = textureSize(u_Texture);
ivec2 texCoord = ivec2(v_TexCoord * texSize);
vec4 msColor = MultiSampleTexture(u_Texture, texCoord, u_TextureSamples);
vec3 color = msColor.rgb * u_Exposure;//texture(u_Texture, v_TexCoord).rgb * u_Exposure;
void main()
{
// 采样 HDR 颜色(单样本)
vec3 color = texture(u_HDRTexture, v_TexCoord).rgb;
// Reinhard tonemapping operator.
// see: "Photographic Tone Reproduction for Digital Images", eq. 4
// 混合 Bloom如果启用
if (u_EnableBloom)
{
vec3 bloomColor = texture(u_BloomTexture, v_TexCoord).rgb;
color += bloomColor; // 在 HDR 空间混合
}
// 应用曝光
if (u_EnableAutoExposure)
color *= u_Exposure;
else
color *= u_ManualExposure;
// Reinhard 色调映射
float luminance = dot(color, vec3(0.2126, 0.7152, 0.0722));
float mappedLuminance = (luminance * (1.0 + luminance / (pureWhite * pureWhite))) / (1.0 + luminance);
// Scale color by ratio of average luminances.
// 按亮度比例缩放颜色
vec3 mappedColor = (mappedLuminance / luminance) * color;
// Gamma correction.
// Gamma 校正
o_Color = vec4(pow(mappedColor, vec3(1.0 / gamma)), 1.0);
}

View File

@ -0,0 +1,23 @@
// Shadow Map shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
uniform mat4 u_LightViewProjection;
uniform mat4 u_Transform;
void main()
{
gl_Position = u_LightViewProjection * u_Transform * vec4(a_Position, 1.0);
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
void main()
{
}

View File

@ -0,0 +1,35 @@
// Shadow Map shader
#type vertex
#version 430
layout(location = 0) in vec3 a_Position;
layout(location = 5) in ivec4 a_BoneIndices;
layout(location = 6) in vec4 a_BoneWeights;
uniform mat4 u_LightViewProjection;
uniform mat4 u_Transform;
const int MAX_BONES = 100;
uniform mat4 u_BoneTransforms[100];
void main()
{
mat4 boneTransform = u_BoneTransforms[a_BoneIndices[0]] * a_BoneWeights[0];
boneTransform += u_BoneTransforms[a_BoneIndices[1]] * a_BoneWeights[1];
boneTransform += u_BoneTransforms[a_BoneIndices[2]] * a_BoneWeights[2];
boneTransform += u_BoneTransforms[a_BoneIndices[3]] * a_BoneWeights[3];
vec4 localPosition = boneTransform * vec4(a_Position, 1.0);
gl_Position = u_LightViewProjection * u_Transform * localPosition;
}
#type fragment
#version 430
layout(location = 0) out vec4 o_Color;
void main()
{
}

View File

@ -24,10 +24,13 @@ layout(location = 0) out vec4 finalColor;
uniform samplerCube u_Texture;
uniform float u_TextureLod;
uniform float u_SkyIntensity;
in vec3 v_Position;
void main()
{
finalColor = textureLod(u_Texture, v_Position, u_TextureLod);
vec3 color = textureLod(u_Texture, v_Position, u_TextureLod).rgb * u_SkyIntensity;
finalColor = vec4(color, 1.0);
}

102
Editor/imgui.ini Normal file
View File

@ -0,0 +1,102 @@
[Window][DockSpace Demo]
Pos=0,0
Size=2560,1566
Collapsed=0
[Window][Debug##Default]
Pos=60,60
Size=400,400
Collapsed=0
[Window][Scene Hierarchy]
Pos=2089,24
Size=471,563
Collapsed=0
DockId=0x00000009,0
[Window][Properties]
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,0
[Window][Scene Renderer]
Pos=0,843
Size=481,723
Collapsed=0
DockId=0x00000006,0
[Window][Materials]
Pos=0,24
Size=481,817
Collapsed=0
DockId=0x00000005,0
[Window][Script Engine Debug]
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,2
[Window][Model]
Pos=1595,454
Size=471,744
Collapsed=0
DockId=0x0000000A,3
[Window][Toolbar]
Pos=483,24
Size=1110,32
Collapsed=0
DockId=0x00000001,0
[Window][Viewport]
Pos=483,58
Size=1604,955
Collapsed=0
DockId=0x0000000B,0
[Window][Environment]
Pos=2089,589
Size=471,977
Collapsed=0
DockId=0x0000000A,1
[Window][Project]
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,0
[Window][Objects]
Pos=483,1015
Size=1604,551
Collapsed=0
DockId=0x0000000C,1
[Window][Physics]
Pos=189,113
Size=468,371
Collapsed=0
[Window][##tool_bar]
Pos=483,24
Size=1604,32
Collapsed=0
DockId=0x00000001,0
[Docking][Data]
DockSpace ID=0xC0DFADC4 Window=0xD0388BC8 Pos=0,58 Size=2560,1542 Split=X Selected=0x0C01D6D5
DockNode ID=0x00000007 Parent=0xC0DFADC4 SizeRef=1557,1542 Split=X
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=481,1542 Split=Y Selected=0x5D711C2C
DockNode ID=0x00000005 Parent=0x00000003 SizeRef=481,817 Selected=0x5D711C2C
DockNode ID=0x00000006 Parent=0x00000003 SizeRef=481,723 Selected=0x68D924E0
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1074,1542 Split=Y
DockNode ID=0x00000001 Parent=0x00000004 SizeRef=2560,32 CentralNode=1 HiddenTabBar=1 Selected=0xE8CD5B84
DockNode ID=0x00000002 Parent=0x00000004 SizeRef=2560,1508 Split=Y Selected=0xC450F867
DockNode ID=0x0000000B Parent=0x00000002 SizeRef=1401,955 HiddenTabBar=1 Selected=0xC450F867
DockNode ID=0x0000000C Parent=0x00000002 SizeRef=1401,551 Selected=0x9C21DE82
DockNode ID=0x00000008 Parent=0xC0DFADC4 SizeRef=471,1542 Split=Y Selected=0x8C72BEA8
DockNode ID=0x00000009 Parent=0x00000008 SizeRef=315,563 Selected=0xB8729153
DockNode ID=0x0000000A Parent=0x00000008 SizeRef=315,977 Selected=0x73E3D51F

View File

@ -5,6 +5,7 @@ namespace Example
public class BasicController : Entity
{
public float Speed;
public float DistanceFromPlayer = 20.0F;
private Entity m_PlayerEntity;
@ -15,13 +16,17 @@ namespace Example
public void OnUpdate(float ts)
{
/*
Mat4 transform = GetTransform();
Vec3 playerTranstation = m_PlayerEntity.GetTransform().Translation;
Vec3 translation = transform.Translation;
translation.XY = m_PlayerEntity.GetTransform().Translation.XY;
translation.XY = playerTranstation.XY;
translation.Z = playerTranstation.Z + DistanceFromPlayer;
translation.Y = Math.Max(translation.Y, 4.5f);
transform.Translation = translation;
SetTransform(transform);
*/
}

166
ExampleApp/Src/FPSPlayer.cs Normal file
View File

@ -0,0 +1,166 @@
using Prism;
namespace FPSExample
{
public class FPSPlayer : Entity
{
public float WalkingSpeed = 10.0f;
public float RunSpeed = 20.0f;
public float JumpForce = 50.0f;
[NonSerialized]
public float MouseSensitivity = 10.0f;
public float CameraForwardOffset = 0.5f;
public float CameraYOffset = 0.5f;
private bool m_Colliding = false;
private float m_CurrentSpeed;
private RigidBodyComponent m_RigidBody;
private TransformComponent m_Transform;
private TransformComponent m_CameraTransform;
private Entity m_CameraEntity;
private Vec2 m_LastMousePosition;
private float m_CurrentYMovement = 0.0f;
private Vec2 m_MovementDirection = new Vec2(0.0f);
private bool m_ShouldJump = false;
void OnCreate()
{
m_Transform = GetComponent<TransformComponent>();
m_RigidBody = GetComponent<RigidBodyComponent>();
m_CurrentSpeed = WalkingSpeed;
AddCollisionBeginCallback((n) => { m_Colliding = true; });
AddCollisionEndCallback((n) => { m_Colliding = false; });
m_CameraEntity = FindEntityByTag("Camera");
m_CameraTransform = m_CameraEntity.GetComponent<TransformComponent>();
m_LastMousePosition = Input.GetMousePosition();
Input.SetCursorMode(Input.CursorMode.Locked);
}
void OnUpdate(float ts)
{
if (Input.IsKeyPressed(KeyCode.Escape) && Input.GetCursorMode() == Input.CursorMode.Locked)
Input.SetCursorMode(Input.CursorMode.Normal);
if(Input.IsMouseButtonPressed(Input.MouseButton.Left) && Input.GetCursorMode() == Input.CursorMode.Normal)
Input.SetCursorMode(Input.CursorMode.Locked);
m_CurrentSpeed = Input.IsKeyPressed(KeyCode.LeftControl) ? RunSpeed : WalkingSpeed;
UpdateRayCasting();
UpdateMovementInput();
UpdateRotation(ts);
UpdateCameraTransform();
}
private void UpdateRotation(float ts)
{
Vec2 currentMousePosition = Input.GetMousePosition();
Vec2 delta = m_LastMousePosition - currentMousePosition;
float m_CurrentYMovement = delta.X * MouseSensitivity * ts;
float xRotation = delta.Y * MouseSensitivity * ts;
m_RigidBody.Rotate(new Vec3(0.0f, m_CurrentYMovement, 0.0f));
if (delta.X != 0 || delta.Y != 0)
{
m_CameraTransform.Rotation += new Vec3(xRotation, m_CurrentYMovement, 0.0f);
}
m_CameraTransform.Rotation = new Vec3(Mathf.Clamp(m_CameraTransform.Rotation.X, -89.0f, 89.0f), m_CameraTransform.Rotation.YZ);
m_LastMousePosition = currentMousePosition;
}
private void UpdateMovementInput()
{
if (Input.IsKeyPressed(KeyCode.W))
m_MovementDirection.Y += 1.0f;
else if (Input.IsKeyPressed(KeyCode.S))
m_MovementDirection.Y -= 1.0f;
else
m_MovementDirection.Y = 0.0f;
if(Input.IsKeyPressed(KeyCode.A))
m_MovementDirection.X -= 1.0f;
else if (Input.IsKeyPressed(KeyCode.D))
m_MovementDirection.X += 1.0f;
else
m_MovementDirection.X = 0.0f;
m_ShouldJump = Input.IsKeyPressed(KeyCode.Space) && !m_ShouldJump;
}
Collider[] colliders = new Collider[10];
private void UpdateRayCasting()
{
RaycastHit hitInfo;
if (Input.IsKeyPressed(KeyCode.H) &&
Physics.Raycast(m_CameraTransform.Translation + (m_CameraTransform.Transform.Forward),
m_CameraTransform.Transform.Forward, 20.0f, out hitInfo))
{
FindEntityByID(hitInfo.EntityID).GetComponent<MeshComponent>().Mesh.GetMaterial(0).Set("u_AlbedoColor", new Vec3(1.0f ,0.0f, 0.0f));
}
if (Input.IsKeyPressed(KeyCode.I))
{
// NOTE: The NonAlloc version of Overlap functions should be used when possible since it doesn't allocate a new array
// whenever you call it. The normal versions allocates a brand new array every time.
int numColliders = Physics.OverlapBoxNonAlloc(m_Transform.Translation, new Vec3(1.0f), colliders);
Console.WriteLine("Colliders: {0}", numColliders);
// When using NonAlloc it's not safe to use a foreach loop since some of the colliders may not exist
for (int i = 0; i < numColliders; i++)
{
Console.WriteLine(colliders[i]);
}
}
}
void OnPhysicsUpdate(float fixedTimeStep)
{
UpdateMovement();
}
private void UpdateMovement()
{
m_RigidBody.Rotate(new Vec3(0.0f, m_CurrentYMovement, 0.0f));
Vec3 movement = m_CameraTransform.Transform.Right * m_MovementDirection.X + m_CameraTransform.Transform.Forward * m_MovementDirection.Y;
if (m_MovementDirection.LengthSquared() != 0.0f)
{
movement.Normalize();
Vec3 velocity = movement * m_CurrentSpeed;
velocity.Y = m_RigidBody.GetLinearVelocity().Y;
m_RigidBody.SetLinearVelocity(velocity);
}
if (m_ShouldJump && m_Colliding)
{
m_RigidBody.AddForce(Vec3.Up * JumpForce, RigidBodyComponent.ForceMode.Impulse);
m_ShouldJump = false;
}
}
private void UpdateCameraTransform(){
Vec3 position = m_Transform.Translation + m_CameraTransform.Transform.Forward * CameraForwardOffset;
position.Y += m_Transform.Translation.Y + CameraYOffset;
m_CameraTransform.Translation = position;
}
}
}

View File

@ -78,6 +78,7 @@ namespace Example
void OnUpdate(float ts)
{
/*
Mat4 transform = GetTransform();
Vec3 translation = transform.Translation;
translation.Y += ts * speed;
@ -91,6 +92,7 @@ namespace Example
transform.Translation = translation;
SetTransform(transform);
*/
}

View File

@ -72,9 +72,11 @@ namespace Example
if (Input.IsKeyPressed(KeyCode.R))
{
/*
Mat4 transform = GetTransform();
transform.Translation = new Vec3(0.0f);
SetTransform(transform);
*/
}
}

View File

@ -0,0 +1,104 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Prism;
namespace Example
{
class PlayerSphere : Entity
{
public float HorizontalForce = 10.0f;
public float JumpForce = 10.0f;
private RigidBodyComponent m_PhysicsBody;
private MaterialInstance m_MeshMaterial;
public int m_CollisionCounter = 0;
public Vec3 MaxSpeed = new Vec3();
private bool Colliding => m_CollisionCounter > 0;
private TransformComponent m_Transform;
void OnCreate()
{
m_PhysicsBody = GetComponent<RigidBodyComponent>();
m_Transform = GetComponent<TransformComponent>();
MeshComponent meshComponent = GetComponent<MeshComponent>();
m_MeshMaterial = meshComponent.Mesh.GetMaterial(0);
m_MeshMaterial.Set("u_Metalness", 0.0f);
AddCollisionBeginCallback(OnPlayerCollisionBegin);
AddCollisionEndCallback(OnPlayerCollisionEnd);
AddTriggerBeginCallback(OnPlayerTriggerBegin);
AddTriggerEndCallback(OnPlayerTriggerEnd);
}
void OnPlayerCollisionBegin(float value)
{
m_CollisionCounter++;
}
void OnPlayerCollisionEnd(float value)
{
m_CollisionCounter--;
}
void OnPlayerTriggerBegin(float value)
{
Console.WriteLine("Player trigger begin");
}
void OnPlayerTriggerEnd(float value)
{
Console.WriteLine("Player trigger end");
}
void OnUpdate(float ts)
{
float movementForce = HorizontalForce;
if (!Colliding)
{
movementForce *= 0.4f;
}
if (Input.IsKeyPressed(KeyCode.W))
m_PhysicsBody.AddForce(m_Transform.Transform.Forward * movementForce);
else if (Input.IsKeyPressed(KeyCode.S))
m_PhysicsBody.AddForce(m_Transform.Transform.Forward * -movementForce);
if (Input.IsKeyPressed(KeyCode.D))
m_PhysicsBody.AddForce(m_Transform.Transform.Right * movementForce);
else if (Input.IsKeyPressed(KeyCode.A))
m_PhysicsBody.AddForce(m_Transform.Transform.Right * -movementForce);
if (Colliding && Input.IsKeyPressed(KeyCode.Space))
m_PhysicsBody.AddForce(m_Transform.Transform.Up * JumpForce);
if (Colliding)
m_MeshMaterial.Set("u_AlbedoColor", new Vec3(1.0f, 0.0f, 0.0f));
else
m_MeshMaterial.Set("u_AlbedoColor", new Vec3(0.8f, 0.8f, 0.8f));
Vec3 linearVelocity = m_PhysicsBody.GetLinearVelocity();
linearVelocity.Clamp(new Vec3(-MaxSpeed.X, -1000, -MaxSpeed.Z), MaxSpeed);
m_PhysicsBody.SetLinearVelocity(linearVelocity);
if (Input.IsKeyPressed(KeyCode.R))
{
/*
Mat4 transform = GetTransform();
transform.Translation = new Vec3(0.0f);
SetTransform(transform);
*/
}
}
}
}

View File

@ -21,6 +21,7 @@ namespace Example
public void OnUpdate(float ts)
{
/*
Rotation += ts;
@ -34,19 +35,10 @@ namespace Example
translation.Z += Velocity.Z * ts;
translation.Y -= SinkRate * ts;
/*
if (Input.IsKeyPressed(KeyCode.Up))
translation.Y += speed;
else if (Input.IsKeyPressed(KeyCode.Down))
translation.Y -= speed;
if (Input.IsKeyPressed(KeyCode.Right))
translation.X += speed;
else if (Input.IsKeyPressed(KeyCode.Left))
translation.X -= speed;
*/
transform.Translation = translation;
SetTransform(transform);
*/
}
}

View File

@ -14,6 +14,7 @@ namespace Example
void OnUpdate(float ts)
{
/*
Mat4 transform = GetTransform();
Vec3 translation = transform.Translation;
@ -21,6 +22,7 @@ namespace Example
transform.Translation = translation;
SetTransform(transform);
*/
}
}

View File

@ -7,4 +7,14 @@
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<Content Include="bin\Debug\net9.0\Prism-ScriptCore.deps.json" />
<Content Include="bin\Debug\net9.0\Prism-ScriptCore.dll" />
<Content Include="bin\Debug\net9.0\Prism-ScriptCore.pdb" />
<Content Include="bin\Release\net9.0\Prism-ScriptCore.deps.json" />
<Content Include="bin\Release\net9.0\Prism-ScriptCore.dll" />
<Content Include="bin\Release\net9.0\Prism-ScriptCore.pdb" />
<Content Include="Prism-ScriptCore.sln" />
</ItemGroup>
</Project>

View File

@ -1,5 +1,8 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.1.11312.151 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prism-ScriptCore", "Prism-ScriptCore.csproj", "{B94EF710-0487-4388-97E3-B650761A849C}"
EndProject
Global
@ -13,4 +16,10 @@ Global
{B94EF710-0487-4388-97E3-B650761A849C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B94EF710-0487-4388-97E3-B650761A849C}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BA69725C-3CFE-4882-9EDF-2A8E92423E8F}
EndGlobalSection
EndGlobal

View File

@ -5,14 +5,54 @@ namespace Prism
{
public class Entity
{
private Action<float>? m_CollisionBeginCallbacks;
private Action<float>? m_CollisionEndCallbacks;
private Action<float>? m_Collision2DBeginCallbacks;
private Action<float>? m_Collision2DEndCallbacks;
private Action<float>? m_TriggerBeginCallbacks;
private Action<float>? m_TriggerEndCallbacks;
public ulong ID { get; private set; }
~Entity()
{
}
private List<Action<float>> m_Collision2DBeginCallbacks = new List<Action<float>>();
private List<Action<float>> m_Collision2DEndCallbacks = new List<Action<float>>();
public Vec3 Translation
{
get
{
return GetComponent<TransformComponent>().Translation;
}
set
{
GetComponent<TransformComponent>().Translation = value;
}
}
public Vec3 Rotation
{
get
{
return GetComponent<TransformComponent>().Rotation;
}
set
{
GetComponent<TransformComponent>().Rotation = value;
}
}
public Vec3 Scale
{
get
{
return GetComponent<TransformComponent>().Scale;
}
set
{
GetComponent<TransformComponent>().Scale = value;
}
}
protected Entity() { ID = 0; }
@ -51,50 +91,86 @@ namespace Prism
return new Entity(entityID);
}
public Mat4 GetTransform()
public Entity FindEntityByID(ulong entityID)
{
Mat4 mat4Instance;
GetTransform_Native(ID, out mat4Instance);
return mat4Instance;
}
public void SetTransform(Mat4 transform)
{
SetTransform_Native(ID, ref transform);
// TODO: to verify it
return new Entity(entityID);
}
public void AddCollision2DBeginCallback(Action<float> callback)
{
m_Collision2DBeginCallbacks.Add(callback);
m_Collision2DBeginCallbacks += callback;
}
public void AddCollision2DEndCallback(Action<float> callback)
{
m_Collision2DEndCallbacks.Add(callback);
m_Collision2DEndCallbacks += callback;
}
public void AddCollisionBeginCallback(Action<float> callback)
{
m_CollisionBeginCallbacks += callback;
}
public void AddCollisionEndCallback(Action<float> callback)
{
m_CollisionEndCallbacks += callback;
}
public void AddTriggerBeginCallback(Action<float> callback)
{
m_TriggerBeginCallbacks += callback;
}
public void AddTriggerEndCallback(Action<float> callback)
{
m_TriggerEndCallbacks += callback;
}
private void OnCollisionBegin(float data)
{
if (m_CollisionBeginCallbacks != null)
m_CollisionBeginCallbacks.Invoke(data);
}
private void OnCollisionEnd(float data)
{
if (m_CollisionEndCallbacks != null)
m_CollisionEndCallbacks.Invoke(data);
}
private void OnCollision2DBegin(float data)
{
foreach (var callback in m_Collision2DBeginCallbacks)
callback.Invoke(data);
if(m_Collision2DBeginCallbacks != null)
m_Collision2DBeginCallbacks.Invoke(data);
}
private void OnCollision2DEnd(float data)
{
foreach (var callback in m_Collision2DEndCallbacks)
callback.Invoke(data);
if(m_Collision2DEndCallbacks != null)
m_Collision2DEndCallbacks.Invoke(data);
}
private void OnTriggerBegin(float data)
{
if (m_TriggerBeginCallbacks != null)
m_TriggerBeginCallbacks.Invoke(data);
}
private void OnTriggerEnd(float data)
{
if (m_TriggerEndCallbacks != null)
m_TriggerEndCallbacks.Invoke(data);
}
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void CreateComponent_Native(ulong entityID, Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool HasComponent_Native(ulong entityID, Type type);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void GetTransform_Native(ulong entityID, out Mat4 matrix);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SetTransform_Native(ulong entityID, ref Mat4 matrix);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern ulong FindEntityByTag_Native(string tag);
}
}

View File

@ -4,14 +4,61 @@ namespace Prism
{
public class Input
{
public enum CursorMode
{
Normal = 0,
Hidden = 1,
Locked = 2,
}
public enum MouseButton
{
Button0 = 0,
Button1 = 1,
Button2 = 2,
Button3 = 3,
Button4 = 4,
Button5 = 5,
Left = Button0,
Right = Button1,
Middle = Button2
}
public static bool IsKeyPressed(KeyCode keycode)
{
return IsKeyPressed_Native(keycode);
}
public static bool IsMouseButtonPressed(MouseButton button)
{
return IsMouseButtonPressed_Native(button);
}
public static Vec2 GetMousePosition()
{
GetMousePosition_Native(out Vec2 position);
return position;
}
public static void SetCursorMode(CursorMode mode) => SetCursorMode_Native(mode);
public static CursorMode GetCursorMode() => GetCursorMode_Native();
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsKeyPressed_Native(KeyCode keycode);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern bool IsMouseButtonPressed_Native(MouseButton button);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void GetMousePosition_Native(out Vec2 position);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern void SetCursorMode_Native(CursorMode mode);
[MethodImpl(MethodImplOptions.InternalCall)]
private static extern CursorMode GetCursorMode_Native();
}
}

View File

@ -0,0 +1,16 @@
namespace Prism
{
public static class Mathf
{
public const float DegreeToRadians = (float)Math.PI * 2.0f / 360.0f;
public const float RadiansToDegrees = 360.0f / ((float)Math.PI * 2.0f);
public static float Clamp(float value, float min, float max)
{
if (value < min) return min;
if (value > max) return max;
return value;
}
}
}

View File

@ -0,0 +1,16 @@
using System.Runtime.InteropServices;
namespace Prism
{
[StructLayout(LayoutKind.Sequential)]
public struct Transform
{
public Vec3 Position;
public Vec3 Rotation;
public Vec3 Scale;
public Vec3 Up { get; }
public Vec3 Right { get; }
public Vec3 Forward { get; }
}
}

View File

@ -8,6 +8,8 @@ namespace Prism
public float X;
public float Y;
public static Vec2 Zero = new Vec2(0.0f, 0.0f);
public Vec2(float scalar)
{
X = Y = scalar;
@ -25,20 +27,68 @@ namespace Prism
}
public void Clamp(Vec2 min, Vec2 max) {
if (X < min.X)
X = min.X;
if (X > max.X)
X = max.X;
X = Mathf.Clamp(X, min.X, max.X);
Y = Mathf.Clamp(Y, min.Y, max.Y);
}
if (Y < min.Y)
Y = min.Y;
if (Y > max.Y)
Y = max.Y;
public float LengthSquared()
{
return X * X + Y * Y;
}
public float Length()
{
return (float)Math.Sqrt(X * X + Y * Y);
}
public Vec2 SafeNormalized()
{
float length = Length();
if (length > float.Epsilon)
{
return new Vec2(X / length, Y / length);
}
else
{
return Zero;
}
}
public Vec2 Normalized()
{
float length = Length();
return new Vec2(X / length, Y / length);
}
public void SafeNormalize()
{
float length = Length();
if (length > float.Epsilon)
{
X = X / length;
Y = Y / length;
}
}
public void Normalize()
{
float length = Length();
X = X / length;
Y = Y / length;
}
public static Vec2 operator -(Vec2 l, Vec2 r)
{
return new Vec2(l.X - r.X, l.Y - r.Y);
}
public static Vec2 operator -(Vec2 vector)
{
return new Vec2(-vector.X, -vector.Y);
}
public override string ToString()
{
return $"({X}, {Y})";
}
}
}

View File

@ -5,6 +5,12 @@ namespace Prism
[StructLayout(LayoutKind.Sequential)]
public struct Vec3
{
public static Vec3 Zero = new Vec3(0.0f, 0.0f, 0.0f);
public static Vec3 Forward = new Vec3(0.0f, 0.0f, -1.0f);
public static Vec3 Right = new Vec3(1.0f, 0.0f, 0.0f);
public static Vec3 Up = new Vec3(0.0f, 1.0f, 0.0f);
public float X;
public float Y;
public float Z;
@ -14,12 +20,25 @@ namespace Prism
X = Y = Z = scalar;
}
public Vec3(Vec2 vec) {
X = vec.X;
Y = vec.Y;
public Vec3(Vec2 vec2) {
X = vec2.X;
Y = vec2.Y;
Z = 0.0f;
}
public Vec3(Vec2 vec2, float z) {
X = vec2.X;
Y = vec2.Y;
Z = z;
}
public Vec3(float x, Vec2 vec2)
{
X = x;
Y = vec2.X;
Z = vec2.Y;
}
public Vec3(float x, float y, float z)
{
X = x;
@ -27,12 +46,55 @@ namespace Prism
Z = z;
}
public Vec3(Vec4 vec) {
X = vec.X;
Y = vec.Y;
Z = vec.Z;
}
public void Clamp(Vec3 min, Vec3 max)
{
X = Mathf.Clamp(X, min.X, max.X);
Y = Mathf.Clamp(Y, min.Y, max.Y);
Z = Mathf.Clamp(Z, min.Z, max.Z);
}
public float LengthSquared()
{
return X * X + Y * Y + Z * Z;
}
public float Length()
{
return (float)Math.Sqrt(X * X + Y * Y + Z * Z);
}
public Vec3 Normalized()
{
float length = Length();
if (length > float.Epsilon)
{
return new Vec3(X / length, Y / length, Z / length);
}
else
{
return Zero;
}
}
public void Normalize()
{
float length = Length();
if (length > float.Epsilon)
{
X = X / length;
Y = Y / length;
Z = Z / length;
}
}
public Vec2 XY {
get { return new Vec2(X, Y); }
set { X = value.X; Y = value.Y; }
@ -49,5 +111,61 @@ namespace Prism
set { Y = value.X; Z = value.Y; }
}
public static Vec3 operator *(Vec3 left, float scalar)
{
return new Vec3(left.X * scalar, left.Y * scalar, left.Z * scalar);
}
public static Vec3 operator *(float scalar, Vec3 right)
{
return new Vec3(scalar * right.X, scalar * right.Y, scalar * right.Z);
}
public static Vec3 operator +(Vec3 left, Vec3 right)
{
return new Vec3(left.X + right.X, left.Y + right.Y, left.Z + right.Z);
}
public static Vec3 operator +(Vec3 left, float right)
{
return new Vec3(left.X + right, left.Y + right, left.Z + right);
}
public static Vec3 operator -(Vec3 left, Vec3 right)
{
return new Vec3(left.X - right.X, left.Y - right.Y, left.Z - right.Z);
}
public static Vec3 operator /(Vec3 left, Vec3 right)
{
return new Vec3(left.X / right.X, left.Y / right.Y, left.Z / right.Z);
}
public static Vec3 operator /(Vec3 left, float scalar)
{
return new Vec3(left.X / scalar, left.Y / scalar, left.Z / scalar);
}
public static Vec3 operator-(Vec3 vector)
{
return new Vec3(-vector.X, -vector.Y, -vector.Z);
}
public static Vec3 Cos(Vec3 vector)
{
return new Vec3((float)Math.Cos(vector.X), (float)Math.Cos(vector.Y), (float)Math.Cos(vector.Z));
}
public static Vec3 Sin(Vec3 vector)
{
return new Vec3((float)Math.Sin(vector.X), (float)Math.Sin(vector.Y), (float)Math.Sin(vector.Z));
}
public override string ToString()
{
return $"({X}, {Y}, {Z})";
}
}
}

View File

@ -2,13 +2,13 @@ using System.Runtime.InteropServices;
namespace Prism
{
[StructLayout(LayoutKind.Explicit)]
[StructLayout(LayoutKind.Sequential)]
public struct Vec4
{
[FieldOffset(0)] public float X;
[FieldOffset(4)] public float Y;
[FieldOffset(8)] public float Z;
[FieldOffset(12)] public float W;
public float X;
public float Y;
public float Z;
public float W;
public Vec4(float scalar)
{

View File

@ -0,0 +1,100 @@
using System;
namespace Prism
{
public class Collider
{
public ulong EntityID { get; protected set; }
public bool IsTrigger { get; protected set; }
private Entity entity;
private RigidBodyComponent _rigidBodyComponent;
public Entity Entity
{
get
{
if (entity == null)
entity = new Entity(EntityID);
return entity;
}
}
public RigidBodyComponent RigidBody
{
get
{
if (_rigidBodyComponent == null)
_rigidBodyComponent = Entity.GetComponent<RigidBodyComponent>();
return _rigidBodyComponent;
}
}
public override string ToString()
{
string type = "Collider";
if (this is BoxCollider) type = "BoxCollider";
else if (this is SphereCollider) type = "SphereCollider";
else if (this is CapsuleCollider) type = "CapsuleCollider";
else if (this is MeshCollider) type = "MeshCollider";
return "Collider(" + type + ", " + EntityID + ", " + IsTrigger + ")";
}
}
public class BoxCollider : Collider
{
public Vec3 Size { get; protected set; }
public Vec3 Offset { get; protected set; }
internal BoxCollider(ulong entityID, bool isTrigger, Vec3 size, Vec3 offset)
{
EntityID = entityID;
Size = size;
Offset = offset;
IsTrigger = isTrigger;
}
}
public class SphereCollider : Collider
{
public float Radius { get; protected set; }
internal SphereCollider(ulong entityID, bool isTrigger, float radius)
{
EntityID = entityID;
Radius = radius;
IsTrigger = isTrigger;
}
}
public class CapsuleCollider : Collider
{
public float Radius { get; protected set; }
public float Height { get; protected set; }
internal CapsuleCollider(ulong entityID, bool isTrigger, float radius, float height)
{
EntityID = entityID;
Radius = radius;
Height = height;
IsTrigger = isTrigger;
}
}
public class MeshCollider : Collider
{
public Mesh Mesh { get; protected set; }
internal MeshCollider(ulong entityID, bool isTrigger, IntPtr filepath)
{
EntityID = entityID;
Mesh = new Mesh(filepath);
IsTrigger = isTrigger;
}
}
}

View File

@ -0,0 +1,74 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Prism
{
[StructLayout(LayoutKind.Sequential)]
public struct RaycastHit
{
public ulong EntityID { get; private set; }
public Vec3 Position { get; private set; }
public Vec3 Normal { get; private set; }
public float Distance { get; private set; }
}
public static class Physics
{
public static bool Raycast(Vec3 origin, Vec3 direction, float maxDistance, out RaycastHit hit)
{
return Raycast_Native(ref origin, ref direction, maxDistance, out hit);
}
public static Collider[] OverlapBox(Vec3 origin, Vec3 halfSize)
{
return OverlapBox_Native(ref origin, ref halfSize);
}
public static Collider[] OverlapCapsule(Vec3 origin, float radius, float halfHeight)
{
return OverlapCapsule_Native(ref origin, radius, halfHeight);
}
public static Collider[] OverlapSphere(Vec3 origin, float radius)
{
return OverlapSphere_Native(ref origin, radius);
}
public static int OverlapBoxNonAlloc(Vec3 origin, Vec3 halfSize, Collider[] colliders)
{
return OverlapBoxNonAlloc_Native(ref origin, ref halfSize, colliders);
}
public static int OverlapCapsuleNonAlloc(Vec3 origin, float radius, float halfHeight, Collider[] colliders)
{
return OverlapCapsuleNonAlloc_Native(ref origin, radius, halfHeight, colliders);
}
public static int OverlapSphereNonAlloc(Vec3 origin, float radius, Collider[] colliders)
{
return OverlapSphereNonAlloc_Native(ref origin, radius, colliders);
}
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern bool Raycast_Native(ref Vec3 origin, ref Vec3 direction, float maxDistance, out RaycastHit hit);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Collider[] OverlapBox_Native(ref Vec3 origin, ref Vec3 halfSize);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Collider[] OverlapCapsule_Native(ref Vec3 origin, float radius, float halfHeight);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Collider[] OverlapSphere_Native(ref Vec3 origin, float radius);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int OverlapBoxNonAlloc_Native(ref Vec3 origin, ref Vec3 halfSize, Collider[] colliders);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int OverlapCapsuleNonAlloc_Native(ref Vec3 origin, float radius, float halfHeight, Collider[] colliders);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern int OverlapSphereNonAlloc_Native(ref Vec3 origin, float radius, Collider[] colliders);
}
}

View File

@ -1,4 +1,5 @@
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
namespace Prism
{
@ -32,12 +33,11 @@ namespace Prism
public class TransformComponent : Component
{
public Mat4 Transform
public Transform Transform
{
get
{
Mat4 result;
GetTransform_Native(Entity.ID, out result);
GetTransform_Native(Entity.ID, out Transform result);
return result;
}
set
@ -46,12 +46,68 @@ namespace Prism
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void GetTransform_Native(ulong entityID, out Mat4 result);
public Vec3 Translation
{
get
{
GetTranslation_Native(Entity.ID, out Vec3 result);
return result;
}
set
{
SetTranslation_Native(Entity.ID, ref value);
}
}
public Vec3 Rotation
{
get
{
GetRotation_Native(Entity.ID, out Vec3 result);
return result;
}
set
{
SetRotation_Native(Entity.ID, ref value);
}
}
public Vec3 Scale
{
get
{
GetScale_Native(Entity.ID, out Vec3 result);
return result;
}
set
{
SetScale_Native(Entity.ID, ref value);
}
}
[MethodImpl(MethodImplOptions.InternalCall)]
public static extern void SetTransform_Native(ulong entityID, ref Mat4 result);
internal static extern void GetTransform_Native(ulong entityID, out Transform inTransform);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetTransform_Native(ulong entityID, ref Transform outTransform);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void GetTranslation_Native(ulong entityID, out Vec3 outTranslation);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetTranslation_Native(ulong entityID, ref Vec3 inTranslation);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void GetRotation_Native(ulong entityID, out Vec3 outRotation);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetRotation_Native(ulong entityID, ref Vec3 inRotation);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void GetScale_Native(ulong entityID, out Vec3 outScale);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetScale_Native(ulong entityID, ref Vec3 inScale);
}
public class MeshComponent : Component
@ -123,4 +179,121 @@ namespace Prism
public class BoxCollider2DComponent : Component
{
}
public class BoxColliderComponent : Component
{
}
public class SphereColliderComponent : Component
{
}
public class RigidBodyComponent : Component
{
public enum Type
{
Static,
Dynamic
}
public enum ForceMode
{
Force = 0,
Impulse,
VelocityChange,
Acceleration
}
public Type BodyType
{
get
{
return GetBodyType_Native(Entity.ID);
}
}
public float Mass
{
get { return GetMass_Native(Entity.ID); }
set { SetMass_Native(Entity.ID, value); }
}
public uint Layer
{
get { return GetLayer_Native(Entity.ID); }
}
public void AddForce(Vec3 force, ForceMode forceMode = ForceMode.Force)
{
AddForce_Native(Entity.ID, ref force, forceMode);
}
public void AddTorque(Vec3 torque, ForceMode forceMode = ForceMode.Force)
{
AddTorque_Native(Entity.ID, ref torque, forceMode);
}
public Vec3 GetLinearVelocity()
{
GetLinearVelocity_Native(Entity.ID, out Vec3 velocity);
return velocity;
}
public void SetLinearVelocity(Vec3 velocity)
{
SetLinearVelocity_Native(Entity.ID, ref velocity);
}
public Vec3 GetAngularVelocity()
{
GetAngularVelocity_Native(Entity.ID, out Vec3 velocity);
return velocity;
}
public void SetAngularVelocity(Vec3 velocity)
{
SetAngularVelocity_Native(Entity.ID, ref velocity);
}
public void Rotate(Vec3 rotation)
{
Rotate_Native(Entity.ID, ref rotation);
}
// TODO: Add SetMaxLinearVelocity() as well
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void AddForce_Native(ulong entityID, ref Vec3 force, ForceMode forceMode);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void AddTorque_Native(ulong entityID, ref Vec3 torque, ForceMode forceMode);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void GetLinearVelocity_Native(ulong entityID, out Vec3 velocity);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetLinearVelocity_Native(ulong entityID, ref Vec3 velocity);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void GetAngularVelocity_Native(ulong entityID, out Vec3 velocity);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void SetAngularVelocity_Native(ulong entityID, ref Vec3 velocity);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern void Rotate_Native(ulong entityID, ref Vec3 rotation);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern uint GetLayer_Native(ulong entityID);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern float GetMass_Native(ulong entityID);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern float SetMass_Native(ulong entityID, float mass);
[MethodImpl(MethodImplOptions.InternalCall)]
internal static extern Type GetBodyType_Native(ulong entityID);
}
}

View File

@ -18,6 +18,7 @@ add_subdirectory(vendor/mono EXCLUDE_FROM_ALL)
add_subdirectory(vendor/FastNoise EXCLUDE_FROM_ALL)
add_subdirectory(vendor/yaml-cpp EXCLUDE_FROM_ALL)
add_subdirectory(vendor/Box2D EXCLUDE_FROM_ALL)
add_subdirectory(vendor/efsw EXCLUDE_FROM_ALL)
# ------------- imgui -------------
@ -46,6 +47,42 @@ list(APPEND SRC_SOURCE ${IMGUIZMO_SOURCE})
# ----------- ImViewGuizmo -------------
set(IMVIEWGUIZMO_DIR vendor/ImViewGuizmo)
# ------------- NVIDIA PhysX -------------
# PhysX/physx/buildtools/presets/*.xml
# PX_GENERATE_STATIC_LIBRARIES=True
# NV_USE_STATIC_WINCRT=False
set(PHYSX_BUILD_TYPE "checked" CACHE STRING "The build type of PhysX")
set_property(CACHE PHYSX_BUILD_TYPE PROPERTY STRINGS debug checked profile release)
if(NOT CMAKE_BUILD_TYPE)
if(PHYSX_BUILD_TYPE STREQUAL "debug" OR PHYSX_BUILD_TYPE STREQUAL "checked")
set(CMAKE_BUILD_TYPE "Debug")
endif()
endif()
if(CMAKE_BUILD_TYPE STREQUAL "Release")
set(PHYSX_BUILD_TYPE "release")
elseif (CMAKE_BUILD_TYPE STREQUAL "Debug")
set(PHYSX_BUILD_TYPE "debug")
endif ()
include_directories(vendor/PhysX/physx/include)
set(PHYSX_LIB_DIR "vendor/PhysX/physx/bin/win.x86_64.vc143.md/${PHYSX_BUILD_TYPE}") # This is the path where PhysX libraries are installed
link_directories(${PHYSX_LIB_DIR})
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
message("Building snippet in debug with PhysX ${PHYSX_BUILD_TYPE} configuration")
add_compile_definitions(_DEBUG)
else()
message("Building snippet in release configuration with PhysX ${PHYSX_BUILD_TYPE} configuration")
add_compile_definitions(NDEBUG)
endif()
# ------------- link libraries -------------
set(LINK_LIBRARIES_PRIVATE
glfw
@ -55,6 +92,14 @@ set(LINK_LIBRARIES_PRIVATE
tinyFileDialogs
FastNoise
yaml-cpp
efsw
PhysXExtensions_static_64
PhysX_static_64
PhysXPvdSDK_static_64
PhysXCommon_static_64
PhysXFoundation_static_64
PhysXCooking_static_64
)
set(LINK_LIBRARIES_PUBLIC
@ -79,11 +124,13 @@ set(TARGET_INCLUDE_DIR
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/src>
${IMGUI_DIR}
${IMGUIZMO_DIR}
${IMVIEWGUIZMO_DIR}
)
# ------------- debug Defines -------------
set(DEBUG_DEFINITIONS
$<$<CONFIG:Debug>:PM_ENABLE_ASSERTS>
$<$<CONFIG:RelWithDebInfo>:PM_ENABLE_ASSERTS>
)
@ -101,6 +148,11 @@ target_compile_definitions(${STATIC_LIBRARY} PRIVATE
target_include_directories(${STATIC_LIBRARY} PUBLIC
${TARGET_INCLUDE_DIR}
)
target_link_directories(${STATIC_LIBRARY} INTERFACE
${PHYSX_LIB_DIR}
)
target_link_libraries(${STATIC_LIBRARY}
PRIVATE
${LINK_LIBRARIES_PRIVATE}

View File

@ -0,0 +1,79 @@
//
// Created by Atdunbg on 2026/3/9.
//
#include "AnimationClip.h"
#include "assimp/scene.h"
struct aiAnimation;
namespace Prism
{
AnimationClip::AnimationClip(const aiScene* scene)
{
const aiAnimation* anim = scene->mAnimations[0]; // 先只处理第一个动画
m_Duration = (float)anim->mDuration;
m_TicksPerSecond = (float)(anim->mTicksPerSecond != 0 ? anim->mTicksPerSecond : 25.0);
for (uint32_t i = 0; i < anim->mNumChannels; i++)
{
aiNodeAnim* nodeAnim = anim->mChannels[i];
BoneChannel channel;
channel.BoneName = nodeAnim->mNodeName.C_Str();
// 位置关键帧
for (uint32_t j = 0; j < nodeAnim->mNumPositionKeys; j++)
{
const auto& key = nodeAnim->mPositionKeys[j];
channel.Positions.emplace_back((float)key.mTime,
glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z));
}
// 旋转关键帧
for (uint32_t j = 0; j < nodeAnim->mNumRotationKeys; j++)
{
const auto& key = nodeAnim->mRotationKeys[j];
channel.Rotations.emplace_back((float)key.mTime,
glm::quat(key.mValue.w, key.mValue.x, key.mValue.y, key.mValue.z));
}
// 缩放关键帧
for (uint32_t j = 0; j < nodeAnim->mNumScalingKeys; j++)
{
const auto& key = nodeAnim->mScalingKeys[j];
channel.Scalings.emplace_back((float)key.mTime,
glm::vec3(key.mValue.x, key.mValue.y, key.mValue.z));
}
m_BoneChannels.push_back(channel);
}
}
glm::mat4 AnimationClip::SampleAtTime(const float timeTicks, const std::string& boneName, const glm::mat4& defaultTransform) const
{
for (const auto& channel : m_BoneChannels) {
if (channel.BoneName == boneName) {
const glm::vec3 pos = SampleChannel(channel.Positions, timeTicks, glm::vec3(0.0f));
const glm::quat rot = SampleChannel(channel.Rotations, timeTicks, glm::quat(1.0f, 0.0f, 0.0f, 0.0f));
const glm::vec3 scl = SampleChannel(channel.Scalings, timeTicks, glm::vec3(1.0f));
return glm::translate(glm::mat4(1.0f), pos) * glm::toMat4(rot) * glm::scale(glm::mat4(1.0f), scl);
}
}
return defaultTransform;
}
template <typename T>
T AnimationClip::SampleChannel(const std::vector<KeyFrame<T>>& keys, float time, const T& defaultValue) const
{
if (keys.empty()) return defaultValue;
if (time <= keys.front().Time) return keys.front().Value;
if (time >= keys.back().Time) return keys.back().Value;
// 找到当前时间所在的关键帧区间
for (size_t i = 0; i < keys.size() - 1; ++i) {
if (time >= keys[i].Time && time <= keys[i+1].Time) {
float t = (time - keys[i].Time) / (keys[i+1].Time - keys[i].Time);
return Interpolate(keys[i].Value, keys[i+1].Value, t);
}
}
return defaultValue;
}
}

View File

@ -0,0 +1,63 @@
//
// Created by Atdunbg on 2026/3/9.
//
#ifndef PRISM_ANIMATIONCLIP_H
#define PRISM_ANIMATIONCLIP_H
#include "Prism/Asset/Asset.h"
#include <glm/glm.hpp>
#define GLM_ENABLE_EXPERIMENTAL
#include <glm/gtx/quaternion.hpp>
struct aiScene;
namespace Prism
{
template<typename T>
struct KeyFrame
{
float Time = 0.0f;
T Value;
KeyFrame() = default;
KeyFrame(const float t, const T& v) : Time(t), Value(v) {}
};
struct BoneChannel {
std::string BoneName;
std::vector<KeyFrame<glm::vec3>> Positions;
std::vector<KeyFrame<glm::quat>> Rotations;
std::vector<KeyFrame<glm::vec3>> Scalings;
};
class PRISM_API AnimationClip : public Asset
{
public:
AnimationClip(const aiScene* scene);
glm::mat4 SampleAtTime(float timeTicks, const std::string& boneName, const glm::mat4& defaultTransform) const;
private:
// 辅助采样模板函数
template<typename T>
T SampleChannel(const std::vector<KeyFrame<T>>& keys, float time, const T& defaultValue) const;
glm::vec3 Interpolate(const glm::vec3& a, const glm::vec3& b, const float t) const {
return glm::mix(a, b, t);
}
glm::quat Interpolate(const glm::quat& a, const glm::quat& b, const float t) const {
return glm::slerp(a, b, t);
}
public:
float m_Duration = 0.0f;
float m_TicksPerSecond;
std::vector<BoneChannel> m_BoneChannels;
};
}
#endif //PRISM_ANIMATIONCLIP_H

View File

@ -0,0 +1,124 @@
//
// Created by Atdunbg on 2026/3/9.
//
#include "AnimatorController.h"
namespace Prism
{
AnimationController::~AnimationController()
{
}
void AnimationController::Update(const float deltaTime)
{
if (m_State != PLAY || !m_currentClip || !m_skeleton)
return;
const float rawTime = m_AnimationCurrentTime + deltaTime * m_currentClip->m_TicksPerSecond * m_TimeMultiplier;
const float duration = m_currentClip->m_Duration;
if (!m_Loop && rawTime >= duration)
{
m_AnimationCurrentTime = 0.0f;
m_State = STOP;
}
else
{
m_AnimationCurrentTime = fmod(rawTime, duration);
}
ComputeBoneTransforms(m_AnimationCurrentTime);
}
void AnimationController::SetSkeleton(const Ref<Skeleton>& skeleton)
{
m_skeleton = skeleton;
if (m_skeleton)
{
ComputeBindPoseTransforms();
}
}
void AnimationController::ComputeBoneTransforms(float time)
{
const auto boneCount = (uint32_t)m_skeleton->m_Bones.size();
m_FinalBoneTransforms.resize(boneCount);
std::vector<glm::mat4> localTransforms(boneCount, glm::mat4(1.0f));
for (uint32_t i = 0; i < boneCount; i++)
{
const BoneInfo& bone = m_skeleton->m_Bones[i];
if (m_currentClip)
{
localTransforms[i] = m_currentClip->SampleAtTime(time, bone.Name, bone.LocalBindPose);
}
else
{
localTransforms[i] = bone.LocalBindPose;
}
}
// 2. 递归计算全局变换(从根骨骼开始)
std::function<void(uint32_t, const glm::mat4&)> computeGlobal = [&](const uint32_t boneIdx, const glm::mat4& parentGlobal)
{
const glm::mat4 global = parentGlobal * localTransforms[boneIdx];
glm::mat4 final = global * m_skeleton->m_Bones[boneIdx].InverseTransform;
final = m_GlobalInverseTransform * final;
m_FinalBoneTransforms[boneIdx] = final;
for (uint32_t childIdx = 0; childIdx < boneCount; childIdx++)
{
if (m_skeleton->m_Bones[childIdx].ParentIndex == (int)boneIdx)
{
computeGlobal(childIdx, global);
}
}
};
for (uint32_t i = 0; i < boneCount; i++)
{
if (m_skeleton->m_Bones[i].ParentIndex == -1)
{
computeGlobal(i, glm::mat4(1.0f));
}
}
}
void AnimationController::ComputeBindPoseTransforms()
{
if (!m_skeleton) return;
uint32_t boneCount = (uint32_t)m_skeleton->m_Bones.size();
m_FinalBoneTransforms.resize(boneCount);
// 1. 收集每个骨骼的绑定姿势局部变换
std::vector<glm::mat4> localTransforms(boneCount);
for (uint32_t i = 0; i < boneCount; i++)
localTransforms[i] = m_skeleton->m_Bones[i].LocalBindPose;
// 2. 递归计算全局变换,并应用逆绑定矩阵和全局逆矩阵
std::function<void(uint32_t, const glm::mat4&)> computeGlobal =
[&](uint32_t idx, const glm::mat4& parentGlobal)
{
glm::mat4 global = parentGlobal * localTransforms[idx];
glm::mat4 final = m_GlobalInverseTransform * global * m_skeleton->m_Bones[idx].InverseTransform;
m_FinalBoneTransforms[idx] = final;
// 处理子骨骼
for (uint32_t child = 0; child < boneCount; child++)
{
if (m_skeleton->m_Bones[child].ParentIndex == (int)idx)
computeGlobal(child, global);
}
};
// 从所有根骨骼开始递归
for (uint32_t i = 0; i < boneCount; i++)
{
if (m_skeleton->m_Bones[i].ParentIndex == -1)
computeGlobal(i, glm::mat4(1.0f));
}
}
}

View File

@ -0,0 +1,60 @@
//
// Created by Atdunbg on 2026/3/9.
//
#ifndef PRISM_ANIMATORCONTROLLER_H
#define PRISM_ANIMATORCONTROLLER_H
#include "AnimationClip.h"
#include "Skeleton.h"
#include "Prism/Asset/Asset.h"
namespace Prism
{
class PRISM_API AnimationController : public Asset
{
public:
enum AnimationState : uint8_t
{
STOP = 0,
PLAY,
PAUSE
};
public:
~AnimationController();
void Update(const float deltaTime);
const std::vector<glm::mat4>& GetFinalBoneTransforms() const { return m_FinalBoneTransforms; }
void SetSkeleton(const Ref<Skeleton>& skeleton);
void SetAnimationClip(const Ref<AnimationClip>& clip) { m_currentClip = clip; }
void SetGlobalInverseTransform(const glm::mat4& inv) { m_GlobalInverseTransform = inv; }
void Play() { m_State = PLAY; }
void Pause() { m_State = PAUSE; }
void Stop() { if (m_State == STOP) return; m_State = STOP; m_AnimationCurrentTime = 0.0f; ComputeBindPoseTransforms(); }
AnimationState GetAnimationState() const { return m_State; }
float& GetCurrentTime() { return m_AnimationCurrentTime; }
float& GetMultiplierTime() { return m_TimeMultiplier; }
bool& GetLoop() { return m_Loop; }
private:
void ComputeBoneTransforms(float time);
void ComputeBindPoseTransforms();
Ref<Skeleton> m_skeleton = nullptr;
Ref<AnimationClip> m_currentClip = nullptr;
AnimationState m_State = STOP;
float m_AnimationCurrentTime = 0.0f;
float m_TimeMultiplier = 1.0f;
bool m_Loop = false;
std::vector<glm::mat4> m_FinalBoneTransforms;
glm::mat4 m_GlobalInverseTransform = glm::mat4(1.0f);
};
}
#endif //PRISM_ANIMATORCONTROLLER_H

View File

@ -0,0 +1,68 @@
//
// Created by Atdunbg on 2026/3/9.
//
#include "Skeleton.h"
#include "assimp/scene.h"
namespace Prism
{
// TODO: this maybe move Utils
extern glm::mat4 Mat4FromAssimpMat4(const aiMatrix4x4& matrix);
Skeleton::Skeleton(const aiScene* scene)
{
// 收集所有骨骼(通过所有 Mesh 中的 aiBone
for (uint32_t am = 0; am < scene->mNumMeshes; am++)
{
const aiMesh* aMesh = scene->mMeshes[am];
for (uint32_t i = 0; i < aMesh->mNumBones; i++)
{
const aiBone* bone = aMesh->mBones[i];
if (std::string boneName = bone->mName.C_Str(); m_NameToIndex.find(boneName) == m_NameToIndex.end())
{
const auto boneIndex = (uint32_t)m_Bones.size();
BoneInfo bi;
bi.Name = boneName;
bi.InverseTransform = Mat4FromAssimpMat4(bone->mOffsetMatrix);
bi.ParentIndex = -1; // 稍后设置
m_Bones.push_back(bi);
m_NameToIndex[boneName] = boneIndex;
}
}
}
// 设置骨骼的父子关系及默认局部变换(遍历节点树)
std::unordered_map<std::string, uint32_t>& nameToIdx = m_NameToIndex;
std::function<void(aiNode*, int)> traverseNodes = [&](const aiNode* node, const int parentBoneIdx)
{
const std::string nodeName = node->mName.C_Str();
const auto it = nameToIdx.find(nodeName);
int currentBoneIdx = -1;
if (it != nameToIdx.end())
{
currentBoneIdx = (int)it->second;
BoneInfo& bone = m_Bones[currentBoneIdx];
bone.ParentIndex = parentBoneIdx;
// 存储默认局部变换(绑定姿势下相对于父节点的变换)
bone.LocalBindPose = Mat4FromAssimpMat4(node->mTransformation);
}
for (uint32_t i = 0; i < node->mNumChildren; i++)
{
traverseNodes(node->mChildren[i], currentBoneIdx);
}
};
traverseNodes(scene->mRootNode, -1);
}
uint32_t Skeleton::GetBoneIndex(const std::string& name) const
{
const auto it = m_NameToIndex.find(name);
return (it != m_NameToIndex.end()) ? it->second : static_cast<uint32_t>(-1);
}
}

View File

@ -0,0 +1,35 @@
//
// Created by Atdunbg on 2026/3/9.
//
#ifndef PRISM_SKELETON_H
#define PRISM_SKELETON_H
#include "glm/glm.hpp"
#include "Prism/Asset/Asset.h"
struct aiScene;
namespace Prism
{
struct BoneInfo
{
std::string Name;
int ParentIndex; // -1 is Root
glm::mat4 LocalBindPose;
glm::mat4 InverseTransform; // aiScene::mOffsetMatrix
};
class PRISM_API Skeleton : public Asset
{
public:
Skeleton(const aiScene* scene);
uint32_t GetBoneIndex(const std::string& name) const;
public:
std::vector<BoneInfo> m_Bones;
std::unordered_map<std::string, uint32_t> m_NameToIndex;
};
}
#endif //PRISM_SKELETON_H

View File

@ -0,0 +1,68 @@
//
// Created by Atdunbg on 2026/2/3.
//
#ifndef PRISM_ASSET_H
#define PRISM_ASSET_H
#include "Prism/Core/UUID.h"
#include "Prism/Core/Ref.h"
namespace Prism
{
enum class AssetType
{
Scene = 0, Mesh, Texture, EnvMap, Audio, Script, PhysicsMaterial, Directory, Other, None
};
using AssetHandle = UUID;
class PRISM_API Asset : public RefCounted
{
public:
AssetHandle Handle;
AssetType Type = AssetType::None;
std::string FilePath;
std::string FileName;
std::string Extension;
AssetHandle ParentDirectory;
bool IsDataLoaded = false;
virtual ~Asset()
{
}
};
class PRISM_API PhysicsMaterial : public Asset
{
public:
float StaticFriction;
float DynamicFriction;
float Bounciness;
PhysicsMaterial() = default;
PhysicsMaterial(const float staticFriction, const float dynamicFriction, const float bounciness)
: StaticFriction(staticFriction), DynamicFriction(dynamicFriction), Bounciness(bounciness)
{
Type = AssetType::PhysicsMaterial;
}
};
// Treating directories as assets simplifies the asset manager window rendering by a lot
class Directory : public Asset
{
public:
std::vector<AssetHandle> ChildDirectories;
Directory()
{
Type = AssetType::Directory;
}
};
}
#endif //PRISM_ASSET_H

View File

@ -0,0 +1,211 @@
//
// Created by Atdunbg on 2026/2/13.
//
#include "AssetSerializer.h"
#include "AssetsManager.h"
#include "Prism/Utilities/StringUtils.h"
#include "Prism/Utilities/FileSystem.h"
#include "Prism/Renderer/Mesh.h"
#include "Prism/Renderer/SceneEnvironment.h"
#include "Prism/Renderer/SceneRenderer.h"
#include "yaml-cpp/yaml.h"
namespace Prism
{
void AssetSerializer::SerializeAsset(const Ref<Asset>& asset, AssetType type)
{
YAML::Emitter out;
switch (type)
{
case AssetType::Texture:
case AssetType::EnvMap:
case AssetType::Audio:
case AssetType::Script:
case AssetType::Directory:
case AssetType::Other:
case AssetType::None:
return;
}
out << YAML::BeginMap;
switch (type)
{
case Prism::AssetType::PhysicsMaterial:
{
Ref<PhysicsMaterial> material = Ref<PhysicsMaterial>(asset);
out << YAML::Key << "StaticFriction" << material->StaticFriction;
out << YAML::Key << "DynamicFriction" << material->DynamicFriction;
out << YAML::Key << "Bounciness" << material->Bounciness;
break;
}
}
out << YAML::EndMap;
std::ofstream fout(asset->FilePath);
fout << out.c_str();
}
Ref<Asset> AssetSerializer::DeserializeYAML(const Ref<Asset>& asset)
{
const std::ifstream stream(asset->FilePath);
std::stringstream strStream;
strStream << stream.rdbuf();
YAML::Node data = YAML::Load(strStream.str());
if (asset->Type == AssetType::PhysicsMaterial)
{
float staticFriction = data["StaticFriction"].as<float>();
float dynamicFriction = data["DynamicFriction"].as<float>();
float bounciness = data["Bounciness"].as<float>();
return Ref<PhysicsMaterial>::Create(staticFriction, dynamicFriction, bounciness);
}
return nullptr;
}
Ref<Asset> AssetSerializer::LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type)
{
Ref<Asset> asset;
if (type == AssetType::Directory)
asset = Ref<Directory>::Create();
else
asset = Ref<Asset>::Create();
const std::string extension = Utils::GetExtension(filepath);
asset->FilePath = filepath;
asset->Type = type;
std::replace(asset->FilePath.begin(), asset->FilePath.end(), '\\', '/');
const bool hasMeta = FileSystem::Exists(asset->FilePath + ".meta");
if (hasMeta)
{
LoadMetaData(asset);
}
else
{
asset->Handle = AssetHandle();
}
// TODO: file or directory Type to fix
asset->Extension = extension;
asset->FileName = Utils::RemoveExtension(Utils::GetFilename(filepath));
asset->ParentDirectory = parentHandle;
asset->IsDataLoaded = false;
if (!hasMeta)
CreateMetaFile(asset);
return asset;
}
Ref<Asset> AssetSerializer::LoadAssetData(Ref<Asset>& asset)
{
if (asset->Type == AssetType::Directory)
return asset;
Ref<Asset> temp = asset;
bool loadYAMLData = true;
switch (asset->Type)
{
case AssetType::Mesh:
{
if (asset->Extension != "blend")
asset = Ref<Mesh>::Create(asset->FilePath);
loadYAMLData = false;
break;
}
case AssetType::Texture:
{
asset = Texture2D::Create(asset->FilePath);
loadYAMLData = false;
break;
}
case AssetType::EnvMap:
{
auto [radiance, irradiance] = SceneRenderer::CreateEnvironmentMap(asset->FilePath);
asset = Ref<Environment>::Create(radiance, irradiance);
loadYAMLData = false;
break;
}
case AssetType::Scene:
case AssetType::Audio:
case AssetType::Script:
case AssetType::Other:
{
loadYAMLData = false;
break;
}
case AssetType::PhysicsMaterial:
break;
}
if (loadYAMLData)
{
asset = DeserializeYAML(asset);
PM_CORE_ASSERT(asset, "Failed to load asset");
}
asset->Handle = temp->Handle;
asset->FilePath = temp->FilePath;
asset->FileName = temp->FileName;
asset->Extension = temp->Extension;
asset->ParentDirectory = temp->ParentDirectory;
asset->Type = temp->Type;
asset->IsDataLoaded = true;
return asset;
}
void AssetSerializer::LoadMetaData(Ref<Asset>& asset)
{
std::ifstream stream(asset->FilePath + ".meta");
std::stringstream strStream;
strStream << stream.rdbuf();
YAML::Node data = YAML::Load(strStream.str());
if (!data["Asset"])
{
PM_CORE_ASSERT("Invalid File Format");
}
asset->Handle = data["Asset"].as<uint64_t>();
asset->FilePath = data["FilePath"].as<std::string>();
asset->Type = (AssetType)data["Type"].as<int>();
if (asset->FileName == "assets" && asset->Handle == 0)
{
asset->Handle = AssetHandle();
CreateMetaFile(asset);
}
}
void AssetSerializer::CreateMetaFile(const Ref<Asset>& asset)
{
YAML::Emitter out;
out << YAML::BeginMap;
out << YAML::Key << "Asset" << YAML::Value << asset->Handle;
out << YAML::Key << "FilePath" << YAML::Value << asset->FilePath;
out << YAML::Key << "Type" << YAML::Value << (int)asset->Type;
out << YAML::EndMap;
std::ofstream fout(asset->FilePath + ".meta");
fout << out.c_str();
}
}

View File

@ -0,0 +1,38 @@
//
// Created by Atdunbg on 2026/2/13.
//
#ifndef PRISM_ASSETSERIALIZER_H
#define PRISM_ASSETSERIALIZER_H
#include "Asset.h"
#include "Prism/Core/Ref.h"
namespace Prism
{
class PRISM_API AssetSerializer
{
public:
template<typename T>
static void SerializeAsset(const Ref<T>& asset)
{
static_assert(std::is_base_of<Asset, T>::value, "SerializeAsset only accepts types that inherit from Asset");
SerializeAsset(asset, asset->Type);
}
static Ref<Asset> LoadAssetInfo(const std::string& filepath, AssetHandle parentHandle, AssetType type);
static Ref<Asset> LoadAssetData(Ref<Asset>& asset);
private:
static void SerializeAsset(const Ref<Asset>& asset, AssetType type);
static Ref<Asset> DeserializeYAML(const Ref<Asset>& asset);
static void LoadMetaData(Ref<Asset>& asset);
static void CreateMetaFile(const Ref<Asset>& asset);
private:
friend class AssetsManager;
};
}
#endif //PRISM_ASSETSERIALIZER_H

View File

@ -0,0 +1,470 @@
//
// Created by Atdunbg on 2026/1/20.
//
#include "AssetsManager.h"
#include <filesystem>
#include <utility>
#include "Prism/Core/Log.h"
#include "Prism/Renderer/Mesh.h"
#include <yaml-cpp/yaml.h>
#include "AssetSerializer.h"
#include "Prism/Renderer/SceneEnvironment.h"
#include "Prism/Utilities/StringUtils.h"
namespace Prism
{
void AssetTypes::Init()
{
s_Types["scene"] = AssetType::Scene;
s_Types["pmx"] = AssetType::Mesh;
s_Types["fbx"] = AssetType::Mesh;
s_Types["dae"] = AssetType::Mesh;
s_Types["obj"] = AssetType::Mesh;
s_Types["png"] = AssetType::Texture;
s_Types["tga"] = AssetType::Texture;
s_Types["hdr"] = AssetType::EnvMap;
s_Types["blend"] = AssetType::Mesh;
s_Types["hpm"] = AssetType::PhysicsMaterial;
s_Types["wav"] = AssetType::Audio;
s_Types["ogg"] = AssetType::Audio;
s_Types["cs"] = AssetType::Script;
}
AssetType AssetTypes::GetAssetTypeFromExtension(const std::string& extension)
{
return s_Types.find(extension) != s_Types.end() ? s_Types[extension] : AssetType::Other;
}
std::map<std::string, AssetType> AssetTypes::s_Types;
AssetsManager::AssetsChangeEventFn AssetsManager::s_AssetsChangeCallback = nullptr;
std::unordered_map<AssetHandle, Ref<Asset>> AssetsManager::s_LoadedAssets;
void AssetsManager::Init()
{
FileSystem::SetChangeCallback(OnFileSystemChanged);
ReloadAssets();
}
void AssetsManager::SetAssetChangeCallback(const AssetsChangeEventFn& callback)
{
s_AssetsChangeCallback = callback;
}
void AssetsManager::Shutdown()
{
s_LoadedAssets.clear();
}
std::vector<Ref<Asset>> AssetsManager::GetAssetsInDirectory(AssetHandle directoryHandle)
{
std::vector<Ref<Asset>> results;
for (const auto& asset : s_LoadedAssets)
{
if (asset.second && asset.second->ParentDirectory == directoryHandle && asset.second->Handle != directoryHandle)
results.push_back(asset.second);
}
return results;
}
std::vector<Ref<Asset>> AssetsManager::SearchFiles(const std::string& query, const std::string& searchPath)
{
std::vector<Ref<Asset>> results;
if (!searchPath.empty())
{
for (const auto&[key, asset] : s_LoadedAssets)
{
if (asset->FileName.find(query) != std::string::npos && asset->FilePath.find(searchPath) != std::string::npos)
{
results.push_back(asset);
}
}
}
return results;
}
std::string AssetsManager::GetParentPath(const std::string& path)
{
return std::filesystem::path(path).parent_path().string();
}
bool AssetsManager::IsDirectory(const std::string& filepath)
{
for (auto&[handle, asset] : s_LoadedAssets)
{
if (asset->Type == AssetType::Directory && asset->FilePath == filepath)
return true;
}
return false;
}
AssetHandle AssetsManager::GetAssetHandleFromFilePath(const std::string& filepath)
{
const std::string normalizedPath = Utils::NormalizePath(filepath);
for (auto&[id, asset] : s_LoadedAssets)
{
if (asset->FilePath == normalizedPath)
return id;
const std::string normalizedPathToLower = Utils::StringToLower(normalizedPath);
const std::string assetFilePath = Utils::StringToLower(asset->FilePath);
if (assetFilePath == normalizedPathToLower)
return id;
}
return 0;
}
bool AssetsManager::IsAssetHandleValid(const AssetHandle& assetHandle)
{
return assetHandle != 0 && s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end();
}
void AssetsManager::Rename(Ref<Asset>& asset, const std::string& newName)
{
const std::string newFilePath = FileSystem::Rename(asset->FilePath, newName);
const std::string oldFilePath = asset->FilePath;
asset->FilePath = newFilePath;
asset->FileName = newName;
if (FileSystem::Exists(oldFilePath + ".meta"))
{
std::string metaFileName = oldFilePath;
if (!asset->Extension.empty())
metaFileName += "." + asset->Extension;
FileSystem::PrismDeleteFile(oldFilePath + ".meta");
AssetSerializer::CreateMetaFile(asset);
}
}
template <typename T>
Ref<T> AssetsManager::GetAsset(AssetHandle assetHandle, bool loadData)
{
PM_CORE_ASSERT(s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end());
Ref<Asset> asset = s_LoadedAssets[assetHandle];
if (!asset->IsDataLoaded && loadData)
{
asset = AssetSerializer::LoadAssetData(asset);
s_LoadedAssets[assetHandle] = asset;
}
return asset.As<T>();
}
template PRISM_API Ref<Asset> AssetsManager::GetAsset(AssetHandle, bool);
template PRISM_API Ref<Mesh> AssetsManager::GetAsset(AssetHandle, bool);
template PRISM_API Ref<PhysicsMaterial> AssetsManager::GetAsset(AssetHandle, bool);
template PRISM_API Ref<Environment> AssetsManager::GetAsset(AssetHandle, bool);
template PRISM_API Ref<Directory> AssetsManager::GetAsset(AssetHandle, bool);
template PRISM_API Ref<Texture2D> AssetsManager::GetAsset(AssetHandle, bool);
template <typename T>
Ref<T> AssetsManager::GetAsset(const std::string& filepath, const bool loadData)
{
return GetAsset<T>(GetAssetHandleFromFilePath(filepath), loadData);
}
template PRISM_API Ref<Asset> AssetsManager::GetAsset(const std::string&, bool);
template PRISM_API Ref<Mesh> AssetsManager::GetAsset(const std::string&, bool);
template PRISM_API Ref<PhysicsMaterial> AssetsManager::GetAsset(const std::string&, bool);
template PRISM_API Ref<Environment> AssetsManager::GetAsset(const std::string&, bool);
template PRISM_API Ref<Directory> AssetsManager::GetAsset(const std::string&, bool);
template PRISM_API Ref<Texture2D> AssetsManager::GetAsset(const std::string&, bool);
// temp
Ref<PhysicsMaterial> AssetsManager::CreateAssetPhysicsMaterial(const std::string& filename, const AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3)
{
const auto& directory = GetAsset<Directory>(directoryHandle);
Ref<PhysicsMaterial> asset = Ref<PhysicsMaterial>::Create(v1, v2, v3);
asset->Type = type;
asset->FilePath = directory->FilePath + "/" + filename;
asset->FileName = Utils::RemoveExtension(Utils::GetFilename(asset->FilePath));
asset->Extension = Utils::GetFilename(filename);
asset->ParentDirectory = directoryHandle;
asset->Handle = std::hash<std::string>()(asset->FilePath);
asset->IsDataLoaded = true;
s_LoadedAssets[asset->Handle] = asset;
AssetSerializer::SerializeAsset(asset);
AssetSerializer::CreateMetaFile(asset);
return asset;
}
void AssetsManager::RemoveAsset(AssetHandle assetHandle)
{
Ref<Asset> asset = s_LoadedAssets[assetHandle];
if (asset->Type == AssetType::Directory)
{
if (IsAssetHandleValid(asset->ParentDirectory))
{
auto& childList = s_LoadedAssets[asset->ParentDirectory].As<Directory>()->ChildDirectories;
childList.erase(std::remove(childList.begin(), childList.end(), assetHandle), childList.end());
}
for (const auto child : asset.As<Directory>()->ChildDirectories)
RemoveAsset(child);
for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); )
{
if (it->second->ParentDirectory != assetHandle)
{
++it;
continue;
}
it = s_LoadedAssets.erase(it);
}
}
s_LoadedAssets.erase(assetHandle);
}
template<typename T, typename... Args>
Ref<T> AssetsManager::CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args)
{
static_assert(std::is_base_of_v<Asset, T>, "CreateAsset only works for types derived from Asset");
const auto& directory = GetAsset<Directory>(directoryHandle);
Ref<T> asset = Ref<T>::Create(std::forward<Args>(args)...);
asset->Type = type;
asset->FilePath = directory->FilePath + "/" + filename;
asset->FileName = Utils::RemoveExtension(Utils::GetFilename(asset->FilePath));
asset->Extension = Utils::GetFilename(filename);
asset->ParentDirectory = directoryHandle;
asset->Handle = std::hash<std::string>()(asset->FilePath);
asset->IsDataLoaded = true;
s_LoadedAssets[asset->Handle] = asset;
AssetSerializer::SerializeAsset(asset);
AssetSerializer::CreateMetaFile(asset);
return asset;
}
bool AssetsManager::IsAssetType(const AssetHandle assetHandle, const AssetType type)
{
return s_LoadedAssets.find(assetHandle) != s_LoadedAssets.end() && s_LoadedAssets[assetHandle]->Type == type;
}
std::string AssetsManager::StripExtras(const std::string& filename)
{
std::vector<std::string> out;
size_t start;
size_t end = 0;
while ((start = filename.find_first_not_of('.', end)) != std::string::npos)
{
end = filename.find('.', start);
out.push_back(filename.substr(start, end - start));
}
if (out[0].length() >= 10)
{
auto cutFilename = out[0].substr(0, 9) + "...";
return cutFilename;
}
const auto filenameLength = out[0].length();
const auto paddingToAdd = 9 - filenameLength;
std::string newFileName;
for (int i = 0; i <= paddingToAdd; i++)
{
newFileName += " ";
}
newFileName += out[0];
return newFileName;
}
Ref<Asset> AssetsManager::ImportAsset(const std::string& filepath, AssetHandle parentHandle)
{
const std::string extension = Utils::GetExtension(filepath);
if (extension == "meta")
return Ref<Asset>();
const AssetType type = AssetTypes::GetAssetTypeFromExtension(extension);
Ref<Asset> asset = AssetSerializer::LoadAssetInfo(filepath, parentHandle, type);
if (s_LoadedAssets.find(asset->Handle) != s_LoadedAssets.end())
{
if (s_LoadedAssets[asset->Handle]->IsDataLoaded)
{
asset = AssetSerializer::LoadAssetData(asset);
}
}
s_LoadedAssets[asset->Handle] = asset;
return asset;
}
Ref<Asset> AssetsManager::ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle)
{
Ref<Directory> dirInfo = AssetSerializer::LoadAssetInfo(directoryPath, parentHandle, AssetType::Directory).As<Directory>();
s_LoadedAssets[dirInfo->Handle] = dirInfo;
if (IsAssetHandleValid(parentHandle))
s_LoadedAssets[parentHandle].As<Directory>()->ChildDirectories.push_back(dirInfo->Handle);
for (const auto& entry : std::filesystem::directory_iterator(directoryPath))
{
if (entry.is_directory())
ProcessDirectory(entry.path().string(), dirInfo->Handle);
else
{
Ref<Asset> asset = ImportAsset(entry.path().string(), dirInfo->Handle);
}
}
return dirInfo;
}
Ref<Asset> AssetsManager::ReloadAssets()
{
return ProcessDirectory("assets", 0);
}
void AssetsManager::OnFileSystemChanged(FileSystemChangedEvent e)
{
e.NewName = Utils::RemoveExtension(e.NewName);
e.OldName = Utils::RemoveExtension(e.OldName);
const AssetHandle parentHandle = FindParentHandle(e.FilePath);
if (std::filesystem::path(e.FilePath).extension() != ".meta")
{
switch (e.Action)
{
case FileSystemAction::Added:
{
if (e.IsDirectory)
ProcessDirectory(e.FilePath, parentHandle);
else
{
Ref<Asset> asset = ImportAsset(e.FilePath, parentHandle);
}
}
break;
case FileSystemAction::Modified:
{
if (!e.IsDirectory)
{
Ref<Asset> asset = ImportAsset(e.FilePath, parentHandle);
}
}
break;
case FileSystemAction::Rename:
{
Ref<Asset> asset;
for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++)
{
if (it->second->FileName == e.OldName)
{
asset = it->second;
}
}
if (asset)
{
if (asset->Type != AssetTypes::GetAssetTypeFromExtension(Utils::GetExtension(e.FilePath)))
{
RemoveAsset(asset->Handle);
FileSystem::PrismDeleteFile(asset->FilePath + ".meta");
asset = ImportAsset(e.FilePath, parentHandle);
}else
{
std::string oldMetaPath = asset->FilePath + ".meta";
std::string newMetaPath = e.FilePath + ".meta";
std::error_code ec;
std::filesystem::rename(oldMetaPath, newMetaPath, ec);
if (ec)
{
PM_CORE_ERROR("Failed to rename meta file: {}", ec.message());
}
asset->FilePath = e.FilePath;
asset->FileName = e.NewName;
}
}
}
break;
case FileSystemAction::Delete:
{
for (auto it = s_LoadedAssets.begin(); it != s_LoadedAssets.end(); it++)
{
std::string filePath = Utils::NormalizePath(it->second->FilePath);
std::string eFilePath = Utils::NormalizePath(e.FilePath);
if (filePath != eFilePath)
continue;
RemoveAsset(it->first);
FileSystem::PrismDeleteFile(eFilePath + ".meta");
break;
}
}
break;
}
}
if (s_AssetsChangeCallback)
s_AssetsChangeCallback();
}
AssetHandle AssetsManager::FindParentHandleInChildren(Ref<Directory>& dir, const std::string& dirName)
{
if (dir->FileName == dirName)
return dir->Handle;
for (const AssetHandle& childHandle : dir->ChildDirectories)
{
Ref<Directory> child = GetAsset<Directory>(childHandle);
AssetHandle dirHandle = FindParentHandleInChildren(child, dirName);
if (IsAssetHandleValid(dirHandle))
return dirHandle;
}
return 0;
}
AssetHandle AssetsManager::FindParentHandle(const std::string& filepath)
{
const std::vector<std::string> parts = Utils::SplitString(filepath, "/\\");
const std::string& parentFolder = parts[parts.size() - 2];
Ref<Directory> assetsDirectory = GetAsset<Directory>(GetAssetHandleFromFilePath("assets"));
return FindParentHandleInChildren(assetsDirectory, parentFolder);
}
}

View File

@ -0,0 +1,92 @@
//
// Created by Atdunbg on 2026/1/20.
//
#ifndef PRISM_ASSETSMANAGER_H
#define PRISM_ASSETSMANAGER_H
#include <map>
#include "Asset.h"
#include "Prism/Utilities/FileSystem.h"
#include "Prism/Core/Ref.h"
namespace Prism
{
class AssetTypes
{
public:
static void Init();
static AssetType GetAssetTypeFromExtension(const std::string& extension);
private:
static std::map<std::string, AssetType> s_Types;
};
class PRISM_API AssetsManager
{
public:
using AssetsChangeEventFn = std::function<void()>;
public:
static void Init();
static void SetAssetChangeCallback(const AssetsChangeEventFn& callback);
static void Shutdown();
static std::vector<Ref<Asset>> GetAssetsInDirectory(AssetHandle directoryHandle);
static std::vector<Ref<Asset>> SearchFiles(const std::string& query, const std::string& searchPath);
static std::string GetParentPath(const std::string& path);
static bool IsDirectory(const std::string& filepath);
static AssetHandle GetAssetHandleFromFilePath(const std::string& filepath);
static bool IsAssetHandleValid(const AssetHandle& assetHandle);
static void Rename(Ref<Asset>& asset, const std::string& newName);
static Ref<PhysicsMaterial> CreateAssetPhysicsMaterial(const std::string& filename, AssetType type, const AssetHandle& directoryHandle, float v1, float v2, float v3);
static void RemoveAsset(AssetHandle assetHandle);
template<typename T, typename... Args>
static Ref<T> CreateAsset(const std::string& filename, AssetType type, AssetHandle directoryHandle, Args&&... args);
template<typename T>
static Ref<T> GetAsset(AssetHandle assetHandle, bool loadData = true);
template<typename T>
static Ref<T> GetAsset(const std::string& filepath, bool loadData = true);
static bool IsAssetType(AssetHandle assetHandle, AssetType type);
static std::string StripExtras(const std::string& filename);
private:
static Ref<Asset> ImportAsset(const std::string& filepath, AssetHandle parentHandle);
static Ref<Asset> ProcessDirectory(const std::string& directoryPath, AssetHandle parentHandle);
/**
* this will load from projectPath all assets, and return the root Asset Ref
* @param projectPath assets path
* @return the root asset Ref
*/
static Ref<Asset> ReloadAssets();
static void OnFileSystemChanged(FileSystemChangedEvent e);
static AssetHandle FindParentHandleInChildren(Ref<Directory>& dir, const std::string& dirName);
static AssetHandle FindParentHandle(const std::string& filepath);
private:
static std::unordered_map<AssetHandle, Ref<Asset>> s_LoadedAssets;
static AssetsChangeEventFn s_AssetsChangeCallback;
};
}
#endif //PRISM_ASSETSMANAGER_H

View File

@ -10,8 +10,9 @@
#include "GLFW/glfw3.h"
#include "Prism/Renderer/FrameBuffer.h"
#include "tinyfiledialogs.h"
#include "Prism/Physics/Physics3D.h"
#include "Prism/Script/ScriptEngine.h"
#include "Prism/Asset/AssetsManager.h"
namespace Prism
{
@ -26,23 +27,37 @@ namespace Prism
Application::Application(const ApplicationProps& props)
{
if (s_Instance != nullptr)
{
PM_CORE_ASSERT(false, "Application already exists!");
}
s_Instance = this;
m_Window = std::unique_ptr<Window>(Window::Create(WindowProps{props.Name, props.Width, props.Height}));
m_Window->SetEventCallback(BIND_EVENT_FN(OnEvent));
m_Window->SetVSync(true);
m_Window->Maximize();
m_ImGuiLayer = new ImGuiLayer("ImGui Layer");
PushOverlay(m_ImGuiLayer);
ScriptEngine::Init("assets/scripts/ExampleApp.dll");
Physics3D::Init();
AssetTypes::Init();
AssetsManager::Init();
Renderer::Init();
Renderer::WaitAndRender();
}
Application::~Application()
{
for (Layer* layer : m_LayerStack)
layer->OnDetach();
Physics3D::Shutdown();
ScriptEngine::Shutdown();
AssetsManager::Shutdown();
}
void Application::Run()
@ -89,7 +104,9 @@ namespace Prism
{
m_ImGuiLayer->Begin();
/*
for (Layer* layer : m_LayerStack)
layer->OnImGuiRender();
ImGui::Begin("Renderer");
const auto& caps = RendererAPI::GetCapabilities();
ImGui::Text("Vendor: %s", caps.Vendor.c_str());
@ -97,92 +114,10 @@ namespace Prism
ImGui::Text("Version: %s", caps.Version.c_str());
ImGui::Text("Frame Time: %.2fms\n", m_TimeStep.GetMilliseconds());
ImGui::End();
*/
for (Layer* layer : m_LayerStack)
layer->OnImGuiRender();
m_ImGuiLayer->End();
}
// TODO: fix this to prase filter
std::string Application::OpenFile(const std::string& filter) const
{
// TODO: will move it to other folder
// 处理过滤器
std::vector<const char*> filterPatterns;
std::vector<std::string> patternStorage;
if (!filter.empty()) {
const char* ptr = filter.c_str();
bool isDescription = true;
while (*ptr) {
if (isDescription) {
// 跳过描述
isDescription = false;
} else {
// 添加模式
patternStorage.push_back(ptr);
isDescription = true;
}
ptr += strlen(ptr) + 1;
}
// 转换为 C 字符串数组
for (const auto& pattern : patternStorage) {
filterPatterns.push_back(pattern.c_str());
}
}
// 如果没有过滤器,添加默认值
if (filterPatterns.empty()) {
filterPatterns.push_back("*");
}
// 构建过滤器描述
std::string filterDesc;
if (!patternStorage.empty()) {
std::stringstream ss;
ss << "Files (";
for (size_t i = 0; i < patternStorage.size(); ++i) {
if (i > 0) ss << ",";
ss << patternStorage[i];
}
ss << ")";
filterDesc = ss.str();
}
const char* result = tinyfd_openFileDialog(
"Open File", // 对话框标题
nullptr, // 初始路径nullptr 表示使用默认
0, // 过滤器数量 (注意:这里需要根据解析情况调整,见下文)
nullptr, // 过滤器模式数组
nullptr, // 过滤器描述
0 // 是否允许多选0 为单选1 为多选
);
// 调用文件对话框
/*
const char* result = tinyfd_openFileDialog(
"Open File", // 标题
nullptr, // 初始目录
static_cast<int>(filterPatterns.size()), // 过滤器数量
filterPatterns.data(), // 过滤器模式
filterDesc.empty() ? nullptr : filterDesc.c_str(), // 描述
0 // 单选
);
*/
return result ? std::string(result) : std::string();
}
std::string Application::SaveFile(const std::string& filter) const
{
return OpenFile(filter);
}
Application& Application::Get()
{
return *s_Instance;

View File

@ -13,10 +13,29 @@
namespace Prism
{
class Project;
struct CommandArgs
{
int count = 0;
char** args = nullptr;
const char* operator[](const int index)
{
if (index >= count)
{
return nullptr;
}
return args[count];
}
};
struct ApplicationProps
{
std::string Name;
uint32_t Width, Height;
CommandArgs CommandArgs;
};
class PRISM_API Application
@ -35,9 +54,6 @@ namespace Prism
void RenderImGui();
std::string OpenFile(const std::string& filter = "All\0*.*\0") const;
std::string SaveFile(const std::string& filter = "All\0*.*\0") const;
inline Window& GetWindow() { return *m_Window; }
static Application& Get();
@ -70,7 +86,7 @@ namespace Prism
// this function will implemented by client
Application* CreateApplication();
Application* CreateApplication(CommandArgs args);
}
#if defined(_MSC_VER) && !defined(__clang__)

View File

@ -20,6 +20,8 @@ namespace Prism {
void ShutdownCore()
{
PM_CORE_TRACE("Shutting down...");
Log::Shutdown();
}
}

View File

@ -56,6 +56,11 @@ namespace Prism
#define PM_CORE_ASSERT(x, ...)
#endif
#ifndef M_PI
#define M_PI 3.1415926f
#endif
namespace Prism
{
using byte = unsigned char;

View File

@ -5,17 +5,27 @@
#ifndef ENTRYPOINT_H
#define ENTRYPOINT_H
#include <filesystem>
#include <iostream>
#include "Application.h"
extern Prism::Application* Prism::CreateApplication();
extern Prism::Application* Prism::CreateApplication(CommandArgs args);
int main(int argc, char** argv)
{
//TODO: this will use other method to impl
std::cout << std::filesystem::current_path() << std::endl;
std::filesystem::current_path(std::filesystem::path(argv[0]).parent_path());
std::cout << std::filesystem::current_path() << std::endl;
#ifdef PRISM_STATIC
Prism::InitializeCore();
#endif
Prism::Application* app = Prism::CreateApplication();
const Prism::CommandArgs args{ argc, argv };
Prism::Application* app = Prism::CreateApplication(args);
app->Run();
delete app;

Some files were not shown because too many files have changed in this diff Show More