Parcourir la source

fix camera view distance and matrix memory layout

Kolja Strohm il y a 3 semaines
Parent
commit
01197ddb20

+ 24 - 2
Camera3D.cpp

@@ -94,7 +94,7 @@ void Cam3D::scrollOut(float val)
 
 // Aligns the camera so that it points exactly at a specific point
 //  ziel: The point the camera should point at
-void Cam3D::setAusrichtung(Vec3<float> ziel)
+void Cam3D::setViewDirection(Vec3<float> ziel)
 {
     Vec3<float> target = (ziel - pos).normalize();
     if (Vec3<float>(0, target.y, target.z).getLength() == 0)
@@ -434,4 +434,26 @@ World3D* Cam3D::getWorld() const
 World3D* Cam3D::zWorld() const
 {
     return welt;
-}
+}
+
+void Framework::Cam3D::setMinDistance(float dist)
+{
+    this->minZ = dist;
+    updateMatrix();
+}
+
+float Framework::Cam3D::getMinDistance() const
+{
+    return minZ;
+}
+
+void Framework::Cam3D::setMaxDistance(float dist)
+{
+    this->maxZ = dist;
+    updateMatrix();
+}
+
+float Framework::Cam3D::getMaxDistance() const
+{
+    return maxZ;
+}

+ 9 - 1
Camera3D.h

@@ -78,7 +78,7 @@ namespace Framework
         DLLEXPORT void scrollOut(float val);
         //! Aligns the camera so that it points exactly at a specific point
         //! \param ziel The point the camera should point at
-        DLLEXPORT void setAusrichtung(Vec3<float> ziel);
+        DLLEXPORT void setViewDirection(Vec3<float> ziel);
         //! Sets the rotation of the camera around individual axes
         //! \param rotation The rotation around individual axes
         DLLEXPORT void setRotation(Vec3<float> rotation);
@@ -163,5 +163,13 @@ namespace Framework
         DLLEXPORT World3D* getWorld() const;
         //! Returns the world without increased reference counter
         DLLEXPORT World3D* zWorld() const;
+        //! Sets the minimum distance of the camera to the target point
+        DLLEXPORT void setMinDistance(float dist);
+        //! Returns the minimum distance of the camera to the target point
+        DLLEXPORT float getMinDistance() const;
+        //! Sets the maximum distance of the camera to the target point
+        DLLEXPORT void setMaxDistance(float dist);
+        //! Returns the maximum distance of the camera to the target point
+        DLLEXPORT float getMaxDistance() const;
     };
 } // namespace Framework

+ 2 - 0
Common.hlsl

@@ -4,6 +4,8 @@
 // and that its size must be declared in the corresponding
 // D3D12_RAYTRACING_SHADER_CONFIG pipeline subobjet.
 
