├── data ├── a16.png ├── crosshair.gltf ├── crosshair2.gltf ├── ship.gltf └── station.gltf ├── screenshots ├── header.gif └── lowres.png ├── packages.config ├── src ├── SpaceDust.h ├── Actor.h ├── GameCamera.h ├── MathUtils.h ├── Actor.cpp ├── Ship.h ├── GameCamera.cpp ├── SpaceDust.cpp ├── Ergo.cpp └── Ship.cpp ├── LICENSE ├── Ergo.sln ├── Ergo.vcxproj.filters ├── readme.md ├── Ergo.vcxproj └── .gitignore /data/a16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brihernandez/Ergo/HEAD/data/a16.png -------------------------------------------------------------------------------- /screenshots/header.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brihernandez/Ergo/HEAD/screenshots/header.gif -------------------------------------------------------------------------------- /screenshots/lowres.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/brihernandez/Ergo/HEAD/screenshots/lowres.png -------------------------------------------------------------------------------- /packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /src/SpaceDust.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | class SpaceDust 7 | { 8 | public: 9 | SpaceDust(float size, int count); 10 | 11 | void UpdateViewPosition(Vector3 viewPosition); 12 | void Draw(Vector3 viewPosition, Vector3 velocity, bool drawDots) const; 13 | 14 | private: 15 | std::vector Points; 16 | std::vector Colors; 17 | float Extent; 18 | }; 19 | -------------------------------------------------------------------------------- /src/Actor.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Actor 6 | { 7 | public: 8 | Actor(); 9 | 10 | Vector3 Position; 11 | Vector3 Velocity; 12 | Quaternion Rotation; 13 | 14 | Vector3 GetForward() const; 15 | Vector3 GetBack() const; 16 | Vector3 GetRight() const; 17 | Vector3 GetLeft() const; 18 | Vector3 GetUp() const; 19 | Vector3 GetDown() const; 20 | 21 | Vector3 TransformPoint(Vector3 point) const; 22 | void RotateLocalEuler(Vector3 axis, float degrees); 23 | }; 24 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2022 Brian Hernandez 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /src/GameCamera.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | class Ship; 6 | 7 | class GameCamera 8 | { 9 | public: 10 | 11 | GameCamera(bool isPerspective, float fieldOfView); 12 | 13 | /// 14 | /// Automatically moves the camera to follow a target ship. 15 | /// 16 | void FollowShip(const Ship& ship, float deltaTime); 17 | 18 | /// 19 | /// Moves the camera to the given positions. Smoothing is automatically applied. 20 | /// 21 | void MoveTo(Vector3 position, Vector3 target, Vector3 up, float deltaTime); 22 | 23 | /// 24 | /// Immediately moves the camera to the given positions with no smoothing. 25 | /// 26 | void SetPosition(Vector3 position, Vector3 target, Vector3 up); 27 | 28 | /// 29 | /// Required to tell raylib that any further 3D calls will be made with this camera. 30 | /// Must be paired with EndDrawing(). 31 | /// 32 | void Begin3DDrawing() const; 33 | 34 | /// 35 | /// Requires to tell raylib to stop 3D rendering with this camera. 36 | /// 37 | void EndDrawing() const; 38 | 39 | Vector3 GetPosition() const; 40 | 41 | private: 42 | Camera3D Camera; 43 | 44 | Vector3 SmoothPosition; 45 | Vector3 SmoothTarget; 46 | Vector3 SmoothUp; 47 | }; 48 | -------------------------------------------------------------------------------- /src/MathUtils.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | // ================================================================================== 6 | // For more info on SmoothDamp see: 7 | // https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/ 8 | // ================================================================================== 9 | 10 | inline float SmoothDamp(float from, float to, float speed, float dt) 11 | { 12 | return Lerp(from, to, 1 - expf(-speed * dt)); 13 | } 14 | 15 | inline Vector3 SmoothDamp(Vector3 from, Vector3 to, float speed, float dt) 16 | { 17 | return Vector3{ 18 | Lerp(from.x, to.x, 1 - expf(-speed * dt)), 19 | Lerp(from.y, to.y, 1 - expf(-speed * dt)), 20 | Lerp(from.z, to.z, 1 - expf(-speed * dt))}; 21 | } 22 | 23 | inline Quaternion SmoothDamp(Quaternion from, Quaternion to, float speed, float dt) 24 | { 25 | return QuaternionSlerp( from, to, 1 - expf(-speed * dt)); 26 | } 27 | 28 | // 29 | //inline float InverseLerp(float from, float to, float value) 30 | //{ 31 | // return (value - from) / (to - from); 32 | //} 33 | // 34 | //inline float Remap(float fromMin, float fromMax, float toMin, float toMax, float value) 35 | //{ 36 | // float t = InverseLerp(fromMin, fromMax, value); 37 | // return Lerp(toMin, toMax, t); 38 | // 39 | //} 40 | -------------------------------------------------------------------------------- /Ergo.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.2.32630.192 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ergo", "Ergo.vcxproj", "{26599881-5658-45ED-9275-096CFAE1063C}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Debug|x86 = Debug|x86 12 | Release|x64 = Release|x64 13 | Release|x86 = Release|x86 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {26599881-5658-45ED-9275-096CFAE1063C}.Debug|x64.ActiveCfg = Debug|x64 17 | {26599881-5658-45ED-9275-096CFAE1063C}.Debug|x64.Build.0 = Debug|x64 18 | {26599881-5658-45ED-9275-096CFAE1063C}.Debug|x86.ActiveCfg = Debug|Win32 19 | {26599881-5658-45ED-9275-096CFAE1063C}.Debug|x86.Build.0 = Debug|Win32 20 | {26599881-5658-45ED-9275-096CFAE1063C}.Release|x64.ActiveCfg = Release|x64 21 | {26599881-5658-45ED-9275-096CFAE1063C}.Release|x64.Build.0 = Release|x64 22 | {26599881-5658-45ED-9275-096CFAE1063C}.Release|x86.ActiveCfg = Release|Win32 23 | {26599881-5658-45ED-9275-096CFAE1063C}.Release|x86.Build.0 = Release|Win32 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {01F1F47A-3D73-41A1-8C21-8096026FD224} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/Actor.cpp: -------------------------------------------------------------------------------- 1 | #include "Actor.h" 2 | 3 | #include 4 | 5 | Actor::Actor() 6 | { 7 | Position = Vector3Zero(); 8 | Velocity = Vector3Zero(); 9 | Rotation = QuaternionIdentity(); 10 | } 11 | 12 | Vector3 Actor::GetForward() const 13 | { 14 | return Vector3RotateByQuaternion( 15 | Vector3{ 0, 0, 1 }, 16 | Rotation); 17 | } 18 | 19 | Vector3 Actor::GetBack() const 20 | { 21 | return Vector3RotateByQuaternion( 22 | Vector3{ 0, 0, -1 }, 23 | Rotation); 24 | } 25 | 26 | Vector3 Actor::GetRight() const 27 | { 28 | return Vector3RotateByQuaternion( 29 | Vector3{ -1, 0, 0 }, 30 | Rotation); 31 | } 32 | 33 | Vector3 Actor::GetLeft() const 34 | { 35 | return Vector3RotateByQuaternion( 36 | Vector3{ 1, 0, 0 }, 37 | Rotation); 38 | } 39 | 40 | Vector3 Actor::GetUp() const 41 | { 42 | return Vector3RotateByQuaternion( 43 | Vector3{ 0, 1, 0 }, 44 | Rotation); 45 | } 46 | 47 | Vector3 Actor::GetDown() const 48 | { 49 | return Vector3RotateByQuaternion( 50 | Vector3{ 0, -1, 0 }, 51 | Rotation); 52 | } 53 | 54 | Vector3 Actor::TransformPoint(Vector3 point) const 55 | { 56 | auto mPos = MatrixTranslate(Position.x, Position.y, Position.z); 57 | auto mRot = QuaternionToMatrix(Rotation); 58 | auto matrix = MatrixMultiply(mRot, mPos); 59 | return Vector3Transform(point, matrix); 60 | } 61 | 62 | void Actor::RotateLocalEuler(Vector3 axis, float degrees) 63 | { 64 | auto radians = degrees * DEG2RAD; 65 | Rotation = QuaternionMultiply( 66 | Rotation, 67 | QuaternionFromAxisAngle(axis, radians)); 68 | } 69 | -------------------------------------------------------------------------------- /src/Ship.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Actor.h" 4 | 5 | struct TrailRung 6 | { 7 | Vector3 LeftPoint; 8 | Vector3 RightPoint; 9 | float TimeToLive; 10 | }; 11 | 12 | class Ship : public Actor 13 | { 14 | public: 15 | float InputForward = 0; 16 | float InputLeft = 0; 17 | float InputUp = 0; 18 | 19 | float InputPitchDown = 0; 20 | float InputRollRight = 0; 21 | float InputYawLeft = 0; 22 | 23 | float MaxSpeed = 20; 24 | float ThrottleResponse = 10; 25 | float TurnRate = 180; 26 | float TurnResponse = 10; 27 | 28 | float Length = 1.0f; 29 | float Width = 1.0f; 30 | 31 | Color TrailColor = DARKGREEN; 32 | 33 | Ship(const char* modelPath, const char* texturePath, Color color); 34 | ~Ship(); 35 | 36 | void Update(float deltaTime); 37 | void Draw(bool showDebugAxes) const; 38 | void DrawTrail() const; 39 | 40 | private: 41 | Model ShipModel = {}; 42 | Color ShipColor = {}; 43 | 44 | static const int RungCount = 16; 45 | TrailRung Rungs[RungCount]; 46 | 47 | float SmoothForward = 0; 48 | float SmoothLeft = 0; 49 | float SmoothUp = 0; 50 | 51 | float SmoothPitchDown = 0; 52 | float SmoothRollRight = 0; 53 | float SmoothYawLeft = 0; 54 | 55 | float VisualBank = 0; 56 | 57 | void PositionActiveTrailRung(); 58 | Vector3 LastRungPosition = { 0, 0, 0 }; 59 | int RungIndex = 0; 60 | }; 61 | 62 | class Crosshair 63 | { 64 | public: 65 | Crosshair(const char* modelPath); 66 | ~Crosshair(); 67 | 68 | void PositionCrosshairOnShip(const Ship& ship, float distance); 69 | void DrawCrosshair() const; 70 | 71 | private: 72 | Model CrosshairModel = {}; 73 | }; 74 | -------------------------------------------------------------------------------- /src/GameCamera.cpp: -------------------------------------------------------------------------------- 1 | #include "GameCamera.h" 2 | 3 | #include 4 | 5 | #include "Ship.h" 6 | #include "MathUtils.h" 7 | 8 | GameCamera::GameCamera(bool isPerspective, float fieldOfView) 9 | { 10 | Camera = Camera3D(); 11 | Camera.position = Vector3{ 0, 10, -10 }; 12 | Camera.target = Vector3{ 0, 0, 0 }; 13 | Camera.up = Vector3{ 0, 1, 0 }; 14 | 15 | Camera.fovy = fieldOfView; 16 | Camera.projection = isPerspective 17 | ? CameraProjection::CAMERA_PERSPECTIVE 18 | : CameraProjection::CAMERA_ORTHOGRAPHIC; 19 | 20 | SmoothPosition = Vector3Zero(); 21 | SmoothTarget = Vector3Zero(); 22 | SmoothUp = Vector3Zero(); 23 | } 24 | 25 | void GameCamera::FollowShip(const Ship& ship, float deltaTime) 26 | { 27 | Vector3 position = ship.TransformPoint({ 0, 1, -3 }); 28 | Vector3 shipForwards = Vector3Scale(ship.GetForward(), 25); 29 | Vector3 target = Vector3Add(ship.Position, shipForwards); 30 | Vector3 up = ship.GetUp(); 31 | 32 | MoveTo(position, target, up, deltaTime); 33 | } 34 | 35 | void GameCamera::MoveTo(Vector3 position, Vector3 target, Vector3 up, float deltaTime) 36 | { 37 | Camera.position = SmoothDamp( 38 | Camera.position, position, 39 | 10, deltaTime); 40 | 41 | Camera.target = SmoothDamp( 42 | Camera.target, target, 43 | 5, deltaTime); 44 | 45 | Camera.up = SmoothDamp( 46 | Camera.up, up, 47 | 5, deltaTime); 48 | } 49 | 50 | void GameCamera::SetPosition(Vector3 position, Vector3 target, Vector3 up) 51 | { 52 | Camera.position = position; 53 | Camera.target = target; 54 | Camera.up = up; 55 | 56 | SmoothPosition = position; 57 | SmoothTarget = target; 58 | SmoothUp = up; 59 | } 60 | 61 | Vector3 GameCamera::GetPosition() const 62 | { 63 | return Camera.position; 64 | } 65 | 66 | void GameCamera::Begin3DDrawing() const 67 | { 68 | BeginMode3D(Camera); 69 | } 70 | 71 | void GameCamera::EndDrawing() const 72 | { 73 | EndMode3D(); 74 | } 75 | -------------------------------------------------------------------------------- /Ergo.vcxproj.filters: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {93995380-89BD-4b04-88EB-625FBE52EBFB} 10 | h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd 11 | 12 | 13 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 14 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 15 | 16 | 17 | 18 | 19 | Source Files 20 | 21 | 22 | Source Files 23 | 24 | 25 | Source Files 26 | 27 | 28 | Source Files 29 | 30 | 31 | Source Files 32 | 33 | 34 | 35 | 36 | Header Files 37 | 38 | 39 | Header Files 40 | 41 | 42 | Header Files 43 | 44 | 45 | Header Files 46 | 47 | 48 | Header Files 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/SpaceDust.cpp: -------------------------------------------------------------------------------- 1 | #include "SpaceDust.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | inline float GetPrettyBadRandomFloat(float min, float max) 9 | { 10 | auto value = static_cast(GetRandomValue((int)min * 1000, (int)max * 1000)); 11 | value /= 1000.f; 12 | return value; 13 | } 14 | 15 | SpaceDust::SpaceDust(float size, int count) 16 | { 17 | Points = std::vector(); 18 | Points.reserve(count); 19 | Extent = size * .5f; 20 | 21 | for (int i = 0; i < count; ++i) 22 | { 23 | auto point = Vector3{ 24 | GetPrettyBadRandomFloat(-Extent, Extent), 25 | GetPrettyBadRandomFloat(-Extent, Extent), 26 | GetPrettyBadRandomFloat(-Extent, Extent) 27 | }; 28 | Points.push_back(point); 29 | 30 | auto color = Color{ 31 | (unsigned char)GetRandomValue(192, 255), 32 | (unsigned char)GetRandomValue(192, 255), 33 | (unsigned char)GetRandomValue(192, 255), 34 | 255 35 | }; 36 | Colors.push_back(color); 37 | } 38 | } 39 | 40 | void SpaceDust::UpdateViewPosition(Vector3 viewPosition) 41 | { 42 | float size = Extent * 2; 43 | for (auto& p : Points) 44 | { 45 | while (p.x > viewPosition.x + Extent) 46 | p.x -= size; 47 | while (p.x < viewPosition.x - Extent) 48 | p.x += size; 49 | 50 | while (p.y > viewPosition.y + Extent) 51 | p.y -= size; 52 | while (p.y < viewPosition.y - Extent) 53 | p.y += size; 54 | 55 | while (p.z > viewPosition.z + Extent) 56 | p.z -= size; 57 | while (p.z < viewPosition.z - Extent) 58 | p.z += size; 59 | } 60 | } 61 | 62 | void SpaceDust::Draw(Vector3 viewPosition, Vector3 velocity, bool drawDots) const 63 | { 64 | BeginBlendMode(BlendMode::BLEND_ADDITIVE); 65 | 66 | for (int i = 0; i < Points.size(); ++i) 67 | { 68 | float distance = Vector3Distance(viewPosition, Points[i]); 69 | 70 | float farLerp = Clamp(Normalize(distance, Extent * .9f, Extent), 0, 1); 71 | unsigned char farAlpha = (unsigned char)Lerp(255, 0, farLerp); 72 | 73 | const float cubeSize = 0.01f; 74 | 75 | if (drawDots) 76 | { 77 | DrawSphereWires( 78 | Points[i], 79 | cubeSize, 80 | 2, 4, 81 | { Colors[i].r, Colors[i].g, Colors[i].b, farAlpha }); 82 | } 83 | 84 | DrawLine3D( 85 | Vector3Add(Points[i], Vector3Scale(velocity, 0.02f)), 86 | Points[i], 87 | { Colors[i].r, Colors[i].g, Colors[i].b, farAlpha }); 88 | } 89 | 90 | rlDrawRenderBatchActive(); 91 | EndBlendMode(); 92 | } 93 | -------------------------------------------------------------------------------- /data/crosshair.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.8.19", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0 12 | ] 13 | } 14 | ], 15 | "nodes" : [ 16 | { 17 | "mesh" : 0, 18 | "name" : "Crosshair" 19 | } 20 | ], 21 | "meshes" : [ 22 | { 23 | "name" : "Plane", 24 | "primitives" : [ 25 | { 26 | "attributes" : { 27 | "POSITION" : 0, 28 | "NORMAL" : 1, 29 | "TEXCOORD_0" : 2 30 | }, 31 | "indices" : 3 32 | } 33 | ] 34 | } 35 | ], 36 | "accessors" : [ 37 | { 38 | "bufferView" : 0, 39 | "componentType" : 5126, 40 | "count" : 24, 41 | "max" : [ 42 | 1.2412012815475464, 43 | 0.9573875665664673, 44 | 0.09539321810007095 45 | ], 46 | "min" : [ 47 | -1.2412012815475464, 48 | -0.9573875665664673, 49 | 0.09539308398962021 50 | ], 51 | "type" : "VEC3" 52 | }, 53 | { 54 | "bufferView" : 1, 55 | "componentType" : 5126, 56 | "count" : 24, 57 | "type" : "VEC3" 58 | }, 59 | { 60 | "bufferView" : 2, 61 | "componentType" : 5126, 62 | "count" : 24, 63 | "type" : "VEC2" 64 | }, 65 | { 66 | "bufferView" : 3, 67 | "componentType" : 5123, 68 | "count" : 24, 69 | "type" : "SCALAR" 70 | } 71 | ], 72 | "bufferViews" : [ 73 | { 74 | "buffer" : 0, 75 | "byteLength" : 288, 76 | "byteOffset" : 0 77 | }, 78 | { 79 | "buffer" : 0, 80 | "byteLength" : 288, 81 | "byteOffset" : 288 82 | }, 83 | { 84 | "buffer" : 0, 85 | "byteLength" : 192, 86 | "byteOffset" : 576 87 | }, 88 | { 89 | "buffer" : 0, 90 | "byteLength" : 48, 91 | "byteOffset" : 768 92 | } 93 | ], 94 | "buffers" : [ 95 | { 96 | "byteLength" : 816, 97 | "uri" : "data:application/octet-stream;base64,rxWUP1yiSj9zXcM9rxWUP1yiSj9zXcM9r9+eP7gafj5zXcM9KLYaP1oXdT+FXcM9upmHPyAeNT96XcM9upmHPyAeNT96XcM9rxWUv1yiSj9zXcM9rxWUv1yiSj9zXcM9r9+ev7gafj5zXcM9KLYav1oXdT+FXcM9upmHvyAeNT96XcM9upmHvyAeNT96XcM9rxWUP1yiSr9zXcM9rxWUP1yiSr9zXcM9r9+eP7gafr5zXcM9KLYaP1oXdb+FXcM9upmHPyAeNb96XcM9upmHPyAeNb96XcM9rxWUv1yiSr9zXcM9rxWUv1yiSr9zXcM9r9+ev7gafr5zXcM9KLYav1oXdb+FXcM9upmHvyAeNb96XcM9upmHvyAeNb96XcM9WTv9tCEmBbMAAIC/3cyqtPeiv7QAAIC/WTv9tCEmBbMAAIC/3cyqtPeiv7QAAIC/WTv9tCEmBbMAAIC/3cyqtPeiv7QAAIC/3cyqNPeiv7QAAIC/WTv9NCEmBbMAAIC/WTv9NCEmBbMAAIC/3cyqNPeiv7QAAIC/3cyqNPeiv7QAAIC/WTv9NCEmBbMAAIC/WTv9tCEmBTMAAIC/3cyqtPeivzQAAIC/WTv9tCEmBTMAAIC/3cyqtPeivzQAAIC/WTv9tCEmBTMAAIC/3cyqtPeivzQAAIC/3cyqNPeivzQAAIC/WTv9NCEmBTMAAIC/WTv9NCEmBTMAAIC/3cyqNPeivzQAAIC/3cyqNPeivzQAAIC/WTv9NCEmBTMAAIC/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/BQADAAEAAgAEAAAACgAGAAkACAAHAAsAEQANAA8ADgAMABAAFgAVABIAFAAXABMA" 98 | } 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /data/crosshair2.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.8.19", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0 12 | ] 13 | } 14 | ], 15 | "nodes" : [ 16 | { 17 | "mesh" : 0, 18 | "name" : "Crosshair" 19 | } 20 | ], 21 | "meshes" : [ 22 | { 23 | "name" : "Plane", 24 | "primitives" : [ 25 | { 26 | "attributes" : { 27 | "POSITION" : 0, 28 | "NORMAL" : 1, 29 | "TEXCOORD_0" : 2 30 | }, 31 | "indices" : 3 32 | } 33 | ] 34 | } 35 | ], 36 | "accessors" : [ 37 | { 38 | "bufferView" : 0, 39 | "componentType" : 5126, 40 | "count" : 24, 41 | "max" : [ 42 | 1.3044021129608154, 43 | 0.8965469598770142, 44 | 2.3914719804452034e-07 45 | ], 46 | "min" : [ 47 | -1.3044021129608154, 48 | -0.8965469598770142, 49 | 2.2876140093330832e-08 50 | ], 51 | "type" : "VEC3" 52 | }, 53 | { 54 | "bufferView" : 1, 55 | "componentType" : 5126, 56 | "count" : 24, 57 | "type" : "VEC3" 58 | }, 59 | { 60 | "bufferView" : 2, 61 | "componentType" : 5126, 62 | "count" : 24, 63 | "type" : "VEC2" 64 | }, 65 | { 66 | "bufferView" : 3, 67 | "componentType" : 5123, 68 | "count" : 24, 69 | "type" : "SCALAR" 70 | } 71 | ], 72 | "bufferViews" : [ 73 | { 74 | "buffer" : 0, 75 | "byteLength" : 288, 76 | "byteOffset" : 0 77 | }, 78 | { 79 | "buffer" : 0, 80 | "byteLength" : 288, 81 | "byteOffset" : 288 82 | }, 83 | { 84 | "buffer" : 0, 85 | "byteLength" : 192, 86 | "byteOffset" : 576 87 | }, 88 | { 89 | "buffer" : 0, 90 | "byteLength" : 48, 91 | "byteOffset" : 768 92 | } 93 | ], 94 | "buffers" : [ 95 | { 96 | "byteLength" : 816, 97 | "uri" : "data:application/octet-stream;base64,Lej0PuiDbD4qgcQy6uiQPyrDMj9mLxU06uiQPyrDMj9mLxU0pvamPxqEZT8kZIA0v9iHP5KDSD/MgjQ0v9iHP5KDSD/MgjQ0Lej0vuiDbD4qgcQy6uiQvyrDMj9mLxU06uiQvyrDMj9mLxU0pvamvxqEZT8kZIA0v9iHv5KDSD/MgjQ0v9iHv5KDSD/MgjQ0Lej0PuiDbL4qgcQy6uiQPyrDMr9mLxU06uiQPyrDMr9mLxU0pvamPxqEZb8kZIA0v9iHP5KDSL/MgjQ0v9iHP5KDSL/MgjQ0Lej0vuiDbL4qgcQy6uiQvyrDMr9mLxU06uiQvyrDMr9mLxU0pvamvxqEZb8kZIA0v9iHv5KDSL/MgjQ0v9iHv5KDSL/MgjQ0vMc2sw1NpTQAAIC/vMc2sw1NpTQAAIC/R3bMM8zv4jQAAIC/R3bMM8zv4jQAAIC/vMc2sw1NpTQAAIC/R3bMM8zv4jQAAIC/vMc2Mw1NpTQAAIC/R3bMs8zv4jQAAIC/vMc2Mw1NpTQAAIC/R3bMs8zv4jQAAIC/R3bMs8zv4jQAAIC/vMc2Mw1NpTQAAIC/vMc2sw1NpbQAAIC/vMc2sw1NpbQAAIC/R3bMM8zv4rQAAIC/R3bMM8zv4rQAAIC/vMc2sw1NpbQAAIC/R3bMM8zv4rQAAIC/vMc2Mw1NpbQAAIC/R3bMs8zv4rQAAIC/vMc2Mw1NpbQAAIC/R3bMs8zv4rQAAIC/R3bMs8zv4rQAAIC/vMc2Mw1NpbQAAIC/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AAAAAAAAgD8AAAAAAACAPwAAAAAAAIA/AgAFAAMAAAAEAAEABwAJAAoABgAIAAsADgAPABEADAANABAAEwAWABUAEgAXABQA" 98 | } 99 | ] 100 | } 101 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # Ergo 2 | ![](screenshots/header.gif) 3 | 4 | Fly a little ship around in a raylib project. The main purpose of this project was to figure out how to handle arbitrary rotations, since the built in rotation functions don't make this easy. 5 | 6 | This *ended up* being another take on simple arcade space flight mechanics. Specifically, this is probably how I'd do a Rogue Squadron kind of game. 7 | 8 | I'm really happy with the space dust and trails too. They look really neat, but I needed some help from the raylib Discord to get the transparencies working correctly. Sometimes you need to force a render batch with `rlDrawRenderBatchActive()` when messing with transparencies and the depth buffer. 9 | 10 | The game can be controlled with either WASD/Arrow keys, or a gamepad. 11 | 12 | ## Rotations 13 | Check `Actor.cpp` and `Ship.cpp` for some silly looking and probably very ineffecient functions to rotate a quaternion representing the ship's rotation. 14 | 15 | Getting a forward (or any direction) vector from some actor is something that needs to be very commonly done. I'm pretty sure there's a smarter way to do this, but I get it by multiplying the forward vector by the ship's Rotation: 16 | 17 | ```cpp 18 | Vector3 Actor::GetForward() const 19 | { 20 | return Vector3RotateByQuaternion( 21 | Vector3{ 0, 0, 1 }, 22 | Rotation); 23 | } 24 | ``` 25 | 26 | Rotating an object in local space is another essential piece of code. In Unity this is a very simple [`Transform.Rotate(Vector3, Space.Self)`](https://docs.unity3d.com/ScriptReference/Transform.Rotate.html), but for raylib you have to write your own. This seemed to work. 27 | 28 | ```cpp 29 | void Actor::RotateLocalEuler(Vector3 axis, float degrees) 30 | { 31 | auto radians = degrees * DEG2RAD; 32 | Rotation = QuaternionMultiply( 33 | Rotation, 34 | QuaternionFromAxisAngle(axis, radians)); 35 | } 36 | ``` 37 | 38 | While not rotation specific, transforming a point from local space to world space is another essential function. In Unity this is just [`Transform.TransformPoint()`](https://docs.unity3d.com/ScriptReference/Transform.TransformPoint.html), but as far as I can tell raylib's `Transform` struct is very basic and used only by model rendering. 39 | 40 | ```cpp 41 | Vector3 Actor::TransformPoint(Vector3 point) const 42 | { 43 | auto mPos = MatrixTranslate(Position.x, Position.y, Position.z); 44 | auto mRot = QuaternionToMatrix(Rotation); 45 | auto matrix = MatrixMultiply(mRot, mPos); 46 | return Vector3Transform(point, matrix); 47 | } 48 | ``` 49 | 50 | ## Smoothing and Ship Flight 51 | I've been asked about how this looks so smooth a lot, and it's [SmoothDamps all the way down](https://www.rorydriscoll.com/2016/03/07/frame-rate-independent-damping-using-lerp/). I use the SmoothDamp function in *everything* I work on because it's an easy way to add smoothing in a way that isn't (usually) affected by framerate. 52 | 53 | ```cpp 54 | inline float SmoothDamp(float from, float to, float speed, float dt) 55 | { 56 | return Lerp(from, to, 1 - expf(-speed * dt)); 57 | } 58 | ``` 59 | 60 | In this specific implementation, smoothing is applied to the inputs the player gives, and the ship's own velocity. For rotations, the smoothing is applied only to the input, not to any kind of angular velocity. 61 | 62 | For example, here's the smoothing applied to the ship input. 63 | ```cpp 64 | // Give the ship some momentum when accelerating. 65 | SmoothForward = SmoothDamp(SmoothForward, InputForward, ThrottleResponse, deltaTime); 66 | SmoothLeft = SmoothDamp(SmoothLeft, InputLeft, ThrottleResponse, deltaTime); 67 | SmoothUp = SmoothDamp(SmoothUp, InputUp, ThrottleResponse, deltaTime); 68 | ``` 69 | 70 | After the velocity the ship should have is figured out, smoothing is applied to move the current velocity to the target velocity. 71 | ```cpp 72 | Velocity = SmoothDamp(Velocity, targetVelocity, 2.5, deltaTime); 73 | Position = Vector3Add(Position, Vector3Scale(Velocity, deltaTime)); 74 | ``` 75 | 76 | ## Arbitrary Render Resolution 77 | ![](screenshots/lowres.png) 78 | 79 | There's also some messy code in there for rendering the game at an arbitrary resolution and separate from the display resolution. This is something that always interests me because I have an unhealthy rose-tinted nostalgia for DOS games. 80 | 81 | This rendering mode can be used by uncommenting `#define RENDER_SMALL` at the top of the `Ergo.cpp`. 82 | -------------------------------------------------------------------------------- /src/Ergo.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "Actor.h" 4 | #include "Ship.h" 5 | #include "SpaceDust.h" 6 | #include "GameCamera.h" 7 | #include "MathUtils.h" 8 | 9 | //#define RENDER_SMALL 10 | 11 | int g_ScreenWidth = 800; 12 | int g_ScreenHeight = 600; 13 | 14 | #ifdef RENDER_SMALL 15 | int g_RenderWidth = 400; 16 | int g_RenderHeight = 300; 17 | #endif // RENDER_SMALL 18 | 19 | void DrawStandardFPS() 20 | { 21 | BeginBlendMode(BlendMode::BLEND_ADDITIVE); 22 | DrawText(TextFormat("FPS %d", GetFPS()), 10, 10, 10, GREEN); 23 | EndBlendMode(); 24 | } 25 | 26 | void ApplyInputToShip(Ship& ship) 27 | { 28 | ship.InputForward = 0; 29 | if (IsKeyDown(KEY_W)) ship.InputForward += 1; 30 | if (IsKeyDown(KEY_S)) ship.InputForward -= 1; 31 | 32 | ship.InputForward -= GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_LEFT_Y); 33 | ship.InputForward = Clamp(ship.InputForward, -1, 1); 34 | 35 | ship.InputLeft = 0; 36 | if (IsKeyDown(KEY_D)) ship.InputLeft -= 1; 37 | if (IsKeyDown(KEY_A)) ship.InputLeft += 1; 38 | 39 | ship.InputLeft -= GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_LEFT_X); 40 | ship.InputLeft = Clamp(ship.InputLeft, -1, 1); 41 | 42 | ship.InputUp = 0; 43 | if (IsKeyDown(KEY_SPACE)) ship.InputUp += 1; 44 | if (IsKeyDown(KEY_LEFT_CONTROL)) ship.InputUp -= 1; 45 | 46 | auto triggerRight = GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_RIGHT_TRIGGER); 47 | triggerRight = Remap(triggerRight, -1, 1, 0, 1); 48 | 49 | auto triggerLeft = GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_LEFT_TRIGGER); 50 | triggerLeft = Remap(triggerLeft, -1, 1, 0, 1); 51 | 52 | ship.InputUp += triggerRight; 53 | ship.InputUp -= triggerLeft; 54 | ship.InputUp = Clamp(ship.InputUp, -1, 1); 55 | 56 | ship.InputYawLeft = 0; 57 | if (IsKeyDown(KEY_RIGHT)) ship.InputYawLeft -= 1; 58 | if (IsKeyDown(KEY_LEFT)) ship.InputYawLeft += 1; 59 | 60 | ship.InputYawLeft -= GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_RIGHT_X); 61 | ship.InputYawLeft = Clamp(ship.InputYawLeft, -1, 1); 62 | 63 | ship.InputPitchDown = 0; 64 | if (IsKeyDown(KEY_UP)) ship.InputPitchDown += 1; 65 | if (IsKeyDown(KEY_DOWN)) ship.InputPitchDown -= 1; 66 | 67 | ship.InputPitchDown += GetGamepadAxisMovement(0, GamepadAxis::GAMEPAD_AXIS_RIGHT_Y); 68 | ship.InputPitchDown = Clamp(ship.InputPitchDown, -1, 1); 69 | 70 | ship.InputRollRight = 0; 71 | if (IsKeyDown(KEY_Q)) ship.InputRollRight -= 1; 72 | if (IsKeyDown(KEY_E)) ship.InputRollRight += 1; 73 | } 74 | 75 | int main() 76 | { 77 | SetConfigFlags(ConfigFlags::FLAG_MSAA_4X_HINT | ConfigFlags::FLAG_VSYNC_HINT); 78 | InitWindow(g_ScreenWidth, g_ScreenHeight, "Ergo"); 79 | 80 | #ifdef RENDER_SMALL 81 | // Set up low resolution rendering independent from the window resolution. 82 | auto renderRatio = (float)g_ScreenWidth / (float)g_RenderWidth; 83 | RenderTexture2D renderTarget = LoadRenderTexture(g_RenderWidth, g_RenderHeight); 84 | SetTextureFilter(renderTarget.texture, TextureFilter::TEXTURE_FILTER_POINT); 85 | 86 | 87 | // Target height is flipped (in the source rectangle) due to OpenGL reasons. 88 | Rectangle sourceRect = { 0, 0, (float)renderTarget.texture.width, -(float)renderTarget.texture.height }; 89 | Rectangle destRect = { -renderRatio, -renderRatio, g_ScreenWidth + (renderRatio * 2), g_ScreenHeight + (renderRatio * 2) }; 90 | Camera2D screenSpaceCamera = { 0 }; 91 | screenSpaceCamera.zoom = 1.0f; 92 | #endif // RENDER_SMALL 93 | 94 | Ship player("data/ship.gltf", "data/a16.png", RAYWHITE); 95 | Ship other("data/ship.gltf", "data/a16.png", RAYWHITE); 96 | other.TrailColor = MAROON; 97 | other.Position = { 10, 2, 10 }; 98 | SpaceDust dust = SpaceDust(25, 255); 99 | 100 | GameCamera cameraFlight = GameCamera(true, 50); 101 | GameCamera cameraHUD = GameCamera(false, 50); 102 | cameraHUD.SetPosition({ 0, 0, -10 }, { 0, 0, 0 }, { 0, 1, 0 }); 103 | 104 | // Test station. 105 | Texture2D texture = LoadTexture("data/a16.png"); 106 | texture.mipmaps = 0; 107 | SetTextureFilter(texture, TEXTURE_FILTER_POINT); 108 | Model stationModel = LoadModel("data/station.gltf"); 109 | stationModel.materials[0].maps[MaterialMapIndex::MATERIAL_MAP_ALBEDO].texture = texture; 110 | stationModel.transform = MatrixTranslate(0, 5, 50); 111 | 112 | Crosshair crosshairNear = Crosshair("data/crosshair2.gltf"); 113 | Crosshair crosshairFar = Crosshair("data/crosshair2.gltf"); 114 | 115 | while (!WindowShouldClose()) 116 | { 117 | auto deltaTime = GetFrameTime(); 118 | 119 | // Capture input 120 | { 121 | ApplyInputToShip(player); 122 | ApplyInputToShip(other); 123 | } 124 | 125 | // Gameplay updates 126 | { 127 | player.Update(deltaTime); 128 | other.Update(deltaTime); 129 | 130 | crosshairNear.PositionCrosshairOnShip(player, 10); 131 | crosshairFar.PositionCrosshairOnShip(player, 30); 132 | } 133 | 134 | // Camera movement and visual effects 135 | { 136 | cameraFlight.FollowShip(player, deltaTime); 137 | dust.UpdateViewPosition(cameraFlight.GetPosition()); 138 | } 139 | 140 | // Render 141 | #ifdef RENDER_SMALL 142 | BeginTextureMode(renderTarget); 143 | #else 144 | BeginDrawing(); 145 | #endif 146 | { 147 | ClearBackground({32, 32, 64, 255}); 148 | 149 | cameraFlight.Begin3DDrawing(); 150 | { 151 | // Opaques 152 | { 153 | DrawGrid(10, 10); 154 | 155 | player.Draw(false); 156 | other.Draw(false); 157 | 158 | DrawModel(stationModel, Vector3Zero(), 1, WHITE); 159 | } 160 | 161 | // Transparencies 162 | { 163 | player.DrawTrail(); 164 | other.DrawTrail(); 165 | 166 | crosshairNear.DrawCrosshair(); 167 | crosshairFar.DrawCrosshair(); 168 | 169 | dust.Draw(cameraFlight.GetPosition(), player.Velocity, false); 170 | } 171 | } 172 | cameraFlight.EndDrawing(); 173 | 174 | //cameraHUD.Begin3DDrawing(); 175 | //{ 176 | // // Test for drawing 3D geometry to an orthrographic "HUD". 177 | // DrawSphereWires({sinf(GetTime()) * 12}, 3, 8, 8, RED); 178 | //} 179 | //cameraHUD.EndDrawing(); 180 | 181 | DrawStandardFPS(); 182 | } 183 | #ifdef RENDER_SMALL 184 | EndTextureMode(); 185 | #else 186 | EndDrawing(); 187 | #endif 188 | 189 | #ifdef RENDER_SMALL 190 | // Draw the render texture target to the screen. 191 | BeginDrawing(); 192 | { 193 | ClearBackground(RED); 194 | 195 | BeginMode2D(screenSpaceCamera); 196 | DrawTexturePro(renderTarget.texture, sourceRect, destRect, { 0, 0 }, 0, WHITE); 197 | EndMode2D(); 198 | } 199 | EndDrawing(); 200 | #endif // RENDER_SMALL 201 | } 202 | 203 | CloseWindow(); 204 | 205 | return 0; 206 | } 207 | -------------------------------------------------------------------------------- /src/Ship.cpp: -------------------------------------------------------------------------------- 1 | #include "Ship.h" 2 | 3 | #include "MathUtils.h" 4 | 5 | #include 6 | #include 7 | 8 | static const float RungDistance = 2.0f; 9 | static const float RungTimeToLive = 2.0f; 10 | 11 | Ship::Ship(const char* modelPath, const char* texturePath, Color color) 12 | { 13 | Texture2D texture = LoadTexture(texturePath); 14 | texture.mipmaps = 0; 15 | SetTextureFilter(texture, TEXTURE_FILTER_POINT); 16 | 17 | ShipModel = LoadModel(modelPath); 18 | ShipModel.materials[0].maps[MaterialMapIndex::MATERIAL_MAP_ALBEDO].texture = texture; 19 | 20 | Rotation = QuaternionFromEuler(1, 2, 0); 21 | 22 | ShipColor = color; 23 | 24 | LastRungPosition = Position; 25 | } 26 | 27 | Ship::~Ship() 28 | { 29 | UnloadModel(ShipModel); 30 | } 31 | 32 | void Ship::Update(float deltaTime) 33 | { 34 | // Give the ship some momentum when accelerating. 35 | SmoothForward = SmoothDamp(SmoothForward, InputForward, ThrottleResponse, deltaTime); 36 | SmoothLeft = SmoothDamp(SmoothLeft, InputLeft, ThrottleResponse, deltaTime); 37 | SmoothUp = SmoothDamp(SmoothUp, InputUp, ThrottleResponse, deltaTime); 38 | 39 | // Flying in reverse should be slower. 40 | auto forwardSpeedMultipilier = SmoothForward > 0.0f ? 1.0f : 0.33f; 41 | 42 | auto targetVelocity = Vector3Zero(); 43 | targetVelocity = Vector3Add( 44 | targetVelocity, 45 | Vector3Scale(GetForward(), MaxSpeed * forwardSpeedMultipilier * SmoothForward)); 46 | targetVelocity = Vector3Add( 47 | targetVelocity, 48 | Vector3Scale(GetUp(), MaxSpeed * .5f * SmoothUp)); 49 | targetVelocity = Vector3Add( 50 | targetVelocity, 51 | Vector3Scale(GetLeft(), MaxSpeed * .5f * SmoothLeft)); 52 | 53 | Velocity = SmoothDamp(Velocity, targetVelocity, 2.5, deltaTime); 54 | Position = Vector3Add(Position, Vector3Scale(Velocity, deltaTime)); 55 | 56 | // Give the ship some inertia when turning. These are the pilot controlled rotations. 57 | SmoothPitchDown = SmoothDamp(SmoothPitchDown, InputPitchDown, TurnResponse, deltaTime); 58 | SmoothRollRight = SmoothDamp(SmoothRollRight, InputRollRight, TurnResponse, deltaTime); 59 | SmoothYawLeft = SmoothDamp(SmoothYawLeft, InputYawLeft, TurnResponse, deltaTime); 60 | 61 | RotateLocalEuler( { 0, 0, 1 }, SmoothRollRight * TurnRate * deltaTime); 62 | RotateLocalEuler({ 1, 0, 0 }, SmoothPitchDown * TurnRate * deltaTime); 63 | RotateLocalEuler({ 0, 1, 0 }, SmoothYawLeft * TurnRate * deltaTime); 64 | 65 | //// Auto-roll from yaw 66 | //// Movement like a 3D space sim. This only feels good if there's no horizon auto-align. 67 | //RotateLocalEuler({ 0, 0, -1 }, SmoothYawLeft * TurnRate * .5f * deltaTime); 68 | 69 | // Auto-roll to align to horizon 70 | if (fabs(GetForward().y) < 0.8) 71 | { 72 | float autoSteerInput = GetRight().y; 73 | RotateLocalEuler({ 0, 0, 1 }, autoSteerInput * TurnRate * .5f * deltaTime); 74 | } 75 | 76 | // When yawing and strafing, there's some bank added to the model for visual flavor. 77 | float targetVisualBank = (-30 * DEG2RAD * SmoothYawLeft) + (-15 * DEG2RAD * SmoothLeft); 78 | VisualBank = SmoothDamp(VisualBank, targetVisualBank, 10, deltaTime); 79 | Quaternion visualRotation = QuaternionMultiply( 80 | Rotation, QuaternionFromAxisAngle({ 0, 0, 1 }, VisualBank)); 81 | 82 | // Sync up the raylib representation of the model with the ship's position so that processing 83 | // doesn't have to happen at the render stage. 84 | auto transform = MatrixTranslate(Position.x, Position.y, Position.z); 85 | transform = MatrixMultiply(QuaternionToMatrix(visualRotation), transform); 86 | ShipModel.transform = transform; 87 | 88 | // The currently active trail rung is dragged directly behind the ship for a smoother trail. 89 | PositionActiveTrailRung(); 90 | if (Vector3Distance(Position, LastRungPosition) > RungDistance) 91 | { 92 | RungIndex = (RungIndex + 1) % RungCount; 93 | LastRungPosition = Position; 94 | } 95 | 96 | for (int i = 0; i < RungCount; ++i) 97 | Rungs[i].TimeToLive -= deltaTime; 98 | } 99 | 100 | void Ship::PositionActiveTrailRung() 101 | { 102 | Rungs[RungIndex].TimeToLive = RungTimeToLive; 103 | float halfWidth = Width / 2.f; 104 | float halfLength = Length / 2.f; 105 | Rungs[RungIndex].LeftPoint = TransformPoint({ -halfWidth, 0.0f, -halfLength }); 106 | Rungs[RungIndex].RightPoint = TransformPoint({ halfWidth, 0.0f, -halfLength }); 107 | } 108 | 109 | void Ship::Draw(bool showDebugAxes) const 110 | { 111 | DrawModel(ShipModel, Vector3Zero(), 1, ShipColor); 112 | 113 | if (showDebugAxes) 114 | { 115 | BeginBlendMode(BlendMode::BLEND_ADDITIVE); 116 | DrawLine3D(Position, Vector3Add(Position, GetForward()), { 0, 0, 255, 255 }); 117 | DrawLine3D(Position, Vector3Add(Position, GetLeft()), { 255, 0, 0, 255 }); 118 | DrawLine3D(Position, Vector3Add(Position, GetUp()), { 0, 255, 0, 255 }); 119 | EndBlendMode(); 120 | } 121 | } 122 | 123 | void Ship::DrawTrail() const 124 | { 125 | BeginBlendMode(BlendMode::BLEND_ADDITIVE); 126 | rlDisableDepthMask(); 127 | 128 | for (int i = 0; i < RungCount; ++i) 129 | { 130 | if (Rungs[i].TimeToLive <= 0) 131 | continue; 132 | 133 | auto& thisRung = Rungs[i % RungCount]; 134 | 135 | Color color = TrailColor; 136 | color.a = 255 * thisRung.TimeToLive / RungTimeToLive; 137 | Color fill = color; 138 | fill.a = color.a / 4; 139 | 140 | // The current rung is dragged along behind the ship, so the crossbar shouldn't be drawn. 141 | // If the crossbar is drawn when the ship is slow, it looks weird having a line behind it. 142 | if (i != RungIndex) 143 | DrawLine3D(thisRung.LeftPoint, thisRung.RightPoint, color); 144 | 145 | auto& nextRung = Rungs[(i + 1) % RungCount]; 146 | if (nextRung.TimeToLive > 0 && thisRung.TimeToLive < nextRung.TimeToLive) 147 | { 148 | DrawLine3D(nextRung.LeftPoint, thisRung.LeftPoint, color); 149 | DrawLine3D(nextRung.RightPoint, thisRung.RightPoint, color); 150 | 151 | DrawTriangle3D(thisRung.LeftPoint, thisRung.RightPoint, nextRung.LeftPoint, fill); 152 | DrawTriangle3D(nextRung.LeftPoint, thisRung.RightPoint, nextRung.RightPoint, fill); 153 | 154 | DrawTriangle3D(nextRung.LeftPoint, thisRung.RightPoint, thisRung.LeftPoint, fill); 155 | DrawTriangle3D(nextRung.RightPoint, thisRung.RightPoint, nextRung.LeftPoint, fill); 156 | } 157 | } 158 | 159 | rlDrawRenderBatchActive(); 160 | rlEnableDepthMask(); 161 | EndBlendMode(); 162 | } 163 | 164 | Crosshair::Crosshair(const char* modelPath) 165 | { 166 | CrosshairModel = LoadModel(modelPath); 167 | } 168 | 169 | Crosshair::~Crosshair() 170 | { 171 | UnloadModel(CrosshairModel); 172 | } 173 | 174 | void Crosshair::PositionCrosshairOnShip(const Ship& ship, float distance) 175 | { 176 | auto crosshairPos = Vector3Add(Vector3Scale(ship.GetForward(), distance), ship.Position); 177 | auto crosshairTransform = MatrixTranslate(crosshairPos.x, crosshairPos.y, crosshairPos.z); 178 | crosshairTransform = MatrixMultiply(QuaternionToMatrix(ship.Rotation), crosshairTransform); 179 | CrosshairModel.transform = crosshairTransform; 180 | } 181 | 182 | void Crosshair::DrawCrosshair() const 183 | { 184 | BeginBlendMode(BlendMode::BLEND_ADDITIVE); 185 | rlDisableDepthTest(); 186 | 187 | DrawModel(CrosshairModel, Vector3Zero(), 1, DARKGREEN); 188 | //DrawModelWires(Model, Vector3Zero(), 1, DARKGREEN); 189 | 190 | rlEnableDepthTest(); 191 | EndBlendMode(); 192 | } 193 | -------------------------------------------------------------------------------- /data/ship.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.8.19", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0 12 | ] 13 | } 14 | ], 15 | "nodes" : [ 16 | { 17 | "mesh" : 0, 18 | "name" : "Ship" 19 | } 20 | ], 21 | "materials" : [ 22 | { 23 | "doubleSided" : true, 24 | "name" : "Mat", 25 | "pbrMetallicRoughness" : { 26 | "baseColorTexture" : { 27 | "index" : 0 28 | }, 29 | "metallicFactor" : 0, 30 | "roughnessFactor" : 0.5 31 | } 32 | } 33 | ], 34 | "meshes" : [ 35 | { 36 | "name" : "Cube", 37 | "primitives" : [ 38 | { 39 | "attributes" : { 40 | "POSITION" : 0, 41 | "NORMAL" : 1, 42 | "TEXCOORD_0" : 2 43 | }, 44 | "indices" : 3, 45 | "material" : 0 46 | } 47 | ] 48 | } 49 | ], 50 | "textures" : [ 51 | { 52 | "sampler" : 0, 53 | "source" : 0 54 | } 55 | ], 56 | "images" : [ 57 | { 58 | "bufferView" : 4, 59 | "mimeType" : "image/png", 60 | "name" : "Image_0" 61 | } 62 | ], 63 | "accessors" : [ 64 | { 65 | "bufferView" : 0, 66 | "componentType" : 5126, 67 | "count" : 108, 68 | "max" : [ 69 | 0.4801882207393646, 70 | 0.11864282190799713, 71 | 0.76969313621521 72 | ], 73 | "min" : [ 74 | -0.4801882207393646, 75 | -0.14245106279850006, 76 | -0.27048659324645996 77 | ], 78 | "type" : "VEC3" 79 | }, 80 | { 81 | "bufferView" : 1, 82 | "componentType" : 5126, 83 | "count" : 108, 84 | "type" : "VEC3" 85 | }, 86 | { 87 | "bufferView" : 2, 88 | "componentType" : 5126, 89 | "count" : 108, 90 | "type" : "VEC2" 91 | }, 92 | { 93 | "bufferView" : 3, 94 | "componentType" : 5123, 95 | "count" : 108, 96 | "type" : "SCALAR" 97 | } 98 | ], 99 | "bufferViews" : [ 100 | { 101 | "buffer" : 0, 102 | "byteLength" : 1296, 103 | "byteOffset" : 0 104 | }, 105 | { 106 | "buffer" : 0, 107 | "byteLength" : 1296, 108 | "byteOffset" : 1296 109 | }, 110 | { 111 | "buffer" : 0, 112 | "byteLength" : 864, 113 | "byteOffset" : 2592 114 | }, 115 | { 116 | "buffer" : 0, 117 | "byteLength" : 216, 118 | "byteOffset" : 3456 119 | }, 120 | { 121 | "buffer" : 0, 122 | "byteLength" : 147, 123 | "byteOffset" : 3672 124 | } 125 | ], 126 | "samplers" : [ 127 | { 128 | "magFilter" : 9728, 129 | "minFilter" : 9984 130 | } 131 | ], 132 | "buffers" : [ 133 | { 134 | "byteLength" : 3820, 135 | "uri" : "data:application/octet-stream;base64,AAAAAAL78j268D49AAAAAAL78j268D49AAAAAAL78j268D49AAAAAAL78j268D49SBztvAAAAADEYEw+O9v1vrHeEb7i7oK+AAAAAAX78r268D49AAAAAAX78r268D49AAAAAAX78r268D49AAAAAAX78r268D49CfvyvajRjrK+8D49CfvyvajRjrK+8D49CfvyvajRjrK+8D49CfvyvajRjrK+8D49AAAAAKjRjrI4fYq+AAAAAKjRjrI4fYq+AAAAAKjRjrI4fYq+AAAAAKjRjrI4fYq+AAAAAGptdDKcCkU/AAAAAGptdDKcCkU/AAAAAGptdDKcCkU/AAAAAGptdDKcCkU/ZBztvAAAAACV0hu+SBztvAAAAADEYEw+O9v1vrHeEb7i7oK+ZBztvAAAAACV0hu+hLzbvY6bOL2lcF2+hLzbvY6bOL2lcF2+hLzbvY6bOL2lcF2+hLzbvY6bOL2lcF2+X545vtXJ7DylcF2+X545vtXJ7DylcF2+X545vtXJ7DylcF2+X545vtXJ7DylcF2+X545vtXJ7DylcF2+e7zbvY6bOL2gm2Y+e7zbvY6bOL2gm2Y+e7zbvY6bOL2gm2Y+e7zbvY6bOL2gm2Y+e7zbvY6bOL2gm2Y+W545vtXJ7Dygm2Y+W545vtXJ7Dygm2Y+W545vtXJ7Dygm2Y+W545vtXJ7Dygm2Y+X545vgTO872lcF2+X545vgTO872lcF2+X545vgTO872lcF2+X545vgTO872lcF2+X545vgTO872lcF2+P6+Cvo6bOL2jcF2+P6+Cvo6bOL2jcF2+P6+Cvo6bOL2jcF2+P6+Cvo6bOL2jcF2+W545vgTO872gm2Y+W545vgTO872gm2Y+W545vgTO872gm2Y+W545vgTO872gm2Y+PK+Cvo6bOL2hm2Y+PK+Cvo6bOL2hm2Y+PK+Cvo6bOL2hm2Y+PK+Cvo6bOL2hm2Y+PK+Cvo6bOL2hm2Y+SBztPAAAAADEYEw+O9v1PrHeEb7i7oK+CfvyPajRjrK+8D49CfvyPajRjrK+8D49CfvyPajRjrK+8D49CfvyPajRjrK+8D49ZBztPAAAAACV0hu+SBztPAAAAADEYEw+O9v1PrHeEb7i7oK+ZBztPAAAAACV0hu+hLzbPY6bOL2lcF2+hLzbPY6bOL2lcF2+hLzbPY6bOL2lcF2+hLzbPY6bOL2lcF2+X545PtXJ7DylcF2+X545PtXJ7DylcF2+X545PtXJ7DylcF2+X545PtXJ7DylcF2+X545PtXJ7DylcF2+e7zbPY6bOL2gm2Y+e7zbPY6bOL2gm2Y+e7zbPY6bOL2gm2Y+e7zbPY6bOL2gm2Y+e7zbPY6bOL2gm2Y+W545PtXJ7Dygm2Y+W545PtXJ7Dygm2Y+W545PtXJ7Dygm2Y+W545PtXJ7Dygm2Y+X545PgTO872lcF2+X545PgTO872lcF2+X545PgTO872lcF2+X545PgTO872lcF2+X545PgTO872lcF2+P6+CPo6bOL2jcF2+P6+CPo6bOL2jcF2+P6+CPo6bOL2jcF2+P6+CPo6bOL2jcF2+W545PgTO872gm2Y+W545PgTO872gm2Y+W545PgTO872gm2Y+W545PgTO872gm2Y+PK+CPo6bOL2hm2Y+PK+CPo6bOL2hm2Y+PK+CPo6bOL2hm2Y+PK+CPo6bOL2hm2Y+PK+CPo6bOL2hm2Y+KNAzvyzQMz/ZB+w9ov8uv6X/Lj8784K+ov8uP6X/Lj8784K+KNAzPyzQMz/ZB+w9CiKavt4fdD/lP0AzCiKavt4fdD/lP0AzJ9AzvyzQM7/gB+w9ov8uv6b/Lr8884K+ov8uP6b/Lr8884K+J9AzPyzQM7/gB+w9KNAzvyzQMz/ZB+w9J9AzvyzQM7/gB+w9ov8uv6b/Lr8884K+ov8uv6X/Lj8784K+ov8uv6b/Lr8884K+ov8uv6X/Lj8784K+ov8uP6b/Lr8884K+ov8uP6X/Lj8784K+KNAzvyzQMz/ZB+w9J9AzvyzQM7/gB+w9J9AzPyzQM7/gB+w9KNAzPyzQMz/ZB+w9CiKavt4fdD/lP0AzCiKaPt4fdL/lP0CzCiKaPt4fdL/lP0CzCiKaPt4fdL/lP0CzAAAAAAAAAAAAAIC/9QQ1P/EENb8MOa2z9AQ1P/EENT/J6gG09AQ1P/IENT8NOa2z9QQ1v/IENT8OOa0z8wQ1v/QENT8LOa0zDErYtAAAAAAAAIC/AAAAAAAAAAAAAIC/9AQ1P/IENT8NOa2zBErYM/y8trQAAIA/BErYM/q8tjQAAIA/9AQ1P/IENb/K6gG09QQ1P/EENb8MOa2z9AQ1P/EENT/J6gG09QQ1v/IENT8OOa0zBErYM/y8trQAAIA/9AQ1P/EENT/J6gG09AQ1P/IENT8NOa2z8QQ1v/UENb8MOa0zDErYtAAAAAAAAIC/AAAAAAAAAAAAAIC/9AQ1P/IENb/K6gG09QQ1P/EENb8MOa2z9gQ1v/AENb8NOa0z8QQ1v/UENb8MOa0z8wQ1v/QENT8LOa0zDErYtAAAAAAAAIC/9gQ1v/AENb8NOa0z8QQ1v/UENb8MOa0zBErYM/q8tjQAAIA/9AQ1P/IENb/K6gG09gQ1v/AENb8NOa0z9QQ1v/IENT8OOa0z8wQ1v/QENT8LOa0zBErYM/y8trQAAIA/BErYM/q8tjQAAIA/CiKaPt4fdD/lP0AzCiKaPt4fdD/lP0Azov8uP6b/Lr8884K+ov8uP6X/Lj8784K+J9AzPyzQM7/gB+w9KNAzPyzQMz/ZB+w9CiKaPt4fdD/lP0AzCiKavt4fdL/lP0CzCiKavt4fdL/lP0CzCiKavt4fdL/lP0Cz9QQ1v/EENb8MOa2z9AQ1v/EENT/J6gG09AQ1v/IENT8NOa2zAAAAAAAAAAAAAIC/9AQ1v/IENT8NOa2zAAAAAAAAAAAAAIC/DErYNAAAAAAAAIC/8wQ1P/QENT8LOa0z9QQ1P/IENT8OOa0z9QQ1v/EENb8MOa2z9AQ1v/IENb/K6gG09AQ1v/EENT/J6gG0BErYs/y8trQAAIA/BErYs/q8tjQAAIA/9AQ1v/EENT/J6gG09AQ1v/IENT8NOa2zBErYs/y8trQAAIA/9QQ1P/IENT8OOa0z9QQ1v/EENb8MOa2z9AQ1v/IENb/K6gG0AAAAAAAAAAAAAIC/DErYNAAAAAAAAIC/8QQ1P/UENb8MOa0zDErYNAAAAAAAAIC/8QQ1P/UENb8MOa0z9gQ1P/AENb8NOa0z8wQ1P/QENT8LOa0z9AQ1v/IENb/K6gG0BErYs/q8tjQAAIA/8QQ1P/UENb8MOa0z9gQ1P/AENb8NOa0zBErYs/y8trQAAIA/BErYs/q8tjQAAIA/9gQ1P/AENb8NOa0z8wQ1P/QENT8LOa0z9QQ1P/IENT8OOa0zSBcBPq0OTj9IFwE+rQ5OP0gXAT6tDk4/SBcBPq0OTj8Otxg+7hRmP9wxbj7uFGY/vma5PYaXrz6+Zrk9hpevPr5muT2Gl68+vma5PYaXrz4gcdc9rQ5OPyEk5D2Gl68+ISTkPYaXrz4gcdc9rQ5OP75muT2Gl68+IHHXPa0OTj++Zrk9hpevPiBx1z2tDk4/SBcBPlpmUz8hJOQ94Ea6PiEk5D3gRro+SBcBPlpmUz8Otxg+onN7P6Di3j2MMbM+HuxEPowxsz6g4t499O7dPsxcYj/fWV0/c2EWP7DY7j1sxmA/uPUaPmzGYD+49Ro+xnVrPziztT3GdWs/OLO1PXi0Zz/fWV0/eLRnP99ZXT8YHmY/uPUaPhysEj4QFeU9HKwSPhAV5T1zYRY/EMsMPnNhFj8Qyww+bMZgPwSXBT7GdWs/oHDgPc4KKD4QFeU9GB5mPwSXBT4YHmY/BJcFPiC5Gz+w2O49zFxiP4yxYj/MXGI/jLFiPyC5Gz+w2O49ILkbP7DY7j3MECE/sNjuPcwQIT+w2O49GB5mPziztT14tGc/jLFiPyC5Gz8Qyww+ILkbPxDLDD4crBI+oFe6PSC5Gz8Qyww+zBAhPxDLDD4YHmY/oHDgPRgeZj+gcOA9zgooPqBXuj3OCig+oFe6PQ63GD7uFGY/3DFuPu4UZj8hJOQ9hpevPiBx1z2tDk4/ISTkPYaXrz4gcdc9rQ5OPw63GD6ic3s/oOLePYwxsz4e7EQ+jDGzPqDi3j307t0+c2EWP7DY7j1sxmA/uPUaPmzGYD+49Ro+zFxiP99ZXT8YHmY/uPUaPni0Zz/fWV0/eLRnP99ZXT/GdWs/OLO1PcZ1az84s7U9c2EWPxDLDD5zYRY/EMsMPmzGYD8ElwU+HKwSPhAV5T0crBI+EBXlPRgeZj8ElwU+GB5mPwSXBT7OCig+EBXlPcZ1az+gcOA9ILkbP7DY7j0guRs/sNjuPcxcYj+MsWI/zFxiP4yxYj8guRs/sNjuPXi0Zz+MsWI/zBAhP7DY7j3MECE/sNjuPRgeZj84s7U9ILkbPxDLDD4crBI+oFe6PSC5Gz8Qyww+ILkbPxDLDD7OCig+oFe6Pc4KKD6gV7o9zBAhPxDLDD4YHmY/oHDgPRgeZj+gcOA9BwAMAA4AEwALAAYADQABAA8AEgAAAAoAGQAXABgAFgAFAAQAHQAiACsAHAAqACcAIwApADwAJAA9ADcANQA5ADEANgAyACwALQA0ACAALgAhABoAJQA4AC8AJgAwABsAOgAoAB4AOwAfADMACAAQAEAAFAAJAEIAQQARAAIAFQBDAAMARwBGAEUARAA+AD8ASQBTAFYASgBXAEwAVQBkAGgAVABnAFgAZQBeAGAAZgBhAGkAXABLAE0AXQBOAF8AUQBIAFoAUgBbAGMAagBiAE8AawBQAFkAiVBORw0KGgoAAAANSUhEUgAAAAQAAAAECAYAAACp8Z5+AAAAAXNSR0IArs4c6QAAAE1JREFUCJljZGBg+G9oNZFB+tcShlQRPQYWj5AQBj1GQQYZxm8Ml/RzGFgsdFQYLoisYwj/78FwSIeNgUVG25LBWvYEw3mWKwxxUp8ZADlYEXc10vtOAAAAAElFTkSuQmCCAA==" 136 | } 137 | ] 138 | } 139 | -------------------------------------------------------------------------------- /Ergo.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 16.0 23 | Win32Proj 24 | {26599881-5658-45ed-9275-096cfae1063c} 25 | Ergo 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v143 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v143 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v143 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v143 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | false 78 | 79 | 80 | true 81 | 82 | 83 | false 84 | 85 | 86 | 87 | Level3 88 | true 89 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 90 | true 91 | 92 | 93 | Console 94 | true 95 | 96 | 97 | 98 | 99 | Level3 100 | true 101 | true 102 | true 103 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 104 | true 105 | 106 | 107 | Console 108 | true 109 | true 110 | true 111 | 112 | 113 | 114 | 115 | Level3 116 | true 117 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 118 | true 119 | $(SolutionDir)include 120 | stdcpp20 121 | 122 | 123 | Console 124 | true 125 | 126 | 127 | 128 | 129 | Level3 130 | true 131 | true 132 | true 133 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 134 | true 135 | $(SolutionDir)include 136 | stdcpp20 137 | 138 | 139 | Console 140 | true 141 | true 142 | true 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. 169 | 170 | 171 | 172 | -------------------------------------------------------------------------------- /data/station.gltf: -------------------------------------------------------------------------------- 1 | { 2 | "asset" : { 3 | "generator" : "Khronos glTF Blender I/O v1.8.19", 4 | "version" : "2.0" 5 | }, 6 | "scene" : 0, 7 | "scenes" : [ 8 | { 9 | "name" : "Scene", 10 | "nodes" : [ 11 | 0 12 | ] 13 | } 14 | ], 15 | "nodes" : [ 16 | { 17 | "mesh" : 0, 18 | "name" : "Station" 19 | } 20 | ], 21 | "materials" : [ 22 | { 23 | "doubleSided" : true, 24 | "name" : "Mat", 25 | "pbrMetallicRoughness" : { 26 | "baseColorTexture" : { 27 | "index" : 0 28 | }, 29 | "metallicFactor" : 0, 30 | "roughnessFactor" : 0.5 31 | } 32 | } 33 | ], 34 | "meshes" : [ 35 | { 36 | "name" : "Cube.001", 37 | "primitives" : [ 38 | { 39 | "attributes" : { 40 | "POSITION" : 0, 41 | "NORMAL" : 1, 42 | "TEXCOORD_0" : 2 43 | }, 44 | "indices" : 3, 45 | "material" : 0 46 | } 47 | ] 48 | } 49 | ], 50 | "textures" : [ 51 | { 52 | "sampler" : 0, 53 | "source" : 0 54 | } 55 | ], 56 | "images" : [ 57 | { 58 | "bufferView" : 4, 59 | "mimeType" : "image/png", 60 | "name" : "Image_0" 61 | } 62 | ], 63 | "accessors" : [ 64 | { 65 | "bufferView" : 0, 66 | "componentType" : 5126, 67 | "count" : 254, 68 | "max" : [ 69 | 15.12708854675293, 70 | 2.145261764526367, 71 | 11.004926681518555 72 | ], 73 | "min" : [ 74 | -15.12708854675293, 75 | -2.945413589477539, 76 | -7.182535648345947 77 | ], 78 | "type" : "VEC3" 79 | }, 80 | { 81 | "bufferView" : 1, 82 | "componentType" : 5126, 83 | "count" : 254, 84 | "type" : "VEC3" 85 | }, 86 | { 87 | "bufferView" : 2, 88 | "componentType" : 5126, 89 | "count" : 254, 90 | "type" : "VEC2" 91 | }, 92 | { 93 | "bufferView" : 3, 94 | "componentType" : 5123, 95 | "count" : 402, 96 | "type" : "SCALAR" 97 | } 98 | ], 99 | "bufferViews" : [ 100 | { 101 | "buffer" : 0, 102 | "byteLength" : 3048, 103 | "byteOffset" : 0 104 | }, 105 | { 106 | "buffer" : 0, 107 | "byteLength" : 3048, 108 | "byteOffset" : 3048 109 | }, 110 | { 111 | "buffer" : 0, 112 | "byteLength" : 2032, 113 | "byteOffset" : 6096 114 | }, 115 | { 116 | "buffer" : 0, 117 | "byteLength" : 804, 118 | "byteOffset" : 8128 119 | }, 120 | { 121 | "buffer" : 0, 122 | "byteLength" : 147, 123 | "byteOffset" : 8932 124 | } 125 | ], 126 | "samplers" : [ 127 | { 128 | "magFilter" : 9728, 129 | "minFilter" : 9984 130 | } 131 | ], 132 | "buffers" : [ 133 | { 134 | "byteLength" : 9080, 135 | "uri" : "data:application/octet-stream;base64,aqVhwRYaAz8KYEU/aqVhwRYaAz8KYEU/aqVhwRYaAz8KYEU/aqVhwWv4p78KYEU/aqVhwWv4p78KYEU/aqVhwWv4p78KYEU/aqVhwRYaAz++GiZAaqVhwRYaAz++GiZAaqVhwRYaAz++GiZAaqVhwWv4p7++GiZAaqVhwWv4p7++GiZAaqVhwWv4p7++GiZAjghywaiBPMAYUIdAjghywaiBPMAYUIdAjghywaiBPMAYUIdAjghywaiBPMAYUIdAjghywfhLCUAYUIdAjghywfhLCUAYUIdAjghywfhLCUAYUIdAjghywfhLCUAYUIdAjghywaiBPMDAtVy/jghywaiBPMDAtVy/jghywaiBPMDAtVy/jghywfhLCUDAtVy/jghywfhLCUDAtVy/jghywfhLCUDAtVy/JpUgwaiBPMAYUIdAJpUgwaiBPMAYUIdAJpUgwaiBPMAYUIdAJpUgwaiBPMAYUIdAJpUgwfhLCUAYUIdAJpUgwfhLCUAYUIdAJpUgwfhLCUAYUIdAJpUgwfhLCUAYUIdAJpUgwaiBPMDAtVy/JpUgwaiBPMDAtVy/JpUgwaiBPMDAtVy/JpUgwaiBPMDAtVy/JpUgwfhLCUDAtVy/JpUgwfhLCUDAtVy/JpUgwfhLCUDAtVy/JpUgwfhLCUDAtVy/AghRwPhLCUDAtVy/AghRwPhLCUDAtVy/AghRwPhLCUDAtVy/AghRwKiBPMDAtVy/AghRwKiBPMDAtVy/AghRwKiBPMDAtVy/AghRwPhLCUAYUIdAAghRwPhLCUAYUIdAAghRwPhLCUAYUIdAAghRwPhLCUAYUIdAAghRwKiBPMAYUIdAAghRwKiBPMAYUIdAAghRwKiBPMAYUIdAAghRwKiBPMAYUIdAaLUFwfhLCUDAtVy/aLUFwfhLCUDAtVy/aLUFwfhLCUDAtVy/aLUFwfhLCUDAtVy/aLUFwaiBPMDAtVy/aLUFwaiBPMDAtVy/aLUFwaiBPMDAtVy/aLUFwaiBPMDAtVy/aLUFwfhLCUAYUIdAaLUFwfhLCUAYUIdAaLUFwfhLCUAYUIdAaLUFwfhLCUAYUIdAaLUFwaiBPMAYUIdAaLUFwaiBPMAYUIdAaLUFwaiBPMAYUIdAaLUFwaiBPMAYUIdAAAAAAGv4p7++GiZAAAAAAGv4p7++GiZAAAAAAGv4p7++GiZAAAAAABYaAz++GiZAAAAAABYaAz++GiZAAAAAABYaAz++GiZAAAAAAGv4p78KYEU/AAAAAGv4p78KYEU/AAAAAGv4p78KYEU/AAAAABYaAz8KYEU/AAAAABYaAz8KYEU/AAAAABYaAz8KYEU/jghywWIocD9V1+XAjghywWIocD9V1+XAjghywWIocD9V1+XAjghywZF/3r9V1+XAjghywZF/3r9V1+XAjghywZF/3r9V1+XAJpUgwZF/3r9V1+XAJpUgwZF/3r9V1+XAJpUgwZF/3r9V1+XAJpUgwWIocD9V1+XAJpUgwWIocD9V1+XAJpUgwWIocD9V1+XAAghRwGIocD9V1+XAAghRwGIocD9V1+XAAghRwGIocD9V1+XAAghRwJF/3r9V1+XAAghRwJF/3r9V1+XAAghRwJF/3r9V1+XAaLUFwWIocD9V1+XAaLUFwWIocD9V1+XAaLUFwWIocD9V1+XAaLUFwZF/3r9V1+XAaLUFwZF/3r9V1+XAaLUFwZF/3r9V1+XAjghywc/e2b8uFDBBjghywc/e2b8uFDBBjghywc/e2b8uFDBBjghywd7mZj8uFDBBjghywd7mZj8uFDBBjghywd7mZj8uFDBBJpUgwd7mZj8uFDBBJpUgwd7mZj8uFDBBJpUgwd7mZj8uFDBBJpUgwc/e2b8uFDBBJpUgwc/e2b8uFDBBJpUgwc/e2b8uFDBBAghRwN7mZj8uFDBBAghRwN7mZj8uFDBBAghRwN7mZj8uFDBBaLUFwd7mZj8uFDBBaLUFwd7mZj8uFDBBaLUFwd7mZj8uFDBBaLUFwc/e2b8uFDBBaLUFwc/e2b8uFDBBaLUFwc/e2b8uFDBBAghRwM/e2b8uFDBBAghRwM/e2b8uFDBBAghRwM/e2b8uFDBBaqVhQRYaAz8KYEU/aqVhQRYaAz8KYEU/aqVhQRYaAz8KYEU/aqVhQWv4p78KYEU/aqVhQWv4p78KYEU/aqVhQWv4p78KYEU/aqVhQRYaAz++GiZAaqVhQRYaAz++GiZAaqVhQRYaAz++GiZAaqVhQWv4p7++GiZAaqVhQWv4p7++GiZAaqVhQWv4p7++GiZAjghyQaiBPMAYUIdAjghyQaiBPMAYUIdAjghyQaiBPMAYUIdAjghyQaiBPMAYUIdAjghyQfhLCUAYUIdAjghyQfhLCUAYUIdAjghyQfhLCUAYUIdAjghyQfhLCUAYUIdAjghyQaiBPMDAtVy/jghyQaiBPMDAtVy/jghyQaiBPMDAtVy/jghyQfhLCUDAtVy/jghyQfhLCUDAtVy/jghyQfhLCUDAtVy/JpUgQaiBPMAYUIdAJpUgQaiBPMAYUIdAJpUgQaiBPMAYUIdAJpUgQaiBPMAYUIdAJpUgQfhLCUAYUIdAJpUgQfhLCUAYUIdAJpUgQfhLCUAYUIdAJpUgQfhLCUAYUIdAJpUgQaiBPMDAtVy/JpUgQaiBPMDAtVy/JpUgQaiBPMDAtVy/JpUgQaiBPMDAtVy/JpUgQfhLCUDAtVy/JpUgQfhLCUDAtVy/JpUgQfhLCUDAtVy/JpUgQfhLCUDAtVy/AghRQPhLCUDAtVy/AghRQPhLCUDAtVy/AghRQPhLCUDAtVy/AghRQPhLCUDAtVy/AghRQKiBPMDAtVy/AghRQKiBPMDAtVy/AghRQKiBPMDAtVy/AghRQKiBPMDAtVy/AghRQPhLCUAYUIdAAghRQPhLCUAYUIdAAghRQPhLCUAYUIdAAghRQPhLCUAYUIdAAghRQKiBPMAYUIdAAghRQKiBPMAYUIdAAghRQKiBPMAYUIdAAghRQKiBPMAYUIdAaLUFQfhLCUDAtVy/aLUFQfhLCUDAtVy/aLUFQfhLCUDAtVy/aLUFQfhLCUDAtVy/aLUFQaiBPMDAtVy/aLUFQaiBPMDAtVy/aLUFQaiBPMDAtVy/aLUFQaiBPMDAtVy/aLUFQfhLCUAYUIdAaLUFQfhLCUAYUIdAaLUFQfhLCUAYUIdAaLUFQfhLCUAYUIdAaLUFQaiBPMAYUIdAaLUFQaiBPMAYUIdAaLUFQaiBPMAYUIdAaLUFQaiBPMAYUIdAjghyQWIocD9V1+XAjghyQWIocD9V1+XAjghyQWIocD9V1+XAjghyQZF/3r9V1+XAjghyQZF/3r9V1+XAjghyQZF/3r9V1+XAJpUgQZF/3r9V1+XAJpUgQZF/3r9V1+XAJpUgQZF/3r9V1+XAJpUgQWIocD9V1+XAJpUgQWIocD9V1+XAJpUgQWIocD9V1+XAAghRQGIocD9V1+XAAghRQGIocD9V1+XAAghRQGIocD9V1+XAAghRQJF/3r9V1+XAAghRQJF/3r9V1+XAAghRQJF/3r9V1+XAaLUFQWIocD9V1+XAaLUFQWIocD9V1+XAaLUFQWIocD9V1+XAaLUFQZF/3r9V1+XAaLUFQZF/3r9V1+XAaLUFQZF/3r9V1+XAjghyQc/e2b8uFDBBjghyQc/e2b8uFDBBjghyQc/e2b8uFDBBjghyQd7mZj8uFDBBjghyQd7mZj8uFDBBjghyQd7mZj8uFDBBJpUgQd7mZj8uFDBBJpUgQd7mZj8uFDBBJpUgQd7mZj8uFDBBJpUgQc/e2b8uFDBBJpUgQc/e2b8uFDBBJpUgQc/e2b8uFDBBAghRQN7mZj8uFDBBAghRQN7mZj8uFDBBAghRQN7mZj8uFDBBaLUFQd7mZj8uFDBBaLUFQd7mZj8uFDBBaLUFQd7mZj8uFDBBaLUFQc/e2b8uFDBBaLUFQc/e2b8uFDBBaLUFQc/e2b8uFDBBAghRQM/e2b8uFDBBAghRQM/e2b8uFDBBAghRQM/e2b8uFDBBAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAvwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAvwAAAAAAAACA//9/vwAAAAB8Gx20AAAAAAAAgL8AAACAAAAAAATMe79Cyzg+AACAvwAAAAAAAACA//9/vwAAAAB8Gx20AAAAAATMez9Cyzg+AAAAAAAAgD8AAACAAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAG10e7+OGkC+AACAvwAAAAAAAACAAAAAAG10ez+OGkC+AAAAAAAAgD8AAACAAAAAAAAAgL8AAACAAAAAAATMe79Cyzg+//9/PwAAAAB8G52zAACAPwAAAAAAAACAAAAAAATMez9Cyzg+AAAAAAAAgD8AAACA//9/PwAAAAB8G52zAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAG10e7+OGkC+AACAPwAAAADW36azAACAPwAAAAAAAACAAAAAAG10ez+OGkC+AAAAAAAAgD8AAACAAACAPwAAAADW36azAACAPwAAAAAAAACAAAAAAG10ez+PGkC+AAAAAP//fz8AAACAAACAPwAAAAAAAACAAAAAAP//f78AAACAAAAAAG10e7+PGkC+AACAPwAAAAAAAACAAAAAAAPMez9Eyzg+AAAAAP//fz8AAACA//9/PwAAAAB8Gx0zAACAPwAAAAAAAACAAAAAAP//f78AAACAAAAAAAPMe79Eyzg+//9/PwAAAAB8Gx0zAACAPwAAAAAAAACAAACAvwAAAADW36azAACAvwAAAAAAAACAAAAAAG10ez+PGkC+AAAAAP//fz8AAACAAACAvwAAAADW36azAACAvwAAAAAAAACAAAAAAP//f78AAACAAAAAAG10e7+PGkC+AACAvwAAAAAAAACA//9/vwAAAAB8G50zAAAAAAPMez9Eyzg+AAAAAP//fz8AAACAAACAvwAAAAAAAACA//9/vwAAAAB8G50zAAAAAP//f78AAACAAAAAAAPMe79Eyzg+AAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAACAvwAAAAAAAACAAAAAAAAAAAAAAIC/AAAAAG10ez+OGkC+AACAvwAAAAAAAACAAAAAAG10e7+OGkC+AAAAAAAAAAAAAIC/AAAAAG10e7+OGkC+AAAAAAAAAAAAAIC/AACAPwAAAADW36azAAAAAAAAAAAAAIC/AAAAAG10ez+OGkC+AACAPwAAAADW36azAAAAAAAAAAAAAIC/AAAAAG10ez+PGkC+AACAPwAAAAAAAACAAAAAAG10e7+PGkC+AAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAACAvwAAAADW36azAAAAAAAAAAAAAIC/AAAAAG10ez+PGkC+AACAvwAAAADW36azAAAAAG10e7+PGkC+AAAAAAAAAAAAAIC///9/vwAAAAB8Gx20AAAAAATMe79Cyzg+AAAAAAAAAAAAAIA///9/vwAAAAB8Gx20AAAAAAAAAAAAAIA/AAAAAATMez9Cyzg+AAAAAAAAAAAAAIA/AAAAAATMez9Cyzg+//9/PwAAAAB8G52zAAAAAATMe79Cyzg+AAAAAAAAAAAAAIA///9/PwAAAAB8G52zAAAAAAAAAAAAAIA/AAAAAAPMez9Eyzg+//9/PwAAAAB8Gx0z//9/vwAAAAB8G50zAAAAAAAAAAAAAIA/AAAAAAPMez9Eyzg+//9/vwAAAAB8G50zAAAAAAPMe79Eyzg+AAAAAAAAAAAAAIA/AAAAAAPMe79Eyzg+AAAAAAAAAAAAAIA///9/PwAAAAB8Gx0zAAAAAAAAAAAAAIC/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIC/AACAPwAAAAAAAACAAAAAAAAAAAAAAIA/AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAAAAAAAAAIA/AACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAATMe79Cyzg+//9/PwAAAAB8G52zAACAPwAAAAAAAACAAAAAAATMez9Cyzg+AAAAAAAAgD8AAACA//9/PwAAAAB8G52zAACAPwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAG10e7+OGkC+AACAPwAAAAAAAACAAAAAAG10ez+OGkC+AAAAAAAAgD8AAACAAACAPwAAAAAAAACAAACAvwAAAAAAAACA//9/vwAAAAB8Gx2zAAAAAAAAgL8AAACAAAAAAATMe79Cyzg+AACAvwAAAAAAAACA//9/vwAAAAB8Gx2zAAAAAATMez9Cyzg+AAAAAAAAgD8AAACAAACAvwAAAADW36azAACAvwAAAAAAAACAAAAAAAAAgL8AAACAAAAAAG10e7+OGkC+AACAvwAAAADW36azAACAvwAAAAAAAACAAAAAAG10ez+OGkC+AAAAAAAAgD8AAACAAACAvwAAAADW3yayAACAvwAAAAAAAACAAAAAAG10ez+PGkC+AAAAAP//fz8AAACAAACAvwAAAADW3yayAACAvwAAAAAAAACAAAAAAP//f78AAACAAAAAAG10e7+PGkC+AACAvwAAAAAAAACA//9/vwAAAAB8Gx0zAAAAAAPMez9Eyzg+AAAAAP//fz8AAACAAACAvwAAAAAAAACA//9/vwAAAAB8Gx0zAAAAAP//f78AAACAAAAAAAPMe79Eyzg+AAAAAG10ez+PGkC+AAAAAP//fz8AAACAAACAPwAAAADW36azAACAPwAAAAAAAACAAAAAAP//f78AAACAAAAAAG10e7+PGkC+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EorAPsNTwz4e2a0+NGfkPgAA0D3DU8M+EorAPgQATgBIAAQASAAKAEkASwAHAEkABwALAFAAUwBNAFAATQBKAAUAAQBRAAUAUQBPAAkABgAAAAkAAAADAAwAEAAXAAwAFwAUABQAFwBUABQAVABXACUAKQAhACUAIQAdADAAQgB9ADAAfQB5ABUAIgAaABUAGgAOACcAGQATACcAEwAfACsAOwBDACsAQwAxAD4ALQA0AD4ANABGADYAMgB6ADYAegCDAC8ALAAzAC8AMwA3ABgAJgBeABgAXgBWAEQAQAA5AEQAOQA9AFIAAgAIAFIACABMAFkAVQBdAFkAXQBbAGsAZwBgAGsAYABkAC4APwBqAC4AagBjACwALwBlACwAZQBiACgAJABcACgAXABfACMAFgBYACMAWABaADwAOABmADwAZgBpADoAKgBhADoAYQBoAHYAcgBwAHYAcABuAIIAeAB8AIIAfACAABEADQBsABEAbABvAA8AGwB1AA8AdQBtAEcANQCBAEcAgQB/AEEARQB+AEEAfgB7AB4AEgBxAB4AcQBzABwAIAB0ABwAdAB3AIcAjQBIAIcASABOAEkAjgCKAEkAigBLAIgATwBRAIgAUQCEAI8AiQCGAI8AhgCMAJMAmgCdAJMAnQCXAJoA0wDQAJoA0ACdAKcAngCiAKcAogCrALgA9AD2ALgA9gDGAJgAkACgAJgAoACoAK0ApQCVAK0AlQCcALEAuQDHALEAxwC/AMIAygC8AMIAvAC0ALsA+wDyALsA8gC3ALMAugC2ALMAtgCvAJsAzwDZAJsA2QCsAM0AxQDBAM0AwQDJAFIATACLAFIAiwCFANIA1gDYANIA2ADOAOQA3wDbAOQA2wDgALUA3gDjALUA4wDDAK4A2gDdAK4A3QCyAKoA1wDUAKoA1ACmAKkA1QDRAKkA0QCZAMQA5QDiAMQA4gDAAL4A4QDcAL4A3ACwAPEA5wDpAPEA6QDtAP0A+QD1AP0A9QDzAJYA6wDoAJYA6ACSAJEA5gDwAJEA8AChAMsA+AD8AMsA/AC9AMgA9wD6AMgA+gDMAKQA7gDqAKQA6gCUAJ8A7wDsAJ8A7ACjAIlQTkcNChoKAAAADUlIRFIAAAAEAAAABAgGAAAAqfGefgAAAAFzUkdCAK7OHOkAAABNSURBVAiZY2RgYPhvaDWRQfrXEoZUET0GFo+QEAY9RkEGGcZvDJf0cxhYLHRUGC6IrGMI/+/BcEiHjYFFRtuSwVr2BMN5lisMcVKfGQA5WBF3NdL7TgAAAABJRU5ErkJgggA=" 136 | } 137 | ] 138 | } 139 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ##### Windows 2 | # Windows thumbnail cache files 3 | Thumbs.db 4 | Thumbs.db:encryptable 5 | ehthumbs.db 6 | ehthumbs_vista.db 7 | 8 | # Dump file 9 | *.stackdump 10 | 11 | # Folder config file 12 | [Dd]esktop.ini 13 | 14 | # Recycle Bin used on file shares 15 | $RECYCLE.BIN/ 16 | 17 | # Windows Installer files 18 | *.cab 19 | *.msi 20 | *.msix 21 | *.msm 22 | *.msp 23 | 24 | # Windows shortcuts 25 | *.lnk 26 | 27 | ##### Linux 28 | *~ 29 | 30 | # temporary files which can be created if a process still has a handle open of a deleted file 31 | .fuse_hidden* 32 | 33 | # KDE directory preferences 34 | .directory 35 | 36 | # Linux trash folder which might appear on any partition or disk 37 | .Trash-* 38 | 39 | # .nfs files are created when an open file is removed but is still being accessed 40 | .nfs* 41 | 42 | ##### MacOS 43 | # General 44 | .DS_Store 45 | .AppleDouble 46 | .LSOverride 47 | 48 | # Thumbnails 49 | ._* 50 | 51 | # Files that might appear in the root of a volume 52 | .DocumentRevisions-V100 53 | .fseventsd 54 | .Spotlight-V100 55 | .TemporaryItems 56 | .Trashes 57 | .VolumeIcon.icns 58 | .com.apple.timemachine.donotpresent 59 | 60 | # Directories potentially created on remote AFP share 61 | .AppleDB 62 | .AppleDesktop 63 | Network Trash Folder 64 | Temporary Items 65 | .apdisk 66 | 67 | ##### Android 68 | # Built application files 69 | *.apk 70 | *.ap_ 71 | *.aab 72 | 73 | # Files for the ART/Dalvik VM 74 | *.dex 75 | 76 | # Java class files 77 | *.class 78 | 79 | # Generated files 80 | bin/ 81 | gen/ 82 | out/ 83 | # Uncomment the following line in case you need and you don't have the release build type files in your app 84 | # release/ 85 | 86 | # Gradle files 87 | .gradle/ 88 | build/ 89 | 90 | # Local configuration file (sdk path, etc) 91 | local.properties 92 | 93 | # Proguard folder generated by Eclipse 94 | proguard/ 95 | 96 | # Log Files 97 | *.log 98 | 99 | # Android Studio Navigation editor temp files 100 | .navigation/ 101 | 102 | # Android Studio captures folder 103 | captures/ 104 | 105 | # IntelliJ 106 | *.iml 107 | .idea/workspace.xml 108 | .idea/tasks.xml 109 | .idea/gradle.xml 110 | .idea/assetWizardSettings.xml 111 | .idea/dictionaries 112 | .idea/libraries 113 | # Android Studio 3 in .gitignore file. 114 | .idea/caches 115 | .idea/modules.xml 116 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 117 | .idea/navEditor.xml 118 | 119 | # Keystore files 120 | # Uncomment the following lines if you do not want to check your keystore files in. 121 | #*.jks 122 | #*.keystore 123 | 124 | # External native build folder generated in Android Studio 2.2 and later 125 | .externalNativeBuild 126 | 127 | # Google Services (e.g. APIs or Firebase) 128 | # google-services.json 129 | 130 | # Freeline 131 | freeline.py 132 | freeline/ 133 | freeline_project_description.json 134 | 135 | # fastlane 136 | fastlane/report.xml 137 | fastlane/Preview.html 138 | fastlane/screenshots 139 | fastlane/test_output 140 | fastlane/readme.md 141 | 142 | # Version control 143 | vcs.xml 144 | 145 | # lint 146 | lint/intermediates/ 147 | lint/generated/ 148 | lint/outputs/ 149 | lint/tmp/ 150 | # lint/reports/ 151 | 152 | ##### Backup 153 | *.bak 154 | *.gho 155 | *.ori 156 | *.orig 157 | *.tmp 158 | 159 | ##### GPG 160 | secring.* 161 | 162 | ##### Dropbox 163 | # Dropbox settings and caches 164 | .dropbox 165 | .dropbox.attr 166 | .dropbox.cache 167 | 168 | ##### SynopsysVCS 169 | # Waveform formats 170 | *.vcd 171 | *.vpd 172 | *.evcd 173 | *.fsdb 174 | 175 | # Default name of the simulation executable. A different name can be 176 | # specified with this switch (the associated daidir database name is 177 | # also taken from here): -o / 178 | simv 179 | 180 | # Generated for Verilog and VHDL top configs 181 | simv.daidir/ 182 | simv.db.dir/ 183 | 184 | # Infrastructure necessary to co-simulate SystemC models with 185 | # Verilog/VHDL models. An alternate directory may be specified with this 186 | # switch: -Mdir= 187 | csrc/ 188 | 189 | # Log file - the following switch allows to specify the file that will be 190 | # used to write all messages from simulation: -l 191 | *.log 192 | 193 | # Coverage results (generated with urg) and database location. The 194 | # following switch can also be used: urg -dir .vdb 195 | simv.vdb/ 196 | urgReport/ 197 | 198 | # DVE and UCLI related files. 199 | DVEfiles/ 200 | ucli.key 201 | 202 | # When the design is elaborated for DirectC, the following file is created 203 | # with declarations for C/C++ functions. 204 | vc_hdrs.h 205 | 206 | ##### SVN 207 | .svn/ 208 | 209 | ##### Mercurial 210 | .hg/ 211 | .hgignore 212 | .hgsigs 213 | .hgsub 214 | .hgsubstate 215 | .hgtags 216 | 217 | ##### Bazaar 218 | .bzr/ 219 | .bzrignore 220 | 221 | ##### CVS 222 | /CVS/* 223 | **/CVS/* 224 | .cvsignore 225 | */.cvsignore 226 | 227 | ##### TortoiseGit 228 | # Project-level settings 229 | /.tgitconfig 230 | 231 | ##### PuTTY 232 | # Private key 233 | *.ppk 234 | 235 | ##### Vim 236 | # Swap 237 | [._]*.s[a-v][a-z] 238 | !*.svg # comment out if you don't need vector files 239 | [._]*.sw[a-p] 240 | [._]s[a-rt-v][a-z] 241 | [._]ss[a-gi-z] 242 | [._]sw[a-p] 243 | 244 | # Session 245 | Session.vim 246 | Sessionx.vim 247 | 248 | # Temporary 249 | .netrwhist 250 | *~ 251 | # Auto-generated tag files 252 | tags 253 | # Persistent undo 254 | [._]*.un~ 255 | 256 | ##### Emacs 257 | # -*- mode: gitignore; -*- 258 | *~ 259 | \#*\# 260 | /.emacs.desktop 261 | /.emacs.desktop.lock 262 | *.elc 263 | auto-save-list 264 | tramp 265 | .\#* 266 | 267 | # Org-mode 268 | .org-id-locations 269 | *_archive 270 | 271 | # flymake-mode 272 | *_flymake.* 273 | 274 | # eshell files 275 | /eshell/history 276 | /eshell/lastdir 277 | 278 | # elpa packages 279 | /elpa/ 280 | 281 | # reftex files 282 | *.rel 283 | 284 | # AUCTeX auto folder 285 | /auto/ 286 | 287 | # cask packages 288 | .cask/ 289 | dist/ 290 | 291 | # Flycheck 292 | flycheck_*.el 293 | 294 | # server auth directory 295 | /server/ 296 | 297 | # projectiles files 298 | .projectile 299 | 300 | # directory configuration 301 | .dir-locals.el 302 | 303 | # network security 304 | /network-security.data 305 | 306 | ##### SublimeText 307 | # Cache files for Sublime Text 308 | *.tmlanguage.cache 309 | *.tmPreferences.cache 310 | *.stTheme.cache 311 | 312 | # Workspace files are user-specific 313 | *.sublime-workspace 314 | 315 | # Project files should be checked into the repository, unless a significant 316 | # proportion of contributors will probably not be using Sublime Text 317 | # *.sublime-project 318 | 319 | # SFTP configuration file 320 | sftp-config.json 321 | sftp-config-alt*.json 322 | 323 | # Package control specific files 324 | Package Control.last-run 325 | Package Control.ca-list 326 | Package Control.ca-bundle 327 | Package Control.system-ca-bundle 328 | Package Control.cache/ 329 | Package Control.ca-certs/ 330 | Package Control.merged-ca-bundle 331 | Package Control.user-ca-bundle 332 | oscrypto-ca-bundle.crt 333 | bh_unicode_properties.cache 334 | 335 | # Sublime-github package stores a github token in this file 336 | # https://packagecontrol.io/packages/sublime-github 337 | GitHub.sublime-settings 338 | 339 | ##### Notepad++ 340 | # Notepad++ backups # 341 | *.bak 342 | 343 | ##### TextMate 344 | *.tmproj 345 | *.tmproject 346 | tmtags 347 | 348 | ##### VisualStudioCode 349 | .vscode/* 350 | !.vscode/settings.json 351 | !.vscode/tasks.json 352 | !.vscode/launch.json 353 | !.vscode/extensions.json 354 | *.code-workspace 355 | 356 | # Local History for Visual Studio Code 357 | .history/ 358 | 359 | ##### NetBeans 360 | **/nbproject/private/ 361 | **/nbproject/Makefile-*.mk 362 | **/nbproject/Package-*.bash 363 | build/ 364 | nbbuild/ 365 | dist/ 366 | nbdist/ 367 | .nb-gradle/ 368 | 369 | ##### JetBrains 370 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider 371 | # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 372 | 373 | # User-specific stuff 374 | .idea/**/workspace.xml 375 | .idea/**/tasks.xml 376 | .idea/**/usage.statistics.xml 377 | .idea/**/dictionaries 378 | .idea/**/shelf 379 | 380 | # Generated files 381 | .idea/**/contentModel.xml 382 | 383 | # Sensitive or high-churn files 384 | .idea/**/dataSources/ 385 | .idea/**/dataSources.ids 386 | .idea/**/dataSources.local.xml 387 | .idea/**/sqlDataSources.xml 388 | .idea/**/dynamic.xml 389 | .idea/**/uiDesigner.xml 390 | .idea/**/dbnavigator.xml 391 | 392 | # Gradle 393 | .idea/**/gradle.xml 394 | .idea/**/libraries 395 | 396 | # Gradle and Maven with auto-import 397 | # When using Gradle or Maven with auto-import, you should exclude module files, 398 | # since they will be recreated, and may cause churn. Uncomment if using 399 | # auto-import. 400 | # .idea/artifacts 401 | # .idea/compiler.xml 402 | # .idea/jarRepositories.xml 403 | # .idea/modules.xml 404 | # .idea/*.iml 405 | # .idea/modules 406 | # *.iml 407 | # *.ipr 408 | 409 | # CMake 410 | cmake-build-*/ 411 | 412 | # Mongo Explorer plugin 413 | .idea/**/mongoSettings.xml 414 | 415 | # File-based project format 416 | *.iws 417 | 418 | # IntelliJ 419 | out/ 420 | 421 | # mpeltonen/sbt-idea plugin 422 | .idea_modules/ 423 | 424 | # JIRA plugin 425 | atlassian-ide-plugin.xml 426 | 427 | # Cursive Clojure plugin 428 | .idea/replstate.xml 429 | 430 | # Crashlytics plugin (for Android Studio and IntelliJ) 431 | com_crashlytics_export_strings.xml 432 | crashlytics.properties 433 | crashlytics-build.properties 434 | fabric.properties 435 | 436 | # Editor-based Rest Client 437 | .idea/httpRequests 438 | 439 | # Android studio 3.1+ serialized cache file 440 | .idea/caches/build_file_checksums.ser 441 | 442 | ##### Eclipse 443 | .metadata 444 | bin/ 445 | tmp/ 446 | *.tmp 447 | *.bak 448 | *.swp 449 | *~.nib 450 | local.properties 451 | .settings/ 452 | .loadpath 453 | .recommenders 454 | 455 | # External tool builders 456 | .externalToolBuilders/ 457 | 458 | # Locally stored "Eclipse launch configurations" 459 | *.launch 460 | 461 | # PyDev specific (Python IDE for Eclipse) 462 | *.pydevproject 463 | 464 | # CDT-specific (C/C++ Development Tooling) 465 | .cproject 466 | 467 | # CDT- autotools 468 | .autotools 469 | 470 | # Java annotation processor (APT) 471 | .factorypath 472 | 473 | # PDT-specific (PHP Development Tools) 474 | .buildpath 475 | 476 | # sbteclipse plugin 477 | .target 478 | 479 | # Tern plugin 480 | .tern-project 481 | 482 | # TeXlipse plugin 483 | .texlipse 484 | 485 | # STS (Spring Tool Suite) 486 | .springBeans 487 | 488 | # Code Recommenders 489 | .recommenders/ 490 | 491 | # Annotation Processing 492 | .apt_generated/ 493 | .apt_generated_test/ 494 | 495 | # Scala IDE specific (Scala & Java development for Eclipse) 496 | .cache-main 497 | .scala_dependencies 498 | .worksheet 499 | 500 | # Uncomment this line if you wish to ignore the project description file. 501 | # Typically, this file would be tracked if it contains build/dependency configurations: 502 | #.project 503 | 504 | ##### Qt 505 | # C++ objects and libs 506 | *.slo 507 | *.lo 508 | *.o 509 | *.a 510 | *.la 511 | *.lai 512 | *.so 513 | *.so.* 514 | *.dll 515 | *.dylib 516 | 517 | # Qt-es 518 | object_script.*.Release 519 | object_script.*.Debug 520 | *_plugin_import.cpp 521 | /.qmake.cache 522 | /.qmake.stash 523 | *.pro.user 524 | *.pro.user.* 525 | *.qbs.user 526 | *.qbs.user.* 527 | *.moc 528 | moc_*.cpp 529 | moc_*.h 530 | qrc_*.cpp 531 | ui_*.h 532 | *.qmlc 533 | *.jsc 534 | Makefile* 535 | *build-* 536 | *.qm 537 | *.prl 538 | 539 | # Qt unit tests 540 | target_wrapper.* 541 | 542 | # QtCreator 543 | *.autosave 544 | 545 | # QtCreator Qml 546 | *.qmlproject.user 547 | *.qmlproject.user.* 548 | 549 | # QtCreator CMake 550 | CMakeLists.txt.user* 551 | 552 | # QtCreator 4.8< compilation database 553 | compile_commands.json 554 | 555 | # QtCreator local machine specific files for imported projects 556 | *creator.user* 557 | 558 | ##### VisualStudio 559 | ##### VisualStudio 560 | ## Ignore Visual Studio temporary files, build results, and 561 | ## files generated by popular Visual Studio add-ons. 562 | ## 563 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 564 | 565 | # User-specific files 566 | *.rsuser 567 | *.suo 568 | *.user 569 | *.userosscache 570 | *.sln.docstates 571 | 572 | # User-specific files (MonoDevelop/Xamarin Studio) 573 | *.userprefs 574 | 575 | # Mono auto generated files 576 | mono_crash.* 577 | 578 | # Build results 579 | [Dd]ebug/ 580 | [Dd]ebugPublic/ 581 | [Rr]elease/ 582 | [Rr]eleases/ 583 | x64/ 584 | x86/ 585 | [Ww][Ii][Nn]32/ 586 | [Aa][Rr][Mm]/ 587 | [Aa][Rr][Mm]64/ 588 | bld/ 589 | [Bb]in/ 590 | [Oo]bj/ 591 | [Ll]og/ 592 | [Ll]ogs/ 593 | 594 | # Visual Studio 2015/2017 cache/options directory 595 | .vs/ 596 | # Uncomment if you have tasks that create the project's static files in wwwroot 597 | #wwwroot/ 598 | 599 | # Visual Studio 2017 auto generated files 600 | Generated\ Files/ 601 | 602 | # MSTest test Results 603 | [Tt]est[Rr]esult*/ 604 | [Bb]uild[Ll]og.* 605 | 606 | # NUnit 607 | *.VisualState.xml 608 | TestResult.xml 609 | nunit-*.xml 610 | 611 | # Build Results of an ATL Project 612 | [Dd]ebugPS/ 613 | [Rr]eleasePS/ 614 | dlldata.c 615 | 616 | # Benchmark Results 617 | BenchmarkDotNet.Artifacts/ 618 | 619 | # .NET Core 620 | project.lock.json 621 | project.fragment.lock.json 622 | artifacts/ 623 | 624 | # ASP.NET Scaffolding 625 | ScaffoldingReadMe.txt 626 | 627 | # StyleCop 628 | StyleCopReport.xml 629 | 630 | # Files built by Visual Studio 631 | *_i.c 632 | *_p.c 633 | *_h.h 634 | *.ilk 635 | *.meta 636 | *.obj 637 | *.iobj 638 | *.pch 639 | *.pdb 640 | *.ipdb 641 | *.pgc 642 | *.pgd 643 | *.rsp 644 | *.sbr 645 | *.tlb 646 | *.tli 647 | *.tlh 648 | *.tmp 649 | *.tmp_proj 650 | *_wpftmp.csproj 651 | *.log 652 | *.vspscc 653 | *.vssscc 654 | .builds 655 | *.pidb 656 | *.svclog 657 | *.scc 658 | 659 | # Chutzpah Test files 660 | _Chutzpah* 661 | 662 | # Visual C++ cache files 663 | ipch/ 664 | *.aps 665 | *.ncb 666 | *.opendb 667 | *.opensdf 668 | *.sdf 669 | *.cachefile 670 | *.VC.db 671 | *.VC.VC.opendb 672 | 673 | # Visual Studio profiler 674 | *.psess 675 | *.vsp 676 | *.vspx 677 | *.sap 678 | 679 | # Visual Studio Trace Files 680 | *.e2e 681 | 682 | # TFS 2012 Local Workspace 683 | $tf/ 684 | 685 | # Guidance Automation Toolkit 686 | *.gpState 687 | 688 | # ReSharper is a .NET coding add-in 689 | _ReSharper*/ 690 | *.[Rr]e[Ss]harper 691 | *.DotSettings.user 692 | 693 | # TeamCity is a build add-in 694 | _TeamCity* 695 | 696 | # DotCover is a Code Coverage Tool 697 | *.dotCover 698 | 699 | # AxoCover is a Code Coverage Tool 700 | .axoCover/* 701 | !.axoCover/settings.json 702 | 703 | # Coverlet is a free, cross platform Code Coverage Tool 704 | coverage*[.json, .xml, .info] 705 | 706 | # Visual Studio code coverage results 707 | *.coverage 708 | *.coveragexml 709 | 710 | # NCrunch 711 | _NCrunch_* 712 | .*crunch*.local.xml 713 | nCrunchTemp_* 714 | 715 | # MightyMoose 716 | *.mm.* 717 | AutoTest.Net/ 718 | 719 | # Web workbench (sass) 720 | .sass-cache/ 721 | 722 | # Installshield output folder 723 | [Ee]xpress/ 724 | 725 | # DocProject is a documentation generator add-in 726 | DocProject/buildhelp/ 727 | DocProject/Help/*.HxT 728 | DocProject/Help/*.HxC 729 | DocProject/Help/*.hhc 730 | DocProject/Help/*.hhk 731 | DocProject/Help/*.hhp 732 | DocProject/Help/Html2 733 | DocProject/Help/html 734 | 735 | # Click-Once directory 736 | publish/ 737 | 738 | # Publish Web Output 739 | *.[Pp]ublish.xml 740 | *.azurePubxml 741 | # Note: Comment the next line if you want to checkin your web deploy settings, 742 | # but database connection strings (with potential passwords) will be unencrypted 743 | *.pubxml 744 | *.publishproj 745 | 746 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 747 | # checkin your Azure Web App publish settings, but sensitive information contained 748 | # in these scripts will be unencrypted 749 | PublishScripts/ 750 | 751 | # NuGet Packages 752 | *.nupkg 753 | # NuGet Symbol Packages 754 | *.snupkg 755 | # The packages folder can be ignored because of Package Restore 756 | **/[Pp]ackages/* 757 | # except build/, which is used as an MSBuild target. 758 | !**/[Pp]ackages/build/ 759 | # Uncomment if necessary however generally it will be regenerated when needed 760 | #!**/[Pp]ackages/repositories.config 761 | # NuGet v3's project.json files produces more ignorable files 762 | *.nuget.props 763 | *.nuget.targets 764 | 765 | # Microsoft Azure Build Output 766 | csx/ 767 | *.build.csdef 768 | 769 | # Microsoft Azure Emulator 770 | ecf/ 771 | rcf/ 772 | 773 | # Windows Store app package directories and files 774 | AppPackages/ 775 | BundleArtifacts/ 776 | Package.StoreAssociation.xml 777 | _pkginfo.txt 778 | *.appx 779 | *.appxbundle 780 | *.appxupload 781 | 782 | # Visual Studio cache files 783 | # files ending in .cache can be ignored 784 | *.[Cc]ache 785 | # but keep track of directories ending in .cache 786 | !?*.[Cc]ache/ 787 | 788 | # Others 789 | ClientBin/ 790 | ~$* 791 | *~ 792 | *.dbmdl 793 | *.dbproj.schemaview 794 | *.jfm 795 | *.pfx 796 | *.publishsettings 797 | orleans.codegen.cs 798 | 799 | # Including strong name files can present a security risk 800 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 801 | #*.snk 802 | 803 | # Since there are multiple workflows, uncomment next line to ignore bower_components 804 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 805 | #bower_components/ 806 | 807 | # RIA/Silverlight projects 808 | Generated_Code/ 809 | 810 | # Backup & report files from converting an old project file 811 | # to a newer Visual Studio version. Backup files are not needed, 812 | # because we have git ;-) 813 | _UpgradeReport_Files/ 814 | Backup*/ 815 | UpgradeLog*.XML 816 | UpgradeLog*.htm 817 | ServiceFabricBackup/ 818 | *.rptproj.bak 819 | 820 | # SQL Server files 821 | *.mdf 822 | *.ldf 823 | *.ndf 824 | 825 | # Business Intelligence projects 826 | *.rdl.data 827 | *.bim.layout 828 | *.bim_*.settings 829 | *.rptproj.rsuser 830 | *- [Bb]ackup.rdl 831 | *- [Bb]ackup ([0-9]).rdl 832 | *- [Bb]ackup ([0-9][0-9]).rdl 833 | 834 | # Microsoft Fakes 835 | FakesAssemblies/ 836 | 837 | # GhostDoc plugin setting file 838 | *.GhostDoc.xml 839 | 840 | # Node.js Tools for Visual Studio 841 | .ntvs_analysis.dat 842 | node_modules/ 843 | 844 | # Visual Studio 6 build log 845 | *.plg 846 | 847 | # Visual Studio 6 workspace options file 848 | *.opt 849 | 850 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 851 | *.vbw 852 | 853 | # Visual Studio LightSwitch build output 854 | **/*.HTMLClient/GeneratedArtifacts 855 | **/*.DesktopClient/GeneratedArtifacts 856 | **/*.DesktopClient/ModelManifest.xml 857 | **/*.Server/GeneratedArtifacts 858 | **/*.Server/ModelManifest.xml 859 | _Pvt_Extensions 860 | 861 | # Paket dependency manager 862 | .paket/paket.exe 863 | paket-files/ 864 | 865 | # FAKE - F# Make 866 | .fake/ 867 | 868 | # CodeRush personal settings 869 | .cr/personal 870 | 871 | # Python Tools for Visual Studio (PTVS) 872 | __pycache__/ 873 | *.pyc 874 | 875 | # Cake - Uncomment if you are using it 876 | # tools/** 877 | # !tools/packages.config 878 | 879 | # Tabs Studio 880 | *.tss 881 | 882 | # Telerik's JustMock configuration file 883 | *.jmconfig 884 | 885 | # BizTalk build output 886 | *.btp.cs 887 | *.btm.cs 888 | *.odx.cs 889 | *.xsd.cs 890 | 891 | # OpenCover UI analysis results 892 | OpenCover/ 893 | 894 | # Azure Stream Analytics local run output 895 | ASALocalRun/ 896 | 897 | # MSBuild Binary and Structured Log 898 | *.binlog 899 | 900 | # NVidia Nsight GPU debugger configuration file 901 | *.nvuser 902 | 903 | # MFractors (Xamarin productivity tool) working folder 904 | .mfractor/ 905 | 906 | # Local History for Visual Studio 907 | .localhistory/ 908 | 909 | # BeatPulse healthcheck temp database 910 | healthchecksdb 911 | 912 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 913 | MigrationBackup/ 914 | 915 | # Ionide (cross platform F# VS Code tools) working folder 916 | .ionide/ 917 | 918 | # Fody - auto-generated XML schema 919 | FodyWeavers.xsd 920 | 921 | ##### Gradle 922 | .gradle 923 | **/build/ 924 | !src/**/build/ 925 | 926 | # Ignore Gradle GUI config 927 | gradle-app.setting 928 | 929 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 930 | !gradle-wrapper.jar 931 | 932 | # Cache of project 933 | .gradletasknamecache 934 | 935 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 936 | # gradle/wrapper/gradle-wrapper.properties 937 | 938 | ##### CMake 939 | CMakeLists.txt.user 940 | CMakeCache.txt 941 | CMakeFiles 942 | CMakeScripts 943 | Testing 944 | Makefile 945 | cmake_install.cmake 946 | install_manifest.txt 947 | compile_commands.json 948 | CTestTestfile.cmake 949 | _deps 950 | 951 | ##### C++ 952 | # Prerequisites 953 | *.d 954 | 955 | # Compiled Object files 956 | *.slo 957 | *.lo 958 | *.o 959 | *.obj 960 | 961 | # Precompiled Headers 962 | *.gch 963 | *.pch 964 | 965 | # Compiled Dynamic libraries 966 | *.so 967 | *.dylib 968 | *.dll 969 | 970 | # Fortran module files 971 | *.mod 972 | *.smod 973 | 974 | # Compiled Static libraries 975 | *.lai 976 | *.la 977 | *.a 978 | *.lib 979 | 980 | # Executables 981 | *.exe 982 | *.out 983 | *.app 984 | 985 | # C/C++ binary extension file 986 | *.bin 987 | 988 | ##### C 989 | # Prerequisites 990 | *.d 991 | 992 | # Object files 993 | *.o 994 | *.ko 995 | *.obj 996 | *.elf 997 | 998 | # Linker output 999 | *.ilk 1000 | *.map 1001 | *.exp 1002 | 1003 | # Precompiled Headers 1004 | *.gch 1005 | *.pch 1006 | 1007 | # Libraries 1008 | *.lib 1009 | *.a 1010 | *.la 1011 | *.lo 1012 | 1013 | # Shared objects (inc. Windows DLLs) 1014 | *.dll 1015 | *.so 1016 | *.so.* 1017 | *.dylib 1018 | 1019 | # Executables 1020 | *.exe 1021 | *.out 1022 | *.app 1023 | *.i*86 1024 | *.x86_64 1025 | *.hex 1026 | 1027 | # Debug files 1028 | *.dSYM/ 1029 | *.su 1030 | *.idb 1031 | *.pdb 1032 | 1033 | # Kernel Module Compile Results 1034 | *.mod* 1035 | *.cmd 1036 | .tmp_versions/ 1037 | modules.order 1038 | Module.symvers 1039 | Mkfile.old 1040 | dkms.conf 1041 | 1042 | # Raspberry Pi Pico Object file 1043 | *.uf2 1044 | # Raspberry Pi Pico disassembler file 1045 | *.dis 1046 | --------------------------------------------------------------------------------