Compare commits
3 commits
doom-rende
...
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 527b3e4c7d | |||
| b3b69f997e | |||
| 2d8e702bf3 |
18 changed files with 2219 additions and 1071 deletions
6
.gitignore
vendored
6
.gitignore
vendored
|
|
@ -22,6 +22,12 @@ err.txt
|
||||||
out.txt
|
out.txt
|
||||||
run_log.txt
|
run_log.txt
|
||||||
|
|
||||||
|
# Debugging, Autonomous Testing & Screenshots
|
||||||
|
screenshots/
|
||||||
|
/*.png
|
||||||
|
/*.json
|
||||||
|
!assets/**
|
||||||
|
|
||||||
# OS / Editor files
|
# OS / Editor files
|
||||||
.DS_Store
|
.DS_Store
|
||||||
Thumbs.db
|
Thumbs.db
|
||||||
|
|
|
||||||
|
|
@ -33,11 +33,11 @@ add_executable(DoorNroll
|
||||||
src/hacking/DbdSkillCheck.cpp
|
src/hacking/DbdSkillCheck.cpp
|
||||||
src/inventory/InventoryUI.cpp
|
src/inventory/InventoryUI.cpp
|
||||||
src/render/CyberRenderer.cpp
|
src/render/CyberRenderer.cpp
|
||||||
src/render/SoftwareRenderer.cpp
|
|
||||||
src/render/UIRenderer.cpp
|
src/render/UIRenderer.cpp
|
||||||
src/hub/HubRoom.cpp
|
src/hub/HubRoom.cpp
|
||||||
src/hub/RestRoom.cpp
|
src/hub/RestRoom.cpp
|
||||||
src/core/SaveSystem.cpp
|
src/core/SaveSystem.cpp
|
||||||
|
src/debug/DebugSystem.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(DoorNroll PRIVATE src)
|
target_include_directories(DoorNroll PRIVATE src)
|
||||||
|
|
|
||||||
398
assets/models/Bed.fbx
Normal file
398
assets/models/Bed.fbx
Normal file
|
|
@ -0,0 +1,398 @@
|
||||||
|
; FBX 7.3.0 project file
|
||||||
|
; Created by the Blockbench FBX Exporter
|
||||||
|
; ----------------------------------------------------
|
||||||
|
;
|
||||||
|
|
||||||
|
FBXHeaderExtension: {
|
||||||
|
FBXHeaderVersion: 1003
|
||||||
|
FBXVersion: 7300
|
||||||
|
EncryptionType: 0
|
||||||
|
CreationTimeStamp: {
|
||||||
|
Version: 1000
|
||||||
|
Year: 2026
|
||||||
|
Month: 10
|
||||||
|
Day: 7
|
||||||
|
Hour: 18
|
||||||
|
Minute: 44
|
||||||
|
Second: 12
|
||||||
|
Millisecond: 852
|
||||||
|
}
|
||||||
|
Creator: "Blockbench 5.1.6"
|
||||||
|
SceneInfo: "SceneInfo::GlobalInfo", "UserData" {
|
||||||
|
Type: "UserData"
|
||||||
|
Version: 100
|
||||||
|
MetaData: {
|
||||||
|
Version: 100
|
||||||
|
Title: ""
|
||||||
|
Subject: ""
|
||||||
|
Author: ""
|
||||||
|
Keywords: ""
|
||||||
|
Revision: ""
|
||||||
|
Comment: ""
|
||||||
|
}
|
||||||
|
Properties70: {
|
||||||
|
P: "DocumentUrl", "KString", "Url", "", "C:\Users\Blockbench\foobar.fbx"
|
||||||
|
P: "SrcDocumentUrl", "KString", "Url", "", "C:\Users\Blockbench\foobar.fbx"
|
||||||
|
P: "Original", "Compound", "", ""
|
||||||
|
P: "Original|ApplicationVendor", "KString", "", "", "Blockbench"
|
||||||
|
P: "Original|ApplicationName", "KString", "", "", "Blockbench FBX Exporter"
|
||||||
|
P: "Original|ApplicationVersion", "KString", "", "", "5.1.6"
|
||||||
|
P: "Original|DateTime_GMT", "DateTime", "", "", "01/01/1970 00:00:00.000"
|
||||||
|
P: "Original|FileName", "KString", "", "", "/foobar.fbx"
|
||||||
|
P: "LastSaved", "Compound", "", ""
|
||||||
|
P: "LastSaved|ApplicationVendor", "KString", "", "", "Blockbench"
|
||||||
|
P: "LastSaved|ApplicationName", "KString", "", "", "Blockbench FBX Exporter"
|
||||||
|
P: "LastSaved|ApplicationVersion", "KString", "", "", "5.1.6"
|
||||||
|
P: "LastSaved|DateTime_GMT", "DateTime", "", "", "01/01/1970 00:00:00.000"
|
||||||
|
P: "Original|ApplicationNativeFile", "KString", "", "", ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
FileId: "iVFoobar"
|
||||||
|
CreationTime: "2026-10-07 15:44:12:852"
|
||||||
|
Creator: "Made with Blockbench"
|
||||||
|
GlobalSettings: {
|
||||||
|
Version: 1000
|
||||||
|
Properties70: {
|
||||||
|
P: "UpAxis", "int", "Integer", "",1
|
||||||
|
P: "UpAxisSign", "int", "Integer", "",1
|
||||||
|
P: "FrontAxis", "int", "Integer", "",2
|
||||||
|
P: "FrontAxisSign", "int", "Integer", "",1
|
||||||
|
P: "CoordAxis", "int", "Integer", "",0
|
||||||
|
P: "CoordAxisSign", "int", "Integer", "",1
|
||||||
|
P: "OriginalUpAxis", "int", "Integer", "",-1
|
||||||
|
P: "OriginalUpAxisSign", "int", "Integer", "",1
|
||||||
|
P: "UnitScaleFactor", "double", "Number", "",1
|
||||||
|
P: "OriginalUnitScaleFactor", "double", "Number", "",1
|
||||||
|
P: "AmbientColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "DefaultCamera", "KString", "", "", "Producer Perspective"
|
||||||
|
P: "TimeMode", "enum", "", "",0
|
||||||
|
P: "TimeSpanStart", "KTime", "Time", "",0
|
||||||
|
P: "TimeSpanStop", "KTime", "Time", "",46186158000
|
||||||
|
P: "CustomFrameRate", "double", "Number", "",24
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Documents Description
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Documents: {
|
||||||
|
Count: 1
|
||||||
|
Document: 1588043550 {
|
||||||
|
Scene: "Scene"
|
||||||
|
Properties70: {
|
||||||
|
P: "SourceObject", "object", "", ""
|
||||||
|
P: "ActiveAnimStackName", "KString", "", "", ""
|
||||||
|
}
|
||||||
|
RootNode: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Document References
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
References:
|
||||||
|
|
||||||
|
; Object definitions
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Definitions: {
|
||||||
|
Version: 100
|
||||||
|
Count: 6
|
||||||
|
ObjectType: "GlobalSettings" {
|
||||||
|
Count: 1
|
||||||
|
}
|
||||||
|
ObjectType: "Model" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxNode" {
|
||||||
|
Properties70: {
|
||||||
|
P: "QuaternionInterpolate", "enum", "", "",0
|
||||||
|
P: "RotationOffset", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingOffset", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationActive", "bool", "", "",0
|
||||||
|
P: "TranslationMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TranslationMinX", "bool", "", "",0
|
||||||
|
P: "TranslationMinY", "bool", "", "",0
|
||||||
|
P: "TranslationMinZ", "bool", "", "",0
|
||||||
|
P: "TranslationMaxX", "bool", "", "",0
|
||||||
|
P: "TranslationMaxY", "bool", "", "",0
|
||||||
|
P: "TranslationMaxZ", "bool", "", "",0
|
||||||
|
P: "RotationOrder", "enum", "", "",5
|
||||||
|
P: "RotationSpaceForLimitOnly", "bool", "", "",0
|
||||||
|
P: "RotationStiffnessX", "double", "Number", "",0
|
||||||
|
P: "RotationStiffnessY", "double", "Number", "",0
|
||||||
|
P: "RotationStiffnessZ", "double", "Number", "",0
|
||||||
|
P: "AxisLen", "double", "Number", "",10
|
||||||
|
P: "PreRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "PostRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationActive", "bool", "", "",0
|
||||||
|
P: "RotationMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "RotationMinX", "bool", "", "",0
|
||||||
|
P: "RotationMinY", "bool", "", "",0
|
||||||
|
P: "RotationMinZ", "bool", "", "",0
|
||||||
|
P: "RotationMaxX", "bool", "", "",0
|
||||||
|
P: "RotationMaxY", "bool", "", "",0
|
||||||
|
P: "RotationMaxZ", "bool", "", "",0
|
||||||
|
P: "InheritType", "enum", "", "",0
|
||||||
|
P: "ScalingActive", "bool", "", "",0
|
||||||
|
P: "ScalingMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "ScalingMax", "Vector3D", "Vector", "",1,1,1
|
||||||
|
P: "ScalingMinX", "bool", "", "",0
|
||||||
|
P: "ScalingMinY", "bool", "", "",0
|
||||||
|
P: "ScalingMinZ", "bool", "", "",0
|
||||||
|
P: "ScalingMaxX", "bool", "", "",0
|
||||||
|
P: "ScalingMaxY", "bool", "", "",0
|
||||||
|
P: "ScalingMaxZ", "bool", "", "",0
|
||||||
|
P: "GeometricTranslation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "GeometricRotation", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "GeometricScaling", "Vector3D", "Vector", "",1,1,1
|
||||||
|
P: "MinDampRangeX", "double", "Number", "",0
|
||||||
|
P: "MinDampRangeY", "double", "Number", "",0
|
||||||
|
P: "MinDampRangeZ", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeX", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeY", "double", "Number", "",0
|
||||||
|
P: "MaxDampRangeZ", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthX", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthY", "double", "Number", "",0
|
||||||
|
P: "MinDampStrengthZ", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthX", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthY", "double", "Number", "",0
|
||||||
|
P: "MaxDampStrengthZ", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleX", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleY", "double", "Number", "",0
|
||||||
|
P: "PreferedAngleZ", "double", "Number", "",0
|
||||||
|
P: "LookAtProperty", "object", "", ""
|
||||||
|
P: "UpVectorProperty", "object", "", ""
|
||||||
|
P: "Show", "bool", "", "",1
|
||||||
|
P: "NegativePercentShapeSupport", "bool", "", "",1
|
||||||
|
P: "DefaultAttributeIndex", "int", "Integer", "",-1
|
||||||
|
P: "Freeze", "bool", "", "",0
|
||||||
|
P: "LODBox", "bool", "", "",0
|
||||||
|
P: "Lcl Translation", "Lcl Translation", "", "A",0,0,0
|
||||||
|
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
|
||||||
|
P: "Lcl Scaling", "Lcl Scaling", "", "A",1,1,1
|
||||||
|
P: "Visibility", "Visibility", "", "A",1
|
||||||
|
P: "Visibility Inheritance", "Visibility Inheritance", "", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Geometry" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxMesh" {
|
||||||
|
Properties70: {
|
||||||
|
P: "Color", "ColorRGB", "Color", "",0.8,0.8,0.8
|
||||||
|
P: "BBoxMin", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "BBoxMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Primary Visibility", "bool", "", "",1
|
||||||
|
P: "Casts Shadows", "bool", "", "",1
|
||||||
|
P: "Receive Shadows", "bool", "", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Material" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxSurfaceLambert" {
|
||||||
|
Properties70: {
|
||||||
|
P: "ShadingModel", "KString", "", "", "Lambert"
|
||||||
|
P: "MultiLayer", "bool", "", "",0
|
||||||
|
P: "EmissiveColor", "Color", "", "A",0,0,0
|
||||||
|
P: "EmissiveFactor", "Number", "", "A",1
|
||||||
|
P: "AmbientColor", "Color", "", "A",0.2,0.2,0.2
|
||||||
|
P: "AmbientFactor", "Number", "", "A",1
|
||||||
|
P: "DiffuseColor", "Color", "", "A",0.8,0.8,0.8
|
||||||
|
P: "DiffuseFactor", "Number", "", "A",1
|
||||||
|
P: "Bump", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "NormalMap", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "BumpFactor", "double", "Number", "",1
|
||||||
|
P: "TransparentColor", "Color", "", "A",0,0,0
|
||||||
|
P: "TransparencyFactor", "Number", "", "A",0
|
||||||
|
P: "DisplacementColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "DisplacementFactor", "double", "Number", "",1
|
||||||
|
P: "VectorDisplacementColor", "ColorRGB", "Color", "",0,0,0
|
||||||
|
P: "VectorDisplacementFactor", "double", "Number", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Texture" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxFileTexture" {
|
||||||
|
Properties70: {
|
||||||
|
P: "TextureTypeUse", "enum", "", "",0
|
||||||
|
P: "Texture alpha", "Number", "", "A",1
|
||||||
|
P: "CurrentMappingType", "enum", "", "",0
|
||||||
|
P: "WrapModeU", "enum", "", "",0
|
||||||
|
P: "WrapModeV", "enum", "", "",0
|
||||||
|
P: "UVSwap", "bool", "", "",0
|
||||||
|
P: "PremultiplyAlpha", "bool", "", "",1
|
||||||
|
P: "Translation", "Vector", "", "A",0,0,0
|
||||||
|
P: "Rotation", "Vector", "", "A",0,0,0
|
||||||
|
P: "Scaling", "Vector", "", "A",1,1,1
|
||||||
|
P: "TextureRotationPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "TextureScalingPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "CurrentTextureBlendMode", "enum", "", "",1
|
||||||
|
P: "UVSet", "KString", "", "", "default"
|
||||||
|
P: "UseMaterial", "bool", "", "",0
|
||||||
|
P: "UseMipMap", "bool", "", "",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ObjectType: "Video" {
|
||||||
|
Count: 1
|
||||||
|
PropertyTemplate: "FbxVideo" {
|
||||||
|
Properties70: {
|
||||||
|
P: "ImageSequence", "bool", "", "",0
|
||||||
|
P: "ImageSequenceOffset", "int", "Integer", "",0
|
||||||
|
P: "FrameRate", "double", "Number", "",0
|
||||||
|
P: "LastFrame", "int", "Integer", "",0
|
||||||
|
P: "Width", "int", "Integer", "",0
|
||||||
|
P: "Height", "int", "Integer", "",0
|
||||||
|
P: "Path", "KString", "XRefUrl", "", ""
|
||||||
|
P: "StartFrame", "int", "Integer", "",0
|
||||||
|
P: "StopFrame", "int", "Integer", "",0
|
||||||
|
P: "PlaySpeed", "double", "Number", "",0
|
||||||
|
P: "Offset", "KTime", "Time", "",0
|
||||||
|
P: "InterlaceMode", "enum", "", "",0
|
||||||
|
P: "FreeRunning", "bool", "", "",0
|
||||||
|
P: "Loop", "bool", "", "",0
|
||||||
|
P: "AccessMode", "enum", "", "",0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Object properties
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Objects: {
|
||||||
|
Model: 70061392, "Model::mesh", "Mesh" {
|
||||||
|
Version: 232
|
||||||
|
Properties70: {
|
||||||
|
P: "RotationActive", "bool", "", "",1
|
||||||
|
P: "InheritType", "enum", "", "",1
|
||||||
|
P: "ScalingMax", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Lcl Translation", "Lcl Translation", "", "A",0,-3.125,-6.25
|
||||||
|
P: "RotationPivot", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Lcl Rotation", "Lcl Rotation", "", "A",0,0,0
|
||||||
|
P: "RotationOrder", "enum", "", "",5
|
||||||
|
P: "DefaultAttributeIndex", "int", "Integer", "",0
|
||||||
|
}
|
||||||
|
Shading: Y
|
||||||
|
Culling: "CullingOff"
|
||||||
|
}
|
||||||
|
Geometry: 79347176, "Geometry::mesh", "Mesh" {
|
||||||
|
Vertices: *240 {
|
||||||
|
a: 40.625,32.8125,40.625,40.625,25,-40.625,40.625,17.1875,40.625,40.625,17.1875,-40.625,-40.625,32.8125,40.625,-40.625,25,-40.625,-40.625,17.1875,40.625,-40.625,17.1875,-40.625,-37.5,15.625,-37.5,-37.5,15.625,-43.75,-37.5,3.125,-37.5,-37.5,3.125,-43.75,-43.75,15.625,-37.5,-43.75,15.625,-43.75,-43.75,3.125,-37.5,-43.75,3.125,-43.75,43.75,15.625,-37.5,43.75,15.625,-43.75,43.75,3.125,-37.5,43.75,3.125,-43.75,37.5,15.625,-37.5,37.5,15.625,-43.75,37.5,3.125,-37.5,37.5,3.125,-43.75,43.75,15.625,56.25,43.75,15.625,50,43.75,3.125,56.25,43.75,3.125,50,37.5,15.625,56.25,37.5,15.625,50,37.5,3.125,56.25,37.5,3.125,50,-37.5,15.625,56.25,-37.5,15.625,50,-37.5,3.125,56.25,-37.5,3.125,50,-43.75,15.625,56.25,-43.75,15.625,50,-43.75,3.125,56.25,-43.75,3.125,50,43.75,21.875,56.25,43.75,21.875,-43.75,43.75,15.625,56.25,43.75,15.625,-43.75,-43.75,21.875,56.25,-43.75,21.875,-43.75,-43.75,15.625,56.25,-43.75,15.625,-43.75,43.75,53.125,56.25,43.75,53.125,53.125,43.75,21.875,56.25,43.75,21.875,53.125,-43.75,53.125,56.25,-43.75,53.125,53.125,-43.75,21.875,56.25,-43.75,21.875,53.125,15.000000000000002,31.25,46.56249999999999,15.000000000000002,31.25,31.562500000000004,-15.000000000000002,31.25,46.56249999999999,-15.000000000000002,31.25,31.562500000000004,15.000000000000002,21.875,46.56249999999999,15.000000000000002,21.875,31.562500000000004,-15.000000000000002,21.875,46.56249999999999,-15.000000000000002,21.875,31.562500000000004,-25,26.5625,46.56249999999999,-25,26.5625,46.56249999999999,-25,26.5625,31.562500000000004,-25,26.5625,31.562500000000004,25,26.5625,46.56249999999999,25,26.5625,46.56249999999999,25,26.5625,31.562500000000004,25,26.5625,31.562500000000004,15.000000000000002,26.5625,26.5625,15.000000000000002,26.5625,26.5625,-15.000000000000002,26.5625,26.5625,-15.000000000000002,26.5625,26.5625,15.000000000000002,26.5625,51.5625,15.000000000000002,26.5625,51.5625,-15.000000000000002,26.5625,51.5625,-15.000000000000002,26.5625,51.5625
|
||||||
|
}
|
||||||
|
PolygonVertexIndex: *264 {
|
||||||
|
a: 1,0,2,-4,6,4,5,-8,4,0,1,-6,3,2,6,-8,2,0,4,-7,5,1,3,-8,9,8,10,-12,14,12,13,-16,12,8,9,-14,11,10,14,-16,10,8,12,-15,13,9,11,-16,17,16,18,-20,22,20,21,-24,20,16,17,-22,19,18,22,-24,18,16,20,-23,21,17,19,-24,25,24,26,-28,30,28,29,-32,28,24,25,-30,27,26,30,-32,26,24,28,-31,29,25,27,-32,33,32,34,-36,38,36,37,-40,36,32,33,-38,35,34,38,-40,34,32,36,-39,37,33,35,-40,41,40,42,-44,46,44,45,-48,44,40,41,-46,43,42,46,-48,42,40,44,-47,45,41,43,-48,49,48,50,-52,54,52,53,-56,52,48,49,-54,51,50,54,-56,50,48,52,-55,53,49,51,-56,69,68,71,-71,64,65,67,-67,56,57,59,-59,63,61,60,-63,76,77,79,-79,73,72,75,-75,57,56,69,-72,65,58,59,-68,77,56,58,-80,59,57,73,-76,68,60,61,-71,63,62,64,-67,62,60,76,-79,72,61,63,-75,72,73,70,-72,77,76,68,-70,75,74,66,-68,78,79,64,-66,60,68,-77,62,78,-65,61,72,-71,63,66,-75,56,77,-70,58,65,-80,57,71,-74,59,75,-68
|
||||||
|
}
|
||||||
|
GeometryVersion: 124
|
||||||
|
LayerElementNormal: 0 {
|
||||||
|
Version: 101
|
||||||
|
Name: ""
|
||||||
|
MappingInformationType: "ByPolygon"
|
||||||
|
ReferenceInformationType: "Direct"
|
||||||
|
Normals: *204 {
|
||||||
|
a: 1,0,0,-1,0,0,0,0.9954090291002811,-0.0957124066442578,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,1,0,0,-1,0,0,0,0,0,0,0,0.42443387623071965,0.9054589359588684,0,-0.42443387623071965,0.9054589359588684,0,0,0.7295372041400855,0.6839411288813295,0,0.7295372041400855,-0.6839411288813295,0.42443387623071965,-0.9054589359588684,0,-0.42443387623071965,-0.9054589359588684,0,0,-0.7295372041400855,0.6839411288813295,0,-0.7295372041400855,-0.6839411288813295,0,0,0,0,0,0,0,0,0,0,0,0,0.3235735780316221,-0.6902902998007938,0.6471471560632436,-0.3235735780316221,-0.6902902998007938,0.6471471560632436,0.3235735780316221,-0.6902902998007938,-0.6471471560632436,-0.3235735780316221,-0.6902902998007938,-0.6471471560632436,0.3235735780316221,0.6902902998007938,0.6471471560632436,-0.3235735780316221,0.6902902998007938,0.6471471560632436,0.3235735780316221,0.6902902998007938,-0.6471471560632436,-0.3235735780316221,0.6902902998007938,-0.6471471560632436
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerElementUV: 0 {
|
||||||
|
Version: 101
|
||||||
|
Name: ""
|
||||||
|
MappingInformationType: "ByPolygonVertex"
|
||||||
|
ReferenceInformationType: "Direct"
|
||||||
|
UV: *528 {
|
||||||
|
a: 0.640625,0.52734375,0.4375,0.546875,0.4375,0.5,0.640625,0.5,0.671875,0.953125,0.671875,1,0.46875,0.98046875,0.46875,0.953125,0.421875,0.734375,0.21875,0.734375,0.21875,0.53125,0.421875,0.53125,0.203125,0.53125,0.203125,0.734375,0,0.734375,0,0.53125,0.671875,0.890625,0.671875,0.9375,0.46875,0.9375,0.46875,0.890625,0.671875,0.453125,0.46875,0.453125,0.46875,0.4375,0.671875,0.4375,0.078125,0.375,0.0625,0.375,0.0625,0.34375,0.078125,0.34375,0.109375,0.34375,0.109375,0.375,0.09375,0.375,0.09375,0.34375,0.265625,0.5,0.28125,0.5,0.28125,0.515625,0.265625,0.515625,0.3125,0.5,0.3125,0.515625,0.296875,0.515625,0.296875,0.5,0.140625,0.34375,0.140625,0.375,0.125,0.375,0.125,0.34375,0.640625,0.875,0.625,0.875,0.625,0.84375,0.640625,0.84375,0.171875,0.375,0.15625,0.375,0.15625,0.34375,0.171875,0.34375,0.640625,0.796875,0.640625,0.828125,0.625,0.828125,0.625,0.796875,0.328125,0.5,0.34375,0.5,0.34375,0.515625,0.328125,0.515625,0.375,0.5,0.375,0.515625,0.359375,0.515625,0.359375,0.5,0.203125,0.34375,0.203125,0.375,0.1875,0.375,0.1875,0.34375,0.234375,0.375,0.21875,0.375,0.21875,0.34375,0.234375,0.34375,0.640625,0.78125,0.625,0.78125,0.625,0.75,0.640625,0.75,0.265625,0.34375,0.265625,0.375,0.25,0.375,0.25,0.34375,0.390625,0.5,0.40625,0.5,0.40625,0.515625,0.390625,0.515625,0.484375,0.359375,0.484375,0.375,0.46875,0.375,0.46875,0.359375,0.296875,0.34375,0.296875,0.375,0.28125,0.375,0.28125,0.34375,0.328125,0.375,0.3125,0.375,0.3125,0.34375,0.328125,0.34375,0.359375,0.375,0.34375,0.375,0.34375,0.34375,0.359375,0.34375,0.390625,0.34375,0.390625,0.375,0.375,0.375,0.375,0.34375,0.5,0.359375,0.515625,0.359375,0.515625,0.375,0.5,0.375,0.546875,0.359375,0.546875,0.375,0.53125,0.375,0.53125,0.359375,0.421875,0.34375,0.421875,0.375,0.40625,0.375,0.40625,0.34375,0.453125,0.375,0.4375,0.375,0.4375,0.34375,0.453125,0.34375,0.25,0.515625,0,0.515625,0,0.5,0.25,0.5,0.25,0.46875,0.25,0.484375,0,0.484375,0,0.46875,0,0.75,0.21875,0.75,0.21875,1,0,1,0.453125,0.75,0.453125,1,0.234375,1,0.234375,0.75,0.484375,0.46875,0.484375,0.484375,0.265625,0.484375,0.265625,0.46875,0.71875,0.484375,0.5,0.484375,0.5,0.46875,0.71875,0.46875,0.015625,0.375,0,0.375,0,0.296875,0.015625,0.296875,0.046875,0.296875,0.046875,0.375,0.03125,0.375,0.03125,0.296875,0,0.4375,0.21875,0.4375,0.21875,0.453125,0,0.453125,0.453125,0.4375,0.453125,0.453125,0.234375,0.453125,0.234375,0.4375,0.65625,0.65625,0.65625,0.734375,0.4375,0.734375,0.4375,0.65625,0.65625,0.640625,0.4375,0.640625,0.4375,0.5625,0.65625,0.5625,0,0.96875,0,0.96875,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.5765625,0.8375,0.5765625,0.875,0.5015625,0.875,0.5015625,0.8375,0.5015625,0.765625,0.5765625,0.765625,0.5765625,0.803125,0.5015625,0.803125,0.078125,1,0.078125,1,0,1,0,1,0.078125,1,0.078125,1,0,1,0,1,0.5765625,0.875,0.5765625,0.8375,0.6041734375,0.8374999999999999,0.6041734375,0.875,0.47395156250000003,0.8375,0.5015625,0.8375,0.5015625,0.875,0.47395156250000003,0.875,0.1078125,0.40474062499999997,0.1078125,0.421875,0.0328125,0.421875,0.0328125,0.40474062499999997,0.4203125,0.421875,0.3453125,0.421875,0.3453125,0.40474062499999997,0.4203125,0.40474062499999997,0.6041734375,0.803125,0.5765625,0.803125,0.5765625,0.765625,0.6041734375,0.765625,0.5015625,0.765625,0.5015625,0.803125,0.47395156250000003,0.803125,0.47395156250000003,0.765625,0.1890625,0.390625,0.2640625,0.390625,0.2640625,0.40775937500000003,0.1890625,0.40775937500000003,0.5015625,0.40775937500000003,0.5015625,0.390625,0.5765625,0.390625,0.5765625,0.40775937500000003,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.03125,1,0.03125,1,0.03125,1,0.03125,1,0,1,0,1,0,0.96875,0,0.96875,0,1,0,1,0.2640625,0.390625,0.29048438787957764,0.39863961954952054,0.2640625,0.407759580463386,0.18906250000000002,0.390625,0.1890625,0.407759580463386,0.16264061212042233,0.39863961954952054,0.5015625,0.390625,0.5015625,0.407759580463386,0.47514208911119205,0.3986401293578338,0.5765625,0.390625,0.602982910888808,0.3986401293578338,0.5765625,0.407759580463386,0.10781249999999999,0.421875,0.1078125,0.404740419536614,0.13423438787957767,0.41386038045047946,0.03281250000000001,0.421875,0.006390612120422329,0.41386038045047946,0.0328125,0.404740419536614,0.3453125,0.421875,0.31889208911119205,0.4138598706421662,0.3453125,0.404740419536614,0.4203125,0.421875,0.4203125,0.404740419536614,0.44673291088880795,0.4138598706421662
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LayerElementMaterial: 0 {
|
||||||
|
Version: 101
|
||||||
|
Name: ""
|
||||||
|
MappingInformationType: "AllSame"
|
||||||
|
ReferenceInformationType: "IndexToDirect"
|
||||||
|
Materials: *1 {
|
||||||
|
a: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Layer: 0 {
|
||||||
|
Version: 100
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementNormal"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementMaterial"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
LayerElement: {
|
||||||
|
Type: "LayerElementUV"
|
||||||
|
TypedIndex: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Material: 77933162, "Material::Bed_textures", "" {
|
||||||
|
Version: 102
|
||||||
|
ShadingModel: "lambert"
|
||||||
|
MultiLayer: 0
|
||||||
|
Properties70: {
|
||||||
|
P: "Emissive", "Vector3D", "Vector", "",0,0,0
|
||||||
|
P: "Ambient", "Vector3D", "Vector", "",0.2,0.2,0.2
|
||||||
|
P: "Diffuse", "Vector3D", "Vector", "",0.8,0.8,0.8
|
||||||
|
P: "Opacity", "double", "Number", "",1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Texture: 77056915, "Texture::Bed_textures", "" {
|
||||||
|
Type: "TextureVideoClip"
|
||||||
|
Version: 202
|
||||||
|
TextureName: "Texture::Bed_textures"
|
||||||
|
Media: "Video::Bed_textures"
|
||||||
|
FileName: ""
|
||||||
|
RelativeFilename: ""
|
||||||
|
ModelUVTranslation: 0,0
|
||||||
|
ModelUVScaling: 1,1
|
||||||
|
Texture_Alpha_Source: "None"
|
||||||
|
Cropping: 0,0,0,0
|
||||||
|
}
|
||||||
|
Video: 74972089, "Video::Bed_textures", "Clip" {
|
||||||
|
Type: "Clip"
|
||||||
|
Properties70: {
|
||||||
|
P: "Path", "KString", "XRefUrl", "", "Bed_textures"
|
||||||
|
}
|
||||||
|
UseMipMap: 0
|
||||||
|
Filename: ""
|
||||||
|
RelativeFilename: ""
|
||||||
|
Content: "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAYAAACqaXHeAAACxElEQVR4AeyZPY4UMRCFZ1oiIweJgAtwFJIVARkSdyBAe4AVAXdAIiNAm3AULkCABDkZQbOfpbeydtfTrmr/MJZH81Q9dj3bVX62u3uWzx9erx4cbj4eHpwb6u3378XFmotbUsGLpWBbpqYUtIlUwblbAirE4mqyWwIeXV8frXBFuEHqlgAtAYvdiMVV3S0BrtFWIM0EVEhqVpPW9Y9/VsNGp24KWDM+xHL3voGykuiWgJJB7Gnrv0/Am/dfjnsC3OJ2S8Ax48Pgh10C8Rbw6u27VYjLSUBtdFNAHNizJ08PQlzO9bBLgOCEn79/HQSVyQ67BOIt4Ounj0chLlcSStq7bS1IzAMa8vDgwM2F1T+3Xfkt2nwsFrLlIUa+8JC0B3BrIGyC2oBybI1B9GwzJKDnAHr3PRPQewZ6999cAezqHtRKVPMEeE4AOMMkoFYg3nabK8A70Fq8mYBamT2XdpsrwHMCwKmV0JAAPYrm2L0D0XNBjt3bVw5/0WOoxdIwr6mtgGeBkmThWH3df49zNgM6/Pb9x4rliRKb+k1da2z1534fwLoEdPDyxfPw5hYVnfpNnVU1+MOrhSIKQAkeEJSHBwduCRRRAErwgAA8PDhwS8CtADpnzXsAVxucxcIrDbcCGAhr3gO4rG0r4JXGEv8RkbpWp6l6yuXDCbAF+Z5Sj3xq24XX0Kc6UX0c5EP+qteJ8JAPZaoneH6nsFWf4lnLw50gQaagBlP1cbl8CTIF+eQsHfnWtGETpAMdLVb75+oq3ARZefJnudB/qxmnrxhBAXFBq2sdZSiFPlEEtjXuJUADy7WPLy/DXWCuv/xaB5rq714CUo6jls8ESJKyo850Kq6pgFRmzrXcOu6pAGvGRvOfChhtRq3xTAVYMzaa/1TAaDNqjWcqwJqx0fynAkabUWs8UwHWjI3mPxVw7jO6d/z/AAAA//+eL7gsAAAABklEQVQDAFQy4r0Mepw6AAAAAElFTkSuQmCC"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
; Object connections
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Connections: {
|
||||||
|
|
||||||
|
;Model::mesh, Model::RootNode
|
||||||
|
C: "OO",70061392,0
|
||||||
|
|
||||||
|
;Geometry::mesh, Model::mesh
|
||||||
|
C: "OO",79347176,70061392
|
||||||
|
|
||||||
|
;Material::Bed_textures, Model::mesh
|
||||||
|
C: "OO",77933162,70061392
|
||||||
|
|
||||||
|
;Texture::Bed_textures, Material::Bed_textures
|
||||||
|
C: "OP",77056915,77933162, "DiffuseColor"
|
||||||
|
|
||||||
|
;Video::Bed_textures, Texture::Bed_textures
|
||||||
|
C: "OO",74972089,77056915
|
||||||
|
}
|
||||||
|
|
||||||
|
; Takes section
|
||||||
|
;------------------------------------------------------------------
|
||||||
|
|
||||||
|
Takes: {
|
||||||
|
Current: ""
|
||||||
|
}
|
||||||
3
assets/models/Bed.mtl
Normal file
3
assets/models/Bed.mtl
Normal file
|
|
@ -0,0 +1,3 @@
|
||||||
|
newmtl BedMaterial
|
||||||
|
Kd 1.0 1.0 1.0
|
||||||
|
map_Kd Bed_textures.png
|
||||||
544
assets/models/Bed.obj
Normal file
544
assets/models/Bed.obj
Normal file
|
|
@ -0,0 +1,544 @@
|
||||||
|
mtllib Bed.mtl
|
||||||
|
o Bed
|
||||||
|
v 40.625000 29.687500 40.625000
|
||||||
|
v 40.625000 21.875000 -40.625000
|
||||||
|
v 40.625000 14.062500 40.625000
|
||||||
|
v 40.625000 14.062500 -40.625000
|
||||||
|
v -40.625000 29.687500 40.625000
|
||||||
|
v -40.625000 21.875000 -40.625000
|
||||||
|
v -40.625000 14.062500 40.625000
|
||||||
|
v -40.625000 14.062500 -40.625000
|
||||||
|
v -37.500000 12.500000 -37.500000
|
||||||
|
v -37.500000 12.500000 -43.750000
|
||||||
|
v -37.500000 0.000000 -37.500000
|
||||||
|
v -37.500000 0.000000 -43.750000
|
||||||
|
v -43.750000 12.500000 -37.500000
|
||||||
|
v -43.750000 12.500000 -43.750000
|
||||||
|
v -43.750000 0.000000 -37.500000
|
||||||
|
v -43.750000 0.000000 -43.750000
|
||||||
|
v 43.750000 12.500000 -37.500000
|
||||||
|
v 43.750000 12.500000 -43.750000
|
||||||
|
v 43.750000 0.000000 -37.500000
|
||||||
|
v 43.750000 0.000000 -43.750000
|
||||||
|
v 37.500000 12.500000 -37.500000
|
||||||
|
v 37.500000 12.500000 -43.750000
|
||||||
|
v 37.500000 0.000000 -37.500000
|
||||||
|
v 37.500000 0.000000 -43.750000
|
||||||
|
v 43.750000 12.500000 56.250000
|
||||||
|
v 43.750000 12.500000 50.000000
|
||||||
|
v 43.750000 0.000000 56.250000
|
||||||
|
v 43.750000 0.000000 50.000000
|
||||||
|
v 37.500000 12.500000 56.250000
|
||||||
|
v 37.500000 12.500000 50.000000
|
||||||
|
v 37.500000 0.000000 56.250000
|
||||||
|
v 37.500000 0.000000 50.000000
|
||||||
|
v -37.500000 12.500000 56.250000
|
||||||
|
v -37.500000 12.500000 50.000000
|
||||||
|
v -37.500000 0.000000 56.250000
|
||||||
|
v -37.500000 0.000000 50.000000
|
||||||
|
v -43.750000 12.500000 56.250000
|
||||||
|
v -43.750000 12.500000 50.000000
|
||||||
|
v -43.750000 0.000000 56.250000
|
||||||
|
v -43.750000 0.000000 50.000000
|
||||||
|
v 43.750000 18.750000 56.250000
|
||||||
|
v 43.750000 18.750000 -43.750000
|
||||||
|
v 43.750000 12.500000 56.250000
|
||||||
|
v 43.750000 12.500000 -43.750000
|
||||||
|
v -43.750000 18.750000 56.250000
|
||||||
|
v -43.750000 18.750000 -43.750000
|
||||||
|
v -43.750000 12.500000 56.250000
|
||||||
|
v -43.750000 12.500000 -43.750000
|
||||||
|
v 43.750000 50.000000 56.250000
|
||||||
|
v 43.750000 50.000000 53.125000
|
||||||
|
v 43.750000 18.750000 56.250000
|
||||||
|
v 43.750000 18.750000 53.125000
|
||||||
|
v -43.750000 50.000000 56.250000
|
||||||
|
v -43.750000 50.000000 53.125000
|
||||||
|
v -43.750000 18.750000 56.250000
|
||||||
|
v -43.750000 18.750000 53.125000
|
||||||
|
v 15.000000 28.125000 46.562500
|
||||||
|
v 15.000000 28.125000 31.562500
|
||||||
|
v -15.000000 28.125000 46.562500
|
||||||
|
v -15.000000 28.125000 31.562500
|
||||||
|
v 15.000000 18.750000 46.562500
|
||||||
|
v 15.000000 18.750000 31.562500
|
||||||
|
v -15.000000 18.750000 46.562500
|
||||||
|
v -15.000000 18.750000 31.562500
|
||||||
|
v -25.000000 23.437500 46.562500
|
||||||
|
v -25.000000 23.437500 46.562500
|
||||||
|
v -25.000000 23.437500 31.562500
|
||||||
|
v -25.000000 23.437500 31.562500
|
||||||
|
v 25.000000 23.437500 46.562500
|
||||||
|
v 25.000000 23.437500 46.562500
|
||||||
|
v 25.000000 23.437500 31.562500
|
||||||
|
v 25.000000 23.437500 31.562500
|
||||||
|
v 15.000000 23.437500 26.562500
|
||||||
|
v 15.000000 23.437500 26.562500
|
||||||
|
v -15.000000 23.437500 26.562500
|
||||||
|
v -15.000000 23.437500 26.562500
|
||||||
|
v 15.000000 23.437500 51.562500
|
||||||
|
v 15.000000 23.437500 51.562500
|
||||||
|
v -15.000000 23.437500 51.562500
|
||||||
|
v -15.000000 23.437500 51.562500
|
||||||
|
vt 0.640625 0.527344
|
||||||
|
vt 0.437500 0.546875
|
||||||
|
vt 0.437500 0.500000
|
||||||
|
vt 0.640625 0.500000
|
||||||
|
vt 0.671875 0.953125
|
||||||
|
vt 0.671875 1.000000
|
||||||
|
vt 0.468750 0.980469
|
||||||
|
vt 0.468750 0.953125
|
||||||
|
vt 0.421875 0.734375
|
||||||
|
vt 0.218750 0.734375
|
||||||
|
vt 0.218750 0.531250
|
||||||
|
vt 0.421875 0.531250
|
||||||
|
vt 0.203125 0.531250
|
||||||
|
vt 0.203125 0.734375
|
||||||
|
vt 0.000000 0.734375
|
||||||
|
vt 0.000000 0.531250
|
||||||
|
vt 0.671875 0.890625
|
||||||
|
vt 0.671875 0.937500
|
||||||
|
vt 0.468750 0.937500
|
||||||
|
vt 0.468750 0.890625
|
||||||
|
vt 0.671875 0.453125
|
||||||
|
vt 0.468750 0.453125
|
||||||
|
vt 0.468750 0.437500
|
||||||
|
vt 0.671875 0.437500
|
||||||
|
vt 0.078125 0.375000
|
||||||
|
vt 0.062500 0.375000
|
||||||
|
vt 0.062500 0.343750
|
||||||
|
vt 0.078125 0.343750
|
||||||
|
vt 0.109375 0.343750
|
||||||
|
vt 0.109375 0.375000
|
||||||
|
vt 0.093750 0.375000
|
||||||
|
vt 0.093750 0.343750
|
||||||
|
vt 0.265625 0.500000
|
||||||
|
vt 0.281250 0.500000
|
||||||
|
vt 0.281250 0.515625
|
||||||
|
vt 0.265625 0.515625
|
||||||
|
vt 0.312500 0.500000
|
||||||
|
vt 0.312500 0.515625
|
||||||
|
vt 0.296875 0.515625
|
||||||
|
vt 0.296875 0.500000
|
||||||
|
vt 0.140625 0.343750
|
||||||
|
vt 0.140625 0.375000
|
||||||
|
vt 0.125000 0.375000
|
||||||
|
vt 0.125000 0.343750
|
||||||
|
vt 0.640625 0.875000
|
||||||
|
vt 0.625000 0.875000
|
||||||
|
vt 0.625000 0.843750
|
||||||
|
vt 0.640625 0.843750
|
||||||
|
vt 0.171875 0.375000
|
||||||
|
vt 0.156250 0.375000
|
||||||
|
vt 0.156250 0.343750
|
||||||
|
vt 0.171875 0.343750
|
||||||
|
vt 0.640625 0.796875
|
||||||
|
vt 0.640625 0.828125
|
||||||
|
vt 0.625000 0.828125
|
||||||
|
vt 0.625000 0.796875
|
||||||
|
vt 0.328125 0.500000
|
||||||
|
vt 0.343750 0.500000
|
||||||
|
vt 0.343750 0.515625
|
||||||
|
vt 0.328125 0.515625
|
||||||
|
vt 0.375000 0.500000
|
||||||
|
vt 0.375000 0.515625
|
||||||
|
vt 0.359375 0.515625
|
||||||
|
vt 0.359375 0.500000
|
||||||
|
vt 0.203125 0.343750
|
||||||
|
vt 0.203125 0.375000
|
||||||
|
vt 0.187500 0.375000
|
||||||
|
vt 0.187500 0.343750
|
||||||
|
vt 0.234375 0.375000
|
||||||
|
vt 0.218750 0.375000
|
||||||
|
vt 0.218750 0.343750
|
||||||
|
vt 0.234375 0.343750
|
||||||
|
vt 0.640625 0.781250
|
||||||
|
vt 0.625000 0.781250
|
||||||
|
vt 0.625000 0.750000
|
||||||
|
vt 0.640625 0.750000
|
||||||
|
vt 0.265625 0.343750
|
||||||
|
vt 0.265625 0.375000
|
||||||
|
vt 0.250000 0.375000
|
||||||
|
vt 0.250000 0.343750
|
||||||
|
vt 0.390625 0.500000
|
||||||
|
vt 0.406250 0.500000
|
||||||
|
vt 0.406250 0.515625
|
||||||
|
vt 0.390625 0.515625
|
||||||
|
vt 0.484375 0.359375
|
||||||
|
vt 0.484375 0.375000
|
||||||
|
vt 0.468750 0.375000
|
||||||
|
vt 0.468750 0.359375
|
||||||
|
vt 0.296875 0.343750
|
||||||
|
vt 0.296875 0.375000
|
||||||
|
vt 0.281250 0.375000
|
||||||
|
vt 0.281250 0.343750
|
||||||
|
vt 0.328125 0.375000
|
||||||
|
vt 0.312500 0.375000
|
||||||
|
vt 0.312500 0.343750
|
||||||
|
vt 0.328125 0.343750
|
||||||
|
vt 0.359375 0.375000
|
||||||
|
vt 0.343750 0.375000
|
||||||
|
vt 0.343750 0.343750
|
||||||
|
vt 0.359375 0.343750
|
||||||
|
vt 0.390625 0.343750
|
||||||
|
vt 0.390625 0.375000
|
||||||
|
vt 0.375000 0.375000
|
||||||
|
vt 0.375000 0.343750
|
||||||
|
vt 0.500000 0.359375
|
||||||
|
vt 0.515625 0.359375
|
||||||
|
vt 0.515625 0.375000
|
||||||
|
vt 0.500000 0.375000
|
||||||
|
vt 0.546875 0.359375
|
||||||
|
vt 0.546875 0.375000
|
||||||
|
vt 0.531250 0.375000
|
||||||
|
vt 0.531250 0.359375
|
||||||
|
vt 0.421875 0.343750
|
||||||
|
vt 0.421875 0.375000
|
||||||
|
vt 0.406250 0.375000
|
||||||
|
vt 0.406250 0.343750
|
||||||
|
vt 0.453125 0.375000
|
||||||
|
vt 0.437500 0.375000
|
||||||
|
vt 0.437500 0.343750
|
||||||
|
vt 0.453125 0.343750
|
||||||
|
vt 0.250000 0.515625
|
||||||
|
vt 0.000000 0.515625
|
||||||
|
vt 0.000000 0.500000
|
||||||
|
vt 0.250000 0.500000
|
||||||
|
vt 0.250000 0.468750
|
||||||
|
vt 0.250000 0.484375
|
||||||
|
vt 0.000000 0.484375
|
||||||
|
vt 0.000000 0.468750
|
||||||
|
vt 0.000000 0.750000
|
||||||
|
vt 0.218750 0.750000
|
||||||
|
vt 0.218750 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.453125 0.750000
|
||||||
|
vt 0.453125 1.000000
|
||||||
|
vt 0.234375 1.000000
|
||||||
|
vt 0.234375 0.750000
|
||||||
|
vt 0.484375 0.468750
|
||||||
|
vt 0.484375 0.484375
|
||||||
|
vt 0.265625 0.484375
|
||||||
|
vt 0.265625 0.468750
|
||||||
|
vt 0.718750 0.484375
|
||||||
|
vt 0.500000 0.484375
|
||||||
|
vt 0.500000 0.468750
|
||||||
|
vt 0.718750 0.468750
|
||||||
|
vt 0.015625 0.375000
|
||||||
|
vt 0.000000 0.375000
|
||||||
|
vt 0.000000 0.296875
|
||||||
|
vt 0.015625 0.296875
|
||||||
|
vt 0.046875 0.296875
|
||||||
|
vt 0.046875 0.375000
|
||||||
|
vt 0.031250 0.375000
|
||||||
|
vt 0.031250 0.296875
|
||||||
|
vt 0.000000 0.437500
|
||||||
|
vt 0.218750 0.437500
|
||||||
|
vt 0.218750 0.453125
|
||||||
|
vt 0.000000 0.453125
|
||||||
|
vt 0.453125 0.437500
|
||||||
|
vt 0.453125 0.453125
|
||||||
|
vt 0.234375 0.453125
|
||||||
|
vt 0.234375 0.437500
|
||||||
|
vt 0.656250 0.656250
|
||||||
|
vt 0.656250 0.734375
|
||||||
|
vt 0.437500 0.734375
|
||||||
|
vt 0.437500 0.656250
|
||||||
|
vt 0.656250 0.640625
|
||||||
|
vt 0.437500 0.640625
|
||||||
|
vt 0.437500 0.562500
|
||||||
|
vt 0.656250 0.562500
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.576562 0.837500
|
||||||
|
vt 0.576562 0.875000
|
||||||
|
vt 0.501563 0.875000
|
||||||
|
vt 0.501563 0.837500
|
||||||
|
vt 0.501563 0.765625
|
||||||
|
vt 0.576562 0.765625
|
||||||
|
vt 0.576562 0.803125
|
||||||
|
vt 0.501563 0.803125
|
||||||
|
vt 0.078125 1.000000
|
||||||
|
vt 0.078125 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.078125 1.000000
|
||||||
|
vt 0.078125 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.576562 0.875000
|
||||||
|
vt 0.576562 0.837500
|
||||||
|
vt 0.604173 0.837500
|
||||||
|
vt 0.604173 0.875000
|
||||||
|
vt 0.473952 0.837500
|
||||||
|
vt 0.501563 0.837500
|
||||||
|
vt 0.501563 0.875000
|
||||||
|
vt 0.473952 0.875000
|
||||||
|
vt 0.107813 0.404741
|
||||||
|
vt 0.107813 0.421875
|
||||||
|
vt 0.032813 0.421875
|
||||||
|
vt 0.032813 0.404741
|
||||||
|
vt 0.420312 0.421875
|
||||||
|
vt 0.345313 0.421875
|
||||||
|
vt 0.345313 0.404741
|
||||||
|
vt 0.420312 0.404741
|
||||||
|
vt 0.604173 0.803125
|
||||||
|
vt 0.576562 0.803125
|
||||||
|
vt 0.576562 0.765625
|
||||||
|
vt 0.604173 0.765625
|
||||||
|
vt 0.501563 0.765625
|
||||||
|
vt 0.501563 0.803125
|
||||||
|
vt 0.473952 0.803125
|
||||||
|
vt 0.473952 0.765625
|
||||||
|
vt 0.189062 0.390625
|
||||||
|
vt 0.264062 0.390625
|
||||||
|
vt 0.264062 0.407759
|
||||||
|
vt 0.189062 0.407759
|
||||||
|
vt 0.501563 0.407759
|
||||||
|
vt 0.501563 0.390625
|
||||||
|
vt 0.576562 0.390625
|
||||||
|
vt 0.576562 0.407759
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.031250 1.000000
|
||||||
|
vt 0.031250 1.000000
|
||||||
|
vt 0.031250 1.000000
|
||||||
|
vt 0.031250 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 0.968750
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.000000 1.000000
|
||||||
|
vt 0.264062 0.390625
|
||||||
|
vt 0.290484 0.398640
|
||||||
|
vt 0.264062 0.407760
|
||||||
|
vt 0.189063 0.390625
|
||||||
|
vt 0.189062 0.407760
|
||||||
|
vt 0.162641 0.398640
|
||||||
|
vt 0.501563 0.390625
|
||||||
|
vt 0.501563 0.407760
|
||||||
|
vt 0.475142 0.398640
|
||||||
|
vt 0.576562 0.390625
|
||||||
|
vt 0.602983 0.398640
|
||||||
|
vt 0.576562 0.407760
|
||||||
|
vt 0.107812 0.421875
|
||||||
|
vt 0.107813 0.404740
|
||||||
|
vt 0.134234 0.413860
|
||||||
|
vt 0.032813 0.421875
|
||||||
|
vt 0.006391 0.413860
|
||||||
|
vt 0.032813 0.404740
|
||||||
|
vt 0.345313 0.421875
|
||||||
|
vt 0.318892 0.413860
|
||||||
|
vt 0.345313 0.404740
|
||||||
|
vt 0.420312 0.421875
|
||||||
|
vt 0.420312 0.404740
|
||||||
|
vt 0.446733 0.413860
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn 0.000000 0.995409 -0.095712
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 1.000000 0.000000 -0.000000
|
||||||
|
vn -1.000000 0.000000 0.000000
|
||||||
|
vn -0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 -0.000000 1.000000
|
||||||
|
vn 0.000000 0.000000 -1.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 -1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.424434 0.905459 -0.000000
|
||||||
|
vn -0.424434 0.905459 0.000000
|
||||||
|
vn 0.000000 0.729537 0.683941
|
||||||
|
vn 0.000000 0.729537 -0.683941
|
||||||
|
vn 0.424434 -0.905459 0.000000
|
||||||
|
vn -0.424434 -0.905459 0.000000
|
||||||
|
vn 0.000000 -0.729537 0.683941
|
||||||
|
vn 0.000000 -0.729537 -0.683941
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.000000 1.000000 0.000000
|
||||||
|
vn 0.323574 -0.690290 0.647147
|
||||||
|
vn -0.323574 -0.690290 0.647147
|
||||||
|
vn 0.323574 -0.690290 -0.647147
|
||||||
|
vn -0.323574 -0.690290 -0.647147
|
||||||
|
vn 0.323574 0.690290 0.647147
|
||||||
|
vn -0.323574 0.690290 0.647147
|
||||||
|
vn 0.323574 0.690290 -0.647147
|
||||||
|
vn -0.323574 0.690290 -0.647147
|
||||||
|
usemtl BedMaterial
|
||||||
|
s 1
|
||||||
|
f 2/1/1 1/2/1 3/3/1
|
||||||
|
f 2/1/1 3/3/1 4/4/1
|
||||||
|
f 7/5/2 5/6/2 6/7/2
|
||||||
|
f 7/5/2 6/7/2 8/8/2
|
||||||
|
f 5/9/3 1/10/3 2/11/3
|
||||||
|
f 5/9/3 2/11/3 6/12/3
|
||||||
|
f 4/13/4 3/14/4 7/15/4
|
||||||
|
f 4/13/4 7/15/4 8/16/4
|
||||||
|
f 3/17/5 1/18/5 5/19/5
|
||||||
|
f 3/17/5 5/19/5 7/20/5
|
||||||
|
f 6/21/6 2/22/6 4/23/6
|
||||||
|
f 6/21/6 4/23/6 8/24/6
|
||||||
|
f 10/25/7 9/26/7 11/27/7
|
||||||
|
f 10/25/7 11/27/7 12/28/7
|
||||||
|
f 15/29/8 13/30/8 14/31/8
|
||||||
|
f 15/29/8 14/31/8 16/32/8
|
||||||
|
f 13/33/9 9/34/9 10/35/9
|
||||||
|
f 13/33/9 10/35/9 14/36/9
|
||||||
|
f 12/37/10 11/38/10 15/39/10
|
||||||
|
f 12/37/10 15/39/10 16/40/10
|
||||||
|
f 11/41/11 9/42/11 13/43/11
|
||||||
|
f 11/41/11 13/43/11 15/44/11
|
||||||
|
f 14/45/12 10/46/12 12/47/12
|
||||||
|
f 14/45/12 12/47/12 16/48/12
|
||||||
|
f 18/49/13 17/50/13 19/51/13
|
||||||
|
f 18/49/13 19/51/13 20/52/13
|
||||||
|
f 23/53/14 21/54/14 22/55/14
|
||||||
|
f 23/53/14 22/55/14 24/56/14
|
||||||
|
f 21/57/15 17/58/15 18/59/15
|
||||||
|
f 21/57/15 18/59/15 22/60/15
|
||||||
|
f 20/61/16 19/62/16 23/63/16
|
||||||
|
f 20/61/16 23/63/16 24/64/16
|
||||||
|
f 19/65/17 17/66/17 21/67/17
|
||||||
|
f 19/65/17 21/67/17 23/68/17
|
||||||
|
f 22/69/18 18/70/18 20/71/18
|
||||||
|
f 22/69/18 20/71/18 24/72/18
|
||||||
|
f 26/73/19 25/74/19 27/75/19
|
||||||
|
f 26/73/19 27/75/19 28/76/19
|
||||||
|
f 31/77/20 29/78/20 30/79/20
|
||||||
|
f 31/77/20 30/79/20 32/80/20
|
||||||
|
f 29/81/21 25/82/21 26/83/21
|
||||||
|
f 29/81/21 26/83/21 30/84/21
|
||||||
|
f 28/85/22 27/86/22 31/87/22
|
||||||
|
f 28/85/22 31/87/22 32/88/22
|
||||||
|
f 27/89/23 25/90/23 29/91/23
|
||||||
|
f 27/89/23 29/91/23 31/92/23
|
||||||
|
f 30/93/24 26/94/24 28/95/24
|
||||||
|
f 30/93/24 28/95/24 32/96/24
|
||||||
|
f 34/97/25 33/98/25 35/99/25
|
||||||
|
f 34/97/25 35/99/25 36/100/25
|
||||||
|
f 39/101/26 37/102/26 38/103/26
|
||||||
|
f 39/101/26 38/103/26 40/104/26
|
||||||
|
f 37/105/27 33/106/27 34/107/27
|
||||||
|
f 37/105/27 34/107/27 38/108/27
|
||||||
|
f 36/109/28 35/110/28 39/111/28
|
||||||
|
f 36/109/28 39/111/28 40/112/28
|
||||||
|
f 35/113/29 33/114/29 37/115/29
|
||||||
|
f 35/113/29 37/115/29 39/116/29
|
||||||
|
f 38/117/30 34/118/30 36/119/30
|
||||||
|
f 38/117/30 36/119/30 40/120/30
|
||||||
|
f 42/121/31 41/122/31 43/123/31
|
||||||
|
f 42/121/31 43/123/31 44/124/31
|
||||||
|
f 47/125/32 45/126/32 46/127/32
|
||||||
|
f 47/125/32 46/127/32 48/128/32
|
||||||
|
f 45/129/33 41/130/33 42/131/33
|
||||||
|
f 45/129/33 42/131/33 46/132/33
|
||||||
|
f 44/133/34 43/134/34 47/135/34
|
||||||
|
f 44/133/34 47/135/34 48/136/34
|
||||||
|
f 43/137/35 41/138/35 45/139/35
|
||||||
|
f 43/137/35 45/139/35 47/140/35
|
||||||
|
f 46/141/36 42/142/36 44/143/36
|
||||||
|
f 46/141/36 44/143/36 48/144/36
|
||||||
|
f 50/145/37 49/146/37 51/147/37
|
||||||
|
f 50/145/37 51/147/37 52/148/37
|
||||||
|
f 55/149/38 53/150/38 54/151/38
|
||||||
|
f 55/149/38 54/151/38 56/152/38
|
||||||
|
f 53/153/39 49/154/39 50/155/39
|
||||||
|
f 53/153/39 50/155/39 54/156/39
|
||||||
|
f 52/157/40 51/158/40 55/159/40
|
||||||
|
f 52/157/40 55/159/40 56/160/40
|
||||||
|
f 51/161/41 49/162/41 53/163/41
|
||||||
|
f 51/161/41 53/163/41 55/164/41
|
||||||
|
f 54/165/42 50/166/42 52/167/42
|
||||||
|
f 54/165/42 52/167/42 56/168/42
|
||||||
|
f 70/169/43 69/170/43 72/171/43
|
||||||
|
f 70/169/43 72/171/43 71/172/43
|
||||||
|
f 65/173/44 66/174/44 68/175/44
|
||||||
|
f 65/173/44 68/175/44 67/176/44
|
||||||
|
f 57/177/45 58/178/45 60/179/45
|
||||||
|
f 57/177/45 60/179/45 59/180/45
|
||||||
|
f 64/181/46 62/182/46 61/183/46
|
||||||
|
f 64/181/46 61/183/46 63/184/46
|
||||||
|
f 77/185/47 78/186/47 80/187/47
|
||||||
|
f 77/185/47 80/187/47 79/188/47
|
||||||
|
f 74/189/48 73/190/48 76/191/48
|
||||||
|
f 74/189/48 76/191/48 75/192/48
|
||||||
|
f 58/193/49 57/194/49 70/195/49
|
||||||
|
f 58/193/49 70/195/49 72/196/49
|
||||||
|
f 66/197/50 59/198/50 60/199/50
|
||||||
|
f 66/197/50 60/199/50 68/200/50
|
||||||
|
f 78/201/51 57/202/51 59/203/51
|
||||||
|
f 78/201/51 59/203/51 80/204/51
|
||||||
|
f 60/205/52 58/206/52 74/207/52
|
||||||
|
f 60/205/52 74/207/52 76/208/52
|
||||||
|
f 69/209/53 61/210/53 62/211/53
|
||||||
|
f 69/209/53 62/211/53 71/212/53
|
||||||
|
f 64/213/54 63/214/54 65/215/54
|
||||||
|
f 64/213/54 65/215/54 67/216/54
|
||||||
|
f 63/217/55 61/218/55 77/219/55
|
||||||
|
f 63/217/55 77/219/55 79/220/55
|
||||||
|
f 73/221/56 62/222/56 64/223/56
|
||||||
|
f 73/221/56 64/223/56 75/224/56
|
||||||
|
f 73/225/57 74/226/57 71/227/57
|
||||||
|
f 73/225/57 71/227/57 72/228/57
|
||||||
|
f 78/229/58 77/230/58 69/231/58
|
||||||
|
f 78/229/58 69/231/58 70/232/58
|
||||||
|
f 76/233/59 75/234/59 67/235/59
|
||||||
|
f 76/233/59 67/235/59 68/236/59
|
||||||
|
f 79/237/60 80/238/60 65/239/60
|
||||||
|
f 79/237/60 65/239/60 66/240/60
|
||||||
|
f 61/241/61 69/242/61 77/243/61
|
||||||
|
f 63/244/62 79/245/62 65/246/62
|
||||||
|
f 62/247/63 73/248/63 71/249/63
|
||||||
|
f 64/250/64 67/251/64 75/252/64
|
||||||
|
f 57/253/65 78/254/65 70/255/65
|
||||||
|
f 59/256/66 66/257/66 80/258/66
|
||||||
|
f 58/259/67 72/260/67 74/261/67
|
||||||
|
f 60/262/68 76/263/68 68/264/68
|
||||||
BIN
assets/models/Bed_textures.png
Normal file
BIN
assets/models/Bed_textures.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 783 B |
78
assets/shaders/lighting.fs
Normal file
78
assets/shaders/lighting.fs
Normal file
|
|
@ -0,0 +1,78 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes (from vertex shader)
|
||||||
|
in vec3 fragPosition;
|
||||||
|
in vec2 fragTexCoord;
|
||||||
|
in vec4 fragColor;
|
||||||
|
in vec3 fragNormal;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform sampler2D texture0;
|
||||||
|
uniform vec4 colDiffuse;
|
||||||
|
|
||||||
|
// Output fragment color
|
||||||
|
out vec4 finalColor;
|
||||||
|
|
||||||
|
#define MAX_LIGHTS 4
|
||||||
|
#define LIGHT_DIRECTIONAL 0
|
||||||
|
#define LIGHT_POINT 1
|
||||||
|
|
||||||
|
struct Light {
|
||||||
|
int enabled;
|
||||||
|
int type;
|
||||||
|
vec3 position;
|
||||||
|
vec3 target;
|
||||||
|
vec4 color;
|
||||||
|
};
|
||||||
|
|
||||||
|
uniform Light lights[MAX_LIGHTS];
|
||||||
|
uniform vec4 ambient;
|
||||||
|
uniform vec3 viewPos;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
vec4 texelColor = texture(texture0, fragTexCoord);
|
||||||
|
if (texelColor.a < 0.1) discard;
|
||||||
|
|
||||||
|
vec3 normal = normalize(fragNormal);
|
||||||
|
vec3 viewD = normalize(viewPos - fragPosition);
|
||||||
|
|
||||||
|
vec3 lightTotal = ambient.rgb;
|
||||||
|
vec3 specularTotal = vec3(0.0);
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_LIGHTS; i++)
|
||||||
|
{
|
||||||
|
if (lights[i].enabled == 1)
|
||||||
|
{
|
||||||
|
vec3 lightDir = vec3(0.0);
|
||||||
|
float attenuation = 1.0;
|
||||||
|
|
||||||
|
if (lights[i].type == LIGHT_DIRECTIONAL)
|
||||||
|
{
|
||||||
|
lightDir = -normalize(lights[i].target - lights[i].position);
|
||||||
|
}
|
||||||
|
else // LIGHT_POINT
|
||||||
|
{
|
||||||
|
vec3 lightVec = lights[i].position - fragPosition;
|
||||||
|
float dist = length(lightVec);
|
||||||
|
lightDir = normalize(lightVec);
|
||||||
|
attenuation = 1.0 / (1.0 + 0.10 * dist + 0.03 * dist * dist);
|
||||||
|
}
|
||||||
|
|
||||||
|
float NdotL = max(dot(normal, lightDir), 0.0);
|
||||||
|
lightTotal += lights[i].color.rgb * NdotL * attenuation;
|
||||||
|
|
||||||
|
if (NdotL > 0.0)
|
||||||
|
{
|
||||||
|
vec3 reflectDir = reflect(-lightDir, normal);
|
||||||
|
float spec = pow(max(dot(viewD, reflectDir), 0.0), 16.0);
|
||||||
|
specularTotal += lights[i].color.rgb * spec * attenuation * 0.25;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
vec4 tint = colDiffuse * fragColor;
|
||||||
|
vec3 result = (texelColor.rgb * tint.rgb) * lightTotal + specularTotal;
|
||||||
|
|
||||||
|
finalColor = vec4(result, texelColor.a * tint.a);
|
||||||
|
}
|
||||||
30
assets/shaders/lighting.vs
Normal file
30
assets/shaders/lighting.vs
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
#version 330
|
||||||
|
|
||||||
|
// Input vertex attributes
|
||||||
|
in vec3 vertexPosition;
|
||||||
|
in vec2 vertexTexCoord;
|
||||||
|
in vec3 vertexNormal;
|
||||||
|
in vec4 vertexColor;
|
||||||
|
|
||||||
|
// Input uniform values
|
||||||
|
uniform mat4 mvp;
|
||||||
|
uniform mat4 matModel;
|
||||||
|
uniform mat4 matNormal;
|
||||||
|
|
||||||
|
// Output vertex attributes (to fragment shader)
|
||||||
|
out vec3 fragPosition;
|
||||||
|
out vec2 fragTexCoord;
|
||||||
|
out vec4 fragColor;
|
||||||
|
out vec3 fragNormal;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
// Send vertex attributes to fragment shader
|
||||||
|
fragPosition = vec3(matModel*vec4(vertexPosition, 1.0));
|
||||||
|
fragTexCoord = vertexTexCoord;
|
||||||
|
fragColor = vertexColor;
|
||||||
|
fragNormal = normalize(vec3(matNormal*vec4(vertexNormal, 1.0)));
|
||||||
|
|
||||||
|
// Calculate final vertex position
|
||||||
|
gl_Position = mvp*vec4(vertexPosition, 1.0);
|
||||||
|
}
|
||||||
119
scripts/convert_blockbench_fbx.py
Normal file
119
scripts/convert_blockbench_fbx.py
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
#!/usr/bin/env python3
|
||||||
|
"""
|
||||||
|
Convert Blockbench ASCII FBX to standard OBJ with normals, UVs, and triangulation for Raylib.
|
||||||
|
Usage: python scripts/convert_blockbench_fbx.py <input.fbx> [output.obj]
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import re
|
||||||
|
|
||||||
|
def convert_fbx_to_obj(fbx_path, obj_path=None):
|
||||||
|
if obj_path is None:
|
||||||
|
obj_path = os.path.splitext(fbx_path)[0] + ".obj"
|
||||||
|
|
||||||
|
mtl_path = os.path.splitext(obj_path)[0] + ".mtl"
|
||||||
|
obj_name = os.path.splitext(os.path.basename(obj_path))[0]
|
||||||
|
tex_name = obj_name + "_textures.png"
|
||||||
|
|
||||||
|
with open(fbx_path, "r", encoding="utf-8", errors="ignore") as f:
|
||||||
|
text = f.read()
|
||||||
|
|
||||||
|
# Vertices
|
||||||
|
v_match = re.search(r"Vertices:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
|
||||||
|
if not v_match:
|
||||||
|
print(f"Error: No vertices found in {fbx_path}")
|
||||||
|
return False
|
||||||
|
raw_v = [float(x.strip()) for x in v_match.group(1).split(",")]
|
||||||
|
min_y = min(raw_v[1::3])
|
||||||
|
# Ground the model so bottom Y is 0.0
|
||||||
|
vertices = [(raw_v[i], raw_v[i+1] - min_y, raw_v[i+2]) for i in range(0, len(raw_v), 3)]
|
||||||
|
|
||||||
|
# Indices
|
||||||
|
p_match = re.search(r"PolygonVertexIndex:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
|
||||||
|
if not p_match:
|
||||||
|
print(f"Error: No indices found in {fbx_path}")
|
||||||
|
return False
|
||||||
|
raw_p = [int(x.strip()) for x in p_match.group(1).split(",")]
|
||||||
|
|
||||||
|
# UVs
|
||||||
|
uv_match = re.search(r"UV:\s*\*\d+\s*\{\s*a:\s*([^}]+)\}", text)
|
||||||
|
raw_uv = [float(x.strip()) for x in uv_match.group(1).split(",")] if uv_match else []
|
||||||
|
uvs = [(raw_uv[i], raw_uv[i+1]) for i in range(0, len(raw_uv), 2)]
|
||||||
|
|
||||||
|
# Polygons
|
||||||
|
polygons_v = []
|
||||||
|
polygons_uv = []
|
||||||
|
cur_v = []
|
||||||
|
cur_uv = []
|
||||||
|
|
||||||
|
for i, idx in enumerate(raw_p):
|
||||||
|
uv_idx = i
|
||||||
|
if idx < 0:
|
||||||
|
cur_v.append(~idx)
|
||||||
|
cur_uv.append(uv_idx)
|
||||||
|
polygons_v.append(cur_v)
|
||||||
|
polygons_uv.append(cur_uv)
|
||||||
|
cur_v = []
|
||||||
|
cur_uv = []
|
||||||
|
else:
|
||||||
|
cur_v.append(idx)
|
||||||
|
cur_uv.append(uv_idx)
|
||||||
|
|
||||||
|
# Compute face normals
|
||||||
|
def calc_normal(p0, p1, p2):
|
||||||
|
ax, ay, az = p1[0]-p0[0], p1[1]-p0[1], p1[2]-p0[2]
|
||||||
|
bx, by, bz = p2[0]-p0[0], p2[1]-p0[1], p2[2]-p0[2]
|
||||||
|
nx = ay*bz - az*by
|
||||||
|
ny = az*bx - ax*bz
|
||||||
|
nz = ax*by - ay*bx
|
||||||
|
l = (nx*nx + ny*ny + nz*nz)**0.5
|
||||||
|
if l < 1e-6:
|
||||||
|
return (0.0, 1.0, 0.0)
|
||||||
|
return (nx/l, ny/l, nz/l)
|
||||||
|
|
||||||
|
normals = []
|
||||||
|
triangles = []
|
||||||
|
|
||||||
|
for pv, puv in zip(polygons_v, polygons_uv):
|
||||||
|
p0 = vertices[pv[0]]
|
||||||
|
p1 = vertices[pv[1]]
|
||||||
|
p2 = vertices[pv[2]]
|
||||||
|
norm = calc_normal(p0, p1, p2)
|
||||||
|
normals.append(norm)
|
||||||
|
norm_idx = len(normals)
|
||||||
|
|
||||||
|
for k in range(1, len(pv) - 1):
|
||||||
|
tri = [
|
||||||
|
(pv[0], puv[0], norm_idx),
|
||||||
|
(pv[k], puv[k], norm_idx),
|
||||||
|
(pv[k+1], puv[k+1], norm_idx)
|
||||||
|
]
|
||||||
|
triangles.append(tri)
|
||||||
|
|
||||||
|
with open(obj_path, "w", encoding="utf-8") as out:
|
||||||
|
out.write(f"mtllib {os.path.basename(mtl_path)}\n")
|
||||||
|
out.write(f"o {obj_name}\n")
|
||||||
|
for v in vertices:
|
||||||
|
out.write(f"v {v[0]:.6f} {v[1]:.6f} {v[2]:.6f}\n")
|
||||||
|
for uv in uvs:
|
||||||
|
out.write(f"vt {uv[0]:.6f} {uv[1]:.6f}\n")
|
||||||
|
for n in normals:
|
||||||
|
out.write(f"vn {n[0]:.6f} {n[1]:.6f} {n[2]:.6f}\n")
|
||||||
|
out.write(f"usemtl {obj_name}Material\ns 1\n")
|
||||||
|
for tri in triangles:
|
||||||
|
face_str = " ".join(f"{vi+1}/{uvi+1}/{ni}" for vi, uvi, ni in tri)
|
||||||
|
out.write(f"f {face_str}\n")
|
||||||
|
|
||||||
|
with open(mtl_path, "w", encoding="utf-8") as out:
|
||||||
|
out.write(f"newmtl {obj_name}Material\n")
|
||||||
|
out.write("Kd 1.0 1.0 1.0\n")
|
||||||
|
out.write(f"map_Kd {tex_name}\n")
|
||||||
|
|
||||||
|
print(f"Successfully converted {fbx_path} -> {obj_path} & {mtl_path}")
|
||||||
|
return True
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("Usage: convert_blockbench_fbx.py <file.fbx> [output.obj]")
|
||||||
|
sys.exit(1)
|
||||||
|
convert_fbx_to_obj(sys.argv[1], sys.argv[2] if len(sys.argv) > 2 else None)
|
||||||
|
|
@ -1,6 +1,7 @@
|
||||||
#include "Game.hpp"
|
#include "Game.hpp"
|
||||||
#include "dungeon/DungeonGen.hpp"
|
#include "dungeon/DungeonGen.hpp"
|
||||||
#include "core/SoundManager.hpp"
|
#include "core/SoundManager.hpp"
|
||||||
|
#include "debug/DebugSystem.hpp"
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
@ -19,6 +20,7 @@ void Game::init() {
|
||||||
m_renderer.setGraphicsQuality(SoundManager::get().getGraphicsQuality());
|
m_renderer.setGraphicsQuality(SoundManager::get().getGraphicsQuality());
|
||||||
m_state = GameState::MAIN_MENU;
|
m_state = GameState::MAIN_MENU;
|
||||||
SoundManager::get().playTrack(MusicTrack::MENU_HUB);
|
SoundManager::get().playTrack(MusicTrack::MENU_HUB);
|
||||||
|
DebugSystem::get().init(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Game::startNewRun() {
|
void Game::startNewRun() {
|
||||||
|
|
@ -137,6 +139,8 @@ void Game::update(float dt) {
|
||||||
// Update dynamic music streaming engine every frame
|
// Update dynamic music streaming engine every frame
|
||||||
SoundManager::get().updateMusic(dt);
|
SoundManager::get().updateMusic(dt);
|
||||||
|
|
||||||
|
DebugSystem::get().update(dt);
|
||||||
|
|
||||||
switch (m_state) {
|
switch (m_state) {
|
||||||
case GameState::MAIN_MENU:
|
case GameState::MAIN_MENU:
|
||||||
updateMainMenu(dt);
|
updateMainMenu(dt);
|
||||||
|
|
@ -1221,7 +1225,12 @@ void Game::draw() {
|
||||||
// Render screen transition wave on top of all 2D/3D elements
|
// Render screen transition wave on top of all 2D/3D elements
|
||||||
m_transition.draw();
|
m_transition.draw();
|
||||||
|
|
||||||
|
// Render debug overlay if enabled
|
||||||
|
DebugSystem::get().drawOverlay();
|
||||||
|
|
||||||
EndDrawing();
|
EndDrawing();
|
||||||
|
|
||||||
|
DebugSystem::get().postDraw();
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace dnr
|
} // namespace dnr
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,7 @@
|
||||||
#include "hacking/HackingGame.hpp"
|
#include "hacking/HackingGame.hpp"
|
||||||
#include "combat/CombatSystem.hpp"
|
#include "combat/CombatSystem.hpp"
|
||||||
#include "inventory/InventoryUI.hpp"
|
#include "inventory/InventoryUI.hpp"
|
||||||
#include "render/SoftwareRenderer.hpp"
|
#include "render/CyberRenderer.hpp"
|
||||||
#include "render/UIRenderer.hpp"
|
#include "render/UIRenderer.hpp"
|
||||||
#include "render/ScreenTransition.hpp"
|
#include "render/ScreenTransition.hpp"
|
||||||
#include "hub/HubRoom.hpp"
|
#include "hub/HubRoom.hpp"
|
||||||
|
|
@ -25,6 +25,9 @@ public:
|
||||||
void draw();
|
void draw();
|
||||||
|
|
||||||
bool shouldClose() const { return m_shouldQuit || WindowShouldClose(); }
|
bool shouldClose() const { return m_shouldQuit || WindowShouldClose(); }
|
||||||
|
void requestQuit() { m_shouldQuit = true; }
|
||||||
|
|
||||||
|
friend class DebugSystem;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void startNewRun();
|
void startNewRun();
|
||||||
|
|
@ -63,7 +66,7 @@ private:
|
||||||
CombatSystem m_combat;
|
CombatSystem m_combat;
|
||||||
InventoryUI m_inventoryUI;
|
InventoryUI m_inventoryUI;
|
||||||
|
|
||||||
SoftwareRenderer m_renderer;
|
CyberRenderer m_renderer;
|
||||||
UIRenderer m_uiRenderer;
|
UIRenderer m_uiRenderer;
|
||||||
ScreenTransition m_transition;
|
ScreenTransition m_transition;
|
||||||
|
|
||||||
|
|
|
||||||
749
src/debug/DebugSystem.cpp
Normal file
749
src/debug/DebugSystem.cpp
Normal file
|
|
@ -0,0 +1,749 @@
|
||||||
|
#include "DebugSystem.hpp"
|
||||||
|
#include "core/Game.hpp"
|
||||||
|
#include "core/SoundManager.hpp"
|
||||||
|
#include "dungeon/DungeonGen.hpp"
|
||||||
|
#include "combat/Enemy.hpp"
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iostream>
|
||||||
|
#include <chrono>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <sstream>
|
||||||
|
#include <queue>
|
||||||
|
#include <cmath>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace dnr {
|
||||||
|
|
||||||
|
DebugSystem& DebugSystem::get() {
|
||||||
|
static DebugSystem instance;
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugSystem::DebugSystem() = default;
|
||||||
|
DebugSystem::~DebugSystem() = default;
|
||||||
|
|
||||||
|
void DebugSystem::parseArgs(int argc, char* argv[]) {
|
||||||
|
for (int i = 1; i < argc; ++i) {
|
||||||
|
std::string arg = argv[i];
|
||||||
|
|
||||||
|
if (arg == "--bot") {
|
||||||
|
m_config.botEnabled = true;
|
||||||
|
} else if (arg == "--bot-duration" && i + 1 < argc) {
|
||||||
|
m_config.botDuration = std::stof(argv[++i]);
|
||||||
|
m_config.botEnabled = true;
|
||||||
|
} else if (arg == "--bot-max-steps" && i + 1 < argc) {
|
||||||
|
m_config.botMaxSteps = std::stoi(argv[++i]);
|
||||||
|
m_config.botEnabled = true;
|
||||||
|
} else if (arg == "--screenshot" && i + 1 < argc) {
|
||||||
|
m_config.singleScreenshotPath = argv[++i];
|
||||||
|
} else if (arg == "--screenshot-events") {
|
||||||
|
m_config.screenshotEvents = true;
|
||||||
|
} else if (arg == "--screenshot-dir" && i + 1 < argc) {
|
||||||
|
m_config.screenshotDir = argv[++i];
|
||||||
|
} else if (arg == "--state-dump" && i + 1 < argc) {
|
||||||
|
m_config.stateDumpPath = argv[++i];
|
||||||
|
} else if (arg == "--warp-state" && i + 1 < argc) {
|
||||||
|
m_config.warpState = argv[++i];
|
||||||
|
} else if (arg == "--floor" && i + 1 < argc) {
|
||||||
|
m_config.targetFloor = std::stoi(argv[++i]);
|
||||||
|
} else if (arg == "--god") {
|
||||||
|
m_config.godMode = true;
|
||||||
|
} else if (arg == "--turbo") {
|
||||||
|
m_config.turboMode = true;
|
||||||
|
} else if (arg == "--overlay") {
|
||||||
|
m_config.showOverlay = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::init(Game* game) {
|
||||||
|
m_game = game;
|
||||||
|
|
||||||
|
try {
|
||||||
|
std::filesystem::create_directories(m_config.screenshotDir);
|
||||||
|
} catch (...) {}
|
||||||
|
|
||||||
|
if (m_config.turboMode) {
|
||||||
|
SetTargetFPS(0); // Unlocked framerate for ultra-fast simulation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::toggleBot() {
|
||||||
|
m_config.botEnabled = !m_config.botEnabled;
|
||||||
|
SoundManager::get().playTumblerClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::toggleGodMode() {
|
||||||
|
m_config.godMode = !m_config.godMode;
|
||||||
|
SoundManager::get().playPickup();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::toggleOverlay() {
|
||||||
|
m_config.showOverlay = !m_config.showOverlay;
|
||||||
|
SoundManager::get().playTumblerClick();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::handleHotkeys() {
|
||||||
|
if (IsKeyPressed(KEY_F1)) {
|
||||||
|
toggleOverlay();
|
||||||
|
}
|
||||||
|
if (IsKeyPressed(KEY_F2)) {
|
||||||
|
toggleGodMode();
|
||||||
|
}
|
||||||
|
if (IsKeyPressed(KEY_F3)) {
|
||||||
|
// Warp to next floor or instant win combat
|
||||||
|
if (m_game) {
|
||||||
|
if (m_game->m_state == GameState::COMBAT) {
|
||||||
|
m_game->m_combat.actionSpinAndShoot(m_game->m_player);
|
||||||
|
if (m_game->m_combat.getEnemy()) {
|
||||||
|
const_cast<Enemy*>(m_game->m_combat.getEnemy())->takeDamage(999, AmmoType::KINETIC);
|
||||||
|
}
|
||||||
|
} else if (m_game->m_state == GameState::EXPLORATION) {
|
||||||
|
m_game->nextFloor();
|
||||||
|
}
|
||||||
|
SoundManager::get().playSkillGreat();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsKeyPressed(KEY_F4)) {
|
||||||
|
// Give resources
|
||||||
|
if (m_game) {
|
||||||
|
for (int t = 1; t <= 6; ++t) {
|
||||||
|
m_game->m_player.addAmmo(static_cast<AmmoType>(t), 5);
|
||||||
|
}
|
||||||
|
m_game->m_player.addTechScraps(100);
|
||||||
|
m_game->m_player.addPicks(5);
|
||||||
|
SoundManager::get().playPickup();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (IsKeyPressed(KEY_F10)) {
|
||||||
|
toggleBot();
|
||||||
|
}
|
||||||
|
if (IsKeyPressed(KEY_F12)) {
|
||||||
|
captureScreenshot("");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::applyWarp() {
|
||||||
|
if (!m_game || m_warpApplied || m_config.warpState.empty()) return;
|
||||||
|
|
||||||
|
m_warpApplied = true;
|
||||||
|
std::string w = m_config.warpState;
|
||||||
|
|
||||||
|
if (w == "HUB") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
} else if (w == "HUB_BED") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
m_game->m_player.setGridPosition(2, 4);
|
||||||
|
m_game->m_player.setFacing(Direction::EAST);
|
||||||
|
} else if (w == "HUB_BED_FRONT") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
m_game->m_player.setGridPosition(4, 3);
|
||||||
|
m_game->m_player.setFacing(Direction::SOUTH);
|
||||||
|
} else if (w == "EXPLORATION") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
if (m_config.targetFloor > 1) {
|
||||||
|
m_game->m_dungeon.setFloor(m_config.targetFloor);
|
||||||
|
}
|
||||||
|
m_game->enterCyberspace();
|
||||||
|
} else if (w == "REST_ROOM") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
m_game->m_dungeon.setFloor(m_config.targetFloor);
|
||||||
|
m_game->enterRestRoom();
|
||||||
|
} else if (w == "COMBAT") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
m_game->enterCyberspace();
|
||||||
|
auto enemy = std::make_unique<Enemy>(EnemyType::CYBER_DRONE, 1);
|
||||||
|
m_game->m_combat.startCombat(m_game->m_player, std::move(enemy));
|
||||||
|
m_game->m_state = GameState::COMBAT;
|
||||||
|
} else if (w == "HACKING") {
|
||||||
|
m_game->startNewRun();
|
||||||
|
m_game->enterCyberspace();
|
||||||
|
m_game->m_hacking.start(1);
|
||||||
|
m_game->m_state = GameState::HACKING;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::update(float dt) {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
m_frameCounter++;
|
||||||
|
|
||||||
|
if (m_frameCounter == 2 && !m_warpApplied) {
|
||||||
|
applyWarp();
|
||||||
|
}
|
||||||
|
|
||||||
|
handleHotkeys();
|
||||||
|
|
||||||
|
// God Mode replenishment
|
||||||
|
if (m_config.godMode) {
|
||||||
|
int maxH = m_game->m_player.getMaxHp();
|
||||||
|
int maxS = m_game->m_player.getMaxShield();
|
||||||
|
m_game->m_player.setStats(maxH, maxH, maxS, maxS, 99);
|
||||||
|
for (int t = 1; t <= 6; ++t) {
|
||||||
|
m_game->m_player.addAmmo(static_cast<AmmoType>(t), 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// State change detection for event screenshots
|
||||||
|
if (m_game->m_state != m_lastObservedState) {
|
||||||
|
if (m_config.screenshotEvents) {
|
||||||
|
std::string stateTag = "state_";
|
||||||
|
switch (m_game->m_state) {
|
||||||
|
case GameState::MAIN_MENU: stateTag += "menu"; break;
|
||||||
|
case GameState::HUB: stateTag += "hub"; break;
|
||||||
|
case GameState::EXPLORATION: stateTag += "explore"; break;
|
||||||
|
case GameState::COMBAT: stateTag += "combat"; break;
|
||||||
|
case GameState::HACKING: stateTag += "hacking"; break;
|
||||||
|
case GameState::REST_ROOM: stateTag += "rest"; break;
|
||||||
|
case GameState::VICTORY_LEVEL: stateTag += "victory"; break;
|
||||||
|
case GameState::GAME_OVER: stateTag += "gameover"; break;
|
||||||
|
default: stateTag += "other"; break;
|
||||||
|
}
|
||||||
|
captureEventScreenshot(stateTag);
|
||||||
|
}
|
||||||
|
m_lastObservedState = m_game->m_state;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bot automation
|
||||||
|
if (m_config.botEnabled) {
|
||||||
|
m_totalBotRunTime += dt;
|
||||||
|
updateBot(dt);
|
||||||
|
|
||||||
|
if (m_config.botDuration > 0.0f && m_totalBotRunTime >= m_config.botDuration) {
|
||||||
|
m_game->m_shouldQuit = true;
|
||||||
|
}
|
||||||
|
if (m_config.botMaxSteps > 0 && m_botStepCount >= m_config.botMaxSteps) {
|
||||||
|
m_game->m_shouldQuit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Continuous state dump
|
||||||
|
if (!m_config.stateDumpPath.empty() && (m_frameCounter % 15 == 0)) {
|
||||||
|
dumpStateToJson(m_config.stateDumpPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::updateBot(float dt) {
|
||||||
|
if (!m_game || m_game->m_transition.isBusy()) return;
|
||||||
|
|
||||||
|
m_botActionTimer += dt;
|
||||||
|
float actionInterval = m_config.turboMode ? 0.02f : 0.15f;
|
||||||
|
|
||||||
|
if (m_botActionTimer < actionInterval) return;
|
||||||
|
m_botActionTimer = 0.0f;
|
||||||
|
m_botStepCount++;
|
||||||
|
|
||||||
|
switch (m_game->m_state) {
|
||||||
|
case GameState::MAIN_MENU:
|
||||||
|
m_game->startNewRun();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::HUB:
|
||||||
|
executeBotHubStep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::EXPLORATION:
|
||||||
|
executeBotExplorationStep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::HACKING:
|
||||||
|
executeBotHackingStep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::COMBAT:
|
||||||
|
executeBotCombatStep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::REST_ROOM:
|
||||||
|
executeBotRestRoomStep();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::VICTORY_LEVEL:
|
||||||
|
m_game->enterRestRoom();
|
||||||
|
break;
|
||||||
|
|
||||||
|
case GameState::GAME_OVER:
|
||||||
|
m_game->startNewRun();
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::executeBotHubStep() {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
if (m_game->m_hubRoom.isFacingPortal(m_game->m_player)) {
|
||||||
|
m_game->tryInteractHub();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = m_game->m_player.getGridX();
|
||||||
|
int py = m_game->m_player.getGridY();
|
||||||
|
Direction facing = m_game->m_player.getFacing();
|
||||||
|
|
||||||
|
// Portal is at (3, 1) facing NORTH
|
||||||
|
if (py > 1) {
|
||||||
|
if (facing != Direction::NORTH) {
|
||||||
|
m_game->m_player.setFacing(Direction::NORTH);
|
||||||
|
} else {
|
||||||
|
m_game->tryMoveHub(0, -1);
|
||||||
|
}
|
||||||
|
} else if (px < 3) {
|
||||||
|
if (facing != Direction::EAST) {
|
||||||
|
m_game->m_player.setFacing(Direction::EAST);
|
||||||
|
} else {
|
||||||
|
m_game->tryMoveHub(1, 0);
|
||||||
|
}
|
||||||
|
} else if (px > 3) {
|
||||||
|
if (facing != Direction::WEST) {
|
||||||
|
m_game->m_player.setFacing(Direction::WEST);
|
||||||
|
} else {
|
||||||
|
m_game->tryMoveHub(-1, 0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_game->m_player.setFacing(Direction::NORTH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::executeBotRestRoomStep() {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
if (m_game->m_restRoom.isFacingGateway(m_game->m_player)) {
|
||||||
|
m_game->tryInteractRestRoom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_game->m_restRoom.isFacingRechargeMatrix(m_game->m_player) && !m_game->m_restRoom.isRecharged()) {
|
||||||
|
m_game->tryInteractRestRoom();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = m_game->m_player.getGridX();
|
||||||
|
int py = m_game->m_player.getGridY();
|
||||||
|
|
||||||
|
// Path around fountain: (3, 4) -> (2, 4) -> (2, 1) -> (3, 1) facing NORTH
|
||||||
|
if (px == 3 && py == 4) {
|
||||||
|
m_game->tryMoveRestRoom(-1, 0); // Move to (2, 4)
|
||||||
|
} else if (px == 2 && py > 1) {
|
||||||
|
m_game->tryMoveRestRoom(0, -1); // Move north to (2, 1)
|
||||||
|
} else if (px == 2 && py == 1) {
|
||||||
|
m_game->tryMoveRestRoom(1, 0); // Move east to (3, 1)
|
||||||
|
} else if (px == 3 && py == 1) {
|
||||||
|
m_game->m_player.setFacing(Direction::NORTH);
|
||||||
|
m_game->tryInteractRestRoom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::executeBotExplorationStep() {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
// Reset visited map on floor change
|
||||||
|
int curFloor = m_game->m_dungeon.getFloor();
|
||||||
|
if (m_lastDungeonFloor != curFloor) {
|
||||||
|
m_visitedDungeonTiles.clear();
|
||||||
|
m_lastDungeonFloor = curFloor;
|
||||||
|
}
|
||||||
|
|
||||||
|
int px = m_game->m_player.getGridX();
|
||||||
|
int py = m_game->m_player.getGridY();
|
||||||
|
m_visitedDungeonTiles.insert({ px, py });
|
||||||
|
|
||||||
|
// Handle implant / shrine modals
|
||||||
|
if (m_game->m_showImplantDraft) {
|
||||||
|
if (!m_game->m_draftChoices.empty()) {
|
||||||
|
m_game->m_player.addImplant(m_game->m_draftChoices[0]);
|
||||||
|
}
|
||||||
|
m_game->m_showImplantDraft = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_game->m_showShrineUI) {
|
||||||
|
m_game->m_showShrineUI = false;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If context prompt is available (door, mine, chest, exit), interact!
|
||||||
|
if (!m_game->m_contextPrompt.empty()) {
|
||||||
|
m_game->tryInteractDungeon();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// BFS Pathfinding to find next actionable or unvisited target
|
||||||
|
int width = m_game->m_dungeon.getWidth();
|
||||||
|
int height = m_game->m_dungeon.getHeight();
|
||||||
|
|
||||||
|
std::vector<std::vector<int>> dist(height, std::vector<int>(width, -1));
|
||||||
|
std::vector<std::vector<std::pair<int, int>>> parent(height, std::vector<std::pair<int, int>>(width, { -1, -1 }));
|
||||||
|
std::queue<std::pair<int, int>> q;
|
||||||
|
|
||||||
|
q.push({ px, py });
|
||||||
|
dist[py][px] = 0;
|
||||||
|
|
||||||
|
int targetX = -1, targetY = -1;
|
||||||
|
bool foundTarget = false;
|
||||||
|
|
||||||
|
int dirs[4][2] = { {0, -1}, {1, 0}, {0, 1}, {-1, 0} };
|
||||||
|
|
||||||
|
while (!q.empty()) {
|
||||||
|
auto [cx, cy] = q.front();
|
||||||
|
q.pop();
|
||||||
|
|
||||||
|
// Priority 1: Unvisited walkable floor or door or exit portal
|
||||||
|
bool isCurrentStart = (cx == px && cy == py);
|
||||||
|
TileType tt = m_game->m_dungeon.getTile(cx, cy);
|
||||||
|
|
||||||
|
if (!isCurrentStart) {
|
||||||
|
if (tt == TileType::EXIT_PORTAL || tt == TileType::LOOT_CACHE ||
|
||||||
|
(tt == TileType::DOOR_LOCKED && m_visitedDungeonTiles.count({cx, cy}) == 0) ||
|
||||||
|
(m_game->m_dungeon.isWalkable(cx, cy) && m_visitedDungeonTiles.count({cx, cy}) == 0)) {
|
||||||
|
targetX = cx;
|
||||||
|
targetY = cy;
|
||||||
|
foundTarget = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
int nx = cx + dirs[i][0];
|
||||||
|
int ny = cy + dirs[i][1];
|
||||||
|
|
||||||
|
if (nx >= 0 && nx < width && ny >= 0 && ny < height) {
|
||||||
|
if (dist[ny][nx] == -1) {
|
||||||
|
bool canTraverse = m_game->m_dungeon.isWalkable(nx, ny) || m_game->m_dungeon.isDoor(nx, ny);
|
||||||
|
if (canTraverse) {
|
||||||
|
dist[ny][nx] = dist[cy][cx] + 1;
|
||||||
|
parent[ny][nx] = { cx, cy };
|
||||||
|
q.push({ nx, ny });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!foundTarget) {
|
||||||
|
// Fallback: search for exit portal anywhere
|
||||||
|
for (int y = 0; y < height && !foundTarget; ++y) {
|
||||||
|
for (int x = 0; x < width && !foundTarget; ++x) {
|
||||||
|
if (m_game->m_dungeon.getTile(x, y) == TileType::EXIT_PORTAL) {
|
||||||
|
targetX = x;
|
||||||
|
targetY = y;
|
||||||
|
foundTarget = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (foundTarget && (targetX != px || targetY != py)) {
|
||||||
|
// Backtrack to get the immediate next step from (px, py)
|
||||||
|
int currX = targetX;
|
||||||
|
int currY = targetY;
|
||||||
|
while (parent[currY][currX].first != -1 &&
|
||||||
|
(parent[currY][currX].first != px || parent[currY][currX].second != py)) {
|
||||||
|
auto p = parent[currY][currX];
|
||||||
|
currX = p.first;
|
||||||
|
currY = p.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
int stepX = currX;
|
||||||
|
int stepY = currY;
|
||||||
|
|
||||||
|
int dx = stepX - px;
|
||||||
|
int dy = stepY - py;
|
||||||
|
|
||||||
|
Direction desiredDir = Direction::NORTH;
|
||||||
|
if (dx == 1) desiredDir = Direction::EAST;
|
||||||
|
else if (dx == -1) desiredDir = Direction::WEST;
|
||||||
|
else if (dy == 1) desiredDir = Direction::SOUTH;
|
||||||
|
else if (dy == -1) desiredDir = Direction::NORTH;
|
||||||
|
|
||||||
|
if (m_game->m_player.getFacing() == desiredDir) {
|
||||||
|
if (m_game->m_dungeon.isDoorLocked(stepX, stepY)) {
|
||||||
|
m_game->tryInteractDungeon();
|
||||||
|
} else {
|
||||||
|
m_game->tryMoveDungeon(dx, dy);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
m_game->m_player.setFacing(desiredDir);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Random wandering if completely stuck
|
||||||
|
Vector2 dirVec = getDirectionOffset(m_game->m_player.getFacing());
|
||||||
|
int frontX = px + static_cast<int>(dirVec.x);
|
||||||
|
int frontY = py + static_cast<int>(dirVec.y);
|
||||||
|
if (m_game->m_dungeon.isWalkable(frontX, frontY)) {
|
||||||
|
m_game->tryMoveDungeon(static_cast<int>(dirVec.x), static_cast<int>(dirVec.y));
|
||||||
|
} else {
|
||||||
|
m_game->m_player.turnRight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::executeBotHackingStep() {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
if (m_game->m_hacking.getPhase() == HackingPhase::PHASE_1_TUMBLER) {
|
||||||
|
auto& tumbler = const_cast<TumblerLock&>(m_game->m_hacking.getTumbler());
|
||||||
|
int pinIdx = tumbler.getSelectedPin();
|
||||||
|
const auto& pin = tumbler.getPin(pinIdx);
|
||||||
|
|
||||||
|
if (pin.isInSweetSpot()) {
|
||||||
|
tumbler.tryLockCurrentPin();
|
||||||
|
if (!tumbler.isAllPinsLocked()) {
|
||||||
|
tumbler.moveRight();
|
||||||
|
}
|
||||||
|
} else if (pin.currentHeight < pin.targetHeight) {
|
||||||
|
tumbler.adjustHeight(0.08f);
|
||||||
|
} else {
|
||||||
|
tumbler.adjustHeight(-0.08f);
|
||||||
|
}
|
||||||
|
} else if (m_game->m_hacking.getPhase() == HackingPhase::PHASE_2_SKILLCHECK) {
|
||||||
|
auto& sc = const_cast<DbdSkillCheck&>(m_game->m_hacking.getSkillCheck());
|
||||||
|
float angle = sc.getNeedleAngle();
|
||||||
|
float gStart = sc.getGreatStart();
|
||||||
|
float gEnd = sc.getGreatEnd();
|
||||||
|
|
||||||
|
if (angle >= gStart && angle <= gEnd) {
|
||||||
|
sc.strike();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::executeBotCombatStep() {
|
||||||
|
if (!m_game) return;
|
||||||
|
|
||||||
|
CombatPhase phase = m_game->m_combat.getPhase();
|
||||||
|
|
||||||
|
if (phase == CombatPhase::PLAYER_TURN) {
|
||||||
|
int hp = m_game->m_player.getHp();
|
||||||
|
int shield = m_game->m_player.getShield();
|
||||||
|
|
||||||
|
if (hp < 40 && shield < 10) {
|
||||||
|
m_game->m_combat.actionShield(m_game->m_player);
|
||||||
|
} else if (m_game->m_combat.getTripleCooldown() == 0) {
|
||||||
|
m_game->m_combat.actionTripleShot(m_game->m_player);
|
||||||
|
} else {
|
||||||
|
m_game->m_combat.actionSpinAndShoot(m_game->m_player);
|
||||||
|
}
|
||||||
|
} else if (phase == CombatPhase::VICTORY) {
|
||||||
|
bool wasElite = m_game->m_combat.wasElite();
|
||||||
|
m_game->m_player.heal(25);
|
||||||
|
m_game->m_player.addAmmo(AmmoType::KINETIC, 1);
|
||||||
|
m_game->m_player.addAmmo(AmmoType::EMP, 1);
|
||||||
|
m_game->m_state = GameState::EXPLORATION;
|
||||||
|
SoundManager::get().playTrack(MusicTrack::EXPLORATION, true);
|
||||||
|
if (wasElite) {
|
||||||
|
m_game->triggerImplantDraft();
|
||||||
|
}
|
||||||
|
} else if (phase == CombatPhase::DEFEAT) {
|
||||||
|
if (m_config.godMode) {
|
||||||
|
m_game->m_player.heal(100);
|
||||||
|
} else {
|
||||||
|
m_game->m_state = GameState::GAME_OVER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::drawOverlay() {
|
||||||
|
if (!m_config.showOverlay || !m_game) return;
|
||||||
|
|
||||||
|
// Strict Zero-Rounding tactical industrial diagnostic HUD
|
||||||
|
int boxX = 16;
|
||||||
|
int boxY = 16;
|
||||||
|
int boxW = 390;
|
||||||
|
int boxH = 220;
|
||||||
|
|
||||||
|
DrawRectangle(boxX, boxY, boxW, boxH, Color{ 6, 10, 16, 235 });
|
||||||
|
DrawRectangleLines(boxX, boxY, boxW, boxH, Color{ 70, 150, 255, 255 }); // Cobalt Blue border
|
||||||
|
DrawRectangle(boxX, boxY, boxW, 24, Color{ 16, 26, 42, 255 });
|
||||||
|
DrawRectangleLines(boxX, boxY, boxW, 24, Color{ 70, 150, 255, 255 });
|
||||||
|
|
||||||
|
DrawText("/// TACTICAL AGENT TELEMETRY ///", boxX + 10, boxY + 6, 12, TacticalColors::PureWhite);
|
||||||
|
|
||||||
|
int textY = boxY + 34;
|
||||||
|
int lineStep = 18;
|
||||||
|
|
||||||
|
std::string stateStr = "STATE: ";
|
||||||
|
switch (m_game->m_state) {
|
||||||
|
case GameState::MAIN_MENU: stateStr += "MAIN_MENU"; break;
|
||||||
|
case GameState::HUB: stateStr += "HUB_ROOM"; break;
|
||||||
|
case GameState::EXPLORATION: stateStr += "EXPLORATION"; break;
|
||||||
|
case GameState::COMBAT: stateStr += "COMBAT"; break;
|
||||||
|
case GameState::HACKING: stateStr += "HACKING_ICE"; break;
|
||||||
|
case GameState::REST_ROOM: stateStr += "REST_ROOM"; break;
|
||||||
|
case GameState::SETTINGS: stateStr += "SETTINGS"; break;
|
||||||
|
case GameState::PAUSE_MENU: stateStr += "PAUSE"; break;
|
||||||
|
case GameState::VICTORY_LEVEL: stateStr += "VICTORY"; break;
|
||||||
|
case GameState::GAME_OVER: stateStr += "GAME_OVER"; break;
|
||||||
|
default: stateStr += "UNKNOWN"; break;
|
||||||
|
}
|
||||||
|
DrawText(stateStr.c_str(), boxX + 12, textY, 13, TacticalColors::PureWhite);
|
||||||
|
textY += lineStep;
|
||||||
|
|
||||||
|
int fps = GetFPS();
|
||||||
|
std::string perfStr = "FPS: " + std::to_string(fps) + " | FRAME: " + std::to_string(m_frameCounter);
|
||||||
|
DrawText(perfStr.c_str(), boxX + 12, textY, 13, Color{ 180, 200, 220, 255 });
|
||||||
|
textY += lineStep;
|
||||||
|
|
||||||
|
const auto& p = m_game->m_player;
|
||||||
|
std::string playerStr = "HP: " + std::to_string(p.getHp()) + "/" + std::to_string(p.getMaxHp()) +
|
||||||
|
" | SHIELD: " + std::to_string(p.getShield()) + "/" + std::to_string(p.getMaxShield()) +
|
||||||
|
" | PICKS: " + std::to_string(p.getCyberPicks());
|
||||||
|
DrawText(playerStr.c_str(), boxX + 12, textY, 13, TacticalColors::PureWhite);
|
||||||
|
textY += lineStep;
|
||||||
|
|
||||||
|
std::string posStr = "GRID: (" + std::to_string(p.getGridX()) + ", " + std::to_string(p.getGridY()) + ") | FLOOR: " +
|
||||||
|
std::to_string(m_game->m_dungeon.getFloor());
|
||||||
|
DrawText(posStr.c_str(), boxX + 12, textY, 13, Color{ 245, 130, 32, 255 }); // Overheat Amber
|
||||||
|
textY += lineStep;
|
||||||
|
|
||||||
|
std::string botStr = m_config.botEnabled ? "[BOT: AUTO-PILOT ON]" : "[BOT: MANUAL]";
|
||||||
|
Color botColor = m_config.botEnabled ? Color{ 95, 185, 75, 255 } : Color{ 140, 150, 160, 255 };
|
||||||
|
DrawText(botStr.c_str(), boxX + 12, textY, 13, botColor);
|
||||||
|
|
||||||
|
std::string godStr = m_config.godMode ? "[GOD: ACTIVE]" : "[GOD: OFF]";
|
||||||
|
Color godColor = m_config.godMode ? Color{ 225, 25, 55, 255 } : Color{ 140, 150, 160, 255 };
|
||||||
|
DrawText(godStr.c_str(), boxX + 210, textY, 13, godColor);
|
||||||
|
textY += lineStep;
|
||||||
|
|
||||||
|
DrawText("F1: HUD | F2: GOD | F3: SKIP | F4: AMMO | F10: BOT | F12: CAPTURE",
|
||||||
|
boxX + 12, textY + 6, 10, Color{ 120, 140, 165, 255 });
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::postDraw() {
|
||||||
|
// Single screenshot capture on designated frame
|
||||||
|
if (!m_config.singleScreenshotPath.empty() && !m_singleScreenshotTaken &&
|
||||||
|
m_frameCounter >= m_config.singleScreenshotFrame) {
|
||||||
|
std::filesystem::path p(m_config.singleScreenshotPath);
|
||||||
|
if (p.has_parent_path()) {
|
||||||
|
std::filesystem::create_directories(p.parent_path());
|
||||||
|
}
|
||||||
|
TakeScreenshot(p.make_preferred().string().c_str());
|
||||||
|
m_singleScreenshotTaken = true;
|
||||||
|
if (m_game) {
|
||||||
|
m_game->m_shouldQuit = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pending event screenshot capture
|
||||||
|
if (!m_pendingScreenshotPath.empty()) {
|
||||||
|
std::filesystem::path p(m_pendingScreenshotPath);
|
||||||
|
if (p.has_parent_path()) {
|
||||||
|
std::filesystem::create_directories(p.parent_path());
|
||||||
|
}
|
||||||
|
TakeScreenshot(p.make_preferred().string().c_str());
|
||||||
|
m_pendingScreenshotPath.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::captureScreenshot(const std::string& customPath) {
|
||||||
|
if (!customPath.empty()) {
|
||||||
|
m_pendingScreenshotPath = customPath;
|
||||||
|
} else {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto timeT = std::chrono::system_clock::to_time_t(now);
|
||||||
|
std::tm tmNow;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
localtime_s(&tmNow, &timeT);
|
||||||
|
#else
|
||||||
|
localtime_r(&timeT, &tmNow);
|
||||||
|
#endif
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << m_config.screenshotDir << "/shot_"
|
||||||
|
<< std::put_time(&tmNow, "%Y%m%d_%H%M%S") << ".png";
|
||||||
|
m_pendingScreenshotPath = ss.str();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::captureEventScreenshot(const std::string& eventTag) {
|
||||||
|
auto now = std::chrono::system_clock::now();
|
||||||
|
auto timeT = std::chrono::system_clock::to_time_t(now);
|
||||||
|
std::tm tmNow;
|
||||||
|
#if defined(_WIN32)
|
||||||
|
localtime_s(&tmNow, &timeT);
|
||||||
|
#else
|
||||||
|
localtime_r(&timeT, &tmNow);
|
||||||
|
#endif
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << m_config.screenshotDir << "/event_"
|
||||||
|
<< std::put_time(&tmNow, "%H%M%S") << "_" << eventTag << ".png";
|
||||||
|
m_pendingScreenshotPath = ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string DebugSystem::serializeStateJson() {
|
||||||
|
if (!m_game) return "{}";
|
||||||
|
|
||||||
|
std::ostringstream ss;
|
||||||
|
ss << "{\n";
|
||||||
|
ss << " \"frame\": " << m_frameCounter << ",\n";
|
||||||
|
ss << " \"time\": " << m_totalBotRunTime << ",\n";
|
||||||
|
|
||||||
|
std::string st = "UNKNOWN";
|
||||||
|
switch (m_game->m_state) {
|
||||||
|
case GameState::MAIN_MENU: st = "MAIN_MENU"; break;
|
||||||
|
case GameState::HUB: st = "HUB"; break;
|
||||||
|
case GameState::EXPLORATION: st = "EXPLORATION"; break;
|
||||||
|
case GameState::COMBAT: st = "COMBAT"; break;
|
||||||
|
case GameState::HACKING: st = "HACKING"; break;
|
||||||
|
case GameState::REST_ROOM: st = "REST_ROOM"; break;
|
||||||
|
case GameState::SETTINGS: st = "SETTINGS"; break;
|
||||||
|
case GameState::PAUSE_MENU: st = "PAUSE_MENU"; break;
|
||||||
|
case GameState::VICTORY_LEVEL: st = "VICTORY_LEVEL"; break;
|
||||||
|
case GameState::GAME_OVER: st = "GAME_OVER"; break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
ss << " \"state\": \"" << st << "\",\n";
|
||||||
|
|
||||||
|
const auto& p = m_game->m_player;
|
||||||
|
ss << " \"player\": {\n";
|
||||||
|
ss << " \"hp\": " << p.getHp() << ",\n";
|
||||||
|
ss << " \"max_hp\": " << p.getMaxHp() << ",\n";
|
||||||
|
ss << " \"shield\": " << p.getShield() << ",\n";
|
||||||
|
ss << " \"max_shield\": " << p.getMaxShield() << ",\n";
|
||||||
|
ss << " \"picks\": " << p.getCyberPicks() << ",\n";
|
||||||
|
ss << " \"scraps\": " << p.getTechScraps() << ",\n";
|
||||||
|
ss << " \"grid_x\": " << p.getGridX() << ",\n";
|
||||||
|
ss << " \"grid_y\": " << p.getGridY() << "\n";
|
||||||
|
ss << " },\n";
|
||||||
|
|
||||||
|
ss << " \"dungeon\": {\n";
|
||||||
|
ss << " \"floor\": " << m_game->m_dungeon.getFloor() << ",\n";
|
||||||
|
ss << " \"visited_tiles\": " << m_visitedDungeonTiles.size() << "\n";
|
||||||
|
ss << " },\n";
|
||||||
|
|
||||||
|
bool inCombat = (m_game->m_state == GameState::COMBAT);
|
||||||
|
ss << " \"combat\": {\n";
|
||||||
|
ss << " \"active\": " << (inCombat ? "true" : "false");
|
||||||
|
if (inCombat && m_game->m_combat.getEnemy()) {
|
||||||
|
const auto* e = m_game->m_combat.getEnemy();
|
||||||
|
ss << ",\n";
|
||||||
|
ss << " \"enemy_name\": \"" << e->getName() << "\",\n";
|
||||||
|
ss << " \"enemy_hp\": " << e->getHp() << ",\n";
|
||||||
|
ss << " \"enemy_max_hp\": " << e->getMaxHp() << ",\n";
|
||||||
|
ss << " \"enemy_shield\": " << e->getShield() << "\n";
|
||||||
|
} else {
|
||||||
|
ss << "\n";
|
||||||
|
}
|
||||||
|
ss << " },\n";
|
||||||
|
|
||||||
|
ss << " \"bot\": {\n";
|
||||||
|
ss << " \"enabled\": " << (m_config.botEnabled ? "true" : "false") << ",\n";
|
||||||
|
ss << " \"steps\": " << m_botStepCount << ",\n";
|
||||||
|
ss << " \"god_mode\": " << (m_config.godMode ? "true" : "false") << "\n";
|
||||||
|
ss << " }\n";
|
||||||
|
|
||||||
|
ss << "}\n";
|
||||||
|
return ss.str();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DebugSystem::dumpStateToJson(const std::string& path) {
|
||||||
|
std::string json = serializeStateJson();
|
||||||
|
std::ofstream out(path, std::ios::trunc);
|
||||||
|
if (out.is_open()) {
|
||||||
|
out << json;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace dnr
|
||||||
91
src/debug/DebugSystem.hpp
Normal file
91
src/debug/DebugSystem.hpp
Normal file
|
|
@ -0,0 +1,91 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "core/Common.hpp"
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace dnr {
|
||||||
|
|
||||||
|
class Game;
|
||||||
|
|
||||||
|
struct DebugConfig {
|
||||||
|
bool botEnabled = false;
|
||||||
|
float botDuration = 0.0f; // Auto-quit after N seconds (0 = infinite)
|
||||||
|
int botMaxSteps = 0; // Step limit (0 = infinite)
|
||||||
|
bool godMode = false;
|
||||||
|
bool turboMode = false;
|
||||||
|
bool showOverlay = false;
|
||||||
|
bool screenshotEvents = false;
|
||||||
|
std::string singleScreenshotPath;
|
||||||
|
int singleScreenshotFrame = 8;
|
||||||
|
std::string screenshotDir = "screenshots";
|
||||||
|
std::string stateDumpPath;
|
||||||
|
std::string warpState; // "HUB", "EXPLORATION", "COMBAT", "REST_ROOM", "HACKING"
|
||||||
|
int targetFloor = 1;
|
||||||
|
};
|
||||||
|
|
||||||
|
class DebugSystem {
|
||||||
|
public:
|
||||||
|
static DebugSystem& get();
|
||||||
|
|
||||||
|
void parseArgs(int argc, char* argv[]);
|
||||||
|
const DebugConfig& getConfig() const { return m_config; }
|
||||||
|
DebugConfig& getConfig() { return m_config; }
|
||||||
|
|
||||||
|
void init(Game* game);
|
||||||
|
void update(float dt);
|
||||||
|
void drawOverlay();
|
||||||
|
void postDraw();
|
||||||
|
|
||||||
|
// Visual captures
|
||||||
|
void captureScreenshot(const std::string& customPath = "");
|
||||||
|
void captureEventScreenshot(const std::string& eventTag);
|
||||||
|
|
||||||
|
// State dump
|
||||||
|
void dumpStateToJson(const std::string& path);
|
||||||
|
|
||||||
|
// Controls
|
||||||
|
void toggleBot();
|
||||||
|
bool isBotEnabled() const { return m_config.botEnabled; }
|
||||||
|
void toggleGodMode();
|
||||||
|
bool isGodMode() const { return m_config.godMode; }
|
||||||
|
void toggleOverlay();
|
||||||
|
bool isOverlayVisible() const { return m_config.showOverlay; }
|
||||||
|
|
||||||
|
void handleHotkeys();
|
||||||
|
void applyWarp();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DebugSystem();
|
||||||
|
~DebugSystem();
|
||||||
|
|
||||||
|
void updateBot(float dt);
|
||||||
|
void executeBotExplorationStep();
|
||||||
|
void executeBotCombatStep();
|
||||||
|
void executeBotHackingStep();
|
||||||
|
void executeBotHubStep();
|
||||||
|
void executeBotRestRoomStep();
|
||||||
|
|
||||||
|
std::string serializeStateJson();
|
||||||
|
|
||||||
|
Game* m_game = nullptr;
|
||||||
|
DebugConfig m_config;
|
||||||
|
|
||||||
|
// Bot telemetry
|
||||||
|
float m_botActionTimer = 0.0f;
|
||||||
|
float m_totalBotRunTime = 0.0f;
|
||||||
|
int m_botStepCount = 0;
|
||||||
|
GameState m_lastObservedState = GameState::MAIN_MENU;
|
||||||
|
std::set<std::pair<int, int>> m_visitedDungeonTiles;
|
||||||
|
int m_lastDungeonFloor = -1;
|
||||||
|
|
||||||
|
// Screenshot & state dump tracking
|
||||||
|
int m_frameCounter = 0;
|
||||||
|
bool m_singleScreenshotTaken = false;
|
||||||
|
bool m_warpApplied = false;
|
||||||
|
std::string m_pendingScreenshotPath;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace dnr
|
||||||
13
src/main.cpp
13
src/main.cpp
|
|
@ -1,14 +1,25 @@
|
||||||
#include "raylib.h"
|
#include "raylib.h"
|
||||||
#include "core/Game.hpp"
|
#include "core/Game.hpp"
|
||||||
|
#include "debug/DebugSystem.hpp"
|
||||||
|
|
||||||
|
int main(int argc, char* argv[]) {
|
||||||
|
dnr::DebugSystem::get().parseArgs(argc, argv);
|
||||||
|
const auto& debugConfig = dnr::DebugSystem::get().getConfig();
|
||||||
|
|
||||||
int main() {
|
|
||||||
const int screenWidth = 1280;
|
const int screenWidth = 1280;
|
||||||
const int screenHeight = 720;
|
const int screenHeight = 720;
|
||||||
|
|
||||||
|
if (!debugConfig.turboMode) {
|
||||||
SetConfigFlags(FLAG_VSYNC_HINT);
|
SetConfigFlags(FLAG_VSYNC_HINT);
|
||||||
|
}
|
||||||
InitWindow(screenWidth, screenHeight, "DoorNroll // Cyber-Revolver Cyberspace Crawler");
|
InitWindow(screenWidth, screenHeight, "DoorNroll // Cyber-Revolver Cyberspace Crawler");
|
||||||
SetExitKey(KEY_NULL); // Prevent ESC from closing the game window
|
SetExitKey(KEY_NULL); // Prevent ESC from closing the game window
|
||||||
|
|
||||||
|
if (debugConfig.turboMode) {
|
||||||
|
SetTargetFPS(0);
|
||||||
|
} else {
|
||||||
SetTargetFPS(60);
|
SetTargetFPS(60);
|
||||||
|
}
|
||||||
|
|
||||||
dnr::Game game;
|
dnr::Game game;
|
||||||
game.init();
|
game.init();
|
||||||
|
|
|
||||||
|
|
@ -1228,12 +1228,92 @@ void CyberRenderer::generateTextures() {
|
||||||
m_texAncientCeil = createAncientCeilTexture();
|
m_texAncientCeil = createAncientCeilTexture();
|
||||||
m_texAncientWood = createAncientWoodTexture();
|
m_texAncientWood = createAncientWoodTexture();
|
||||||
|
|
||||||
|
// Load custom 3D model dynamic lighting shader
|
||||||
|
std::vector<std::pair<std::string, std::string>> lightingShaderPaths = {
|
||||||
|
{ "assets/shaders/lighting.vs", "assets/shaders/lighting.fs" },
|
||||||
|
{ "../assets/shaders/lighting.vs", "../assets/shaders/lighting.fs" },
|
||||||
|
{ "shaders/lighting.vs", "shaders/lighting.fs" }
|
||||||
|
};
|
||||||
|
m_modelLightingLoaded = false;
|
||||||
|
for (const auto& sp : lightingShaderPaths) {
|
||||||
|
if (FileExists(sp.first.c_str()) && FileExists(sp.second.c_str())) {
|
||||||
|
m_shaderModelLighting = LoadShader(sp.first.c_str(), sp.second.c_str());
|
||||||
|
if (m_shaderModelLighting.id != 0) {
|
||||||
|
m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW] = GetShaderLocation(m_shaderModelLighting, "viewPos");
|
||||||
|
m_locLightViewPos = m_shaderModelLighting.locs[SHADER_LOC_VECTOR_VIEW];
|
||||||
|
m_locLightAmbient = GetShaderLocation(m_shaderModelLighting, "ambient");
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
m_modelLights[i].enabled = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].enabled", i));
|
||||||
|
m_modelLights[i].type = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].type", i));
|
||||||
|
m_modelLights[i].position = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].position", i));
|
||||||
|
m_modelLights[i].target = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].target", i));
|
||||||
|
m_modelLights[i].color = GetShaderLocation(m_shaderModelLighting, TextFormat("lights[%d].color", i));
|
||||||
|
}
|
||||||
|
m_modelLightingLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load custom imported 3D Bed model
|
||||||
|
std::vector<std::string> bedModelPaths = {
|
||||||
|
"assets/models/Bed.obj",
|
||||||
|
"../assets/models/Bed.obj",
|
||||||
|
"models/Bed.obj"
|
||||||
|
};
|
||||||
|
std::vector<std::string> bedTexPaths = {
|
||||||
|
"assets/models/Bed_textures.png",
|
||||||
|
"../assets/models/Bed_textures.png",
|
||||||
|
"models/Bed_textures.png"
|
||||||
|
};
|
||||||
|
|
||||||
|
m_bedModelLoaded = false;
|
||||||
|
for (const auto& mp : bedModelPaths) {
|
||||||
|
if (FileExists(mp.c_str())) {
|
||||||
|
m_bedModel = LoadModel(mp.c_str());
|
||||||
|
m_bedModelLoaded = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bedModelLoaded) {
|
||||||
|
if (m_modelLightingLoaded) {
|
||||||
|
for (int i = 0; i < m_bedModel.materialCount; ++i) {
|
||||||
|
m_bedModel.materials[i].shader = m_shaderModelLighting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (const auto& tp : bedTexPaths) {
|
||||||
|
if (FileExists(tp.c_str())) {
|
||||||
|
m_texBedModel = LoadTexture(tp.c_str());
|
||||||
|
SetTextureFilter(m_texBedModel, TEXTURE_FILTER_POINT);
|
||||||
|
if (m_bedModel.materialCount > 0) {
|
||||||
|
m_bedModel.materials[0].maps[MATERIAL_MAP_DIFFUSE].texture = m_texBedModel;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
m_texturesLoaded = true;
|
m_texturesLoaded = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CyberRenderer::unloadTextures() {
|
void CyberRenderer::unloadTextures() {
|
||||||
if (!m_texturesLoaded) return;
|
if (!m_texturesLoaded) return;
|
||||||
|
|
||||||
|
if (m_modelLightingLoaded) {
|
||||||
|
UnloadShader(m_shaderModelLighting);
|
||||||
|
m_modelLightingLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_bedModelLoaded) {
|
||||||
|
UnloadModel(m_bedModel);
|
||||||
|
if (m_texBedModel.id != 0) {
|
||||||
|
UnloadTexture(m_texBedModel);
|
||||||
|
m_texBedModel.id = 0;
|
||||||
|
}
|
||||||
|
m_bedModelLoaded = false;
|
||||||
|
}
|
||||||
|
|
||||||
UnloadTexture(m_texDungeonWall);
|
UnloadTexture(m_texDungeonWall);
|
||||||
UnloadTexture(m_texDungeonFloor);
|
UnloadTexture(m_texDungeonFloor);
|
||||||
UnloadTexture(m_texDungeonCeil);
|
UnloadTexture(m_texDungeonCeil);
|
||||||
|
|
@ -1904,6 +1984,60 @@ void CyberRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player,
|
||||||
endPostProcess(animTime);
|
endPostProcess(animTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CyberRenderer::updateHubModelLighting(const Camera3D& camera, float animTime) {
|
||||||
|
if (!m_modelLightingLoaded) return;
|
||||||
|
|
||||||
|
// Camera view position for specular highlights
|
||||||
|
float camPos[3] = { camera.position.x, camera.position.y, camera.position.z };
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_locLightViewPos, camPos, SHADER_UNIFORM_VEC3);
|
||||||
|
|
||||||
|
// Cyber-noir ambient illumination (subtle slate blue-gray base)
|
||||||
|
float ambient[4] = { 0.18f, 0.18f, 0.22f, 1.0f };
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_locLightAmbient, ambient, SHADER_UNIFORM_VEC4);
|
||||||
|
|
||||||
|
struct HubLight {
|
||||||
|
int enabled;
|
||||||
|
int type;
|
||||||
|
Vector3 pos;
|
||||||
|
Vector3 target;
|
||||||
|
float color[4];
|
||||||
|
};
|
||||||
|
|
||||||
|
float deskFlicker = 0.90f + 0.10f * std::sin(animTime * 6.0f);
|
||||||
|
float portalPulse = 0.75f + 0.25f * std::sin(animTime * 3.5f);
|
||||||
|
|
||||||
|
HubLight lights[4] = {
|
||||||
|
// Light 0: Megacity Skyline Window glow (East wall) - cool neon cobalt/violet
|
||||||
|
{ 1, 1, { 11.0f, 1.8f, 6.0f }, { 0, 0, 0 }, { 0.35f, 0.40f, 0.85f, 1.0f } },
|
||||||
|
// Light 1: Workstation CRT monitors (West desk) - cyan flicker
|
||||||
|
{ 1, 1, { 1.3f, 1.3f, 5.0f }, { 0, 0, 0 }, { 0.15f * deskFlicker, 0.55f * deskFlicker, 0.85f * deskFlicker, 1.0f } },
|
||||||
|
// Light 2: Cyberspace Gateway Portal (North wall) - emerald pulse
|
||||||
|
{ 1, 1, { 6.0f, 1.8f, 1.0f }, { 0, 0, 0 }, { 0.10f * portalPulse, 0.80f * portalPulse, 0.40f * portalPulse, 1.0f } },
|
||||||
|
// Light 3: Ceiling warm ambient lamp (Above room center/bed approach) - warm tungsten
|
||||||
|
{ 1, 1, { 8.5f, 2.7f, 8.5f }, { 0, 0, 0 }, { 0.70f, 0.58f, 0.42f, 1.0f } }
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
if (m_modelLights[i].enabled != -1) {
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_modelLights[i].enabled, &lights[i].enabled, SHADER_UNIFORM_INT);
|
||||||
|
}
|
||||||
|
if (m_modelLights[i].type != -1) {
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_modelLights[i].type, &lights[i].type, SHADER_UNIFORM_INT);
|
||||||
|
}
|
||||||
|
if (m_modelLights[i].position != -1) {
|
||||||
|
float p[3] = { lights[i].pos.x, lights[i].pos.y, lights[i].pos.z };
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_modelLights[i].position, p, SHADER_UNIFORM_VEC3);
|
||||||
|
}
|
||||||
|
if (m_modelLights[i].target != -1) {
|
||||||
|
float t[3] = { lights[i].target.x, lights[i].target.y, lights[i].target.z };
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_modelLights[i].target, t, SHADER_UNIFORM_VEC3);
|
||||||
|
}
|
||||||
|
if (m_modelLights[i].color != -1) {
|
||||||
|
SetShaderValue(m_shaderModelLighting, m_modelLights[i].color, lights[i].color, SHADER_UNIFORM_VEC4);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
|
void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
|
||||||
if (!m_texturesLoaded) {
|
if (!m_texturesLoaded) {
|
||||||
generateTextures();
|
generateTextures();
|
||||||
|
|
@ -1920,6 +2054,8 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
|
||||||
|
|
||||||
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
m_camera.target = { eye.x + dirX, eye.y, eye.z + dirZ };
|
||||||
|
|
||||||
|
updateHubModelLighting(m_camera, animTime);
|
||||||
|
|
||||||
BeginMode3D(m_camera);
|
BeginMode3D(m_camera);
|
||||||
|
|
||||||
const float tileSize = 2.0f;
|
const float tileSize = 2.0f;
|
||||||
|
|
@ -2091,6 +2227,13 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
|
||||||
Color{ 0, static_cast<unsigned char>(180 + ledPulse * 75), 255, 255 });
|
Color{ 0, static_cast<unsigned char>(180 + ledPulse * 75), 255, 255 });
|
||||||
|
|
||||||
// 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f])
|
// 8. BED (South-East corner: X in [8.5f, 11.2f], Z in [8.8f, 11.2f])
|
||||||
|
if (m_bedModelLoaded) {
|
||||||
|
// Draw user custom imported 3D Bed model
|
||||||
|
Vector3 customBedPos = { 9.7f, 0.0f, 9.75f };
|
||||||
|
float customBedScale = 0.024f;
|
||||||
|
DrawModelEx(m_bedModel, customBedPos, Vector3{ 0.0f, 1.0f, 0.0f }, 0.0f,
|
||||||
|
Vector3{ customBedScale, customBedScale, customBedScale }, WHITE);
|
||||||
|
} else {
|
||||||
Vector3 bedPos = { 9.8f, 0.30f, 10.0f };
|
Vector3 bedPos = { 9.8f, 0.30f, 10.0f };
|
||||||
Vector3 bedSize = { 2.3f, 0.45f, 2.2f };
|
Vector3 bedSize = { 2.3f, 0.45f, 2.2f };
|
||||||
DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 38, 26, 18, 255 });
|
DrawCube({ bedPos.x, 0.08f, bedPos.z }, bedSize.x + 0.1f, 0.16f, bedSize.z + 0.1f, Color{ 38, 26, 18, 255 });
|
||||||
|
|
@ -2104,6 +2247,7 @@ void CyberRenderer::renderHub(const HubRoom& hub, const Player& player, float an
|
||||||
Vector3 pillow2 = { 10.4f, 0.58f, 10.7f };
|
Vector3 pillow2 = { 10.4f, 0.58f, 10.7f };
|
||||||
DrawCube(pillow1, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
|
DrawCube(pillow1, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
|
||||||
DrawCube(pillow2, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
|
DrawCube(pillow2, 0.9f, 0.12f, 0.50f, Color{ 160, 165, 180, 255 });
|
||||||
|
}
|
||||||
|
|
||||||
// 9. CYBERSPACE GATEWAY DOOR (North wall at X = 6.0f, Z = minCoord)
|
// 9. CYBERSPACE GATEWAY DOOR (North wall at X = 6.0f, Z = minCoord)
|
||||||
Vector3 gateFramePos = { 6.0f, roomHeight * 0.5f, minCoord + 0.05f };
|
Vector3 gateFramePos = { 6.0f, roomHeight * 0.5f, minCoord + 0.05f };
|
||||||
|
|
|
||||||
|
|
@ -71,6 +71,23 @@ private:
|
||||||
Texture2D m_texWindowView{};
|
Texture2D m_texWindowView{};
|
||||||
Texture2D m_texCyberGate{};
|
Texture2D m_texCyberGate{};
|
||||||
|
|
||||||
|
// Custom 3D Models & Dynamic Lighting
|
||||||
|
Model m_bedModel{};
|
||||||
|
Texture2D m_texBedModel{};
|
||||||
|
bool m_bedModelLoaded = false;
|
||||||
|
Shader m_shaderModelLighting{};
|
||||||
|
bool m_modelLightingLoaded = false;
|
||||||
|
int m_locLightAmbient = -1;
|
||||||
|
int m_locLightViewPos = -1;
|
||||||
|
struct ModelLightLocs {
|
||||||
|
int enabled = -1;
|
||||||
|
int type = -1;
|
||||||
|
int position = -1;
|
||||||
|
int target = -1;
|
||||||
|
int color = -1;
|
||||||
|
} m_modelLights[4];
|
||||||
|
void updateHubModelLighting(const Camera3D& camera, float animTime);
|
||||||
|
|
||||||
// Ancient Palace Storeroom Procedural Textures
|
// Ancient Palace Storeroom Procedural Textures
|
||||||
Texture2D m_texAncientWall{};
|
Texture2D m_texAncientWall{};
|
||||||
Texture2D m_texAncientFloor{};
|
Texture2D m_texAncientFloor{};
|
||||||
|
|
|
||||||
|
|
@ -1,941 +0,0 @@
|
||||||
#include "SoftwareRenderer.hpp"
|
|
||||||
#include "hub/HubRoom.hpp"
|
|
||||||
#include "hub/RestRoom.hpp"
|
|
||||||
#include "combat/CombatSystem.hpp"
|
|
||||||
#include <cmath>
|
|
||||||
#include <algorithm>
|
|
||||||
#include <cstdlib>
|
|
||||||
#include <cstring>
|
|
||||||
#include <iostream>
|
|
||||||
|
|
||||||
namespace dnr {
|
|
||||||
|
|
||||||
SoftwareRenderer::SoftwareRenderer() = default;
|
|
||||||
SoftwareRenderer::~SoftwareRenderer() {
|
|
||||||
cleanup();
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::init() {
|
|
||||||
if (m_initialized) return;
|
|
||||||
|
|
||||||
m_framebuffer.resize(FB_WIDTH * FB_HEIGHT, Color{ 6, 8, 12, 255 });
|
|
||||||
m_depthBuffer.resize(FB_WIDTH, 1000.0f);
|
|
||||||
|
|
||||||
m_screenImage.data = m_framebuffer.data();
|
|
||||||
m_screenImage.width = FB_WIDTH;
|
|
||||||
m_screenImage.height = FB_HEIGHT;
|
|
||||||
m_screenImage.mipmaps = 1;
|
|
||||||
m_screenImage.format = PIXELFORMAT_UNCOMPRESSED_R8G8B8A8;
|
|
||||||
|
|
||||||
m_screenTexture = LoadTextureFromImage(m_screenImage);
|
|
||||||
SetTextureFilter(m_screenTexture, TEXTURE_FILTER_POINT);
|
|
||||||
|
|
||||||
generateCpuTextures();
|
|
||||||
|
|
||||||
m_initialized = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::cleanup() {
|
|
||||||
if (m_initialized) {
|
|
||||||
UnloadTexture(m_screenTexture);
|
|
||||||
m_initialized = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void SoftwareRenderer::triggerGlitch(float intensity) {
|
|
||||||
m_glitchTimer = std::max(m_glitchTimer, intensity);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Texture Generation & Loading
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::generateCpuTextures() {
|
|
||||||
auto makeTex = [](CpuTexture& t, int w = TEX_SIZE, int h = TEX_SIZE) {
|
|
||||||
t.width = w;
|
|
||||||
t.height = h;
|
|
||||||
t.pixels.resize(w * h, Color{ 30, 30, 30, 255 });
|
|
||||||
};
|
|
||||||
|
|
||||||
// 1. Dungeon Wall: Dark tactical gunmetal panel with reinforced seams & rivets
|
|
||||||
makeTex(m_texDungeonWall);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isBorder = (x < 4 || x > 251 || y < 4 || y > 251 || x == 127 || x == 128 || y == 127 || y == 128);
|
|
||||||
bool isRivet = ((x == 12 || x == 243 || x == 118 || x == 137) && (y % 32 >= 14 && y % 32 <= 17));
|
|
||||||
int noise = ((x * 19 + y * 37) % 9) - 4;
|
|
||||||
int base = isBorder ? 18 : (isRivet ? 95 : 38 + noise);
|
|
||||||
m_texDungeonWall.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)std::clamp(base - 4, 8, 255),
|
|
||||||
(unsigned char)std::clamp(base, 10, 255),
|
|
||||||
(unsigned char)std::clamp(base + 8, 14, 255),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Dungeon Floor: Industrial steel grating
|
|
||||||
makeTex(m_texDungeonFloor);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isGrate = (x % 16 <= 2 || y % 16 <= 2);
|
|
||||||
int base = isGrate ? 12 : 32 + ((x ^ y) % 7);
|
|
||||||
m_texDungeonFloor.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)base,
|
|
||||||
(unsigned char)(base + 3),
|
|
||||||
(unsigned char)(base + 7),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Dungeon Ceiling: Metallic beam ribs with dim red emergency light strip
|
|
||||||
makeTex(m_texDungeonCeil);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isStrip = (x >= 124 && x <= 131 && y % 64 >= 20 && y % 64 <= 44);
|
|
||||||
int base = ((y % 32 < 4) ? 14 : 26) + ((x * 13) % 5);
|
|
||||||
if (isStrip) {
|
|
||||||
m_texDungeonCeil.pixels[y * TEX_SIZE + x] = Color{ 220, 25, 45, 255 };
|
|
||||||
} else {
|
|
||||||
m_texDungeonCeil.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)base,
|
|
||||||
(unsigned char)(base + 2),
|
|
||||||
(unsigned char)(base + 6),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 4. Dungeon Blast Door: Heavy pneumatic airlock door with yellow warning chevron
|
|
||||||
makeTex(m_texDungeonDoor);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isSeam = (x == 127 || x == 128 || y == 64 || y == 192);
|
|
||||||
bool isWarning = (y >= 115 && y <= 140 && ((x + y) / 16) % 2 == 0);
|
|
||||||
if (isWarning) {
|
|
||||||
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{ 235, 160, 20, 255 };
|
|
||||||
} else if (isSeam) {
|
|
||||||
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{ 14, 18, 24, 255 };
|
|
||||||
} else {
|
|
||||||
int base = 48 + ((x * 7 + y * 11) % 6);
|
|
||||||
m_texDungeonDoor.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)base,
|
|
||||||
(unsigned char)(base + 2),
|
|
||||||
(unsigned char)(base + 6),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5. Parquet Floor: Dark smoked walnut planks
|
|
||||||
makeTex(m_texParquet);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
int row = y / 32;
|
|
||||||
int rowY = y % 32;
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int shift = (row % 2 == 0) ? 0 : 48;
|
|
||||||
int col = (x + shift) / 96;
|
|
||||||
int colX = (x + shift) % 96;
|
|
||||||
bool isSeam = (rowY == 0 || colX == 0);
|
|
||||||
if (isSeam) {
|
|
||||||
m_texParquet.pixels[y * TEX_SIZE + x] = Color{ 16, 12, 10, 255 };
|
|
||||||
} else {
|
|
||||||
int base = 42 + ((row * 13 + col * 23) % 11) - 5;
|
|
||||||
m_texParquet.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)std::clamp(base + 8, 15, 120),
|
|
||||||
(unsigned char)std::clamp(base + 2, 12, 110),
|
|
||||||
(unsigned char)std::clamp(base - 2, 10, 95),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. Rug: Luxury Persian / cyber antique rug
|
|
||||||
makeTex(m_texRug);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int mDist = std::max(std::abs(x - 128), std::abs(y - 128));
|
|
||||||
Color c;
|
|
||||||
if (mDist > 115) c = Color{ 115, 92, 48, 255 }; // Outer gold border
|
|
||||||
else if (mDist > 105) c = Color{ 22, 28, 42, 255 }; // Dark groove
|
|
||||||
else if (mDist > 98) c = Color{ 145, 110, 55, 255 }; // Inner accent
|
|
||||||
else c = Color{ 36, 44, 62, 255 }; // Midnight blue field
|
|
||||||
m_texRug.pixels[y * TEX_SIZE + x] = c;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 7. Plaster Wall: Dark slate graphite
|
|
||||||
makeTex(m_texPlasterWall);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int grain = ((x * 23 + y * 41 + (x ^ y) * 17) % 13) - 6;
|
|
||||||
int base = 34 + grain;
|
|
||||||
m_texPlasterWall.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)std::clamp(base, 12, 90),
|
|
||||||
(unsigned char)std::clamp(base + 4, 15, 95),
|
|
||||||
(unsigned char)std::clamp(base + 10, 18, 110),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 8. Room Window: Try loading Room_window.jpg
|
|
||||||
makeTex(m_texWindowView);
|
|
||||||
bool loadedWindow = false;
|
|
||||||
const std::vector<std::string> winPaths = {
|
|
||||||
"assets/Pictures/Room_window.jpg",
|
|
||||||
"assets/Pictures/Room_window.png",
|
|
||||||
"../assets/Pictures/Room_window.jpg",
|
|
||||||
"assets/Room_window.jpg"
|
|
||||||
};
|
|
||||||
for (const auto& wp : winPaths) {
|
|
||||||
if (FileExists(wp.c_str())) {
|
|
||||||
Image img = LoadImage(wp.c_str());
|
|
||||||
if (img.data) {
|
|
||||||
ImageResize(&img, TEX_SIZE, TEX_SIZE);
|
|
||||||
ImageFormat(&img, PIXELFORMAT_UNCOMPRESSED_R8G8B8A8);
|
|
||||||
Color* pix = (Color*)img.data;
|
|
||||||
m_texWindowView.pixels.assign(pix, pix + TEX_SIZE * TEX_SIZE);
|
|
||||||
UnloadImage(img);
|
|
||||||
loadedWindow = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!loadedWindow) {
|
|
||||||
// Fallback procedural night skyline
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
float t = y / 256.0f;
|
|
||||||
int b = static_cast<int>(18 + (1.0f - t) * 75);
|
|
||||||
m_texWindowView.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)(b * 0.7f),
|
|
||||||
(unsigned char)(b * 0.4f),
|
|
||||||
(unsigned char)b,
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 9. Cyber Gate & Other Textures
|
|
||||||
makeTex(m_texCyberGate);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isFrame = (x < 24 || x > 231 || y < 24);
|
|
||||||
bool isGateCore = !isFrame && (std::abs(x - 128) < 85);
|
|
||||||
if (isGateCore) {
|
|
||||||
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 0, 255, 128, 220 };
|
|
||||||
} else if (isFrame) {
|
|
||||||
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 24, 32, 44, 255 };
|
|
||||||
} else {
|
|
||||||
m_texCyberGate.pixels[y * TEX_SIZE + x] = Color{ 10, 14, 20, 255 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 10. Ancient Palace Textures (Rest Room)
|
|
||||||
makeTex(m_texAncientWall);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int brickY = y / 32;
|
|
||||||
int shift = (brickY % 2 == 0) ? 0 : 32;
|
|
||||||
int brickX = (x + shift) / 64;
|
|
||||||
bool isMortar = (y % 32 <= 2 || (x + shift) % 64 <= 2);
|
|
||||||
int base = isMortar ? 20 : 54 + ((brickX * 17 + brickY * 31) % 15) - 7;
|
|
||||||
m_texAncientWall.pixels[y * TEX_SIZE + x] = Color{
|
|
||||||
(unsigned char)(base + 6),
|
|
||||||
(unsigned char)base,
|
|
||||||
(unsigned char)(base - 4),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
m_texAncientFloor = m_texAncientWall;
|
|
||||||
m_texAncientCeil = m_texAncientWall;
|
|
||||||
|
|
||||||
// 11. SPRITES: Drone Sprite
|
|
||||||
makeTex(m_texDrone);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int dx = x - 128;
|
|
||||||
int dy = y - 128;
|
|
||||||
float r = std::sqrt(float(dx * dx + dy * dy));
|
|
||||||
if (r < 75.0f) {
|
|
||||||
// Drone body chassis
|
|
||||||
if (r < 28.0f) {
|
|
||||||
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 240, 245, 255, 255 }; // Glowing core
|
|
||||||
} else if (std::abs(dy) < 14 && dx > -45 && dx < 45) {
|
|
||||||
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 230, 25, 45, 255 }; // Laser visor
|
|
||||||
} else {
|
|
||||||
int b = 35 + (int)(r * 0.4f);
|
|
||||||
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)b, (unsigned char)(b + 5), (unsigned char)(b + 12), 255 };
|
|
||||||
}
|
|
||||||
} else if (r < 82.0f && (std::abs(dx) > 55 || std::abs(dy) > 55)) {
|
|
||||||
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 60, 75, 95, 255 }; // Thruster pods
|
|
||||||
} else {
|
|
||||||
m_texDrone.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 }; // Transparent background
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 12. SPRITES: Elite Drone
|
|
||||||
makeTex(m_texDroneElite);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int dx = x - 128;
|
|
||||||
int dy = y - 128;
|
|
||||||
float r = std::sqrt(float(dx * dx + dy * dy));
|
|
||||||
if (r < 85.0f) {
|
|
||||||
if (r < 32.0f) {
|
|
||||||
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 255, 60, 75, 255 }; // Crimson reactor
|
|
||||||
} else if (r > 65.0f && r < 75.0f) {
|
|
||||||
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 220, 25, 45, 255 }; // Armor shroud
|
|
||||||
} else {
|
|
||||||
int b = 25 + (int)(r * 0.35f);
|
|
||||||
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)(b + 15), (unsigned char)b, (unsigned char)b, 255 };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_texDroneElite.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 13. SPRITES: Desk with Dual Monitors
|
|
||||||
makeTex(m_texDesk);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isTable = (y >= 170 && y <= 230 && x >= 30 && x <= 226);
|
|
||||||
bool isLegs = (y > 230 && ((x >= 35 && x <= 50) || (x >= 206 && x <= 221)));
|
|
||||||
bool isMonLeft = (y >= 70 && y <= 165 && x >= 45 && x <= 125);
|
|
||||||
bool isMonRight = (y >= 70 && y <= 165 && x >= 135 && x <= 215);
|
|
||||||
|
|
||||||
if (isMonLeft) {
|
|
||||||
bool isCode = (y % 12 >= 8 && x % 20 < 15);
|
|
||||||
m_texDesk.pixels[y * TEX_SIZE + x] = isCode ? Color{ 220, 230, 245, 255 } : Color{ 14, 20, 32, 255 };
|
|
||||||
} else if (isMonRight) {
|
|
||||||
int rx = x - 175;
|
|
||||||
int ry = y - 117;
|
|
||||||
bool isRadar = (std::abs(rx * rx + ry * ry - 900) < 60 || rx == 0 || ry == 0);
|
|
||||||
m_texDesk.pixels[y * TEX_SIZE + x] = isRadar ? Color{ 0, 255, 180, 255 } : Color{ 8, 16, 26, 255 };
|
|
||||||
} else if (isTable) {
|
|
||||||
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 48, 38, 32, 255 };
|
|
||||||
} else if (isLegs) {
|
|
||||||
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 25, 28, 35, 255 };
|
|
||||||
} else {
|
|
||||||
m_texDesk.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 14. SPRITES: Cyber Bed
|
|
||||||
makeTex(m_texBed);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isBedFrame = (y >= 160 && y <= 245 && x >= 35 && x <= 220);
|
|
||||||
bool isPillow = (y >= 170 && y <= 200 && x >= 45 && x <= 95);
|
|
||||||
if (isPillow) {
|
|
||||||
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 210, 218, 230, 255 };
|
|
||||||
} else if (isBedFrame) {
|
|
||||||
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 26, 32, 48, 255 };
|
|
||||||
} else {
|
|
||||||
m_texBed.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 15. SPRITES: Loot Cache Crate
|
|
||||||
makeTex(m_texLootCache);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isBox = (y >= 140 && y <= 240 && x >= 55 && x <= 201);
|
|
||||||
if (isBox) {
|
|
||||||
bool isLed = (y >= 175 && y <= 195 && x >= 115 && x <= 141);
|
|
||||||
if (isLed) {
|
|
||||||
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ 255, 30, 50, 255 };
|
|
||||||
} else {
|
|
||||||
int b = 38 + ((x % 32 < 2 || y % 32 < 2) ? -12 : 0);
|
|
||||||
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ (unsigned char)b, (unsigned char)(b + 4), (unsigned char)(b + 8), 255 };
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
m_texLootCache.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 16. SPRITES: Exit Portal Beacon
|
|
||||||
makeTex(m_texExitPortal);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
int dx = x - 128;
|
|
||||||
bool isCore = (std::abs(dx) < 22 && y >= 60 && y <= 235);
|
|
||||||
bool isBase = (y >= 235 && std::abs(dx) < 65);
|
|
||||||
if (isCore) {
|
|
||||||
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 240, 30, 60, 255 };
|
|
||||||
} else if (isBase) {
|
|
||||||
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 30, 38, 52, 255 };
|
|
||||||
} else {
|
|
||||||
m_texExitPortal.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 17. SPRITES: Proximity Mine
|
|
||||||
makeTex(m_texMine);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isPlate = (y >= 210 && y <= 245 && x >= 70 && x <= 186);
|
|
||||||
bool isBlinker = (y >= 218 && y <= 232 && x >= 118 && x <= 138);
|
|
||||||
if (isBlinker) {
|
|
||||||
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 255, 40, 50, 255 };
|
|
||||||
} else if (isPlate) {
|
|
||||||
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 36, 42, 54, 255 };
|
|
||||||
} else {
|
|
||||||
m_texMine.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 18. SPRITES: Cyber Shrine Terminal
|
|
||||||
makeTex(m_texShrine);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isMonolith = (y >= 70 && y <= 245 && x >= 85 && x <= 171);
|
|
||||||
bool isScreen = (y >= 90 && y <= 150 && x >= 95 && x <= 161);
|
|
||||||
if (isScreen) {
|
|
||||||
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 230, 30, 50, 255 };
|
|
||||||
} else if (isMonolith) {
|
|
||||||
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 20, 26, 36, 255 };
|
|
||||||
} else {
|
|
||||||
m_texShrine.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 19. SPRITES: Laser Beam Strip
|
|
||||||
makeTex(m_texLaserBeam);
|
|
||||||
for (int y = 0; y < TEX_SIZE; ++y) {
|
|
||||||
for (int x = 0; x < TEX_SIZE; ++x) {
|
|
||||||
bool isBeam = (y >= 120 && y <= 136);
|
|
||||||
if (isBeam) {
|
|
||||||
m_texLaserBeam.pixels[y * TEX_SIZE + x] = (y >= 126 && y <= 130) ? WHITE : Color{ 240, 30, 60, 255 };
|
|
||||||
} else {
|
|
||||||
m_texLaserBeam.pixels[y * TEX_SIZE + x] = Color{ 0, 0, 0, 0 };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const SoftwareRenderer::CpuTexture& SoftwareRenderer::getTexture(int index) const {
|
|
||||||
switch (index) {
|
|
||||||
case 1: return m_texDungeonWall;
|
|
||||||
case 2: return m_texDungeonFloor;
|
|
||||||
case 3: return m_texDungeonCeil;
|
|
||||||
case 4: return m_texDungeonDoor;
|
|
||||||
case 5: return m_texParquet;
|
|
||||||
case 6: return m_texRug;
|
|
||||||
case 7: return m_texPlasterWall;
|
|
||||||
case 8: return m_texWindowView;
|
|
||||||
case 9: return m_texCyberGate;
|
|
||||||
case 10: return m_texAncientWall;
|
|
||||||
case 11: return m_texAncientFloor;
|
|
||||||
case 12: return m_texAncientCeil;
|
|
||||||
case 13: return m_texDrone;
|
|
||||||
case 14: return m_texDroneElite;
|
|
||||||
case 15: return m_texDesk;
|
|
||||||
case 16: return m_texBed;
|
|
||||||
case 17: return m_texLootCache;
|
|
||||||
case 18: return m_texExitPortal;
|
|
||||||
case 19: return m_texMine;
|
|
||||||
case 20: return m_texShrine;
|
|
||||||
case 21: return m_texLaserBeam;
|
|
||||||
default: return m_texDungeonWall;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Core DDA Raycaster with Diminishing Light & Horizontal Spans
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::renderSceneRaycast(float posX, float posY, float dirX, float dirY,
|
|
||||||
int mapW, int mapH,
|
|
||||||
const std::vector<int>& mapGrid,
|
|
||||||
int wallTex, int floorTex, int ceilTex,
|
|
||||||
float animTime,
|
|
||||||
const std::vector<BillboardSprite>& sprites) {
|
|
||||||
float fov = 0.66f;
|
|
||||||
float planeX = -dirY * fov;
|
|
||||||
float planeY = dirX * fov;
|
|
||||||
|
|
||||||
std::vector<int> wallDrawStart(FB_WIDTH);
|
|
||||||
std::vector<int> wallDrawEnd(FB_WIDTH);
|
|
||||||
|
|
||||||
const auto& texWall = getTexture(wallTex);
|
|
||||||
const auto& texFloor = getTexture(floorTex);
|
|
||||||
const auto& texCeil = getTexture(ceilTex);
|
|
||||||
|
|
||||||
// 1. Raycast Wall Columns
|
|
||||||
for (int x = 0; x < FB_WIDTH; ++x) {
|
|
||||||
float cameraX = 2.0f * x / float(FB_WIDTH) - 1.0f;
|
|
||||||
float rayDirX = dirX + planeX * cameraX;
|
|
||||||
float rayDirY = dirY + planeY * cameraX;
|
|
||||||
|
|
||||||
int mapX = int(posX);
|
|
||||||
int mapY = int(posY);
|
|
||||||
|
|
||||||
float deltaDistX = (rayDirX == 0.0f) ? 1e30f : std::abs(1.0f / rayDirX);
|
|
||||||
float deltaDistY = (rayDirY == 0.0f) ? 1e30f : std::abs(1.0f / rayDirY);
|
|
||||||
|
|
||||||
float perpWallDist = 0.0f;
|
|
||||||
int stepX = 0;
|
|
||||||
int stepY = 0;
|
|
||||||
float sideDistX = 0.0f;
|
|
||||||
float sideDistY = 0.0f;
|
|
||||||
|
|
||||||
if (rayDirX < 0) {
|
|
||||||
stepX = -1;
|
|
||||||
sideDistX = (posX - mapX) * deltaDistX;
|
|
||||||
} else {
|
|
||||||
stepX = 1;
|
|
||||||
sideDistX = (mapX + 1.0f - posX) * deltaDistX;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rayDirY < 0) {
|
|
||||||
stepY = -1;
|
|
||||||
sideDistY = (posY - mapY) * deltaDistY;
|
|
||||||
} else {
|
|
||||||
stepY = 1;
|
|
||||||
sideDistY = (mapY + 1.0f - posY) * deltaDistY;
|
|
||||||
}
|
|
||||||
|
|
||||||
int hit = 0;
|
|
||||||
int side = 0; // 0 = X-axis, 1 = Y-axis
|
|
||||||
int hitTile = 1;
|
|
||||||
|
|
||||||
while (hit == 0) {
|
|
||||||
if (sideDistX < sideDistY) {
|
|
||||||
sideDistX += deltaDistX;
|
|
||||||
mapX += stepX;
|
|
||||||
side = 0;
|
|
||||||
} else {
|
|
||||||
sideDistY += deltaDistY;
|
|
||||||
mapY += stepY;
|
|
||||||
side = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mapX < 0 || mapX >= mapW || mapY < 0 || mapY >= mapH) {
|
|
||||||
hit = 1;
|
|
||||||
hitTile = 1;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
int t = mapGrid[mapY * mapW + mapX];
|
|
||||||
if (t > 0) {
|
|
||||||
hit = 1;
|
|
||||||
hitTile = t;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (side == 0) perpWallDist = (sideDistX - deltaDistX);
|
|
||||||
else perpWallDist = (sideDistY - deltaDistY);
|
|
||||||
if (perpWallDist < 0.1f) perpWallDist = 0.1f;
|
|
||||||
|
|
||||||
m_depthBuffer[x] = perpWallDist;
|
|
||||||
|
|
||||||
int lineHeight = int(FB_HEIGHT / perpWallDist);
|
|
||||||
int drawStart = -lineHeight / 2 + FB_HEIGHT / 2;
|
|
||||||
if (drawStart < 0) drawStart = 0;
|
|
||||||
int drawEnd = lineHeight / 2 + FB_HEIGHT / 2;
|
|
||||||
if (drawEnd >= FB_HEIGHT) drawEnd = FB_HEIGHT - 1;
|
|
||||||
|
|
||||||
wallDrawStart[x] = drawStart;
|
|
||||||
wallDrawEnd[x] = drawEnd;
|
|
||||||
|
|
||||||
// Texture coordinate calculation
|
|
||||||
float wallX = 0.0f;
|
|
||||||
if (side == 0) wallX = posY + perpWallDist * rayDirY;
|
|
||||||
else wallX = posX + perpWallDist * rayDirX;
|
|
||||||
wallX -= std::floor(wallX);
|
|
||||||
|
|
||||||
int texX = int(wallX * float(TEX_SIZE));
|
|
||||||
if (side == 0 && rayDirX > 0) texX = TEX_SIZE - texX - 1;
|
|
||||||
if (side == 1 && rayDirY < 0) texX = TEX_SIZE - texX - 1;
|
|
||||||
|
|
||||||
// Doom diminishing light formula
|
|
||||||
float shade = 1.0f / (1.0f + perpWallDist * 0.14f);
|
|
||||||
if (side == 1) shade *= 0.80f; // Ambient directional shadow
|
|
||||||
|
|
||||||
// Tactical flashlight cone focus in center
|
|
||||||
float angleDot = (rayDirX * dirX + rayDirY * dirY);
|
|
||||||
float cone = std::clamp((angleDot - 0.75f) / 0.25f, 0.0f, 1.0f);
|
|
||||||
shade = std::clamp(shade * (0.65f + cone * 0.60f), 0.08f, 1.0f);
|
|
||||||
|
|
||||||
// Select wall texture (doors use door texture, window wall uses window)
|
|
||||||
const CpuTexture* currWallTex = &texWall;
|
|
||||||
if (hitTile == 4) currWallTex = &m_texDungeonDoor;
|
|
||||||
else if (hitTile == 8) currWallTex = &m_texWindowView;
|
|
||||||
else if (hitTile == 9) currWallTex = &m_texCyberGate;
|
|
||||||
|
|
||||||
float step = 1.0f * TEX_SIZE / lineHeight;
|
|
||||||
float texPos = (drawStart - FB_HEIGHT / 2 + lineHeight / 2) * step;
|
|
||||||
|
|
||||||
for (int y = drawStart; y <= drawEnd; ++y) {
|
|
||||||
int texY = int(texPos) & (TEX_SIZE - 1);
|
|
||||||
texPos += step;
|
|
||||||
|
|
||||||
Color c = currWallTex->sample(texX, texY);
|
|
||||||
Color finalCol = {
|
|
||||||
(unsigned char)(c.r * shade),
|
|
||||||
(unsigned char)(c.g * shade),
|
|
||||||
(unsigned char)(c.b * shade),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
m_framebuffer[y * FB_WIDTH + x] = finalCol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 2. Horizontal Floor and Ceiling Spans
|
|
||||||
float rayDirX0 = dirX - planeX;
|
|
||||||
float rayDirY0 = dirY - planeY;
|
|
||||||
float rayDirX1 = dirX + planeX;
|
|
||||||
float rayDirY1 = dirY + planeY;
|
|
||||||
|
|
||||||
for (int y = FB_HEIGHT / 2 + 1; y < FB_HEIGHT; ++y) {
|
|
||||||
int p = y - FB_HEIGHT / 2;
|
|
||||||
float posZ = 0.5f * FB_HEIGHT;
|
|
||||||
float rowDistance = posZ / p;
|
|
||||||
|
|
||||||
float floorStepX = rowDistance * (rayDirX1 - rayDirX0) / FB_WIDTH;
|
|
||||||
float floorStepY = rowDistance * (rayDirY1 - rayDirY0) / FB_WIDTH;
|
|
||||||
|
|
||||||
float floorX = posX + rowDistance * rayDirX0;
|
|
||||||
float floorY = posY + rowDistance * rayDirY0;
|
|
||||||
|
|
||||||
float rowShade = std::clamp(1.0f / (1.0f + rowDistance * 0.16f), 0.06f, 1.0f);
|
|
||||||
|
|
||||||
for (int x = 0; x < FB_WIDTH; ++x) {
|
|
||||||
int tx = int(TEX_SIZE * (floorX - std::floor(floorX))) & (TEX_SIZE - 1);
|
|
||||||
int ty = int(TEX_SIZE * (floorY - std::floor(floorY))) & (TEX_SIZE - 1);
|
|
||||||
|
|
||||||
floorX += floorStepX;
|
|
||||||
floorY += floorStepY;
|
|
||||||
|
|
||||||
// Draw floor pixel if below wall column
|
|
||||||
if (y > wallDrawEnd[x]) {
|
|
||||||
Color fc = texFloor.sample(tx, ty);
|
|
||||||
m_framebuffer[y * FB_WIDTH + x] = Color{
|
|
||||||
(unsigned char)(fc.r * rowShade),
|
|
||||||
(unsigned char)(fc.g * rowShade),
|
|
||||||
(unsigned char)(fc.b * rowShade),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw ceiling pixel symmetrically
|
|
||||||
int ceilY = FB_HEIGHT - y - 1;
|
|
||||||
if (ceilY < wallDrawStart[x]) {
|
|
||||||
Color cc = texCeil.sample(tx, ty);
|
|
||||||
m_framebuffer[ceilY * FB_WIDTH + x] = Color{
|
|
||||||
(unsigned char)(cc.r * rowShade),
|
|
||||||
(unsigned char)(cc.g * rowShade),
|
|
||||||
(unsigned char)(cc.b * rowShade),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 3. Depth-Sorted Billboard Sprites
|
|
||||||
if (!sprites.empty()) {
|
|
||||||
std::vector<BillboardSprite> sortedSprites = sprites;
|
|
||||||
for (auto& spr : sortedSprites) {
|
|
||||||
float dx = spr.x - posX;
|
|
||||||
float dy = spr.y - posY;
|
|
||||||
spr.distSq = dx * dx + dy * dy;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::sort(sortedSprites.begin(), sortedSprites.end(), [](const BillboardSprite& a, const BillboardSprite& b) {
|
|
||||||
return a.distSq > b.distSq;
|
|
||||||
});
|
|
||||||
|
|
||||||
for (const auto& spr : sortedSprites) {
|
|
||||||
float spriteX = spr.x - posX;
|
|
||||||
float spriteY = spr.y - posY;
|
|
||||||
|
|
||||||
float invDet = 1.0f / (planeX * dirY - dirX * planeY);
|
|
||||||
float transformX = invDet * (dirY * spriteX - dirX * spriteY);
|
|
||||||
float transformY = invDet * (-planeY * spriteX + planeX * spriteY);
|
|
||||||
|
|
||||||
if (transformY <= 0.15f) continue; // Behind or right on top of camera
|
|
||||||
|
|
||||||
int spriteScreenX = int((FB_WIDTH / 2) * (1.0f + transformX / transformY));
|
|
||||||
int spriteHeight = std::abs(int(FB_HEIGHT / transformY * spr.scale));
|
|
||||||
int vShift = int(spr.vOffset * spriteHeight);
|
|
||||||
|
|
||||||
int drawStartY = -spriteHeight / 2 + FB_HEIGHT / 2 + vShift;
|
|
||||||
if (drawStartY < 0) drawStartY = 0;
|
|
||||||
int drawEndY = spriteHeight / 2 + FB_HEIGHT / 2 + vShift;
|
|
||||||
if (drawEndY >= FB_HEIGHT) drawEndY = FB_HEIGHT - 1;
|
|
||||||
|
|
||||||
int spriteWidth = std::abs(int(FB_HEIGHT / transformY * spr.scale));
|
|
||||||
int drawStartX = -spriteWidth / 2 + spriteScreenX;
|
|
||||||
if (drawStartX < 0) drawStartX = 0;
|
|
||||||
int drawEndX = spriteWidth / 2 + spriteScreenX;
|
|
||||||
if (drawEndX >= FB_WIDTH) drawEndX = FB_WIDTH - 1;
|
|
||||||
|
|
||||||
float sprShade = std::clamp(1.0f / (1.0f + transformY * 0.14f), 0.12f, 1.0f);
|
|
||||||
const auto& sprTex = getTexture(spr.texIndex);
|
|
||||||
|
|
||||||
for (int stripe = drawStartX; stripe <= drawEndX; ++stripe) {
|
|
||||||
int texX = int((stripe - (-spriteWidth / 2 + spriteScreenX)) * TEX_SIZE / spriteWidth);
|
|
||||||
if (texX < 0 || texX >= TEX_SIZE) continue;
|
|
||||||
|
|
||||||
if (transformY < m_depthBuffer[stripe]) {
|
|
||||||
for (int y = drawStartY; y <= drawEndY; ++y) {
|
|
||||||
int d = (y - vShift) * 256 - FB_HEIGHT * 128 + spriteHeight * 128;
|
|
||||||
int texY = ((d * TEX_SIZE) / spriteHeight) / 256;
|
|
||||||
texY = std::clamp(texY, 0, TEX_SIZE - 1);
|
|
||||||
|
|
||||||
Color c = sprTex.sample(texX, texY);
|
|
||||||
if (c.a > 20) {
|
|
||||||
Color finalCol;
|
|
||||||
if (spr.tint.r != 255 || spr.tint.g != 255 || spr.tint.b != 255) {
|
|
||||||
float tr = spr.tint.r / 255.0f;
|
|
||||||
float tg = spr.tint.g / 255.0f;
|
|
||||||
float tb = spr.tint.b / 255.0f;
|
|
||||||
finalCol = Color{
|
|
||||||
(unsigned char)(c.r * tr * sprShade),
|
|
||||||
(unsigned char)(c.g * tg * sprShade),
|
|
||||||
(unsigned char)(c.b * tb * sprShade),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
finalCol = Color{
|
|
||||||
(unsigned char)(c.r * sprShade),
|
|
||||||
(unsigned char)(c.g * sprShade),
|
|
||||||
(unsigned char)(c.b * sprShade),
|
|
||||||
255
|
|
||||||
};
|
|
||||||
}
|
|
||||||
m_framebuffer[y * FB_WIDTH + stripe] = finalCol;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Presentation with 2X Integer Upscaling
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::presentFramebuffer() {
|
|
||||||
float dt = GetFrameTime();
|
|
||||||
if (m_glitchTimer > 0.0f) {
|
|
||||||
m_glitchTimer -= dt * 2.0f;
|
|
||||||
// CRT horizontal displacement slice artifact
|
|
||||||
int sliceY = (std::rand() % (FB_HEIGHT - 30));
|
|
||||||
int shift = (std::rand() % 15) - 7;
|
|
||||||
for (int y = sliceY; y < sliceY + 20 && y < FB_HEIGHT; ++y) {
|
|
||||||
for (int x = 0; x < FB_WIDTH; ++x) {
|
|
||||||
int srcX = std::clamp(x + shift, 0, FB_WIDTH - 1);
|
|
||||||
m_framebuffer[y * FB_WIDTH + x] = m_framebuffer[y * FB_WIDTH + srcX];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateTexture(m_screenTexture, m_framebuffer.data());
|
|
||||||
|
|
||||||
// 2x Nearest-Neighbor Upscale to 1280x720 window
|
|
||||||
Rectangle src = { 0.0f, 0.0f, (float)FB_WIDTH, (float)FB_HEIGHT };
|
|
||||||
Rectangle dst = { 0.0f, 0.0f, (float)GetScreenWidth(), (float)GetScreenHeight() };
|
|
||||||
DrawTexturePro(m_screenTexture, src, dst, Vector2{ 0.0f, 0.0f }, 0.0f, WHITE);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Exploration 3D Raycasting
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::render3D(const Dungeon& dungeon, const Player& player, float animTime) {
|
|
||||||
if (!m_initialized) init();
|
|
||||||
|
|
||||||
int mapW = dungeon.getWidth();
|
|
||||||
int mapH = dungeon.getHeight();
|
|
||||||
|
|
||||||
std::vector<int> grid(mapW * mapH, 0);
|
|
||||||
for (int y = 0; y < mapH; ++y) {
|
|
||||||
for (int x = 0; x < mapW; ++x) {
|
|
||||||
TileType t = dungeon.getTile(x, y);
|
|
||||||
if (t == TileType::WALL) grid[y * mapW + x] = 1;
|
|
||||||
else if (t == TileType::DOOR_LOCKED || t == TileType::DOOR_UNLOCKED) grid[y * mapW + x] = 4;
|
|
||||||
else grid[y * mapW + x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float posX = player.getVisualPosition().x / 2.0f;
|
|
||||||
float posY = player.getVisualPosition().z / 2.0f;
|
|
||||||
|
|
||||||
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
||||||
float dirX = std::sin(rad);
|
|
||||||
float dirY = -std::cos(rad);
|
|
||||||
|
|
||||||
// Build sprites list: Caches, Portals, Mines, Shrines, Lasers, Drone Patrols
|
|
||||||
std::vector<BillboardSprite> sprites;
|
|
||||||
|
|
||||||
for (int y = 0; y < mapH; ++y) {
|
|
||||||
for (int x = 0; x < mapW; ++x) {
|
|
||||||
TileType t = dungeon.getTile(x, y);
|
|
||||||
if (t == TileType::LOOT_CACHE) {
|
|
||||||
sprites.push_back({ x + 0.5f, y + 0.5f, 17, 0.75f, 0.15f, WHITE, false });
|
|
||||||
} else if (t == TileType::EXIT_PORTAL) {
|
|
||||||
sprites.push_back({ x + 0.5f, y + 0.5f, 18, 0.95f, 0.05f, WHITE, false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& l : dungeon.getLasers()) {
|
|
||||||
if (!l.disarmed) {
|
|
||||||
sprites.push_back({ l.x + 0.5f, l.y + 0.5f, 21, 0.85f, 0.0f, (l.active ? TacticalColors::BrightRed : Color{ 70, 70, 80, 255 }), false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& m : dungeon.getMines()) {
|
|
||||||
if (!m.defused && !m.triggered) {
|
|
||||||
sprites.push_back({ m.x + 0.5f, m.y + 0.5f, 19, 0.6f, 0.35f, WHITE, false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& s : dungeon.getShrines()) {
|
|
||||||
sprites.push_back({ s.x + 0.5f, s.y + 0.5f, 20, 0.9f, 0.05f, (s.used ? TacticalColors::SteelMuted : WHITE), false });
|
|
||||||
}
|
|
||||||
|
|
||||||
auto& enemies = const_cast<Dungeon&>(dungeon).getEnemies();
|
|
||||||
for (const auto& me : enemies) {
|
|
||||||
if (me.alive) {
|
|
||||||
sprites.push_back({ me.x + 0.5f, me.y + 0.5f, me.enemy.isElite() ? 14 : 13, 0.85f, 0.0f, me.enemy.getColor(), false });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSceneRaycast(posX, posY, dirX, dirY, mapW, mapH, grid, 1, 2, 3, animTime, sprites);
|
|
||||||
presentFramebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Combat 3D Raycasting
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::renderCombat3D(const Dungeon& dungeon, const Player& player, const CombatSystem& combat, float animTime) {
|
|
||||||
if (!m_initialized) init();
|
|
||||||
|
|
||||||
int mapW = dungeon.getWidth();
|
|
||||||
int mapH = dungeon.getHeight();
|
|
||||||
|
|
||||||
std::vector<int> grid(mapW * mapH, 0);
|
|
||||||
for (int y = 0; y < mapH; ++y) {
|
|
||||||
for (int x = 0; x < mapW; ++x) {
|
|
||||||
TileType t = dungeon.getTile(x, y);
|
|
||||||
grid[y * mapW + x] = (t == TileType::WALL) ? 1 : 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float posX = player.getVisualPosition().x / 2.0f;
|
|
||||||
float posY = player.getVisualPosition().z / 2.0f;
|
|
||||||
|
|
||||||
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
||||||
float dirX = std::sin(rad);
|
|
||||||
float dirY = -std::cos(rad);
|
|
||||||
|
|
||||||
std::vector<BillboardSprite> sprites;
|
|
||||||
const auto& enemy = combat.getEnemy();
|
|
||||||
if (enemy && enemy->isAlive()) {
|
|
||||||
float bob = std::sin(animTime * 3.5f) * 0.05f;
|
|
||||||
float distFront = 2.2f;
|
|
||||||
|
|
||||||
float droneX = posX + dirX * distFront;
|
|
||||||
float droneY = posY + dirY * distFront;
|
|
||||||
|
|
||||||
bool isHit = (combat.getPhase() == CombatPhase::PLAYER_ACTION_ANIM);
|
|
||||||
Color tint = isHit ? WHITE : enemy->getColor();
|
|
||||||
|
|
||||||
sprites.push_back({ droneX, droneY, enemy->isElite() ? 14 : 13, 1.05f, bob, tint, false });
|
|
||||||
}
|
|
||||||
|
|
||||||
renderSceneRaycast(posX, posY, dirX, dirY, mapW, mapH, grid, 1, 2, 3, animTime, sprites);
|
|
||||||
presentFramebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Apartment Hub 2.5D Raycasting
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::renderHub(const HubRoom& hub, const Player& player, float animTime) {
|
|
||||||
if (!m_initialized) init();
|
|
||||||
|
|
||||||
// 6x6 cozy apartment room
|
|
||||||
const int roomW = 6;
|
|
||||||
const int roomH = 6;
|
|
||||||
std::vector<int> grid(roomW * roomH, 0);
|
|
||||||
|
|
||||||
for (int y = 0; y < roomH; ++y) {
|
|
||||||
for (int x = 0; x < roomW; ++x) {
|
|
||||||
if (x == 0 || y == 5) grid[y * roomW + x] = 7; // Plaster Wall
|
|
||||||
else if (x == 5) grid[y * roomW + x] = 8; // Room Window (East wall with Room_window.jpg!)
|
|
||||||
else if (y == 0) grid[y * roomW + x] = 9; // Cyber Gate (North wall)
|
|
||||||
else grid[y * roomW + x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float posX = std::clamp(player.getVisualPosition().x / 2.0f, 1.2f, 4.8f);
|
|
||||||
float posY = std::clamp(player.getVisualPosition().z / 2.0f, 1.2f, 4.8f);
|
|
||||||
|
|
||||||
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
||||||
float dirX = std::sin(rad);
|
|
||||||
float dirY = -std::cos(rad);
|
|
||||||
|
|
||||||
// Place apartment furniture as authentic 2.5D Doom-style sprites
|
|
||||||
std::vector<BillboardSprite> sprites = {
|
|
||||||
{ 3.0f, 1.3f, 15, 1.2f, 0.10f, WHITE, false }, // Workstation Desk with Dual Glowing Monitors
|
|
||||||
{ 1.4f, 4.0f, 16, 1.2f, 0.15f, WHITE, false } // Midnight Indigo Cyber Bed
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSceneRaycast(posX, posY, dirX, dirY, roomW, roomH, grid, 7, 5, 7, animTime, sprites);
|
|
||||||
presentFramebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
// Safe Haven Rest Room 2.5D Raycasting
|
|
||||||
// ----------------------------------------------------------------------------
|
|
||||||
void SoftwareRenderer::renderRestRoom(const RestRoom& restRoom, const Player& player, float animTime) {
|
|
||||||
if (!m_initialized) init();
|
|
||||||
|
|
||||||
const int roomW = 6;
|
|
||||||
const int roomH = 6;
|
|
||||||
std::vector<int> grid(roomW * roomH, 0);
|
|
||||||
|
|
||||||
for (int y = 0; y < roomH; ++y) {
|
|
||||||
for (int x = 0; x < roomW; ++x) {
|
|
||||||
if (x == 0 || x == 5 || y == 0 || y == 5) {
|
|
||||||
grid[y * roomW + x] = 10; // Ancient Palace Wall
|
|
||||||
} else {
|
|
||||||
grid[y * roomW + x] = 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
float posX = std::clamp(player.getVisualPosition().x / 2.0f, 1.2f, 4.8f);
|
|
||||||
float posY = std::clamp(player.getVisualPosition().z / 2.0f, 1.2f, 4.8f);
|
|
||||||
|
|
||||||
float rad = player.getVisualYaw() * (3.14159265f / 180.0f);
|
|
||||||
float dirX = std::sin(rad);
|
|
||||||
float dirY = -std::cos(rad);
|
|
||||||
|
|
||||||
// Workbench and Cyber Portal
|
|
||||||
std::vector<BillboardSprite> sprites = {
|
|
||||||
{ 3.0f, 1.2f, 15, 1.1f, 0.10f, WHITE, false },
|
|
||||||
{ 3.0f, 4.6f, 18, 0.95f, 0.05f, WHITE, false }
|
|
||||||
};
|
|
||||||
|
|
||||||
renderSceneRaycast(posX, posY, dirX, dirY, roomW, roomH, grid, 10, 11, 12, animTime, sprites);
|
|
||||||
presentFramebuffer();
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace dnr
|
|
||||||
|
|
@ -1,113 +0,0 @@
|
||||||
#pragma once
|
|
||||||
|
|
||||||
#include "core/Common.hpp"
|
|
||||||
#include "dungeon/Dungeon.hpp"
|
|
||||||
#include "dungeon/Player.hpp"
|
|
||||||
#include <vector>
|
|
||||||
#include <memory>
|
|
||||||
#include <string>
|
|
||||||
|
|
||||||
namespace dnr {
|
|
||||||
|
|
||||||
class HubRoom;
|
|
||||||
class RestRoom;
|
|
||||||
class CombatSystem;
|
|
||||||
|
|
||||||
class SoftwareRenderer {
|
|
||||||
public:
|
|
||||||
SoftwareRenderer();
|
|
||||||
~SoftwareRenderer();
|
|
||||||
|
|
||||||
void init();
|
|
||||||
void cleanup();
|
|
||||||
|
|
||||||
void render3D(const Dungeon& dungeon, const Player& player, float animTime);
|
|
||||||
void renderCombat3D(const Dungeon& dungeon, const Player& player, const CombatSystem& combat, float animTime);
|
|
||||||
void renderHub(const HubRoom& hub, const Player& player, float animTime);
|
|
||||||
void renderRestRoom(const RestRoom& restRoom, const Player& player, float animTime);
|
|
||||||
|
|
||||||
void triggerGlitch(float intensity = 0.7f);
|
|
||||||
void setGraphicsQuality(GraphicsQuality q) { m_graphicsQuality = q; }
|
|
||||||
GraphicsQuality getGraphicsQuality() const { return m_graphicsQuality; }
|
|
||||||
|
|
||||||
private:
|
|
||||||
static constexpr int FB_WIDTH = 640;
|
|
||||||
static constexpr int FB_HEIGHT = 360;
|
|
||||||
static constexpr int TEX_SIZE = 256;
|
|
||||||
|
|
||||||
struct CpuTexture {
|
|
||||||
int width = TEX_SIZE;
|
|
||||||
int height = TEX_SIZE;
|
|
||||||
std::vector<Color> pixels;
|
|
||||||
|
|
||||||
Color sample(int x, int y) const {
|
|
||||||
if (pixels.empty()) return MAGENTA;
|
|
||||||
int px = (x >= 0 ? x : 0) % width;
|
|
||||||
int py = (y >= 0 ? y : 0) % height;
|
|
||||||
return pixels[py * width + px];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BillboardSprite {
|
|
||||||
float x = 0.0f;
|
|
||||||
float y = 0.0f;
|
|
||||||
int texIndex = 0;
|
|
||||||
float scale = 1.0f;
|
|
||||||
float vOffset = 0.0f; // vertical shift in height units
|
|
||||||
Color tint = WHITE;
|
|
||||||
bool additive = false;
|
|
||||||
float distSq = 0.0f;
|
|
||||||
};
|
|
||||||
|
|
||||||
void generateCpuTextures();
|
|
||||||
void renderSceneRaycast(float posX, float posY, float dirX, float dirY,
|
|
||||||
int mapW, int mapH,
|
|
||||||
const std::vector<int>& mapGrid,
|
|
||||||
int wallTex, int floorTex, int ceilTex,
|
|
||||||
float animTime,
|
|
||||||
const std::vector<BillboardSprite>& sprites);
|
|
||||||
|
|
||||||
void presentFramebuffer();
|
|
||||||
|
|
||||||
// Framebuffer & Raylib Texture
|
|
||||||
std::vector<Color> m_framebuffer;
|
|
||||||
std::vector<float> m_depthBuffer;
|
|
||||||
Image m_screenImage{};
|
|
||||||
Texture2D m_screenTexture{};
|
|
||||||
bool m_initialized = false;
|
|
||||||
GraphicsQuality m_graphicsQuality = GraphicsQuality::BALANCED;
|
|
||||||
float m_glitchTimer = 0.0f;
|
|
||||||
|
|
||||||
// CPU Textures
|
|
||||||
CpuTexture m_texDungeonWall;
|
|
||||||
CpuTexture m_texDungeonFloor;
|
|
||||||
CpuTexture m_texDungeonCeil;
|
|
||||||
CpuTexture m_texDungeonDoor;
|
|
||||||
|
|
||||||
CpuTexture m_texParquet;
|
|
||||||
CpuTexture m_texRug;
|
|
||||||
CpuTexture m_texPlasterWall;
|
|
||||||
CpuTexture m_texWindowView;
|
|
||||||
CpuTexture m_texPoster;
|
|
||||||
CpuTexture m_texBed;
|
|
||||||
CpuTexture m_texDesk;
|
|
||||||
CpuTexture m_texCyberGate;
|
|
||||||
|
|
||||||
CpuTexture m_texAncientWall;
|
|
||||||
CpuTexture m_texAncientFloor;
|
|
||||||
CpuTexture m_texAncientCeil;
|
|
||||||
CpuTexture m_texWorkbench;
|
|
||||||
|
|
||||||
// Sprite Textures
|
|
||||||
CpuTexture m_texDrone;
|
|
||||||
CpuTexture m_texDroneElite;
|
|
||||||
CpuTexture m_texLootCache;
|
|
||||||
CpuTexture m_texExitPortal;
|
|
||||||
CpuTexture m_texLaserBeam;
|
|
||||||
CpuTexture m_texMine;
|
|
||||||
CpuTexture m_texShrine;
|
|
||||||
|
|
||||||
const CpuTexture& getTexture(int index) const;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace dnr
|
|
||||||
Loading…
Add table
Reference in a new issue