Implement Mesh partitioning
103
README.md
@@ -28,75 +28,72 @@ This is the current state of the engine architecture:
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Application
|
||||
Renderer
|
||||
Scene
|
||||
Camera
|
||||
Framebuffer
|
||||
end
|
||||
subgraph Infrastructure["Infrastructure"]
|
||||
direction LR
|
||||
|
||||
subgraph World
|
||||
Entity
|
||||
Transform
|
||||
Mesh
|
||||
Material
|
||||
LightEnvironment
|
||||
end
|
||||
|
||||
subgraph SceneData["Scene Data"]
|
||||
Vertices
|
||||
Indices
|
||||
Shader
|
||||
Texture
|
||||
UniformBuffer
|
||||
DirectionalLight
|
||||
PointLight
|
||||
SpotLight
|
||||
subgraph Platform
|
||||
direction TB
|
||||
Window --> GLFW
|
||||
Window --> glad
|
||||
Window --> ImGui["Dear ImGui"]
|
||||
end
|
||||
|
||||
subgraph Loading
|
||||
Loader
|
||||
tinyobjloader
|
||||
stb_image
|
||||
direction TB
|
||||
Loader --> tinyobjloader
|
||||
Loader --> stb_image
|
||||
end
|
||||
end
|
||||
|
||||
subgraph Platform
|
||||
Window
|
||||
GLFW
|
||||
glad
|
||||
ImGui["Dear ImGui"]
|
||||
end
|
||||
|
||||
Renderer --> Scene
|
||||
subgraph Application
|
||||
direction LR
|
||||
Framebuffer
|
||||
Renderer --> Camera
|
||||
Renderer --> UniformBuffer
|
||||
Renderer --> Scene
|
||||
Renderer -->|Updates| UniformBuffer
|
||||
end
|
||||
|
||||
subgraph World
|
||||
direction LR
|
||||
|
||||
subgraph SceneGraph["Scene Graph"]
|
||||
direction TB
|
||||
Scene --> Entity
|
||||
Scene --> LightEnvironment
|
||||
|
||||
Entity --> Transform
|
||||
Entity --> Mesh
|
||||
Entity --> Material
|
||||
|
||||
Mesh --> Vertices
|
||||
Mesh --> Indices
|
||||
|
||||
Material --> Shader
|
||||
Material --> Texture
|
||||
Shader --> UniformBuffer
|
||||
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
|
||||
|
||||
Loader --> tinyobjloader
|
||||
tinyobjloader --> Entity
|
||||
subgraph SceneData["Scene Data"]
|
||||
direction LR
|
||||
|
||||
Texture --> stb_image
|
||||
subgraph Geometry
|
||||
direction TB
|
||||
Mesh --> Vertices
|
||||
Mesh --> Indices
|
||||
end
|
||||
|
||||
Window --> GLFW
|
||||
Window --> glad
|
||||
Window --> ImGui
|
||||
subgraph Resources
|
||||
direction TB
|
||||
Material --> Shader
|
||||
Material --> Texture
|
||||
UniformBuffer -->|Global data| Shader
|
||||
end
|
||||
end
|
||||
|
||||
tinyobjloader -->|Generates| Entity
|
||||
Texture -.->|Decodes via| stb_image
|
||||
|
||||
classDef application fill:#7c3aed,stroke:#4c1d95,color:#fff
|
||||
classDef world fill:#2563eb,stroke:#1e3a8a,color:#fff
|
||||
@@ -105,8 +102,8 @@ flowchart TB
|
||||
classDef platform fill:#64748b,stroke:#334155,color:#fff
|
||||
|
||||
class Renderer,Scene,Camera,Framebuffer application
|
||||
class Entity,Transform,Mesh,Material,LightEnvironment world
|
||||
class Vertices,Indices,Shader,Texture,UniformBuffer,DirectionalLight,PointLight,SpotLight data
|
||||
class Entity,Transform,Mesh,SubMesh,Material,LightEnvironment world
|
||||
class UniformBuffer,Shader,Texture,Vertices,Indices,DirectionalLight,PointLight,SpotLight data
|
||||
class Loader,tinyobjloader,stb_image loading
|
||||
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:
|
||||
Transform transform{};
|
||||
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)
|
||||
, material(material) {}
|
||||
};
|
||||
|
||||
138
src/loader.cpp
@@ -34,87 +34,129 @@ Opt<Entity> load_obj(const std::string& file_path, Rc<Shader> shader)
|
||||
std::string err;
|
||||
|
||||
fs::path parent_path = fs::path{file_path}.parent_path();
|
||||
parent_path += fs::path::preferred_separator;
|
||||
std::string base_dir = parent_path.string();
|
||||
std::string base_dir = !parent_path.empty()
|
||||
? parent_path.string() + fs::path::preferred_separator
|
||||
: "";
|
||||
|
||||
bool ok = tinyobj::LoadObj(&attrib, &shapes, &materials, &err,
|
||||
file_path.c_str(), base_dir.c_str());
|
||||
if (!ok || !err.empty()) {
|
||||
std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << std::endl << err;
|
||||
if (!ok) {
|
||||
std::cerr << "ERROR: LOAD_OBJ: Could not load " << file_path << ": " << err;
|
||||
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<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& index : shape.mesh.indices) {
|
||||
if (unique_vertices.count(index) == 0) {
|
||||
size_t index_offset = 0;
|
||||
|
||||
for (size_t f = 0; f < shape.mesh.num_face_vertices.size(); ++f) {
|
||||
size_t fv = static_cast<size_t>(shape.mesh.num_face_vertices[f]);
|
||||
int mat_id = shape.mesh.material_ids[f];
|
||||
|
||||
for (size_t v = 0; v < fv; ++v) {
|
||||
tinyobj::index_t idx = shape.mesh.indices[index_offset + v];
|
||||
|
||||
if (unique_vertices.count(idx) == 0) {
|
||||
Vertex vertex{};
|
||||
|
||||
vertex.position = {
|
||||
attrib.vertices[3 * index.vertex_index + 0],
|
||||
attrib.vertices[3 * index.vertex_index + 1],
|
||||
attrib.vertices[3 * index.vertex_index + 2]
|
||||
attrib.vertices[3 * idx.vertex_index + 0],
|
||||
attrib.vertices[3 * idx.vertex_index + 1],
|
||||
attrib.vertices[3 * idx.vertex_index + 2]
|
||||
};
|
||||
|
||||
if (index.normal_index >= 0) {
|
||||
if (idx.normal_index >= 0) {
|
||||
vertex.normal = {
|
||||
attrib.normals[3 * index.normal_index + 0],
|
||||
attrib.normals[3 * index.normal_index + 1],
|
||||
attrib.normals[3 * index.normal_index + 2]
|
||||
attrib.normals[3 * idx.normal_index + 0],
|
||||
attrib.normals[3 * idx.normal_index + 1],
|
||||
attrib.normals[3 * idx.normal_index + 2]
|
||||
};
|
||||
}
|
||||
|
||||
if (index.texcoord_index >= 0) {
|
||||
if (idx.texcoord_index >= 0) {
|
||||
vertex.texture_coords = {
|
||||
attrib.texcoords[2 * index.texcoord_index + 0],
|
||||
1.0f - attrib.texcoords[2 * index.texcoord_index + 1]
|
||||
attrib.texcoords[2 * idx.texcoord_index + 0],
|
||||
1.0f - attrib.texcoords[2 * idx.texcoord_index + 1]
|
||||
};
|
||||
}
|
||||
|
||||
unique_vertices[index] = static_cast<GLuint>(vertices.size());
|
||||
unique_vertices[idx] = static_cast<GLuint>(vertices.size());
|
||||
vertices.push_back(vertex);
|
||||
}
|
||||
|
||||
indices.push_back(unique_vertices[index]);
|
||||
mat_to_indices[mat_id].push_back(unique_vertices[idx]);
|
||||
}
|
||||
|
||||
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) {
|
||||
std::cerr << "ERROR: LOAD_OBJ: Could not create mesh" << std::endl;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
auto material = make_rc<Material>();
|
||||
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};
|
||||
return Entity{mesh};
|
||||
}
|
||||
|
||||
} // 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 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 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_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());
|
||||
|
||||
@@ -94,17 +94,17 @@ int main()
|
||||
/******************** Scene construction ********************/
|
||||
|
||||
// Car scene
|
||||
// Rc<Scene> car_scene = make_rc<Scene>();
|
||||
// user_data.scenes.push_back(car_scene);
|
||||
//
|
||||
// car_scene->add_entity(*car);
|
||||
//
|
||||
// PointLight light_white1;
|
||||
// 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.specular = glm::vec3(2.0f, 2.0f, 2.0f); // White highlights
|
||||
//
|
||||
// car_scene->light_environment.point_lights.push_back(light_white1);
|
||||
Rc<Scene> car_scene = make_rc<Scene>();
|
||||
user_data.scenes.push_back(car_scene);
|
||||
|
||||
car_scene->add_entity(*car);
|
||||
|
||||
PointLight light_white1;
|
||||
light_white1.position = glm::vec3(0.0f, 3.0f, 3.0f);
|
||||
light_white1.diffuse = glm::vec3(1.0f, 1.0f, 1.0f); // Bright white
|
||||
light_white1.specular = glm::vec3(1.0f, 1.0f, 1.0f); // White highlights
|
||||
|
||||
car_scene->light_environment.point_lights.push_back(light_white1);
|
||||
|
||||
// Backpack scene
|
||||
Rc<Scene> backpack_scene = make_rc<Scene>();
|
||||
|
||||
@@ -7,24 +7,27 @@ void Material::bind() const
|
||||
{
|
||||
shader->bind();
|
||||
|
||||
// Diffuse (slot 0)
|
||||
if (diffuse_map) {
|
||||
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_vec3("u_material.diffuse_color", diffuse_color);
|
||||
shader->set_vec3("u_material.specular_color", specular_color);
|
||||
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
|
||||
|
||||
@@ -12,6 +12,9 @@ struct Material
|
||||
Rc<Shader> shader;
|
||||
Rc<Texture> diffuse_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};
|
||||
|
||||
void bind() const;
|
||||
|
||||
45
src/mesh.cpp
@@ -7,7 +7,8 @@ namespace Fan
|
||||
{
|
||||
|
||||
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{};
|
||||
bool has_indices = !indices.empty();
|
||||
@@ -38,7 +39,18 @@ Rc<Mesh> Mesh::create(const std::vector<Vertex>& vertices,
|
||||
glEnableVertexAttribArray(1);
|
||||
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()
|
||||
@@ -51,6 +63,7 @@ Mesh::~Mesh()
|
||||
Mesh::Mesh(Mesh&& other) noexcept
|
||||
: m_vertices(std::move(other.m_vertices))
|
||||
, 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_VBO(std::exchange(other.m_VBO, 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_indices, other.m_indices);
|
||||
std::swap(m_submeshes, other.m_submeshes);
|
||||
std::swap(m_has_indices, other.m_has_indices);
|
||||
std::swap(m_VBO, other.m_VBO);
|
||||
std::swap(m_VAO, other.m_VAO);
|
||||
@@ -72,15 +86,34 @@ Mesh& Mesh::operator=(Mesh&& other) noexcept
|
||||
return *this;
|
||||
}
|
||||
|
||||
const std::vector<Mesh::SubMesh>& Mesh::get_submeshes() const
|
||||
{
|
||||
return m_submeshes;
|
||||
}
|
||||
|
||||
void Mesh::draw() const
|
||||
{
|
||||
glBindVertexArray(m_VAO);
|
||||
|
||||
if (m_has_indices) {
|
||||
glDrawElements(GL_TRIANGLES, m_indices.size(), GL_UNSIGNED_INT, 0);
|
||||
} else {
|
||||
glDrawArrays(GL_TRIANGLES, 0, m_vertices.size());
|
||||
for (const auto& submesh : m_submeshes) {
|
||||
draw_submesh(submesh.index_offset, submesh.index_count);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
17
src/mesh.hpp
@@ -5,6 +5,7 @@
|
||||
#include <glad/gl.h>
|
||||
|
||||
#include "vertex.hpp"
|
||||
#include "material.hpp"
|
||||
#include "utils.hpp"
|
||||
|
||||
namespace Fan
|
||||
@@ -13,14 +14,24 @@ namespace Fan
|
||||
class Mesh
|
||||
{
|
||||
public:
|
||||
struct SubMesh
|
||||
{
|
||||
GLuint index_offset{};
|
||||
GLuint index_count{};
|
||||
Rc<Material> material{};
|
||||
};
|
||||
|
||||
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,
|
||||
const std::vector<GLuint>& indices,
|
||||
const std::vector<SubMesh>& submeshes,
|
||||
bool has_indices,
|
||||
GLuint VBO, GLuint VAO, GLuint EBO)
|
||||
: m_vertices(vertices)
|
||||
, m_indices(indices)
|
||||
, m_submeshes(submeshes)
|
||||
, m_has_indices(has_indices)
|
||||
, m_VBO(VBO)
|
||||
, m_VAO(VAO)
|
||||
@@ -32,11 +43,15 @@ public:
|
||||
Mesh(Mesh&& other) noexcept;
|
||||
Mesh& operator=(Mesh&& other) noexcept;
|
||||
|
||||
const std::vector<SubMesh>& get_submeshes() const;
|
||||
|
||||
void draw() const;
|
||||
void draw_submesh(GLuint index_offset, GLuint index_count) const;
|
||||
|
||||
private:
|
||||
std::vector<Vertex> m_vertices{};
|
||||
std::vector<GLuint> m_indices{};
|
||||
std::vector<SubMesh> m_submeshes{};
|
||||
bool m_has_indices{};
|
||||
|
||||
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);
|
||||
|
||||
// Sort Entities: Shader -> Material -> Entities
|
||||
using RenderQueue = std::unordered_map<Shader*, std::unordered_map<Material*, std::vector<const Entity*>>>;
|
||||
struct DrawCommand
|
||||
{
|
||||
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;
|
||||
|
||||
for (const auto& entity : scene.get_entities()) {
|
||||
if (!entity.mesh || !entity.material || !entity.material->shader) {
|
||||
// Ignore empty entities
|
||||
continue;
|
||||
}
|
||||
if (!entity.mesh) { continue; }
|
||||
|
||||
// If Entity has an explicit material override
|
||||
if (entity.material) {
|
||||
if (!entity.material->shader) { continue; }
|
||||
|
||||
Shader* shader_ptr = entity.material->shader.get();
|
||||
Material* material_ptr = entity.material.get();
|
||||
render_queue[shader_ptr][material_ptr].push_back(&entity);
|
||||
|
||||
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
|
||||
});
|
||||
}
|
||||
|
||||
// Draw using the sorted queue
|
||||
// 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
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Execute sorted queue for minimal state switching
|
||||
for (const auto& [shader, materials] : render_queue) {
|
||||
shader->bind();
|
||||
|
||||
for (const auto& [material, entities] : materials) {
|
||||
for (const auto& [material, draw_command] : materials) {
|
||||
material->bind();
|
||||
|
||||
for (const auto& entity : entities) {
|
||||
shader->set_mat4("u_model", entity->transform.get_model_matrix());
|
||||
entity->mesh->draw();
|
||||
for (const auto& cmd: draw_command) {
|
||||
shader->set_mat4("u_model", cmd.transform->get_model_matrix());
|
||||
cmd.mesh->draw_submesh(cmd.index_offset, cmd.index_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,10 @@
|
||||
struct Material {
|
||||
sampler2D diffuse;
|
||||
sampler2D specular;
|
||||
int use_diffuse_map;
|
||||
int use_specular_map;
|
||||
vec3 diffuse_color;
|
||||
vec3 specular_color;
|
||||
float shininess;
|
||||
};
|
||||
|
||||
@@ -59,6 +63,20 @@ in vec2 texture_coords;
|
||||
|
||||
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_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);
|
||||
@@ -93,9 +111,9 @@ vec3 calculate_directional_light(DirLight light, vec3 normal, vec3 view_dir)
|
||||
vec3 reflect_dir = reflect(-light_dir, normal);
|
||||
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 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||
vec3 specular = light.specular.xyz * spec * get_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 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 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||
vec3 specular = light.specular.xyz * spec * get_specular();
|
||||
|
||||
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 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 diffuse = light.diffuse.xyz * diff * vec3(texture(u_material.diffuse, texture_coords));
|
||||
vec3 specular = light.specular.xyz * spec * vec3(texture(u_material.specular, texture_coords));
|
||||
vec3 ambient = light.ambient.xyz * get_diffuse();
|
||||
vec3 diffuse = light.diffuse.xyz * diff * get_diffuse();
|
||||
vec3 specular = light.specular.xyz * spec * get_specular();
|
||||
|
||||
return (ambient + diffuse + specular) * attenuation * intensity;
|
||||
}
|
||||
|
||||