+#pragma pack_matrix(row_major)
+
 struct [raypayload] HitInfo
 {
     float4 colorAndDistance : write(caller, closesthit, miss) : read(caller);

+ 3 - 4
DX12GraphicsApi.cpp

@@ -228,8 +228,8 @@ void Framework::DirectX12::renderKamera(
     tlas->endUpdate();
     settings.inverseProjection = zKamera->getInverseProjectionMatrix();
     settings.inverseView = zKamera->getInverseViewMatrix();
-    settings.minDistance = zKamera->zViewPort()->front;
-    settings.maxDistance = zKamera->zViewPort()->back;
+    settings.minDistance = zKamera->getMinDistance();
+    settings.maxDistance = zKamera->getMaxDistance();
     settings.useRays = 1;
     settings.renderGui = (int)guiVisible;
     rayGenSettingsBuffer->setData(&settings, 1);
@@ -241,8 +241,7 @@ void Framework::DirectX12::renderKamera(
         globalDescriptorHeap->updateTLASInput(
             3, DX12_SHADER_REGISTER_T_SHADER_RESOURCE, tlas);
     }
-    sbt->setGlobalDescriptorHeap(dynamic_cast<DX12GlobalDescriptorHeap*>(
-        globalDescriptorHeap->getThis()));
+    sbt->setGlobalDescriptorHeap(globalDescriptorHeap);
     sbt->endUpdate(device, directCommandQueue);
     ID3D12DescriptorHeap* heaps[] = {globalDescriptorHeap->zDescriptorHeap()};
     directCommandQueue->zCommandList()->SetDescriptorHeaps(1, heaps);

+ 10 - 7
DX12Shader.cpp

@@ -1409,14 +1409,17 @@ Framework::DX12ShaderBindingTable::~DX12ShaderBindingTable()
 void Framework::DX12ShaderBindingTable::setGlobalDescriptorHeap(
     DX12GlobalDescriptorHeap* zGlobalDescriptorHeap)
 {
-    if (this->globalDescriptorHeap)
+    if (this->globalDescriptorHeap != zGlobalDescriptorHeap)
     {
-        this->globalDescriptorHeap->release();
-    }
-    this->globalDescriptorHeap = zGlobalDescriptorHeap;
-    if (this->globalDescriptorHeap)
-    {
-        this->globalDescriptorHeap->getThis();
+        if (this->globalDescriptorHeap)
+        {
+            this->globalDescriptorHeap->release();
+        }
+        this->globalDescriptorHeap = zGlobalDescriptorHeap;
+        if (this->globalDescriptorHeap)
+        {
+            this->globalDescriptorHeap->getThis();
+        }
     }
 }
 

+ 269 - 0
Framework Tests/Camera3D.cpp

@@ -0,0 +1,269 @@
+#include "pch.h"
+
+#include "Camera3D.h"
+#include "CppUnitTest.h"
+#include "DX12GraphicsApi.h"
+#include "Globals.h"
+#include "Model3D.h"
+#include "RenderThread.h"
+#include "Screen.h"
+#include "Window.h"
+#include "World3D.h"
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace FrameworkTests
+{
+
+    TEST_CLASS (Cam3Tests)
+    {
+    public:
+        TEST_METHOD (TestProjection)
+        {
+            Framework::Cam3D cam;
+            cam.setPosition(Framework::Vec3<float>(0, 0, 0));
+            cam.setRotation(Framework::Vec3<float>(0, 0, 0));
+            Framework::Vec3<float> target(0, 1000, 0);
+            cam.setViewDirection(target);
+            Framework::Mat4<float> projection = cam.getProjectionMatrix();
+            Framework::Mat4<float> inverse = cam.getInverseProjectionMatrix();
+            Framework::Mat4<float> view = cam.getViewMatrix();
+            Framework::Mat4<float> iView = cam.getInverseViewMatrix();
+            for (int i = 0; i < 10; i++)
+            {
+                for (int j = 0; j < 10; j++)
+                {
+                    Framework::Vec4<float> origin(0, 0, 0, 1.f);
+                    Framework::Vec4<float> target(
+                        i / 5.f - 1.f, j / 5.f - 1.f, -1.f, 1.f);
+                    Framework::Vec4<float> transformedOrigin = iView * origin;
+                    Framework::Vec4<float> transformedTarget
+                        = iView * (inverse * target);
+                    Framework::Vec4<float> transformedDirection
+                        = transformedTarget - transformedOrigin;
+                }
+            }
+        }
+
+        TEST_METHOD (TestRaytracing)
+        {
+            Framework::initFramework();
+            Framework::setDebugDX(1);
+            Framework::NativeWindow window;
+            WNDCLASS wc = Framework::F_Normal(GetModuleHandle(0));
+            wc.lpszClassName = "Factory Craft";
+            window.create(WS_POPUPWINDOW, wc);
+            Framework::Monitor m = Framework::getMonitor(0);
+            window.setBounds(Framework::Point(m.x, m.y),
+                Framework::Point(m.width, m.height));
+            window.setDisplayMode(SW_SHOWNORMAL);
+            window.setPreCloseAction([&window](void* p, void* f) {
+                Framework::StopMessageLoop(window.getWindowHandle());
+            });
+            window.setMouseAction(Framework::_ret1ME);
+            window.setKeyboardAction(Framework::_ret1TE);
+            Framework::Screen3D screen(
+                dynamic_cast<Framework::NativeWindow*>(window.getThis()),
+                new Framework::DirectX12());
+            screen.setHandleUserInputsOnTick(0);
+            window.setScreen(
+                dynamic_cast<Framework::Screen*>(screen.getThis()));
+            screen.setFillColor(0);
+            screen.setTestRend(0);
+
+            Framework::Cam3D cam;
+            cam.setPosition({0.f, 0.f, 0.f});
+            Framework::Vec3<float> target(0, 1000, 0);
+            cam.setViewDirection(target);
+            Framework::World3D world;
+
+            Framework::Model3D cube;
+            Framework::Model3DData* data
+                = screen.zGraphicsApi()->createModel("cube");
+            data->setAmbientFactor(0.f);
+            data->setDiffusFactor(1.f);
+            data->setSpecularFactor(0.f);
+            float size = 1;
+            float left, right, top, bottom;
+            // Calculate the screen coordinates of the left side of the bitmap.
+            right = (float)((-size / 2.0));
+            // Calculate the screen coordinates of the right side of the bitmap.
+            left = right + (float)size;
+            // Calculate the screen coordinates of the top of the bitmap.
+            top = (float)(size / 2.0);
+            // Calculate the screen coordinates of the bottom of the bitmap.
+            bottom = top - (float)size;
+            float front = -size / 2;
+            float back = front + size;
+
+            Framework::Vertex3D* vertecies = new Framework::Vertex3D[24];
+            for (int i = 0; i < 24; i++)
+                vertecies[i].knochenId = 0;
+            // front side
+            vertecies[0].pos = Framework::Vec3<float>(left, front, top);
+            vertecies[0].tPos = Framework::Vec2<float>(0.f, 0.f);
+            vertecies[1].pos = Framework::Vec3<float>(right, front, top);
+            vertecies[1].tPos = Framework::Vec2<float>(1.f, 0.f);
+            vertecies[2].pos = Framework::Vec3<float>(left, front, bottom);
+            vertecies[2].tPos = Framework::Vec2<float>(0.f, 1.f);
+            vertecies[3].pos = Framework::Vec3<float>(right, front, bottom);
+            vertecies[3].tPos = Framework::Vec2<float>(1.f, 1.f);
+            // back side
+            vertecies[4].pos = Framework::Vec3<float>(right, back, top);
+            vertecies[4].tPos = Framework::Vec2<float>(0.0f, 0.0f);
+            vertecies[5].pos = Framework::Vec3<float>(left, back, top);
+            vertecies[5].tPos = Framework::Vec2<float>(1.0f, 0.0f);
+            vertecies[6].pos = Framework::Vec3<float>(right, back, bottom);
+            vertecies[6].tPos = Framework::Vec2<float>(0.0f, 1.0f);
+            vertecies[7].pos = Framework::Vec3<float>(left, back, bottom);
+            vertecies[7].tPos = Framework::Vec2<float>(1.0f, 1.0f);
+            // left side
+            vertecies[8].pos = Framework::Vec3<float>(left, back, top);
+            vertecies[8].tPos = Framework::Vec2<float>(0.f, 0.f);
+            vertecies[9].pos = Framework::Vec3<float>(left, front, top);
+            vertecies[9].tPos = Framework::Vec2<float>(1.f, 0.f);
+            vertecies[10].pos = Framework::Vec3<float>(left, back, bottom);
+            vertecies[10].tPos = Framework::Vec2<float>(0.f, 1.f);
+            vertecies[11].pos = Framework::Vec3<float>(left, front, bottom);
+            vertecies[11].tPos = Framework::Vec2<float>(1.f, 1.f);
+            // right side
+            vertecies[12].pos = Framework::Vec3<float>(right, front, top);
+            vertecies[12].tPos = Framework::Vec2<float>(0.0f, 0.0f);
+            vertecies[13].pos = Framework::Vec3<float>(right, back, top);
+            vertecies[13].tPos = Framework::Vec2<float>(1.0f, 0.0f);
+            vertecies[14].pos = Framework::Vec3<float>(right, front, bottom);
+            vertecies[14].tPos = Framework::Vec2<float>(0.0f, 1.0f);
+            vertecies[15].pos = Framework::Vec3<float>(right, back, bottom);
+            vertecies[15].tPos = Framework::Vec2<float>(1.0f, 1.0f);
+            // top side
+            vertecies[16].pos = Framework::Vec3<float>(left, back, top);
+            vertecies[16].tPos = Framework::Vec2<float>(0.f, 0.f);
+            vertecies[17].pos = Framework::Vec3<float>(right, back, top);
+            vertecies[17].tPos = Framework::Vec2<float>(1.f, 0.f);
+            vertecies[18].pos = Framework::Vec3<float>(left, front, top);
+            vertecies[18].tPos = Framework::Vec2<float>(0.f, 1.f);
+            vertecies[19].pos = Framework::Vec3<float>(right, front, top);
+            vertecies[19].tPos = Framework::Vec2<float>(1.f, 1.f);
+            // botom side
+            vertecies[20].pos = Framework::Vec3<float>(left, front, bottom);
+            vertecies[20].tPos = Framework::Vec2<float>(0.0f, 0.0f);
+            vertecies[21].pos = Framework::Vec3<float>(right, front, bottom);
+            vertecies[21].tPos = Framework::Vec2<float>(1.0f, 0.0f);
+            vertecies[22].pos = Framework::Vec3<float>(left, back, bottom);
+            vertecies[22].tPos = Framework::Vec2<float>(0.0f, 1.0f);
+            vertecies[23].pos = Framework::Vec3<float>(right, back, bottom);
+            vertecies[23].tPos = Framework::Vec2<float>(1.0f, 1.0f);
+
+            data->setVertecies(vertecies, 24);
+            // the order of the polygons has to be NORTH (front), EAST (left),
+            // SOUTH (back), WEST (right), TOP, BOTTOM according to the Area
+            // definition front side
+            Framework::Polygon3D* p
+                = new Framework::Polygon3D(); // looking from (0,0,0) to (1,0,0)
+                                              // to see this side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0;
+            p->indexList[1] = 1;
+            p->indexList[2] = 2;
+            p->indexList[3] = 1;
+            p->indexList[4] = 3;
+            p->indexList[5] = 2;
+            data->addPolygon(p);
+            // left side
+            p = new Framework::Polygon3D(); // looking from (0,0,0) to (0,1,0)
+                                            // to see this
+                                            // side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0 + 8;
+            p->indexList[1] = 1 + 8;
+            p->indexList[2] = 2 + 8;
+            p->indexList[3] = 1 + 8;
+            p->indexList[4] = 3 + 8;
+            p->indexList[5] = 2 + 8;
+            data->addPolygon(p);
+            // back side
+            p = new Framework::Polygon3D(); // looking from (0,0,0) to (-1,0,0)
+                                            // to see this
+                                            // side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0 + 4;
+            p->indexList[1] = 1 + 4;
+            p->indexList[2] = 2 + 4;
+            p->indexList[3] = 1 + 4;
+            p->indexList[4] = 3 + 4;
+            p->indexList[5] = 2 + 4;
+            data->addPolygon(p);
+            // right side
+            p = new Framework::Polygon3D(); // looking from (0,0,0) to (0,-1,0)
+                                            // to see this
+                                            // side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0 + 12;
+            p->indexList[1] = 1 + 12;
+            p->indexList[2] = 2 + 12;
+            p->indexList[3] = 1 + 12;
+            p->indexList[4] = 3 + 12;
+            p->indexList[5] = 2 + 12;
+            data->addPolygon(p);
+            // top side
+            p = new Framework::Polygon3D(); // looking from (0,0,0) to (0,0,-1)
+                                            // to see this
+                                            // side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0 + 16;
+            p->indexList[1] = 1 + 16;
+            p->indexList[2] = 2 + 16;
+            p->indexList[3] = 1 + 16;
+            p->indexList[4] = 3 + 16;
+            p->indexList[5] = 2 + 16;
+            data->addPolygon(p);
+            // botom side
+            p = new Framework::Polygon3D(); // looking from (0,0,0) to (0,0,1)
+                                            // to see this
+                                            // side
+            p->indexAnz = 6;
+            p->indexList = new int[p->indexAnz];
+            p->indexList[0] = 0 + 20;
+            p->indexList[1] = 1 + 20;
+            p->indexList[2] = 2 + 20;
+            p->indexList[3] = 1 + 20;
+            p->indexList[4] = 3 + 20;
+            p->indexList[5] = 2 + 20;
+            data->addPolygon(p);
+            data->calculateNormals();
+            cube.setModelData(data);
+            cube.setPosition(0, 100, 0);
+            cube.setSize(50);
+            cube.setRotation(0.5, 0.5, 0.5);
+            cube.tick(0.1);
+            world.addDrawable(
+                dynamic_cast<Framework::Model3D*>(cube.getThis()));
+
+            cam.setWorld(dynamic_cast<Framework::World3D*>(world.getThis()));
+            cam.addStyle(Framework::Cam3D::Style::Movable
+                         | Framework::Cam3D::Style::Rotatable
+                         | Framework::Cam3D::Style::Zoomable
+                         | Framework::Cam3D::Style::Tick);
+            screen.addKamera(dynamic_cast<Framework::Cam3D*>(cam.getThis()));
+
+            Framework::RenderTh rTh;
+            rTh.setMaxFps(-1);
+            rTh.setQuiet(0);
+            rTh.setScreen(dynamic_cast<Framework::Screen*>(screen.getThis()));
+            rTh.setTickFunktion([&cube](void* p, void* f, double tick) {
+                cube.setRotationX(cube.getXRotation() + tick);
+                cube.setRotationY(cube.getXRotation() + tick / 10);
+                cube.setRotationZ(cube.getXRotation() + tick / 50);
+            });
+
+            rTh.beginn();
+            Framework::StartMessageLoop();
+            rTh.terminate();
+        }
+    };
+} // namespace FrameworkTests

+ 1 - 0
Framework Tests/Framework Tests.vcxproj

@@ -177,6 +177,7 @@
     <ClCompile Include="Assembly.cpp" />
     <ClCompile Include="Base64.cpp" />
     <ClCompile Include="Cache.cpp" />
+    <ClCompile Include="Camera3D.cpp" />
     <ClCompile Include="Critical.cpp" />
     <ClCompile Include="Json.cpp" />
     <ClCompile Include="pch.cpp">

+ 3 - 0
Framework Tests/Framework Tests.vcxproj.filters

@@ -51,6 +51,9 @@
     <ClCompile Include="Critical.cpp">
       <Filter>Source Files</Filter>
     </ClCompile>
+    <ClCompile Include="Camera3D.cpp">
+      <Filter>Source Files</Filter>
+    </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="pch.h">

+ 2 - 1
Framework.vcxproj

@@ -244,6 +244,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <ClInclude Include="FileSystem.h" />
     <ClInclude Include="Diagram.h" />
     <ClInclude Include="TriangleList.h" />
+    <ClInclude Include="Vec4.h" />
     <ClInclude Include="Window.h" />
     <ClInclude Include="Progress.h" />
     <ClInclude Include="FrameworkMath.h" />
@@ -506,7 +507,7 @@ copy "x64\Release\Framework.dll" "..\..\Spiele Platform\SMP\Fertig\x64\framework
     <FxCompile Include="RayGen.hlsl">
       <EntryPointName Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
       </EntryPointName>
-      <ShaderModel Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">6.3</ShaderModel>
+      <ShaderModel Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">6.8</ShaderModel>
       <AdditionalOptions Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">-Fd "x64\Debug\DX12RayGenShader.pdb" %(AdditionalOptions)</AdditionalOptions>
       <HeaderFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">DX12RayGenShader.h</HeaderFileOutput>
       <ObjectFileOutput Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">

+ 3 - 0
Framework.vcxproj.filters

@@ -427,6 +427,9 @@
     <ClInclude Include="DX11Texture.h">
       <Filter>Framework\Graphics\DX\DX11</Filter>
     </ClInclude>
+    <ClInclude Include="Vec4.h">
+      <Filter>Framework\Graphics\4D</Filter>
+    </ClInclude>
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="Model3DCollection.h">

+ 1 - 1
Hit.hlsl

@@ -3,5 +3,5 @@
 [shader("closesthit")]
 void ClosestHit(inout HitInfo payload, Attributes attrib)
 {
-    payload.colorAndDistance = float4(1, 1, 0, RayTCurrent());
+    payload.colorAndDistance = float4(1, 0, 0, RayTCurrent());
 }

+ 17 - 0
Mat4.h

@@ -5,6 +5,7 @@
 #include "Logging.h"
 #include "Mat3.h"
 #include "Vec3.h"
+#include "Vec4.h"
 
 namespace Framework
 {
@@ -69,6 +70,22 @@ namespace Framework
             return result;
         }
 
+        //! Multiplies the matrix with a vector
+        //! \param r The vector
+        Vec4<T> operator*(const Vec4<T>& r) const
+        {
+            Vec4<T> result;
+            result.x = elements[0][0] * r.x + elements[0][1] * r.y
+                     + elements[0][2] * r.z + elements[0][3] * r.w;
+            result.y = elements[1][0] * r.x + elements[1][1] * r.y
+                     + elements[1][2] * r.z + elements[1][3] * r.w;
+            result.z = elements[2][0] * r.x + elements[2][1] * r.y
+                     + elements[2][2] * r.z + elements[2][3] * r.w;
+            result.w = elements[3][0] * r.x + elements[3][1] * r.y
+                     + elements[3][2] * r.z + elements[3][3] * r.w;
+            return result;
+        }
+
         //! Multiplies the matrix with a vector
         //! \param r The vector
         Vec3<T> operator*(const Vec3<T>& r) const

+ 1 - 1
Miss.hlsl

@@ -3,5 +3,5 @@
 [shader("miss")]
 void Miss(inout HitInfo payload : SV_RayPayload)
 {
-    payload.colorAndDistance = float4(0.2f, 0.2f, 0.8f, -1.f);
+    payload.colorAndDistance = float4(0.2f, 0.2f, 0.2f, -1.f);
 }

+ 4 - 4
RayGen.hlsl

@@ -29,15 +29,15 @@ void RayGen()
     // (often maps to pixels, so this could represent a pixel coordinate).
     float2 dispatchDimensions = float2(DispatchRaysDimensions().xy);
     uint2 dispatchIndex = DispatchRaysIndex().xy;
-    float2 dispatchPercentage = dispatchIndex / dispatchDimensions;
+    float2 dispatchPercentage = (dispatchIndex + 0.5) / dispatchDimensions;
     
     if (useRays)
     {
-        float2 d = (((dispatchIndex.xy + 0.5f) / dispatchDimensions.xy) * 2.f - 1.f);
+        float2 d = (dispatchPercentage * 2.f - 1.f);
         RayDesc ray;
         ray.Origin = mul(inverseView, float4(0, 0, 0, 1)).xyz;
-        float4 target = mul(inverseProjection, float4(d.x, -d.y, 1, 1));
-        ray.Direction = mul(inverseView, float4(target.xyz, 0)).xyz;
+        float4 target = mul(inverseProjection, float4(d.x, d.y, -1, 1));
+        ray.Direction = mul(inverseView, target).xyz - ray.Origin;
         ray.TMin = minDistance;
         ray.TMax = maxDistance;
         TraceRay(TLAS, /*RayFlags*/0, /*InstanceInclusionMask*/0xFF, /*RayContributionToHitGroupIndex*/0,

+ 219 - 0
Vec4.h

@@ -0,0 +1,219 @@
+#pragma once
+
+namespace Framework
+{
+    template<typename T>
+    //! A 3D vector
+    class Vec4
+    {
+    public:
+        T x; //! X component of the vector
+        T y; //! Y component of the vector
+        T z; //! Z component of the vector
+        T w; //! W component of the vector
+
+        //! Constructor
+        inline Vec4() {}
+
+        //! Constructor
+        //! \param x The X component of the new vector
+        //! \param y The Y component of the new vector
+        //! \param z The Z component of the new vector
+        //! \param w The W component of the new vector
+        inline Vec4(T x, T y, T z, T w)
+            : x(x),
+              y(y),
+              z(z),
+              w(w)
+        {}
+
+        //! Constructor
+        //! \param vect A vector whose values should be copied
+        inline Vec4(const Vec4& vect)
+            : Vec4(vect.x, vect.y, vect.z, vect.w)
+        {}
+
+        //! Scales the vector so that it has length 1
+        inline Vec4& normalize()
+        {
+            const T length = getLength();
+            x /= length;
+            y /= length;
+            z /= length;
+            w /= length;
+            return *this;
+        }
+
+        //! Swaps the values of the vector with those of another vector
+        //! \param vect The other vector
+        inline Vec4& swap(Vec4& vect)
+        {
+            const Vec4 tmp = vect;
+            vect = *this;
+            *this = tmp;
+            return *this;
+        }
+
+        //! Calculates an angle between this and another vector
+        inline float angle(Vec4 vect)
+        {
+            return lowPrecisionACos(
+                (float)(*this * vect)
+                / ((float)getLength() * (float)vect.getLength()));
+        }
+
+        //! Copies the values of another vector
+        //! \param r The other vector
+        inline Vec4 operator=(const Vec4& r)
+        {
+            x = r.x;
+            y = r.y;
+            z = r.z;
+            w = r.w;
+            return *this;
+        }
+
+        //! Adds another vector to this one
+        //! \param r The other vector
+        inline Vec4 operator+=(const Vec4& r)
+        {
+            x += r.x;
+            y += r.y;
+            z += r.z;
+            w += r.w;
+            return *this;
+        }
+
+        //! Subtracts another vector from this one
+        //! \param r The other vector
+        inline Vec4 operator-=(const Vec4& r)
+        {
+            x -= r.x;
+            y -= r.y;
+            z -= r.z;
+            w -= r.w;
+            return *this;
+        }
+
+        //! Scales this vector
+        //! \param r The factor
+        inline Vec4 operator*=(const T& r)
+        {
+            x *= r;
+            y *= r;
+            z *= r;
+            w *= r;
+            return *this;
+        }
+
+        //! Scales this vector by 1/factor
+        //! \param r The factor
+        inline Vec4 operator/=(const T& r)
+        {
+            x /= r;
+            y /= r;
+            z /= r;
+            w /= r;
+            return *this;
+        }
+
+        //! Calculates the square of the distance between two vectors
+        //! \param p The other vector
+        inline T distanceSq(const Vec4& p) const
+        {
+            return (x - p.x) * (x - p.x) + (y - p.y) * (y - p.y)
+                 + (z - p.z) * (z - p.z) + (w - p.w) * (w - p.w);
+        }
+
+        //! Calculates the distance between two vectors
+        //! \param p The other vector
+        inline T distance(const Vec4& p) const
+        {
+            return sqrt(distanceSq(p));
+        }
+
+        //! Returns a new vector that is the negation of this one
+        inline Vec4 operator-() const
+        {
+            return {-x, -y, -z, -w};
+        }
+
+        template<typename T2>
+        //! Converts the vector type to another
+        inline operator Vec4<T2>() const
+        {
+            return {(T2)x, (T2)y, (T2)z, (T2)w};
+        }
+
+        //! Calculates the square of the vector's length
+        inline T getLengthSq() const
+        {
+            return *this * *this;
+        }
+
+        //! Calculates the length of the vector
+        inline T getLength() const
+        {
+            return (T)sqrt(getLengthSq());
+        }
+
+        //! Calculates the dot product between two vectors
+        //! \param r The other vector
+        inline T operator*(const Vec4& r) const
+        {
+            return x * r.x + y * r.y + z * r.z + w * r.w;
+        }
+
+        //! Calculates the sum of two vectors
+        //! \param r The other vector
+        inline Vec4 operator+(const Vec4& r) const
+        {
+            return Vec4(*this) += r;
+        }
+
+        //! Subtracts two vectors from each other
+        //! \param r The other vector
+        inline Vec4 operator-(const Vec4& r) const
+        {
+            return Vec4(*this) -= r;
+        }
+
+        //! Scales the vector without modifying it
+        //! \param r The factor
+        inline Vec4 operator*(const T& r) const
+        {
+            return Vec4(*this) *= r;
+        }
+
+        //! Scales the vector by 1/factor without modifying it
+        //! \param r The factor
+        inline Vec4 operator/(const T& r) const
+        {
+            return Vec4(*this) /= r;
+        }
+
+        //! Checks two vectors for equality
+        //! \param r The other vector
+        inline bool operator==(const Vec4& r) const
+        {
+            return x == r.x && y == r.y && z == r.z && w == r.w;
+        }
+
+        //! Checks two vectors for inequality
+        //! \param r The other vector
+        inline bool operator!=(const Vec4& r) const
+        {
+            return !(*this == r);
+        }
+
+        //! Returns the cross product between this and another vector
+        //! \param b The other vector
+        inline Vec4 crossProduct(const Vec4& b) const
+        {
+            return Vec4(y * b.z - z * b.y,
+                z * b.x - x * b.z,
+                x * b.y - y * b.x,
+                w * b.w - w * b.w);
+        }
+    };
+} // namespace Framework