Implement Mesh partitioning
119
README.md
@@ -28,75 +28,72 @@ This is the current state of the engine architecture:
|
|||||||
|
|
||||||
```mermaid
|
```mermaid
|
||||||
flowchart TB
|
flowchart TB
|
||||||
|
subgraph Infrastructure["Infrastructure"]
|
||||||
|
direction LR
|
||||||
|
|
||||||
|
subgraph Platform
|
||||||
|
direction TB
|
||||||
|
Window --> GLFW
|
||||||
|
Window --> glad
|
||||||
|
Window --> ImGui["Dear ImGui"]
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Loading
|
||||||
|
direction TB
|
||||||
|
Loader --> tinyobjloader
|
||||||
|
Loader --> stb_image
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
subgraph Application
|
subgraph Application
|
||||||
Renderer
|
direction LR
|
||||||
Scene
|
|
||||||
Camera
|
|
||||||
Framebuffer
|
Framebuffer
|
||||||
|
Renderer --> Camera
|
||||||
|
Renderer --> Scene
|
||||||
|
Renderer -->|Updates| UniformBuffer
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph World
|
subgraph World
|
||||||
Entity
|
direction LR
|
||||||
Transform
|
|
||||||
Mesh
|
subgraph SceneGraph["Scene Graph"]
|
||||||
Material
|
direction TB
|
||||||
LightEnvironment
|
Scene --> Entity
|
||||||
|
Entity --> Transform
|
||||||
|
Entity --> Mesh
|
||||||
|
Entity -.->|Optional override| Material
|
||||||
|
Mesh -->|Partitions| SubMesh
|
||||||
|
SubMesh -->|Owns| Material
|
||||||
|
end
|
||||||
|
|
||||||
|
subgraph Lighting
|
||||||
|
direction TB
|
||||||
|
Scene --> LightEnvironment
|
||||||
|
LightEnvironment --> DirectionalLight
|
||||||
|
LightEnvironment --> PointLight
|
||||||
|
LightEnvironment --> SpotLight
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph SceneData["Scene Data"]
|
subgraph SceneData["Scene Data"]
|
||||||
Vertices
|
direction LR
|
||||||
Indices
|
|
||||||
Shader
|
subgraph Geometry
|
||||||
Texture
|
direction TB
|
||||||
UniformBuffer
|
Mesh --> Vertices
|
||||||
DirectionalLight
|
Mesh --> Indices
|
||||||
PointLight
|
end
|
||||||
SpotLight
|
|
||||||
|
subgraph Resources
|
||||||
|
direction TB
|
||||||
|
Material --> Shader
|
||||||
|
Material --> Texture
|
||||||
|
UniformBuffer -->|Global data| Shader
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
subgraph Loading
|
tinyobjloader -->|Generates| Entity
|
||||||
Loader
|
Texture -.->|Decodes via| stb_image
|
||||||
tinyobjloader
|
|
||||||
stb_image
|
|
||||||
end
|
|
||||||
|
|
||||||
subgraph Platform
|
|
||||||
Window
|
|
||||||
GLFW
|
|
||||||
glad
|
|
||||||
ImGui["Dear ImGui"]
|
|
||||||
end
|
|
||||||
|
|
||||||
Renderer --> Scene
|
|
||||||
Renderer --> Camera
|
|
||||||
Renderer --> UniformBuffer
|
|
||||||
|
|
||||||
Scene --> Entity
|
|
||||||
Scene --> LightEnvironment
|
|
||||||
|
|
||||||
Entity --> Transform
|
|
||||||
Entity --> Mesh
|
|
||||||
Entity --> Material
|
|
||||||
|
|
||||||
Mesh --> Vertices
|
|
||||||
Mesh --> Indices
|
|
||||||
|
|
||||||
Material --> Shader
|
|
||||||
Material --> Texture
|
|
||||||
Shader --> UniformBuffer
|
|
||||||
|
|
||||||
LightEnvironment --> DirectionalLight
|
|
||||||
LightEnvironment --> PointLight
|
|
||||||
LightEnvironment --> SpotLight
|
|
||||||
|
|
||||||
Loader --> tinyobjloader
|
|
||||||
tinyobjloader --> Entity
|
|
||||||
|
|
||||||
Texture --> stb_image
|
|
||||||
|
|
||||||
Window --> GLFW
|
|
||||||
Window --> glad
|
|
||||||
Window --> ImGui
|
|
||||||
|
|
||||||
classDef application fill:#7c3aed,stroke:#4c1d95,color:#fff
|
classDef application fill:#7c3aed,stroke:#4c1d95,color:#fff
|
||||||
classDef world fill:#2563eb,stroke:#1e3a8a,color:#fff
|
classDef world fill:#2563eb,stroke:#1e3a8a,color:#fff
|
||||||
@@ -105,8 +102,8 @@ flowchart TB
|
|||||||
classDef platform fill:#64748b,stroke:#334155,color:#fff
|
classDef platform fill:#64748b,stroke:#334155,color:#fff
|
||||||
|
|
||||||
class Renderer,Scene,Camera,Framebuffer application
|
class Renderer,Scene,Camera,Framebuffer application
|
||||||
class Entity,Transform,Mesh,Material,LightEnvironment world
|
class Entity,Transform,Mesh,SubMesh,Material,LightEnvironment world
|
||||||
class Vertices,Indices,Shader,Texture,UniformBuffer,DirectionalLight,PointLight,SpotLight data
|
class UniformBuffer,Shader,Texture,Vertices,Indices,DirectionalLight,PointLight,SpotLight data
|
||||||
class Loader,tinyobjloader,stb_image loading
|
class Loader,tinyobjloader,stb_image loading
|
||||||
class Window,GLFW,glad,ImGui platform
|
class Window,GLFW,glad,ImGui platform
|
||||||
```
|
```
|
||||||
|
|||||||
313
assets/audi_r8/Audi_R8_2017.mtl
Normal file
@@ -0,0 +1,313 @@
|
|||||||
|
# Blender MTL File: 'Audi_R8_2017.blend'
|
||||||
|
# Material Count: 31
|
||||||
|
|
||||||
|
newmtl AirBackOutput
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl AudiLogoChrome
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Axe
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl BlackGlossy
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl BodyMaterials(00297F)
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.000000 0.160784 0.498039
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Bolt(Black)
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Brake
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.100000 0.100000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl BrakeDisc
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.100000 0.100000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl CarbonFibre
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.150000 0.150000 0.150000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Centre(Black)
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl FarLight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl FrontHighlightChrome
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl GlassBackHighlight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.100000 0.100000 0.120000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl GlassFrontHighlight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.100000 0.100000 0.120000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Ground
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl HoneyComb
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl InteriorFrontHighlight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl LightBackHighlight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl LightFrontHighlight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl LightSource
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Mirrors
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl MirrorsLightSouces
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl NumberPlateLight
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl NumbrePlateAudiR8
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
map_Kd NumernSchildAudiR8.png
|
||||||
|
|
||||||
|
newmtl Ring(Light)
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl RoofCanvas
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.640000 0.640000 0.640000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl Tyre
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.030000 0.030000 0.030000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl WheelsChrome
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.800000 0.800000 0.800000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl WindscreenGlassBlack
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl WindscreenGlassGrey
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.100000 0.100000 0.120000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
|
|
||||||
|
newmtl undercarriage
|
||||||
|
Ns 96.078431
|
||||||
|
Ka 1.000000 1.000000 1.000000
|
||||||
|
Kd 0.050000 0.050000 0.050000
|
||||||
|
Ks 0.500000 0.500000 0.500000
|
||||||
|
Ke 0.000000 0.000000 0.000000
|
||||||
|
Ni 1.000000
|
||||||
|
d 1.000000
|
||||||
|
illum 2
|
||||||
1145920
assets/audi_r8/Audi_R8_2017.obj
Normal file
BIN
assets/audi_r8/NumernSchildAudiR8.png
Normal file
|
After Width: | Height: | Size: 271 KiB |
3
assets/audi_r8/source_attribution.txt
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
Model by hermes25, from: https://free3d.com/3d-model/audi-r8-spyder-22646.html
|
||||||
|
|
||||||
|
Modified material for easier lighting and diffuse colors
|
||||||
76
assets/mtl_converter.py
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
import argparse
|
||||||
|
import re
|
||||||
|
import os
|
||||||
|
import sys
|
||||||
|
|
||||||
|
|
||||||
|
COLOR_RULES = {
|
||||||
|
"black": "0.05 0.05 0.05", "dark": "0.15 0.15 0.15", "grey": "0.5 0.5 0.5",
|
||||||
|
"white": "0.9 0.9 0.9", "chrome": "0.8 0.8 0.8", "metal": "0.7 0.7 0.7",
|
||||||
|
"silver": "0.75 0.75 0.75", "gold": "0.8 0.7 0.2", "copper": "0.7 0.4 0.2",
|
||||||
|
"glass": "0.1 0.1 0.12", "window": "0.1 0.1 0.12", "mirror": "0.9 0.9 0.9",
|
||||||
|
"tyre": "0.03 0.03 0.03", "tire": "0.03 0.03 0.03", "rubber": "0.03 0.03 0.03",
|
||||||
|
"wood": "0.4 0.25 0.1", "carbon": "0.15 0.15 0.15", "plastic": "0.8 0.8 0.8",
|
||||||
|
"red": "0.8 0.1 0.1", "green": "0.1 0.8 0.1", "blue": "0.1 0.1 0.8",
|
||||||
|
"yellow": "0.8 0.8 0.1"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def hex_to_kd(hex_str):
|
||||||
|
r, g, b = int(hex_str[0:2], 16), int(hex_str[2:4], 16), int(hex_str[4:6], 16)
|
||||||
|
return f"{r/255.0:.6f} {g/255.0:.6f} {b/255.0:.6f}"
|
||||||
|
|
||||||
|
|
||||||
|
def get_color(name):
|
||||||
|
match = re.search(r'\(([A-Fa-f0-9]{6})\)', name)
|
||||||
|
if match:
|
||||||
|
return hex_to_kd(match.group(1))
|
||||||
|
|
||||||
|
name_lower = name.lower()
|
||||||
|
for kw, kd in COLOR_RULES.items():
|
||||||
|
if kw in name_lower: return kd
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
parser = argparse.ArgumentParser(description="Auto-fix missing Kd colors in MTL files based on material names.")
|
||||||
|
parser.add_argument("input", help="Path to the input .mtl file")
|
||||||
|
parser.add_argument("-o", "--output", help="Path to output file (default: <input>_fixed.mtl)")
|
||||||
|
parser.add_argument("-w", "--overwrite", action="store_true", help="Overwrite the input file directly")
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if not os.path.exists(args.input):
|
||||||
|
print(f"Error: {args.input} not found.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
with open(args.input, 'r') as f:
|
||||||
|
lines = f.readlines()
|
||||||
|
|
||||||
|
out_lines = []
|
||||||
|
current_mat = ""
|
||||||
|
changes = 0
|
||||||
|
|
||||||
|
for line in lines:
|
||||||
|
if line.startswith("newmtl "):
|
||||||
|
current_mat = line.split()[1].strip()
|
||||||
|
out_lines.append(line)
|
||||||
|
elif line.startswith("Kd ") and current_mat:
|
||||||
|
new_kd = get_color(current_mat)
|
||||||
|
if new_kd:
|
||||||
|
out_lines.append(f"Kd {new_kd}\n")
|
||||||
|
changes += 1
|
||||||
|
else:
|
||||||
|
out_lines.append(line)
|
||||||
|
else:
|
||||||
|
out_lines.append(line)
|
||||||
|
|
||||||
|
out_path = args.input if args.overwrite else (args.output or args.input.replace(".mtl", "_fixed.mtl"))
|
||||||
|
|
||||||
|
with open(out_path, 'w') as f:
|
||||||
|
f.writelines(out_lines)
|
||||||
|
|
||||||
|
print(f"Done! {changes} materials updated. Saved to {out_path}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
|
Before Width: | Height: | Size: 58 KiB After Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 181 KiB After Width: | Height: | Size: 181 KiB |
|
Before Width: | Height: | Size: 457 KiB After Width: | Height: | Size: 457 KiB |
|
Before Width: | Height: | Size: 141 KiB After Width: | Height: | Size: 141 KiB |
|
Before Width: | Height: | Size: 231 B After Width: | Height: | Size: 231 B |
|
Before Width: | Height: | Size: 251 KiB After Width: | Height: | Size: 251 KiB |
@@ -14,9 +14,9 @@ class Entity
|
|||||||
public:
|
public:
|
||||||
Transform transform{};
|
Transform transform{};
|
||||||
Rc<Mesh> mesh;
|
Rc<Mesh> mesh;
|
||||||
Rc<Material> material;
|
Rc<Material> material{}; // Optional material override
|
||||||
|
|
||||||
Entity(Rc<Mesh> mesh, Rc<Material> material)
|
Entity(Rc<Mesh> mesh, Rc<Material> material = nullptr)
|
||||||
: mesh(mesh)
|
: mesh(mesh)
|
||||||
, material(material) {}
|
, material(material) {}
|
||||||
};
|
};
|
||||||
|
|||||||
156
src/loader.cpp
@@ -34,87 +34,129 @@ Opt<Entity> load_obj(const std::string& file_path, Rc<Shader> shader)
|
|||||||
std::string err;
|
std::string err;
|
||||||
|
|
||||||
fs::path parent_path = fs::path{file_path}.parent_path();
|
fs::path parent_path = fs::path{file_path}.parent_path();
|
||||||
parent_path += fs::path::preferred_separator;
|
std::string base_dir = !parent_path.empty()
|
||||||
std::string base_dir = parent_path.string();
|
? parent_path.string() + fs::path::preferred_separator
|
||||||
|
: "";
|
||||||
|
|
||||||
bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
|
bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
|
||||||
file_path.c_str(), base_dir.c_str());
|
file_path.c_str(), base_dir.c_str());
|
||||||
if (!ok || !err.empty()) {
|
if (!ok) {
|
||||||
std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << std::endl << err;
|
std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << ": " << err;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build the unified mesh
|
std::vector<Rc<Material>> loaded_materials;
|
||||||
|
loaded_materials.reserve(materials.size());
|
||||||
|
|
||||||
|
// Load materials
|
||||||
|
for (const auto& mtl : materials) {
|
||||||
|
auto mat = make_rc<Material>();
|
||||||
|
mat->shader = shader;
|
||||||
|
mat->shininess = mtl.shininess;
|
||||||
|
|
||||||
|
mat->diffuse_color = glm::vec3{mtl.diffuse[0], mtl.diffuse[1], mtl.diffuse[2]};
|
||||||
|
mat->specular_color = glm::vec3{mtl.specular[0], mtl.specular[1], mtl.specular[2]};
|
||||||
|
|
||||||
|
if (!mtl.diffuse_texname.empty()) {
|
||||||
|
mat->diffuse_map = Texture::create(base_dir + mtl.diffuse_texname);
|
||||||
|
if (!mat->diffuse_map) {
|
||||||
|
std::cerr << "WARNING: LOAD_OBJ: Could not create diffuse texture: "
|
||||||
|
<< mtl.diffuse_texname << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!mtl.specular_texname.empty()) {
|
||||||
|
mat->specular_map = Texture::create(base_dir + mtl.specular_texname);
|
||||||
|
if (!mat->specular_map) {
|
||||||
|
std::cerr << "WARNING: LOAD_OBJ: Could not create specular texture: "
|
||||||
|
<< mtl.specular_texname << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
loaded_materials.push_back(mat);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default material fallback for faces without a material (-1)
|
||||||
|
auto default_material = make_rc<Material>();
|
||||||
|
default_material->shader = shader;
|
||||||
|
|
||||||
|
// Map faces/indices grouped by material id
|
||||||
std::vector<Vertex> vertices;
|
std::vector<Vertex> vertices;
|
||||||
std::vector<GLuint> indices;
|
std::unordered_map<tinyobj::index_t, GLuint, VertexIndexHash, VertexIndexEqual> unique_vertices;
|
||||||
std::unordered_map<tinyobj::index_t, GLuint,
|
|
||||||
VertexIndexHash, VertexIndexEqual> unique_vertices;
|
// Group index buffers: material_id -> list of index references
|
||||||
|
std::unordered_map<int, std::vector<GLuint>> mat_to_indices;
|
||||||
|
|
||||||
for (const auto& shape : shapes) {
|
for (const auto& shape : shapes) {
|
||||||
for (const auto& index : shape.mesh.indices) {
|
size_t index_offset = 0;
|
||||||
if (unique_vertices.count(index) == 0) {
|
|
||||||
Vertex vertex{};
|
|
||||||
|
|
||||||
vertex.position = {
|
for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) {
|
||||||
attrib.vertices[3 * index.vertex_index + 0],
|
size_t fv = static_cast<size_t>(shape.mesh.num_face_vertices[f]);
|
||||||
attrib.vertices[3 * index.vertex_index + 1],
|
int mat_id = shape.mesh.material_ids[f];
|
||||||
attrib.vertices[3 * index.vertex_index + 2]
|
|
||||||
};
|
|
||||||
|
|
||||||
if (index.normal_index >= 0) {
|
for (size_t v = 0; v < fv; ++v) {
|
||||||
vertex.normal = {
|
tinyobj::index_t idx = shape.mesh.indices[index_offset + v];
|
||||||
attrib.normals[3 * index.normal_index + 0],
|
|
||||||
attrib.normals[3 * index.normal_index + 1],
|
if (unique_vertices.count(idx) == 0) {
|
||||||
attrib.normals[3 * index.normal_index + 2]
|
Vertex vertex{};
|
||||||
|
|
||||||
|
vertex.position = {
|
||||||
|
attrib.vertices[3 * idx.vertex_index + 0],
|
||||||
|
attrib.vertices[3 * idx.vertex_index + 1],
|
||||||
|
attrib.vertices[3 * idx.vertex_index + 2]
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (idx.normal_index >= 0) {
|
||||||
|
vertex.normal = {
|
||||||
|
attrib.normals[3 * idx.normal_index + 0],
|
||||||
|
attrib.normals[3 * idx.normal_index + 1],
|
||||||
|
attrib.normals[3 * idx.normal_index + 2]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (idx.texcoord_index >= 0) {
|
||||||
|
vertex.texture_coords = {
|
||||||
|
attrib.texcoords[2 * idx.texcoord_index + 0],
|
||||||
|
1.0f - attrib.texcoords[2 * idx.texcoord_index + 1]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_vertices[idx] = static_cast<GLuint>(vertices.size());
|
||||||
|
vertices.push_back(vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (index.texcoord_index >= 0) {
|
mat_to_indices[mat_id].push_back(unique_vertices[idx]);
|
||||||
vertex.texture_coords = {
|
|
||||||
attrib.texcoords[2 * index.texcoord_index + 0],
|
|
||||||
1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
unique_vertices[index] = static_cast<GLuint>(vertices.size());
|
|
||||||
vertices.push_back(vertex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
indices.push_back(unique_vertices[index]);
|
index_offset += fv;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto mesh = Mesh::create(vertices, indices);
|
// Assemble unified index buffer and SubMesh definitions
|
||||||
|
std::vector<GLuint> all_indices;
|
||||||
|
std::vector<Mesh::SubMesh> submeshes;
|
||||||
|
|
||||||
|
for (const auto& [mat_id, indices] : mat_to_indices) {
|
||||||
|
Mesh::SubMesh submesh{};
|
||||||
|
submesh.index_offset = static_cast<GLuint>(all_indices.size());
|
||||||
|
submesh.index_count = static_cast<GLuint>(indices.size());
|
||||||
|
|
||||||
|
if (0 <= mat_id && mat_id < static_cast<int>(loaded_materials.size())) {
|
||||||
|
submesh.material = loaded_materials[mat_id];
|
||||||
|
} else {
|
||||||
|
submesh.material = default_material;
|
||||||
|
}
|
||||||
|
|
||||||
|
all_indices.insert(all_indices.end(), indices.begin(), indices.end());
|
||||||
|
submeshes.push_back(submesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto mesh = Mesh::create(vertices, all_indices, submeshes);
|
||||||
if (!mesh) {
|
if (!mesh) {
|
||||||
std::cerr << "ERROR: LOAD_OBJ: Could not create mesh" << std::endl;
|
std::cerr << "ERROR: LOAD_OBJ: Could not create mesh" << std::endl;
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto material = make_rc<Material>();
|
return Entity{mesh};
|
||||||
material->shader = shader; // Assigned required shader
|
|
||||||
|
|
||||||
// If the .obj file had an .mtl file attached to it
|
|
||||||
if (!materials.empty()) {
|
|
||||||
// Take only the first material found
|
|
||||||
const tinyobj::material_t& mtl = materials[0];
|
|
||||||
|
|
||||||
if (!mtl.diffuse_texname.empty()) {
|
|
||||||
material->diffuse_map = Texture::create(base_dir + mtl.diffuse_texname);
|
|
||||||
if (!material->diffuse_map) {
|
|
||||||
std::cerr << "WARNING: LOAD_OBJ: Could not create diffuse texture" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!mtl.specular_texname.empty()) {
|
|
||||||
material->specular_map = Texture::create(base_dir + mtl.specular_texname);
|
|
||||||
if (!material->specular_map) {
|
|
||||||
std::cerr << "WARNING: LOAD_OBJ: Could not create specular texture" << std::endl;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
material->shininess = mtl.shininess;
|
|
||||||
}
|
|
||||||
|
|
||||||
return Entity{mesh, material};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
28
src/main.cpp
@@ -71,15 +71,15 @@ int main()
|
|||||||
auto shader = Shader::create("./src/shaders/default.vert", "./src/shaders/default.frag");
|
auto shader = Shader::create("./src/shaders/default.vert", "./src/shaders/default.frag");
|
||||||
auto debug_shader = Shader::create("./src/shaders/debug.vert", "./src/shaders/debug.frag", false);
|
auto debug_shader = Shader::create("./src/shaders/debug.vert", "./src/shaders/debug.frag", false);
|
||||||
|
|
||||||
// auto car = load_obj("./assets/AudiR8Spyder_2017/3D Models/Audi_R8_2017.obj", shader);
|
auto car = load_obj("./assets/audi_r8/Audi_R8_2017.obj", shader);
|
||||||
|
|
||||||
auto backpack = load_obj("./assets/backpack/backpack.obj", shader);
|
auto backpack = load_obj("./assets/backpack/backpack.obj", shader);
|
||||||
|
|
||||||
auto cube_mesh = Mesh::create(get_cube_vertices());
|
auto cube_mesh = Mesh::create(get_cube_vertices());
|
||||||
auto cube_texture = Texture::create("./assets/container.jpg");
|
auto cube_texture = Texture::create("./assets/textures/container.jpg");
|
||||||
|
|
||||||
auto default_mesh = Mesh::create(get_cube_vertices());
|
auto default_mesh = Mesh::create(get_cube_vertices());
|
||||||
auto default_texture = Texture::create("./assets/debugempty.png");
|
auto default_texture = Texture::create("./assets/textures/debugempty.png");
|
||||||
|
|
||||||
auto framebuffer = FrameBuffer::create(window->get_width(), window->get_height());
|
auto framebuffer = FrameBuffer::create(window->get_width(), window->get_height());
|
||||||
|
|
||||||
@@ -94,17 +94,17 @@ int main()
|
|||||||
/******************** Scene construction ********************/
|
/******************** Scene construction ********************/
|
||||||
|
|
||||||
// Car scene
|
// Car scene
|
||||||
// Rc<Scene> car_scene = make_rc<Scene>();
|
Rc<Scene> car_scene = make_rc<Scene>();
|
||||||
// user_data.scenes.push_back(car_scene);
|
user_data.scenes.push_back(car_scene);
|
||||||
//
|
|
||||||
// car_scene->add_entity(*car);
|
car_scene->add_entity(*car);
|
||||||
//
|
|
||||||
// PointLight light_white1;
|
PointLight light_white1;
|
||||||
// light_white1.position = glm::vec3(0.0f, 3.0f, 3.0f);
|
light_white1.position = glm::vec3(0.0f, 3.0f, 3.0f);
|
||||||
// light_white1.diffuse = glm::vec3(2.0f, 2.0f, 2.0f); // Bright white
|
light_white1.diffuse = glm::vec3(1.0f, 1.0f, 1.0f); // Bright white
|
||||||
// light_white1.specular = glm::vec3(2.0f, 2.0f, 2.0f); // White highlights
|
light_white1.specular = glm::vec3(1.0f, 1.0f, 1.0f); // White highlights
|
||||||
//
|
|
||||||
// car_scene->light_environment.point_lights.push_back(light_white1);
|
car_scene->light_environment.point_lights.push_back(light_white1);
|
||||||
|
|
||||||
// Backpack scene
|
// Backpack scene
|
||||||
Rc<Scene> backpack_scene = make_rc<Scene>();
|
Rc<Scene> backpack_scene = make_rc<Scene>();
|
||||||
|
|||||||
@@ -7,24 +7,27 @@ void Material::bind() const
|
|||||||
{
|
{
|
||||||
shader->bind();
|
shader->bind();
|
||||||
|
|
||||||
// Diffuse (slot 0)
|
shader->set_vec3("u_material.diffuse_color", diffuse_color);
|
||||||
if (diffuse_map) {
|
shader->set_vec3("u_material.specular_color", specular_color);
|
||||||
diffuse_map->bind(0);
|
|
||||||
} else {
|
|
||||||
Texture::unbind(0);
|
|
||||||
}
|
|
||||||
shader->set_int("u_material.diffuse", 0);
|
|
||||||
|
|
||||||
// Specular (slot 1)
|
|
||||||
if (specular_map) {
|
|
||||||
specular_map->bind(1);
|
|
||||||
} else {
|
|
||||||
Texture::unbind(1);
|
|
||||||
}
|
|
||||||
shader->set_int("u_material.specular", 1);
|
|
||||||
|
|
||||||
// Shininess
|
|
||||||
shader->set_float("u_material.shininess", shininess);
|
shader->set_float("u_material.shininess", shininess);
|
||||||
|
|
||||||
|
// Bind diffuse map to texture unit 0
|
||||||
|
if (diffuse_map) {
|
||||||
|
shader->set_int("u_material.use_diffuse_map", 1);
|
||||||
|
diffuse_map->bind(0);
|
||||||
|
shader->set_int("u_material.diffuse", 0);
|
||||||
|
} else {
|
||||||
|
shader->set_int("u_material.use_diffuse_map", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bind specular map to texture unit 1
|
||||||
|
if (specular_map) {
|
||||||
|
shader->set_int("u_material.use_specular_map", 1);
|
||||||
|
specular_map->bind(1);
|
||||||
|
shader->set_int("u_material.specular", 1);
|
||||||
|
} else {
|
||||||
|
shader->set_int("u_material.use_specular_map", 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ struct Material
|
|||||||
Rc<Shader> shader;
|
Rc<Shader> shader;
|
||||||
Rc<Texture> diffuse_map;
|
Rc<Texture> diffuse_map;
|
||||||
Rc<Texture> specular_map;
|
Rc<Texture> specular_map;
|
||||||
|
|
||||||
|
glm::vec3 diffuse_color{1.0f, 1.0f, 1.0f};
|
||||||
|
glm::vec3 specular_color{1.0f, 1.0f, 1.0f};
|
||||||
float shininess{32.0f};
|
float shininess{32.0f};
|
||||||
|
|
||||||
void bind() const;
|
void bind() const;
|
||||||
|
|||||||
45
src/mesh.cpp
@@ -7,7 +7,8 @@ namespace Fan
|
|||||||
{
|
{
|
||||||
|
|
||||||
Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
|
Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
|
||||||
const std::vector<GLuint>& indices/* = {}*/)
|
const std::vector<GLuint>& indices/* = {}*/,
|
||||||
|
const std::vector<SubMesh>& submeshes/* = {}*/)
|
||||||
{
|
{
|
||||||
GLuint VBO{}, VAO{}, EBO{};
|
GLuint VBO{}, VAO{}, EBO{};
|
||||||
bool has_indices = !indices.empty();
|
bool has_indices = !indices.empty();
|
||||||
@@ -38,7 +39,18 @@ Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
|
|||||||
glEnableVertexAttribArray(1);
|
glEnableVertexAttribArray(1);
|
||||||
glEnableVertexAttribArray(2);
|
glEnableVertexAttribArray(2);
|
||||||
|
|
||||||
return make_rc<Mesh>(vertices, indices, has_indices, VBO, VAO, EBO);
|
// Submesh setup
|
||||||
|
std::vector<SubMesh> final_submeshes = submeshes;
|
||||||
|
|
||||||
|
// If no submeshes were explicitly defined, create one default submesh spanning the entire mesh
|
||||||
|
if (final_submeshes.empty()) {
|
||||||
|
SubMesh default_submesh{};
|
||||||
|
default_submesh.index_offset = 0;
|
||||||
|
default_submesh.index_count = indices.empty() ? vertices.size() : indices.size();
|
||||||
|
final_submeshes.push_back(default_submesh);
|
||||||
|
}
|
||||||
|
|
||||||
|
return make_rc<Mesh>(vertices, indices, final_submeshes, has_indices, VBO, VAO, EBO);
|
||||||
}
|
}
|
||||||
|
|
||||||
Mesh::~Mesh()
|
Mesh::~Mesh()
|
||||||
@@ -51,6 +63,7 @@ Mesh::~Mesh()
|
|||||||
Mesh::Mesh(Mesh&& other) noexcept
|
Mesh::Mesh(Mesh&& other) noexcept
|
||||||
: m_vertices(std::move(other.m_vertices))
|
: m_vertices(std::move(other.m_vertices))
|
||||||
, m_indices(std::move(other.m_indices))
|
, m_indices(std::move(other.m_indices))
|
||||||
|
, m_submeshes(std::move(other.m_submeshes))
|
||||||
, m_has_indices(std::exchange(other.m_has_indices, false))
|
, m_has_indices(std::exchange(other.m_has_indices, false))
|
||||||
, m_VBO(std::exchange(other.m_VBO, 0))
|
, m_VBO(std::exchange(other.m_VBO, 0))
|
||||||
, m_VAO(std::exchange(other.m_VAO, 0))
|
, m_VAO(std::exchange(other.m_VAO, 0))
|
||||||
@@ -64,6 +77,7 @@ Mesh& Mesh::operator=(Mesh&& other) noexcept
|
|||||||
|
|
||||||
std::swap(m_vertices, other.m_vertices);
|
std::swap(m_vertices, other.m_vertices);
|
||||||
std::swap(m_indices, other.m_indices);
|
std::swap(m_indices, other.m_indices);
|
||||||
|
std::swap(m_submeshes, other.m_submeshes);
|
||||||
std::swap(m_has_indices, other.m_has_indices);
|
std::swap(m_has_indices, other.m_has_indices);
|
||||||
std::swap(m_VBO, other.m_VBO);
|
std::swap(m_VBO, other.m_VBO);
|
||||||
std::swap(m_VAO, other.m_VAO);
|
std::swap(m_VAO, other.m_VAO);
|
||||||
@@ -72,15 +86,34 @@ Mesh& Mesh::operator=(Mesh&& other) noexcept
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const std::vector<Mesh::SubMesh>& Mesh::get_submeshes() const
|
||||||
|
{
|
||||||
|
return m_submeshes;
|
||||||
|
}
|
||||||
|
|
||||||
void Mesh::draw() const
|
void Mesh::draw() const
|
||||||
{
|
{
|
||||||
glBindVertexArray(m_VAO);
|
glBindVertexArray(m_VAO);
|
||||||
|
|
||||||
if (m_has_indices) {
|
for (const auto& submesh : m_submeshes) {
|
||||||
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, 0);
|
draw_submesh(submesh.index_offset, submesh.index_count);
|
||||||
} else {
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Mesh::draw_submesh(GLuint index_offset, GLuint index_count) const
|
||||||
|
{
|
||||||
|
glBindVertexArray(m_VAO);
|
||||||
|
|
||||||
|
if (m_has_indices) {
|
||||||
|
glDrawElements(GL_TRIANGLES, index_count, GL_UNSIGNED_INT,
|
||||||
|
reinterpret_cast<const void*>(index_offset * sizeof(GLuint)));
|
||||||
|
} else {
|
||||||
|
glDrawArrays(GL_TRIANGLES, index_offset, index_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindVertexArray(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace Fan
|
} // namespace Fan
|
||||||
|
|||||||
17
src/mesh.hpp
@@ -5,6 +5,7 @@
|
|||||||
#include <glad/gl.h>
|
#include <glad/gl.h>
|
||||||
|
|
||||||
#include "vertex.hpp"
|
#include "vertex.hpp"
|
||||||
|
#include "material.hpp"
|
||||||
#include "utils.hpp"
|
#include "utils.hpp"
|
||||||
|
|
||||||
namespace Fan
|
namespace Fan
|
||||||
@@ -13,14 +14,24 @@ namespace Fan
|
|||||||
class Mesh
|
class Mesh
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
struct SubMesh
|
||||||
|
{
|
||||||
|
GLuint index_offset{};
|
||||||
|
GLuint index_count{};
|
||||||
|
Rc<Material> material{};
|
||||||
|
};
|
||||||
|
|
||||||
static Rc<Mesh> create(const std::vector<Vertex>& vertices,
|
static Rc<Mesh> create(const std::vector<Vertex>& vertices,
|
||||||
const std::vector<GLuint>& indices = {});
|
const std::vector<GLuint>& indices = {},
|
||||||
|
const std::vector<SubMesh>& submeshes = {});
|
||||||
Mesh(const std::vector<Vertex>& vertices,
|
Mesh(const std::vector<Vertex>& vertices,
|
||||||
const std::vector<GLuint>& indices,
|
const std::vector<GLuint>& indices,
|
||||||
|
const std::vector<SubMesh>& submeshes,
|
||||||
bool has_indices,
|
bool has_indices,
|
||||||
GLuint VBO, GLuint VAO, GLuint EBO)
|
GLuint VBO, GLuint VAO, GLuint EBO)
|
||||||
: m_vertices(vertices)
|
: m_vertices(vertices)
|
||||||
, m_indices(indices)
|
, m_indices(indices)
|
||||||
|
, m_submeshes(submeshes)
|
||||||
, m_has_indices(has_indices)
|
, m_has_indices(has_indices)
|
||||||
, m_VBO(VBO)
|
, m_VBO(VBO)
|
||||||
, m_VAO(VAO)
|
, m_VAO(VAO)
|
||||||
@@ -32,11 +43,15 @@ public:
|
|||||||
Mesh(Mesh&& other) noexcept;
|
Mesh(Mesh&& other) noexcept;
|
||||||
Mesh& operator=(Mesh&& other) noexcept;
|
Mesh& operator=(Mesh&& other) noexcept;
|
||||||
|
|
||||||
|
const std::vector<SubMesh>& get_submeshes() const;
|
||||||
|
|
||||||
void draw() const;
|
void draw() const;
|
||||||
|
void draw_submesh(GLuint index_offset, GLuint index_count) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<Vertex> m_vertices{};
|
std::vector<Vertex> m_vertices{};
|
||||||
std::vector<GLuint> m_indices{};
|
std::vector<GLuint> m_indices{};
|
||||||
|
std::vector<SubMesh> m_submeshes{};
|
||||||
bool m_has_indices{};
|
bool m_has_indices{};
|
||||||
|
|
||||||
GLuint m_VBO{};
|
GLuint m_VBO{};
|
||||||
|
|||||||
@@ -49,31 +49,65 @@ void Renderer::draw_scene(const Scene& scene, const Camera& camera,
|
|||||||
{
|
{
|
||||||
update_global_data(scene, camera, screen_width, screen_height);
|
update_global_data(scene, camera, screen_width, screen_height);
|
||||||
|
|
||||||
// Sort Entities: Shader -> Material -> Entities
|
struct DrawCommand
|
||||||
using RenderQueue = std::unordered_map<Shader*, std::unordered_map<Material*, std::vector<const Entity*>>>;
|
{
|
||||||
|
const Transform* transform;
|
||||||
|
const Mesh* mesh;
|
||||||
|
GLuint index_offset;
|
||||||
|
GLuint index_count;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Sort Entities: Shader -> Material -> SubMesh Draw Commands
|
||||||
|
using RenderQueue = std::unordered_map<Shader*, std::unordered_map<Material*, std::vector<DrawCommand>>>;
|
||||||
RenderQueue render_queue;
|
RenderQueue render_queue;
|
||||||
|
|
||||||
for (const auto& entity : scene.get_entities()) {
|
for (const auto& entity : scene.get_entities()) {
|
||||||
if (!entity.mesh || !entity.material || !entity.material->shader) {
|
if (!entity.mesh) { continue; }
|
||||||
// Ignore empty entities
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Shader* shader_ptr = entity.material->shader.get();
|
// If Entity has an explicit material override
|
||||||
Material* material_ptr = entity.material.get();
|
if (entity.material) {
|
||||||
render_queue[shader_ptr][material_ptr].push_back(&entity);
|
if (!entity.material->shader) { continue; }
|
||||||
|
|
||||||
|
Shader* shader_ptr = entity.material->shader.get();
|
||||||
|
Material* material_ptr = entity.material.get();
|
||||||
|
|
||||||
|
for (const auto& submesh : entity.mesh->get_submeshes()) {
|
||||||
|
render_queue[shader_ptr][material_ptr].push_back({
|
||||||
|
&entity.transform,
|
||||||
|
entity.mesh.get(),
|
||||||
|
submesh.index_offset,
|
||||||
|
submesh.index_count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Otherwise use SubMesh's native material
|
||||||
|
} else {
|
||||||
|
for (const auto& submesh : entity.mesh->get_submeshes()) {
|
||||||
|
if (!submesh.material || !submesh.material->shader) { continue; }
|
||||||
|
|
||||||
|
Shader* shader_ptr = submesh.material->shader.get();
|
||||||
|
Material* material_ptr = submesh.material.get();
|
||||||
|
|
||||||
|
render_queue[shader_ptr][material_ptr].push_back({
|
||||||
|
&entity.transform,
|
||||||
|
entity.mesh.get(),
|
||||||
|
submesh.index_offset,
|
||||||
|
submesh.index_count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Draw using the sorted queue
|
// Execute sorted queue for minimal state switching
|
||||||
for (const auto& [shader, materials] : render_queue) {
|
for (const auto& [shader, materials] : render_queue) {
|
||||||
shader->bind();
|
shader->bind();
|
||||||
|
|
||||||
for (const auto& [material, entities] : materials) {
|
for (const auto& [material, draw_command] : materials) {
|
||||||
material->bind();
|
material->bind();
|
||||||
|
|
||||||
for (const auto& entity : entities) {
|
for (const auto& cmd: draw_command) {
|
||||||
shader->set_mat4("u_model", entity->transform.get_model_matrix());
|
shader->set_mat4("u_model", cmd.transform->get_model_matrix());
|
||||||
entity->mesh->draw();
|
cmd.mesh->draw_submesh(cmd.index_offset, cmd.index_count);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,10 @@
|
|||||||
struct Material {
|
struct Material {
|
||||||
sampler2D diffuse;
|
sampler2D diffuse;
|
||||||
sampler2D specular;
|
sampler2D specular;
|
||||||
|
int use_diffuse_map;
|
||||||
|
int use_specular_map;
|
||||||
|
vec3 diffuse_color;
|
||||||
|
vec3 specular_color;
|
||||||
float shininess;
|
float shininess;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -59,6 +63,20 @@ in vec2 texture_coords;
|
|||||||
|
|
||||||
out vec4 frag_color;
|
out vec4 frag_color;
|
||||||
|
|
||||||
|
vec3 get_diffuse() {
|
||||||
|
if (u_material.use_diffuse_map == 1) {
|
||||||
|
return vec3(texture(u_material.diffuse, texture_coords)) * u_material.diffuse_color;
|
||||||
|
}
|
||||||
|
return u_material.diffuse_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 get_specular() {
|
||||||
|
if (u_material.use_specular_map == 1) {
|
||||||
|
return vec3(texture(u_material.specular, texture_coords)) * u_material.specular_color;
|
||||||
|
}
|
||||||
|
return u_material.specular_color;
|
||||||
|
}
|
||||||
|
|
||||||
vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir);
|
vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir);
|
||||||
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
||||||
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3 view_dir);
|
||||||
@@ -93,9 +111,9 @@ vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir)
|
|||||||
vec3 reflect_dir = reflect(-light_dir, normal);
|
vec3 reflect_dir = reflect(-light_dir, normal);
|
||||||
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
float spec = pow(max(dot(view_dir, reflect_dir), 0.0), u_material.shininess);
|
||||||
|
|
||||||
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||||
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
vec3 specular = light.specular.xyz * spec * get_specular();
|
||||||
|
|
||||||
return ambient + diffuse + specular;
|
return ambient + diffuse + specular;
|
||||||
}
|
}
|
||||||
@@ -110,9 +128,9 @@ vec3 calculate_point_light(PointLight light, vec3 normal, vec3 frag_position, ve
|
|||||||
float dist = length(light.position.xyz - frag_position);
|
float dist = length(light.position.xyz - frag_position);
|
||||||
float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist));
|
float attenuation = 1.0 / (light.attenuation.x + light.attenuation.y * dist + light.attenuation.z * (dist * dist));
|
||||||
|
|
||||||
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||||
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
vec3 specular = light.specular.xyz * spec * get_specular();
|
||||||
|
|
||||||
return (ambient + diffuse + specular) * attenuation;
|
return (ambient + diffuse + specular) * attenuation;
|
||||||
}
|
}
|
||||||
@@ -131,9 +149,9 @@ vec3 calculate_spot_light(SpotLight light, vec3 normal, vec3 frag_position, vec3
|
|||||||
float epsilon = light.cut_offs.x - light.cut_offs.y;
|
float epsilon = light.cut_offs.x - light.cut_offs.y;
|
||||||
float intensity = clamp((theta - light.cut_offs.y) / epsilon, 0.0, 1.0);
|
float intensity = clamp((theta - light.cut_offs.y) / epsilon, 0.0, 1.0);
|
||||||
|
|
||||||
vec3 ambient = light.ambient.xyz * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||||
vec3 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
vec3 specular = light.specular.xyz * spec * get_specular();
|
||||||
|
|
||||||
return (ambient + diffuse + specular) * attenuation * intensity;
|
return (ambient + diffuse + specular) * attenuation * intensity;
|
||||||
}
|
}
|
||||||
|
|||||||