Created
October 6, 2018 22:54
-
-
Save WillWetzel/df76b931d4bfd0f806f0f6dccecf42fd to your computer and use it in GitHub Desktop.
Software Rasteriser
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Colour.h" | |
Colour Colour::White(255,255,255,255); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Colour | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Simple class to hold colours made up of 4 bytes. | |
You might be wondering why in the class, the bytes aren't arranged in rgb order | |
This is due to how Win32 internally stores its bitmaps - in bgra. In order to | |
speed up mem copies to the window buffer, we keep our colours in the same order | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
struct Colour | |
{ | |
union | |
{ | |
struct | |
{ | |
unsigned char b; | |
unsigned char g; | |
unsigned char r; | |
unsigned char a; | |
}; | |
unsigned int c; | |
}; | |
Colour() | |
{ | |
r = 0; | |
g = 0; | |
b = 0; | |
a = 255; | |
} | |
Colour(unsigned char r, unsigned char g, unsigned char b, unsigned char a) | |
{ | |
this->r = r; | |
this->g = g; | |
this->b = b; | |
this->a = a; | |
} | |
inline void Reset() | |
{ | |
r = 0; | |
g = 0; | |
b = 0; | |
a = 255; | |
} | |
inline void Set(const unsigned char &red, const unsigned char &green, const unsigned char &blue, | |
const unsigned char &alpha) | |
{ | |
r = red; | |
g = green; | |
b = blue; | |
a = alpha; | |
} | |
static Colour Lerp(const Colour &a, const Colour &b, float by) | |
{ | |
Colour p; | |
float minusBy = 1.0f - by; | |
p.r = unsigned char((b.r * by) + (a.r * minusBy)); | |
p.g = unsigned char((b.g * by) + (a.g * minusBy)); | |
p.b = unsigned char((b.b * by) + (a.b * minusBy)); | |
p.a = unsigned char((b.a * by) + (a.a * minusBy)); | |
return p; | |
} | |
inline Colour operator*(const float factor) const | |
{ | |
return Colour((unsigned char)(float)(r * factor), (unsigned char)(float)(g * factor), | |
(unsigned char)(float)(b * factor), (unsigned char)(float)(a * factor)); | |
} | |
inline Colour operator+(const Colour &add) const | |
{ | |
return Colour(r + add.r, g + add.g, b + add.b, a + add.a); | |
} | |
inline void operator+=(const Colour &add) | |
{ | |
r += add.r; | |
g += add.g; | |
b += add.b; | |
a += add.a; | |
} | |
inline Colour operator-(const Colour &sub) const | |
{ | |
return Colour(r - sub.r, g - sub.g, b - sub.b, a - sub.a); | |
} | |
static Colour White; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Author:Rich Davison | |
Description: Some random variables and functions, for lack of a better place | |
to put them. | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
// It's pi(ish)... | |
static const float PI = 3.14159265358979323846f; | |
// It's pi...divided by 360.0f! | |
static const float PI_OVER_360 = PI / 360.0f; | |
// Radians to degrees | |
static inline double RadToDeg(const double deg) | |
{ | |
return deg * 180.0 / PI; | |
}; | |
// Degrees to radians | |
static inline double DegToRad(const double rad) | |
{ | |
return rad * PI / 180.0; | |
}; | |
// I blame Microsoft... | |
#define max(a, b) (((a) > (b)) ? (a) : (b)) | |
#define min(a, b) (((a) < (b)) ? (a) : (b)) | |
#define clamp(a, b, c) (a < b ? b : (a > c ? c : a)) | |
typedef unsigned int uint; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:InputDevice | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Abstract base class for Windows RAW keyboard / mouse input | |
Input devices can be temporarily sent to sleep (so keyboard input doesn't work | |
when the game is minimised etc), and obviously woken up again. | |
Input devices may also keep track of 'holds' - i.e keys or buttons pressed for | |
more than one frame. This allows you to have both things that trigger once, | |
and are continuously active as long as a key / button is pressed. | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include <windows.h> | |
/* | |
Microsoft helpfully don't seem to have this in any of their header files, | |
despite it being how RAW input works....GG guys. | |
*/ | |
#ifndef HID_USAGE_PAGE_GENERIC | |
#define HID_USAGE_PAGE_GENERIC ((USHORT)0x01) | |
#endif | |
#ifndef HID_USAGE_GENERIC_MOUSE | |
#define HID_USAGE_GENERIC_MOUSE ((USHORT)0x02) | |
#endif | |
#ifndef HID_USAGE_GENERIC_KEYBOARD | |
#define HID_USAGE_GENERIC_KEYBOARD ((USHORT)0x06) | |
#endif | |
class InputDevice | |
{ | |
protected: | |
friend class Window; | |
InputDevice(void) | |
{ | |
isAwake = true; | |
}; | |
~InputDevice(void) {}; | |
protected: | |
virtual void Update(RAWINPUT *raw) = 0; | |
virtual void UpdateHolds() | |
{ | |
} | |
virtual void Sleep() | |
{ | |
isAwake = false; | |
} | |
virtual void Wake() | |
{ | |
isAwake = true; | |
} | |
bool isAwake; // Is the device awake... | |
RAWINPUTDEVICE rid; // Windows OS hook | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Keyboard.h" | |
Keyboard *Keyboard::instance = 0; | |
Keyboard::Keyboard(HWND &hwnd) | |
{ | |
// Initialise the arrays to false! | |
ZeroMemory(keyStates, KEY_MAX * sizeof(bool)); | |
ZeroMemory(holdStates, KEY_MAX * sizeof(bool)); | |
// Tedious windows RAW input stuff | |
rid.usUsagePage = HID_USAGE_PAGE_GENERIC; // The keyboard isn't anything fancy | |
rid.usUsage = HID_USAGE_GENERIC_KEYBOARD; // but it's definitely a keyboard! | |
rid.dwFlags = RIDEV_INPUTSINK; // Yes, we want to always receive RAW input... | |
rid.hwndTarget = hwnd; // Windows OS window handle | |
RegisterRawInputDevices(&rid, 1, sizeof(rid)); // We just want one keyboard, please! | |
} | |
void Keyboard::Initialise(HWND &hwnd) | |
{ | |
instance = new Keyboard(hwnd); | |
} | |
void Keyboard::Destroy() | |
{ | |
delete instance; | |
} | |
/* | |
Updates variables controlling whether a keyboard key has been | |
held for multiple frames. | |
*/ | |
void Keyboard::UpdateHolds() | |
{ | |
memcpy(instance->holdStates, instance->keyStates, KEY_MAX * sizeof(bool)); | |
} | |
/* | |
Sends the keyboard to sleep, so it doesn't process any | |
keypresses until it receives a Wake() | |
*/ | |
void Keyboard::Sleep() | |
{ | |
isAwake = false; // Night night! | |
// Prevents incorrectly thinking keys have been held / pressed when waking back up | |
ZeroMemory(instance->keyStates, KEY_MAX * sizeof(bool)); | |
ZeroMemory(instance->holdStates, KEY_MAX * sizeof(bool)); | |
} | |
/* | |
Returns if the key is down. Doesn't need bounds checking - | |
a KeyboardKeys enum is always in range | |
*/ | |
bool Keyboard::KeyDown(KeyboardKeys key) | |
{ | |
return instance->keyStates[key]; | |
} | |
/* | |
Returns if the key is down, and has been held down for multiple updates. | |
Doesn't need bounds checking - a KeyboardKeys enum is always in range | |
*/ | |
bool Keyboard::KeyHeld(KeyboardKeys key) | |
{ | |
if (instance->KeyDown(key) && instance->holdStates[key]) | |
{ | |
return true; | |
} | |
return false; | |
} | |
/* | |
Returns true only if the key is down, but WASN't down last update. | |
Doesn't need bounds checking - a KeyboardKeys enum is always in range | |
*/ | |
bool Keyboard::KeyTriggered(KeyboardKeys key) | |
{ | |
return (instance->KeyDown(key) && !instance->KeyHeld(key)); | |
} | |
/* | |
Updates the keyboard state with data received from the OS. | |
*/ | |
void Keyboard::Update(RAWINPUT *raw) | |
{ | |
if (isAwake) | |
{ | |
DWORD key = (DWORD)raw->data.keyboard.VKey; | |
// We should do bounds checking! | |
if (key < 0 || key > KEY_MAX) | |
{ | |
return; | |
} | |
// First bit of the flags tag determines whether the key is down or up | |
keyStates[key] = !(raw->data.keyboard.Flags & RI_KEY_BREAK); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Keyboard | |
Implements:InputDevice | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:RAW Input keyboard class. I've made absolutely no attempt to ensure | |
that this class works for multiple keyboards attached to a single system. | |
You shouldn't really have to mess with this class too much, there's nothing too | |
interesting about it! | |
STUDENT CHALLENGE! You could have a function pointer per key, that can be | |
automatically called when a key is triggered / held? (Checked from within the | |
Update function) | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "InputDevice.h" | |
// http://msdn.microsoft.com/en-us/library/ms645540(VS.85).aspx | |
enum KeyboardKeys | |
{ | |
KEY_LBUTTON = 0x01, // Left mouse button | |
KEY_RBUTTON = 0x02, // Right mouse button | |
KEY_CANCEL = 0x03, // Control-break processing | |
KEY_MBUTTON = 0x04, // Middle mouse button (three-button mouse) | |
KEY_XBUTTON1 = 0x05, // Windows 2000/XP: X1 mouse button | |
KEY_XBUTTON2 = 0x06, // Windows 2000/XP: X2 mouse button | |
KEY_BACK = 0x08, // BACKSPACE key | |
KEY_TAB = 0x09, // TAB key | |
KEY_CLEAR = 0x0C, // CLEAR key | |
KEY_RETURN = 0x0D, // ENTER key | |
KEY_SHIFT = 0x10, // SHIFT key | |
KEY_CONTROL = 0x11, // CTRL key | |
KEY_MENU = 0x12, // ALT key | |
KEY_PAUSE = 0x13, // PAUSE key | |
KEY_CAPITAL = 0x14, // CAPS LOCK key | |
KEY_KANA = 0x15, // IME Kana mode | |
KEY_HANGUEL = 0x15, // IME Hanguel mode (maintained for compatibility use KEY_HANGUL) | |
KEY_HANGUL = 0x15, // IME Hangul mode | |
KEY_JUNJA = 0x17, // IME Junja mode | |
KEY_FINAL = 0x18, // IME final mode | |
KEY_HANJA = 0x19, // IME Hanja mode | |
KEY_KANJI = 0x19, // IME Kanji mode | |
KEY_ESCAPE = 0x1B, // ESC key | |
KEY_CONVERT = 0x1C, // IME convert | |
KEY_NONCONVERT = 0x1D, // IME nonconvert | |
KEY_ACCEPT = 0x1E, // IME accept | |
KEY_MODECHANGE = 0x1F, // IME mode change request | |
KEY_SPACE = 0x20, // SPACEBAR | |
KEY_PRIOR = 0x21, // PAGE UP key | |
KEY_NEXT = 0x22, // PAGE DOWN key | |
KEY_END = 0x23, // END key | |
KEY_HOME = 0x24, // HOME key | |
KEY_LEFT = 0x25, // LEFT ARROW key | |
KEY_UP = 0x26, // UP ARROW key | |
KEY_RIGHT = 0x27, // RIGHT ARROW key | |
KEY_DOWN = 0x28, // DOWN ARROW key | |
KEY_SELECT = 0x29, // SELECT key | |
KEY_PRINT = 0x2A, // PRINT key | |
KEY_EXECUT = 0x2B, // EXECUTE key | |
KEY_SNAPSHOT = 0x2C, // PRINT SCREEN key | |
KEY_INSERT = 0x2D, // INS key | |
KEY_DELETE = 0x2E, // DEL key | |
KEY_HELP = 0x2F, // HELP key | |
KEY_0 = 0x30, // 0 key | |
KEY_1 = 0x31, // 1 key | |
KEY_2 = 0x32, // 2 key | |
KEY_3 = 0x33, // 3 key | |
KEY_4 = 0x34, // 4 key | |
KEY_5 = 0x35, // 5 key | |
KEY_6 = 0x36, // 6 key | |
KEY_7 = 0x37, // 7 key | |
KEY_8 = 0x38, // 8 key | |
KEY_9 = 0x39, // 9 key | |
KEY_A = 0x41, // A key | |
KEY_B = 0x42, // B key | |
KEY_C = 0x43, // C key | |
KEY_D = 0x44, // D key | |
KEY_E = 0x45, // E key | |
KEY_F = 0x46, // F key | |
KEY_G = 0x47, // G key | |
KEY_H = 0x48, // H key | |
KEY_I = 0x49, // I key | |
KEY_J = 0x4A, // J key | |
KEY_K = 0x4B, // K key | |
KEY_L = 0x4C, // L key | |
KEY_M = 0x4D, // M key | |
KEY_N = 0x4E, // N key | |
KEY_O = 0x4F, // O key | |
KEY_P = 0x50, // P key | |
KEY_Q = 0x51, // Q key | |
KEY_R = 0x52, // R key | |
KEY_S = 0x53, // S key | |
KEY_T = 0x54, // T key | |
KEY_U = 0x55, // U key | |
KEY_V = 0x56, // V key | |
KEY_W = 0x57, // W key | |
KEY_X = 0x58, // X key | |
KEY_Y = 0x59, // Y key | |
KEY_Z = 0x5A, // Z key | |
KEY_LWIN = 0x5B, // Left Windows key (Microsoft� Natural� keyboard) | |
KEY_RWIN = 0x5C, // Right Windows key (Natural keyboard) | |
KEY_APPS = 0x5D, // Applications key (Natural keyboard) | |
KEY_SLEEP = 0x5F, // Computer Sleep key | |
KEY_NUMPAD0 = 0x60, // Numeric keypad 0 key | |
KEY_NUMPAD1 = 0x61, // Numeric keypad 1 key | |
KEY_NUMPAD2 = 0x62, // Numeric keypad 2 key | |
KEY_NUMPAD3 = 0x63, // Numeric keypad 3 key | |
KEY_NUMPAD4 = 0x64, // Numeric keypad 4 key | |
KEY_NUMPAD5 = 0x65, // Numeric keypad 5 key | |
KEY_NUMPAD6 = 0x66, // Numeric keypad 6 key | |
KEY_NUMPAD7 = 0x67, // Numeric keypad 7 key | |
KEY_NUMPAD8 = 0x68, // Numeric keypad 8 key | |
KEY_NUMPAD9 = 0x69, // Numeric keypad 9 key | |
KEY_MULTIPLY = 0x6A, // Multiply key | |
KEY_ADD = 0x6B, // Add key | |
KEY_SEPARATOR = 0x6C, // Separator key | |
KEY_SUBTRACT = 0x6D, // Subtract key | |
KEY_DECIMAL = 0x6E, // Decimal key | |
KEY_DIVIDE = 0x6F, // Divide key | |
KEY_F1 = 0x70, // F1 key | |
KEY_F2 = 0x71, // F2 key | |
KEY_F3 = 0x72, // F3 key | |
KEY_F4 = 0x73, // F4 key | |
KEY_F5 = 0x74, // F5 key | |
KEY_F6 = 0x75, // F6 key | |
KEY_F7 = 0x76, // F7 key | |
KEY_F8 = 0x77, // F8 key | |
KEY_F9 = 0x78, // F9 key | |
KEY_F10 = 0x79, // F10 key | |
KEY_F11 = 0x7A, // F11 key | |
KEY_F12 = 0x7B, // F12 key | |
KEY_F13 = 0x7C, // F13 key | |
KEY_F14 = 0x7D, // F14 key | |
KEY_F15 = 0x7E, // F15 key | |
KEY_F16 = 0x7F, // F16 key | |
KEY_F17 = 0x80, // F17 key | |
KEY_F18 = 0x81, // F18 key | |
KEY_F19 = 0x82, // F19 key | |
KEY_F20 = 0x83, // F20 key | |
KEY_F21 = 0x84, // F21 key | |
KEY_F22 = 0x85, // F22 key | |
KEY_F23 = 0x86, // F23 key | |
KEY_F24 = 0x87, // F24 key | |
KEY_NUMLOCK = 0x90, // NUM LOCK key | |
KEY_SCROLL = 0x91, // SCROLL LOCK key | |
KEY_LSHIFT = 0xA0, // Left SHIFT key | |
KEY_RSHIFT = 0xA1, // Right SHIFT key | |
KEY_LCONTROL = 0xA2, // Left CONTROL key | |
KEY_RCONTROL = 0xA3, // Right CONTROL key | |
KEY_LMENU = 0xA4, // Left MENU key | |
KEY_RMENU = 0xA5, // Right MENU key | |
KEY_PLUS = 0xBB, // Plus Key (+) | |
KEY_COMMA = 0xBC, // Comma Key (,) | |
KEY_MINUS = 0xBD, // Minus Key (-) | |
KEY_PERIOD = 0xBE, // Period Key (.) | |
KEY_ATTN = 0xF6, // Attn key | |
KEY_CRSEL = 0xF7, // CrSel key | |
KEY_EXSEL = 0xF8, // ExSel key | |
KEY_EREOF = 0xF9, // Erase EOF key | |
KEY_PLAY = 0xFA, // Play key | |
KEY_ZOOM = 0xFB, // Zoom key | |
KEY_PA1 = 0xFD, // PA1 key | |
KEY_OEM_CLEAR = 0xFE, // Clear key | |
KEY_MAX = 0xFF | |
}; | |
class Keyboard : public InputDevice | |
{ | |
public: | |
friend class Window; | |
// Is this key currently pressed down? | |
static bool KeyDown(KeyboardKeys key); | |
// Has this key been held down for multiple frames? | |
static bool KeyHeld(KeyboardKeys key); | |
// Is this the first update the key has been pressed for? | |
static bool KeyTriggered(KeyboardKeys key); | |
protected: | |
Keyboard(HWND &hwnd); | |
~Keyboard(void) | |
{ | |
} | |
// Update the holdStates array...call this each frame! | |
virtual void UpdateHolds(); | |
// Update the keyStates array etc...call this each frame! | |
virtual void Update(RAWINPUT *raw); | |
// Sends the keyboard to sleep | |
virtual void Sleep(); | |
static void Initialise(HWND &hwnd); | |
static void Destroy(); | |
static Keyboard *instance; | |
bool keyStates[KEY_MAX]; // Is the key down? | |
bool holdStates[KEY_MAX]; // Has the key been down for multiple updates? | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Name: Will Wetzel - 130251255 | |
* Project: CSC3223 Graphics for Games, Coursework 1 | |
* Date: 15/11/2016 | |
* Description: This code creates a space scene with a moving space ship using basic shapes such as triangles and | |
* N sided shapes. The software Rasteriser is complete up to Tutorial 12 given to us in Blackboard. | |
*/ | |
#include "SoftwareRasteriser.h" | |
#include "Mesh.h" | |
void generateStarfield(vector<RenderObject *> &out, const int num = 100, | |
const float xyFact = 1.0f, const float zFact = 1.0f); | |
void generateAsteroid(vector<RenderObject *> &out, const Vector3 &position, const float scale); | |
int main() { | |
const int num = 100; //High number to increase the chance they will be shown in the window.. | |
//.. Aiming for between 5 and 10 to be present. | |
SoftwareRasteriser r(800, 600); | |
const float aspect = (float)800 / (float)600; | |
r.SetProjectionMatrix(Matrix4::Perspective(0.1f, 100.0f, aspect, 45.0f)); | |
vector<RenderObject *> elements; //Array to store objects to be displayed in. | |
generateStarfield(elements, 10000); | |
for (int i = 0; i < num; ++i) | |
{ | |
const float x = ((float)((rand() % 100) - 50)) * 1; | |
const float y = ((float)((rand() % 100) - 50)) * 1; | |
const float z = ((float)((rand() % 100) - 50)) * 1; | |
const float scale = ((float)(rand() % 100)) / 100.0f; | |
generateAsteroid(elements, Vector3(x, y, z), scale); | |
} | |
RenderObject *ship = new RenderObject(); | |
ship->mesh = Mesh::GenerateTriangle(); //Generate triangle for ship and rotate and scale. | |
ship->modelMatrix = Matrix4::Scale(Vector3(0.5, 0.5, 0.5)) * | |
Matrix4::Rotation(40.0f, Vector3(1.0, 1.0, 0.0)); | |
elements.push_back(ship); | |
Matrix4 viewMatrix = Matrix4::Translation(Vector3(0.0f, 0.0f, -10.0f)); | |
while (r.UpdateWindow()) { | |
//Moving ship if key is pressed. | |
if (Keyboard::KeyDown(KEY_A)) { | |
viewMatrix = viewMatrix * | |
Matrix4::Translation(Vector3(-0.01f, 0, 0)); | |
} | |
if (Keyboard::KeyDown(KEY_D)) { | |
viewMatrix = viewMatrix * | |
Matrix4::Translation(Vector3(0.01f, 0, 0)); | |
} | |
if (Keyboard::KeyDown(KEY_W)) { | |
viewMatrix = viewMatrix * | |
Matrix4::Translation(Vector3(0.0, 0.01f, 0)); | |
} | |
if (Keyboard::KeyDown(KEY_S)) { | |
viewMatrix = viewMatrix * | |
Matrix4::Translation(Vector3(0.0, -0.01f, 0)); | |
} | |
r.SetViewMatrix(viewMatrix); | |
r.ClearBuffers(); | |
// Draw opbjects to be displayed. | |
for (vector<RenderObject *>::iterator it = elements.begin(); it != elements.end(); ++it) | |
r.DrawObject(*it); | |
r.SwapBuffers(); | |
} | |
elements.clear(); | |
return 0; | |
} | |
/*Generate star field for background. Position of stars is random.*/ | |
void generateStarfield(vector<RenderObject *> &out, const int num, const float xyFact, | |
const float zFact) | |
{ | |
for (int i = 0; i < num; ++i) | |
{ | |
const float x = ((float)((rand() % 100) - 50)) * xyFact; | |
const float y = ((float)((rand() % 100) - 50)) * xyFact; | |
const float z = ((float)((rand() % 100) - 50)) * zFact; | |
// Generate random colour | |
const int r = (rand() % 100) + 155; | |
const int g = (rand() % 100) + 155; | |
const int b = (rand() % 100) + 155; | |
Colour c = Colour(r, g, b, 255); | |
RenderObject *o = new RenderObject(); | |
o->mesh = Mesh::GeneratePoint(Vector3(), c); | |
o->modelMatrix = Matrix4::Translation(Vector3(x, y, z)); | |
out.push_back(o); | |
} | |
} | |
/*Method to generate Asteroid using parameters passed from main program.*/ | |
void generateAsteroid(vector<RenderObject *> &out, const Vector3 &position, const float scale) | |
{ | |
const Matrix4 modelMat = | |
Matrix4::Translation(position) * Matrix4::Scale(Vector3(scale, scale, scale)); | |
// Random rotation between 160 - 200 | |
const float rot = (float)((rand() % 40) + 160); | |
RenderObject *o1 = new RenderObject(); | |
o1->mesh = Mesh::GenerateNSided2D(7); | |
o1->modelMatrix = modelMat * Matrix4::Scale(Vector3(0.85f, 0.85f, 0.85f)) * | |
Matrix4::Rotation(rot, Vector3(0.0f, 0.0f, 1.0f)); | |
out.push_back(o1); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Matrix4.h" | |
Matrix4::Matrix4(void) | |
{ | |
ToIdentity(); | |
} | |
Matrix4::Matrix4(float elements[16]) | |
{ | |
memcpy(this->values, elements, 16 * sizeof(float)); | |
} | |
Matrix4::~Matrix4(void) | |
{ | |
ToIdentity(); | |
} | |
void Matrix4::ToIdentity() | |
{ | |
ToZero(); | |
values[0] = 1.0f; | |
values[5] = 1.0f; | |
values[10] = 1.0f; | |
values[15] = 1.0f; | |
} | |
void Matrix4::ToZero() | |
{ | |
for (int i = 0; i < 16; i++) | |
{ | |
values[i] = 0.0f; | |
} | |
} | |
Vector3 Matrix4::GetPositionVector() const | |
{ | |
return Vector3(values[12], values[13], values[14]); | |
} | |
void Matrix4::SetPositionVector(const Vector3 in) | |
{ | |
values[12] = in.x; | |
values[13] = in.y; | |
values[14] = in.z; | |
} | |
Vector3 Matrix4::GetScalingVector() const | |
{ | |
return Vector3(values[0], values[5], values[10]); | |
} | |
void Matrix4::SetScalingVector(const Vector3 &in) | |
{ | |
values[0] = in.x; | |
values[5] = in.y; | |
values[10] = in.z; | |
} | |
Matrix4 Matrix4::Perspective(float znear, float zfar, float aspect, float fov) | |
{ | |
Matrix4 m; | |
const float f = 1.0f / tan(fov * PI_OVER_360); | |
float negDepth = znear - zfar; | |
m.values[0] = f / aspect; | |
m.values[5] = f; | |
m.values[10] = (zfar + znear) / negDepth; | |
m.values[11] = -1.0f; | |
m.values[14] = 2.0f * (znear * zfar) / negDepth; | |
m.values[15] = 0.0f; | |
return m; | |
} | |
// http://www.opengl.org/sdk/docs/man/xhtml/glOrtho.xml | |
Matrix4 Matrix4::Orthographic(float znear, float zfar, float right, float left, float top, | |
float bottom) | |
{ | |
Matrix4 m; | |
m.values[0] = 2.0f / (right - left); | |
m.values[5] = 2.0f / (top - bottom); | |
m.values[10] = -2.0f / (zfar - znear); | |
m.values[12] = -(right + left) / (right - left); | |
m.values[13] = -(top + bottom) / (top - bottom); | |
m.values[14] = -(zfar + znear) / (zfar - znear); | |
m.values[15] = 1.0f; | |
return m; | |
} | |
Matrix4 Matrix4::BuildViewMatrix(const Vector3 &from, const Vector3 &lookingAt, | |
const Vector3 up /*= Vector3(1,0,0)*/) | |
{ | |
Matrix4 r; | |
r.SetPositionVector(Vector3(-from.x, -from.y, -from.z)); | |
Matrix4 m; | |
Vector3 f = (lookingAt - from); | |
f.Normalise(); | |
Vector3 s = Vector3::Cross(f, up); | |
Vector3 u = Vector3::Cross(s, f); | |
m.values[0] = s.x; | |
m.values[4] = s.y; | |
m.values[8] = s.z; | |
m.values[1] = u.x; | |
m.values[5] = u.y; | |
m.values[9] = u.z; | |
m.values[2] = -f.x; | |
m.values[6] = -f.y; | |
m.values[10] = -f.z; | |
return m * r; | |
} | |
Matrix4 Matrix4::Rotation(float degrees, const Vector3 &inaxis) | |
{ | |
Matrix4 m; | |
Vector3 axis = inaxis; | |
axis.Normalise(); | |
float c = cos((float)DegToRad(degrees)); | |
float s = sin((float)DegToRad(degrees)); | |
m.values[0] = (axis.x * axis.x) * (1.0f - c) + c; | |
m.values[1] = (axis.y * axis.x) * (1.0f - c) + (axis.z * s); | |
m.values[2] = (axis.z * axis.x) * (1.0f - c) - (axis.y * s); | |
m.values[4] = (axis.x * axis.y) * (1.0f - c) - (axis.z * s); | |
m.values[5] = (axis.y * axis.y) * (1.0f - c) + c; | |
m.values[6] = (axis.z * axis.y) * (1.0f - c) + (axis.x * s); | |
m.values[8] = (axis.x * axis.z) * (1.0f - c) + (axis.y * s); | |
m.values[9] = (axis.y * axis.z) * (1.0f - c) - (axis.x * s); | |
m.values[10] = (axis.z * axis.z) * (1.0f - c) + c; | |
return m; | |
} | |
Matrix4 Matrix4::Scale(const Vector3 &scale) | |
{ | |
Matrix4 m; | |
m.values[0] = scale.x; | |
m.values[5] = scale.y; | |
m.values[10] = scale.z; | |
return m; | |
} | |
Matrix4 Matrix4::Translation(const Vector3 &translation) | |
{ | |
Matrix4 m; | |
m.values[12] = translation.x; | |
m.values[13] = translation.y; | |
m.values[14] = translation.z; | |
return m; | |
} | |
// Yoinked from the Open Source Doom 3 release - all credit goes to id software! | |
Matrix4 Matrix4::Inverse() | |
{ | |
float det, invDet; | |
Matrix4 mat = *this; | |
mat.values[0]; | |
// 2x2 sub-determinants required to calculate 4x4 determinant | |
float det2_01_01 = mat.values[0] * mat.values[5] - mat.values[1] * mat.values[4]; | |
float det2_01_02 = mat.values[0] * mat.values[6] - mat.values[2] * mat.values[4]; | |
float det2_01_03 = mat.values[0] * mat.values[7] - mat.values[3] * mat.values[4]; | |
float det2_01_12 = mat.values[1] * mat.values[6] - mat.values[2] * mat.values[5]; | |
float det2_01_13 = mat.values[1] * mat.values[7] - mat.values[3] * mat.values[5]; | |
float det2_01_23 = mat.values[2] * mat.values[7] - mat.values[3] * mat.values[6]; | |
// 3x3 sub-determinants required to calculate 4x4 determinant | |
float det3_201_012 = | |
mat.values[8] * det2_01_12 - mat.values[9] * det2_01_02 + mat.values[10] * det2_01_01; | |
float det3_201_013 = | |
mat.values[8] * det2_01_13 - mat.values[9] * det2_01_03 + mat.values[11] * det2_01_01; | |
float det3_201_023 = | |
mat.values[8] * det2_01_23 - mat.values[10] * det2_01_03 + mat.values[11] * det2_01_02; | |
float det3_201_123 = | |
mat.values[9] * det2_01_23 - mat.values[10] * det2_01_13 + mat.values[11] * det2_01_12; | |
det = (-det3_201_123 * mat.values[12] + det3_201_023 * mat.values[13] - | |
det3_201_013 * mat.values[14] + det3_201_012 * mat.values[15]); | |
invDet = 1.0f / det; | |
// remaining 2x2 sub-determinants | |
float det2_03_01 = mat.values[0] * mat.values[13] - mat.values[1] * mat.values[12]; | |
float det2_03_02 = mat.values[0] * mat.values[14] - mat.values[2] * mat.values[12]; | |
float det2_03_03 = mat.values[0] * mat.values[15] - mat.values[3] * mat.values[12]; | |
float det2_03_12 = mat.values[1] * mat.values[14] - mat.values[2] * mat.values[13]; | |
float det2_03_13 = mat.values[1] * mat.values[15] - mat.values[3] * mat.values[13]; | |
float det2_03_23 = mat.values[2] * mat.values[15] - mat.values[3] * mat.values[14]; | |
float det2_13_01 = mat.values[4] * mat.values[13] - mat.values[5] * mat.values[12]; | |
float det2_13_02 = mat.values[4] * mat.values[14] - mat.values[6] * mat.values[12]; | |
float det2_13_03 = mat.values[4] * mat.values[15] - mat.values[7] * mat.values[12]; | |
float det2_13_12 = mat.values[5] * mat.values[14] - mat.values[6] * mat.values[13]; | |
float det2_13_13 = mat.values[5] * mat.values[15] - mat.values[7] * mat.values[13]; | |
float det2_13_23 = mat.values[6] * mat.values[15] - mat.values[7] * mat.values[14]; | |
// remaining 3x3 sub-determinants | |
float det3_203_012 = | |
mat.values[8] * det2_03_12 - mat.values[9] * det2_03_02 + mat.values[10] * det2_03_01; | |
float det3_203_013 = | |
mat.values[8] * det2_03_13 - mat.values[9] * det2_03_03 + mat.values[11] * det2_03_01; | |
float det3_203_023 = | |
mat.values[8] * det2_03_23 - mat.values[10] * det2_03_03 + mat.values[11] * det2_03_02; | |
float det3_203_123 = | |
mat.values[9] * det2_03_23 - mat.values[10] * det2_03_13 + mat.values[11] * det2_03_12; | |
float det3_213_012 = | |
mat.values[8] * det2_13_12 - mat.values[9] * det2_13_02 + mat.values[10] * det2_13_01; | |
float det3_213_013 = | |
mat.values[8] * det2_13_13 - mat.values[9] * det2_13_03 + mat.values[11] * det2_13_01; | |
float det3_213_023 = | |
mat.values[8] * det2_13_23 - mat.values[10] * det2_13_03 + mat.values[11] * det2_13_02; | |
float det3_213_123 = | |
mat.values[9] * det2_13_23 - mat.values[10] * det2_13_13 + mat.values[11] * det2_13_12; | |
float det3_301_012 = | |
mat.values[12] * det2_01_12 - mat.values[13] * det2_01_02 + mat.values[14] * det2_01_01; | |
float det3_301_013 = | |
mat.values[12] * det2_01_13 - mat.values[13] * det2_01_03 + mat.values[15] * det2_01_01; | |
float det3_301_023 = | |
mat.values[12] * det2_01_23 - mat.values[14] * det2_01_03 + mat.values[15] * det2_01_02; | |
float det3_301_123 = | |
mat.values[13] * det2_01_23 - mat.values[14] * det2_01_13 + mat.values[15] * det2_01_12; | |
mat.values[0] = -det3_213_123 * invDet; | |
mat.values[4] = +det3_213_023 * invDet; | |
mat.values[8] = -det3_213_013 * invDet; | |
mat.values[12] = +det3_213_012 * invDet; | |
mat.values[1] = +det3_203_123 * invDet; | |
mat.values[5] = -det3_203_023 * invDet; | |
mat.values[9] = +det3_203_013 * invDet; | |
mat.values[13] = -det3_203_012 * invDet; | |
mat.values[2] = +det3_301_123 * invDet; | |
mat.values[6] = -det3_301_023 * invDet; | |
mat.values[10] = +det3_301_013 * invDet; | |
mat.values[14] = -det3_301_012 * invDet; | |
mat.values[3] = -det3_201_123 * invDet; | |
mat.values[7] = +det3_201_023 * invDet; | |
mat.values[11] = -det3_201_013 * invDet; | |
mat.values[15] = +det3_201_012 * invDet; | |
return mat; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Matrix4 | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:VERY simple 4 by 4 matrix class. Students are encouraged to modify | |
this as necessary! Overloading the [] operator to allow access to the values | |
array in a neater way might be a good start, as the floats that make the matrix | |
up are currently public. | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include <iostream> | |
#include "common.h" | |
#include "Vector3.h" | |
#include "Vector4.h" | |
class Vector3; | |
class Matrix4 | |
{ | |
public: | |
Matrix4(void); | |
Matrix4(float elements[16]); | |
~Matrix4(void); | |
float values[16]; | |
// Set all matrix values to zero | |
void ToZero(); | |
// Sets matrix to identity matrix (1.0 down the diagonal) | |
void ToIdentity(); | |
// Gets the OpenGL position vector (floats 12,13, and 14) | |
Vector3 GetPositionVector() const; | |
// Sets the OpenGL position vector (floats 12,13, and 14) | |
void SetPositionVector(const Vector3 in); | |
// Gets the scale vector (floats 1,5, and 10) | |
Vector3 GetScalingVector() const; | |
// Sets the scale vector (floats 1,5, and 10) | |
void SetScalingVector(const Vector3 &in); | |
// Creates a rotation matrix that rotates by 'degrees' around the 'axis' | |
// Analogous to glRotatef | |
static Matrix4 Rotation(float degrees, const Vector3 &axis); | |
// Creates a scaling matrix (puts the 'scale' vector down the diagonal) | |
// Analogous to glScalef | |
static Matrix4 Scale(const Vector3 &scale); | |
// Creates a translation matrix (identity, with 'translation' vector at | |
// floats 12, 13, and 14. Analogous to glTranslatef | |
static Matrix4 Translation(const Vector3 &translation); | |
// Creates a perspective matrix, with 'znear' and 'zfar' as the near and | |
// far planes, using 'aspect' and 'fov' as the aspect ratio and vertical | |
// field of vision, respectively. | |
static Matrix4 Perspective(float znear, float zfar, float aspect, float fov); | |
// Creates an orthographic matrix with 'znear' and 'zfar' as the near and | |
// far planes, and so on. Descriptive variable names are a good thing! | |
static Matrix4 Orthographic(float znear, float zfar, float right, float left, float top, | |
float bottom); | |
// Builds a view matrix suitable for sending straight to the vertex shader. | |
// Puts the camera at 'from', with 'lookingAt' centered on the screen, with | |
//'up' as the...up axis (pointing towards the top of the screen) | |
static Matrix4 BuildViewMatrix(const Vector3 &from, const Vector3 &lookingAt, | |
const Vector3 up = Vector3(0, 1, 0)); | |
Matrix4 Inverse(); | |
Matrix4 GetTransposedRotation() | |
{ | |
Matrix4 temp; | |
temp.values[1] = values[4]; | |
temp.values[4] = values[1]; | |
temp.values[2] = values[8]; | |
temp.values[8] = values[2]; | |
temp.values[6] = values[9]; | |
temp.values[9] = values[6]; | |
return temp; | |
} | |
void SetRow(unsigned int row, const Vector4 &val) | |
{ | |
if (row < 3) | |
{ | |
int start = 4 * row; | |
values[start += 4] = val.x; | |
values[start += 4] = val.y; | |
values[start += 4] = val.z; | |
values[start += 4] = val.w; | |
} | |
} | |
void SetColumn(unsigned int column, const Vector4 &val) | |
{ | |
if (column < 3) | |
{ | |
memcpy(&values[4 * column], &val, sizeof(Vector4)); | |
} | |
} | |
Vector4 GetRow(unsigned int row) | |
{ | |
Vector4 out(0, 0, 0, 1); | |
if (row < 3) | |
{ | |
int start = 4 * row; | |
out.x = values[start += 4]; | |
out.y = values[start += 4]; | |
out.z = values[start += 4]; | |
out.w = values[start += 4]; | |
} | |
return out; | |
} | |
Vector4 GetColumn(unsigned int column) | |
{ | |
Vector4 out(0, 0, 0, 1); | |
if (column < 3) | |
{ | |
memcpy(&out, &values[4 * column], sizeof(Vector4)); | |
} | |
return out; | |
} | |
// Multiplies 'this' matrix by matrix 'a'. Performs the multiplication in 'OpenGL' order (ie, | |
// backwards) | |
inline Matrix4 operator*(const Matrix4 &b) const | |
{ | |
Matrix4 out; | |
for (unsigned int col = 0; col < 4; ++col) | |
{ | |
for (unsigned int row = 0; row < 4; ++row) | |
{ | |
int current = row + (col * 4); | |
out.values[row + (col * 4)] = 0.0f; | |
for (unsigned int i = 0; i < 4; ++i) | |
{ | |
out.values[row + (col * 4)] += this->values[row + (i * 4)] * b.values[(col * 4) + i]; | |
} | |
} | |
} | |
return out; | |
} | |
inline Vector3 operator*(const Vector3 &v) const | |
{ | |
Vector3 vec; | |
float temp; | |
vec.x = v.x * values[0] + v.y * values[4] + v.z * values[8] + values[12]; | |
vec.y = v.x * values[1] + v.y * values[5] + v.z * values[9] + values[13]; | |
vec.z = v.x * values[2] + v.y * values[6] + v.z * values[10] + values[14]; | |
temp = v.x * values[3] + v.y * values[7] + v.z * values[11] + values[15]; | |
vec.x = vec.x / temp; | |
vec.y = vec.y / temp; | |
vec.z = vec.z / temp; | |
return vec; | |
}; | |
inline Vector4 operator*(const Vector4 &v) const | |
{ | |
return Vector4(v.x * values[0] + v.y * values[4] + v.z * values[8] + v.w * values[12], | |
v.x * values[1] + v.y * values[5] + v.z * values[9] + v.w * values[13], | |
v.x * values[2] + v.y * values[6] + v.z * values[10] + v.w * values[14], | |
v.x * values[3] + v.y * values[7] + v.z * values[11] + v.w * values[15]); | |
}; | |
// Handy string output for the matrix. Can get a bit messy, but better than nothing! | |
inline friend std::ostream &operator<<(std::ostream &o, const Matrix4 &m) | |
{ | |
o << "Mat4("; | |
o << "\t" << m.values[0] << "," << m.values[1] << "," << m.values[2] << "," << m.values[3] | |
<< std::endl; | |
o << "\t\t" << m.values[4] << "," << m.values[5] << "," << m.values[6] << "," << m.values[7] | |
<< std::endl; | |
o << "\t\t" << m.values[8] << "," << m.values[9] << "," << m.values[10] << "," << m.values[11] | |
<< std::endl; | |
o << "\t\t" << m.values[12] << "," << m.values[13] << "," << m.values[14] << "," << m.values[15] | |
<< " )" << std::endl; | |
return o; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Mesh.h" | |
#include <vector> | |
Mesh::Mesh(void) | |
{ | |
type = PRIMITIVE_POINTS; | |
numVertices = 0; | |
vertices = NULL; | |
colours = NULL; | |
textureCoords = NULL; | |
} | |
Mesh::~Mesh(void) | |
{ | |
delete[] vertices; | |
delete[] colours; | |
delete[] textureCoords; | |
} | |
Mesh *Mesh::LoadMeshFile(const string &filename) | |
{ | |
ifstream f(filename); | |
if (!f) | |
return NULL; | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLES; | |
f >> m->numVertices; | |
int hasTex = 0; | |
f >> hasTex; | |
int hasColour = 0; | |
f >> hasColour; | |
m->vertices = new Vector4[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
for (unsigned int i = 0; i < m->numVertices; ++i) | |
{ | |
f >> m->vertices[i].x; | |
f >> m->vertices[i].y; | |
f >> m->vertices[i].z; | |
} | |
if (hasColour) | |
{ | |
for (unsigned int i = 0; i < m->numVertices; ++i) | |
{ | |
f >> m->colours[i].r; | |
f >> m->colours[i].g; | |
f >> m->colours[i].b; | |
f >> m->colours[i].a; | |
} | |
} | |
if (hasTex) | |
{ | |
for (unsigned int i = 0; i < m->numVertices; ++i) | |
{ | |
f >> m->textureCoords[i].x; | |
f >> m->textureCoords[i].y; | |
} | |
} | |
return m; | |
} | |
Mesh *Mesh::GeneratePoint(const Vector3 &from, const Colour &col) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_POINTS; | |
m->numVertices = 1; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
m->vertices[0] = Vector4(from.x, from.y, from.z, 1.0f); | |
m->colours[0] = Colour(col.r, col.g, col.b, col.a); | |
m->textureCoords[0] = Vector2(0.0f, 0.0f); | |
return m; | |
} | |
Mesh *Mesh::GenerateLine(const Vector3 &from, const Vector3 &to) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_LINES; | |
m->numVertices = 2; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
m->vertices[0] = Vector4(from.x, from.y, from.z, 1.0f); | |
m->colours[0] = Colour(255, 0, 0, 255); | |
m->textureCoords[0] = Vector2(0.0f, 0.0f); | |
m->vertices[1] = Vector4(to.x, to.y, to.z, 1.0f); | |
m->colours[1] = Colour(0, 0, 255, 255); | |
m->textureCoords[1] = Vector2(1.0f, 1.0f); | |
return m; | |
} | |
Mesh *Mesh::GenerateNSided2D(const int n) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_LINES; | |
m->numVertices = n * 2; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
const float p = 2 * PI / n; | |
for (int i = 0; i < m->numVertices; i += 2) | |
{ | |
const float x1 = cos(p * i); | |
const float y1 = sin(p * i); | |
m->vertices[i] = Vector4(x1, y1, 0.0, 1.0f); | |
m->colours[i] = Colour(255, 255, 255, 255); | |
m->textureCoords[i] = Vector2(x1, y1); | |
const float x2 = cos(p * (i + 1)); | |
const float y2 = sin(p * (i + 1)); | |
m->vertices[i + 1] = Vector4(x2, y2, 0.0, 1.0f); | |
m->colours[i + 1] = Colour(255, 255, 255, 255); | |
m->textureCoords[i + 1] = Vector2(x2, y2); | |
} | |
return m; | |
} | |
Mesh *Mesh::GenerateTriangle() | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLES; | |
m->numVertices = 3; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
//m->vertices[0] = Vector4(0.55f, 0.55f, -0.6, 1.0f); | |
//m->vertices[1] = Vector4(0.5f, 0.65f, -0.6, 1.0f); | |
//m->vertices[2] = Vector4(0.45f, 0.55f, -0.6, 1.0f); | |
m->vertices[0] = Vector4(0.5f, -0.5f, 0.0f, 1.0f); | |
m->vertices[1] = Vector4(0.0f, 0.5f, 0.0f, 1.0f); | |
m->vertices[2] = Vector4(-0.5f, -0.5f, 0.0f, 1.0f); | |
m->colours[0] = Colour(255, 0, 0, 127); | |
m->colours[1] = Colour(0, 255, 0, 127); | |
m->colours[2] = Colour(0, 0, 255, 127); | |
m->textureCoords[0] = Vector2(0.0f, 0.0f); | |
m->textureCoords[1] = Vector2(0.5f, 1.0f); | |
m->textureCoords[2] = Vector2(1.0f, 0.0f); | |
return m; | |
} | |
Mesh *Mesh::GenerateTriangleStrip() | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLE_STRIP; | |
m->numVertices = 5; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
m->vertices[0] = Vector4(0.0f, 0.0f, 0.0f, 1.0f); | |
m->vertices[1] = Vector4(0.25f, 0.5f, 0.0f, 1.0f); | |
m->vertices[2] = Vector4(0.5f, 0.0f, 0.0f, 1.0f); | |
m->vertices[3] = Vector4(0.75f, 0.5f, 0.0f, 1.0f); | |
m->vertices[4] = Vector4(1.0f, 0.0f, 0.0f, 1.0f); | |
m->colours[0] = Colour(255, 0, 0, 127); | |
m->colours[1] = Colour(0, 255, 0, 127); | |
m->colours[2] = Colour(0, 0, 255, 127); | |
m->colours[3] = Colour(255, 0, 0, 255); | |
m->colours[4] = Colour(0, 255, 0, 255); | |
m->textureCoords[0] = Vector2(0.0f, 0.0f); | |
m->textureCoords[1] = Vector2(0.5f, 1.0f); | |
m->textureCoords[2] = Vector2(1.0f, 0.0f); | |
m->textureCoords[3] = Vector2(1.0f, 0.5f); | |
m->textureCoords[4] = Vector2(0.5f, 0.5f); | |
return m; | |
} | |
Mesh *Mesh::GenerateTriangleFan() | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLE_FAN; | |
m->numVertices = 5; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
m->vertices[0] = Vector4(0.0f, 0.0f, 0.0f, 1.0f); | |
m->vertices[1] = Vector4(0.5f, 0.5f, 0.0f, 1.0f); | |
m->vertices[2] = Vector4(0.5f, -0.5f, 0.0f, 1.0f); | |
m->vertices[3] = Vector4(-0.5f, -0.5f, 0.0f, 1.0f); | |
m->vertices[4] = Vector4(-0.5f, 0.5f, 0.0f, 1.0f); | |
m->colours[0] = Colour(0, 0, 0, 127); | |
m->colours[1] = Colour(255, 0, 0, 127); | |
m->colours[2] = Colour(0, 255, 0, 127); | |
m->colours[3] = Colour(0, 0, 255, 127); | |
m->colours[4] = Colour(255, 255, 255, 127); | |
m->textureCoords[0] = Vector2(0.0f, 0.0f); | |
m->textureCoords[1] = Vector2(0.5f, 1.0f); | |
m->textureCoords[2] = Vector2(1.0f, 0.0f); | |
m->textureCoords[3] = Vector2(1.0f, 0.0f); | |
m->textureCoords[4] = Vector2(1.0f, 0.0f); | |
return m; | |
} | |
/** | |
* Generates a 3D sphere. | |
* | |
* \param radius Radius (default 1.0) | |
* \param resolution Number of "slices" in lon and lat | |
* \param c Solid colour | |
* \return New mesh | |
*/ | |
Mesh *Mesh::GenerateSphere(const float radius, const int resolution, const Colour &c) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLE_STRIP; | |
m->numVertices = resolution * resolution * 2; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
const float deltaTheta = (PI / resolution); | |
const float deltaPhi = ((PI * 2) / resolution); | |
const float texEpsilon = 1.0f / resolution; | |
int n = 0; | |
for (int i = 0; i < resolution; i++) | |
{ | |
const float theta1 = i * deltaTheta; | |
const float theta2 = (i + 1) * deltaTheta; | |
const float u1 = i * texEpsilon; | |
const float u2 = (i + 1) * texEpsilon; | |
for (int j = 0; j < resolution; j++) | |
{ | |
const float phi = j * deltaPhi; | |
const float v = j * texEpsilon; | |
m->vertices[n] = Vector4(cos(theta1) * sin(phi) * radius, sin(theta1) * sin(phi) * radius, | |
cos(phi) * radius, 1.0f); | |
m->colours[n] = c; | |
m->textureCoords[n] = Vector2(u1, v); | |
n++; | |
m->vertices[n] = Vector4(cos(theta2) * sin(phi) * radius, sin(theta2) * sin(phi) * radius, | |
cos(phi) * radius, 1.0f); | |
m->colours[n] = c; | |
m->textureCoords[n] = Vector2(u2, v); | |
n++; | |
} | |
} | |
return m; | |
} | |
/** | |
* Generates a 2D filled circle/disc. | |
* | |
* \param radius Radius (default 1.0) | |
* \param resolution Number of "slices" in angle | |
* \return New mesh | |
*/ | |
Mesh *Mesh::GenerateDisc2D(const float radius, const int resolution) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLE_FAN; | |
m->numVertices = resolution + 2; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
const float deltaA = ((PI * 2) / resolution); | |
// "Origin" vertex | |
m->vertices[0] = Vector4(0.0f, 0.0f, 0.0f, 1.0f); | |
m->colours[0] = Colour::White; | |
m->textureCoords[0] = Vector2(0.5f, 0.5f); | |
for (int i = 1; i < resolution + 2; ++i) | |
{ | |
const float a = i * deltaA; | |
m->vertices[i] = Vector4(cos(a) * radius, sin(a) * radius, 0.0f, 1.0f); | |
m->colours[i] = Colour::White; | |
Vector2 v = Vector2(abs(cos(a)), abs(sin(a))); | |
m->textureCoords[i] = v; | |
} | |
return m; | |
} | |
/** | |
* Generates a 2D ring. | |
* | |
* \param radiusOuter Outer radius | |
* \param radiusInner Inner radius | |
* \param resolution Number of "slices" in angle | |
* \return New mesh | |
*/ | |
Mesh *Mesh::GenerateRing2D(const float radiusOuter, const float radiusInner, const int resolution) | |
{ | |
Mesh *m = new Mesh(); | |
m->type = PRIMITIVE_TRIANGLE_STRIP; | |
m->numVertices = (resolution + 2) * 2; | |
m->vertices = new Vector4[m->numVertices]; | |
m->colours = new Colour[m->numVertices]; | |
m->textureCoords = new Vector2[m->numVertices]; | |
const float deltaA = ((PI * 2) / resolution); | |
int n = 0; | |
for (int i = 0; i < resolution + 2; i++) | |
{ | |
const float a = i * deltaA; | |
m->vertices[n] = Vector4(cos(a) * radiusOuter, sin(a) * radiusOuter, 0.0f, 1.0f); | |
m->colours[n] = Colour::White; | |
m->textureCoords[n] = Vector2(abs(cos(a)), abs(sin(a))); | |
n++; | |
m->vertices[n] = Vector4(cos(a) * radiusInner, sin(a) * radiusInner, 0.0f, 1.0f); | |
m->colours[n] = Colour::White; | |
m->textureCoords[n] = Vector2(abs(cos(a)), abs(sin(a))); | |
n++; | |
} | |
return m; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Mesh | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description: Class to represent the geometric data that makes up the meshes | |
we render on screen. | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "Vector4.h" | |
#include "Colour.h" | |
#include "Vector3.h" | |
#include "Vector2.h" | |
#include "Common.h" | |
#include <string> | |
#include <fstream> | |
using std::ifstream; | |
using std::string; | |
enum PrimitiveType | |
{ | |
PRIMITIVE_POINTS, | |
PRIMITIVE_LINES, | |
PRIMITIVE_TRIANGLES, | |
PRIMITIVE_TRIANGLE_STRIP, | |
PRIMITIVE_TRIANGLE_FAN | |
}; | |
class Mesh | |
{ | |
friend class SoftwareRasteriser; | |
public: | |
Mesh(void); | |
~Mesh(void); | |
static Mesh *LoadMeshFile(const string &filename); | |
static Mesh *GeneratePoint(const Vector3 &pos, const Colour &col = Colour(255, 255, 255, 255)); | |
static Mesh *GenerateLine(const Vector3 &from, const Vector3 &to); | |
static Mesh *GenerateNSided2D(const int n); | |
static Mesh *GenerateTriangle(); | |
static Mesh *GenerateTriangleStrip(); | |
static Mesh *GenerateTriangleFan(); | |
static Mesh *GenerateSphere(const float radius = 1.0f, const int resolution = 10, | |
const Colour &c = Colour::White); | |
static Mesh *GenerateDisc2D(const float radius = 1.0f, const int resolution = 10); | |
static Mesh *GenerateRing2D(const float radiusOuter = 1.0f, const float radiusInner = 0.8f, | |
const int resolution = 10); | |
PrimitiveType GetType() | |
{ | |
return type; | |
} | |
protected: | |
PrimitiveType type; | |
uint numVertices; | |
Vector4 *vertices; | |
Colour *colours; | |
Vector2 *textureCoords; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Mouse.h" | |
Mouse *Mouse::instance = 0; | |
Mouse::Mouse(HWND &hwnd) | |
{ | |
ZeroMemory(buttons, sizeof(bool) * MOUSE_MAX); | |
ZeroMemory(holdButtons, sizeof(bool) * MOUSE_MAX); | |
ZeroMemory(doubleClicks, sizeof(bool) * MOUSE_MAX); | |
ZeroMemory(lastClickTime, sizeof(float) * MOUSE_MAX); | |
lastWheel = 0; | |
frameWheel = 0; | |
sensitivity = 0.07f; // Chosen for no other reason than it's a nice value for my Deathadder ;) | |
clickLimit = 200.0f; | |
rid.usUsagePage = HID_USAGE_PAGE_GENERIC; | |
rid.usUsage = HID_USAGE_GENERIC_MOUSE; | |
rid.dwFlags = RIDEV_INPUTSINK; | |
rid.hwndTarget = hwnd; | |
RegisterRawInputDevices(&rid, 1, sizeof(rid)); | |
} | |
void Mouse::Initialise(HWND &hwnd) | |
{ | |
instance = new Mouse(hwnd); | |
} | |
void Mouse::Destroy() | |
{ | |
delete instance; | |
} | |
void Mouse::Update(RAWINPUT *raw) | |
{ | |
if (isAwake) | |
{ | |
/* | |
Update the absolute and relative mouse movements | |
*/ | |
relativePosition.x += ((float)raw->data.mouse.lLastX) * sensitivity; | |
relativePosition.y += ((float)raw->data.mouse.lLastY) * sensitivity; | |
absolutePosition.x += (float)raw->data.mouse.lLastX; | |
absolutePosition.y += (float)raw->data.mouse.lLastY; | |
/* | |
Bounds check the absolute position of the mouse, so it doesn't disappear off screen edges... | |
*/ | |
absolutePosition.x = max(absolutePosition.x, 0.0f); | |
absolutePosition.x = min(absolutePosition.x, absolutePositionBounds.x); | |
absolutePosition.y = max(absolutePosition.y, 0.0f); | |
absolutePosition.y = min(absolutePosition.y, absolutePositionBounds.y); | |
/* | |
TODO: How framerate independent is this? | |
*/ | |
if (raw->data.mouse.usButtonFlags & RI_MOUSE_WHEEL) | |
{ | |
if (raw->data.mouse.usButtonData == 120) | |
{ | |
frameWheel = 1; | |
} | |
else | |
{ | |
frameWheel = -1; | |
} | |
} | |
/* | |
Oh, Microsoft... | |
*/ | |
static int buttondowns[5] = { RI_MOUSE_BUTTON_1_DOWN, RI_MOUSE_BUTTON_2_DOWN, | |
RI_MOUSE_BUTTON_3_DOWN, RI_MOUSE_BUTTON_4_DOWN, | |
RI_MOUSE_BUTTON_5_DOWN }; | |
static int buttonps[5] = { RI_MOUSE_BUTTON_1_UP, RI_MOUSE_BUTTON_2_UP, RI_MOUSE_BUTTON_3_UP, | |
RI_MOUSE_BUTTON_4_UP, RI_MOUSE_BUTTON_5_UP }; | |
for (int i = 0; i < 5; ++i) | |
{ | |
if (raw->data.mouse.usButtonFlags & buttondowns[i]) | |
{ | |
// The button was pressed! | |
buttons[i] = true; | |
/* | |
If it wasn't too long ago since we last clicked, we trigger a double click! | |
*/ | |
if (lastClickTime[i] > 0) | |
{ | |
doubleClicks[i] = true; | |
} | |
/* | |
No matter whether the mouse was double clicked or not, we reset the clicklimit | |
*/ | |
lastClickTime[i] = clickLimit; | |
} | |
else if (raw->data.mouse.usButtonFlags & buttonps[i]) | |
{ | |
// The button has been released! | |
buttons[i] = false; | |
holdButtons[i] = false; | |
} | |
} | |
} | |
} | |
/* | |
Sets the mouse sensitivity (higher = mouse pointer moves more!) | |
Lower bounds checked. | |
*/ | |
void Mouse::SetMouseSensitivity(float amount) | |
{ | |
if (amount == 0.0f) | |
{ | |
amount = 1.0f; | |
} | |
sensitivity = amount; | |
} | |
/* | |
Updates variables controlling whether a mouse button has been | |
held for multiple frames. Also updates relative movement. | |
*/ | |
void Mouse::UpdateHolds() | |
{ | |
memcpy(holdButtons, buttons, MOUSE_MAX * sizeof(bool)); | |
// We sneak this in here, too. Resets how much the mouse has moved | |
// since last update | |
relativePosition.ToZero(); | |
// And the same for the mouse wheel | |
frameWheel = 0; | |
} | |
/* | |
Sends the mouse to sleep, so it doesn't process any | |
movement or buttons until it receives a Wake() | |
*/ | |
void Mouse::Sleep() | |
{ | |
isAwake = false; // Bye bye for now | |
ZeroMemory(holdButtons, MOUSE_MAX * sizeof(bool)); | |
ZeroMemory(buttons, MOUSE_MAX * sizeof(bool)); | |
} | |
/* | |
Forces the mouse pointer to a specific point in absolute space. | |
*/ | |
void Mouse::SetAbsolutePosition(unsigned int x, unsigned int y) | |
{ | |
absolutePosition.x = (float)x; | |
absolutePosition.y = (float)y; | |
} | |
/* | |
Returns if the button is down. Doesn't need bounds checking - | |
an INPUT_KEYS enum is always in range | |
*/ | |
bool Mouse::ButtonDown(MouseButtons b) | |
{ | |
return instance->buttons[b]; | |
} | |
/* | |
Returns if the button is down, and has been held down for multiple updates. | |
Doesn't need bounds checking - an INPUT_KEYS enum is always in range | |
*/ | |
bool Mouse::ButtonHeld(MouseButtons b) | |
{ | |
return instance->holdButtons[b]; | |
} | |
/* | |
Returns how much the mouse has moved by since the last frame. | |
*/ | |
Vector2 Mouse::GetRelativePosition() | |
{ | |
return instance->relativePosition; | |
} | |
/* | |
Returns the mouse pointer position in absolute space. | |
*/ | |
Vector2 Mouse::GetAbsolutePosition() | |
{ | |
return instance->absolutePosition; | |
} | |
/* | |
Returns how much the mouse has moved by since the last frame. | |
*/ | |
void Mouse::SetAbsolutePositionBounds(unsigned int maxX, unsigned int maxY) | |
{ | |
absolutePositionBounds.x = (float)maxX; | |
absolutePositionBounds.y = (float)maxY; | |
} | |
/* | |
Has the mousewheel been moved since the last frame? | |
*/ | |
bool Mouse::WheelMoved() | |
{ | |
return instance->frameWheel != 0; | |
}; | |
/* | |
Returns whether the button was double clicked in this frame | |
*/ | |
bool Mouse::DoubleClicked(MouseButtons button) | |
{ | |
return (instance->buttons[button] && instance->doubleClicks[button]); | |
} | |
/* | |
Get the mousewheel movement. Positive values mean the mousewheel | |
has moved up, negative down. Can be 0 (no movement) | |
*/ | |
int Mouse::GetWheelMovement() | |
{ | |
return (int)instance->frameWheel; | |
} | |
/* | |
Updates the double click timers for each mouse button. msec is milliseconds | |
since the last UpdateDoubleClick call. Timers going over the double click | |
limit set the relevant double click value to false. | |
*/ | |
void Mouse::UpdateDoubleClick(float msec) | |
{ | |
for (int i = 0; i < MOUSE_MAX; ++i) | |
{ | |
if (lastClickTime[i] > 0) | |
{ | |
lastClickTime[i] -= msec; | |
if (lastClickTime[i] <= 0.0f) | |
{ | |
doubleClicks[i] = false; | |
lastClickTime[i] = 0.0f; | |
} | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Mouse | |
Implements:InputDevice | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Windows RAW input mouse, with a couple of game-related enhancements | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "InputDevice.h" | |
#include "Vector2.h" | |
// Presumably RAW input does actually support those fancy mice with greater | |
// than 5 buttons in some capacity, but I have a 5 button mouse so I don't | |
// care to find out how ;) | |
enum MouseButtons | |
{ | |
MOUSE_LEFT = 0, | |
MOUSE_RIGHT = 1, | |
MOUSE_MIDDLE = 2, | |
MOUSE_FOUR = 3, | |
MOUSE_FIVE = 4, | |
MOUSE_MAX = 5 | |
}; | |
class Mouse : public InputDevice | |
{ | |
public: | |
friend class Window; | |
// Is this mouse button currently pressed down? | |
static bool ButtonDown(MouseButtons button); | |
// Has this mouse button been held down for multiple frames? | |
static bool ButtonHeld(MouseButtons button); | |
// Has this mouse button been double clicked? | |
static bool DoubleClicked(MouseButtons button); | |
// Get how much this mouse has moved since last frame | |
static Vector2 GetRelativePosition(); | |
// Get the window position of the mouse pointer | |
static Vector2 GetAbsolutePosition(); | |
// Determines the maximum amount of ms that can pass between | |
// 2 mouse presses while still counting as a 'double click' | |
static void SetDoubleClickLimit(float msec); | |
// Has the mouse wheel moved since the last update? | |
static bool WheelMoved(); | |
// Get the mousewheel movement. Positive means scroll up, | |
// negative means scroll down, 0 means no movement. | |
static int GetWheelMovement(); | |
// Sets the mouse sensitivity. Currently only affects the 'relative' | |
//(i.e FPS-style) mouse movement. Students! Maybe you'd like to | |
// implement a 'MenuSensitivity' for absolute movement? | |
void SetMouseSensitivity(float amount); | |
protected: | |
Mouse(HWND &hwnd); | |
~Mouse(void) | |
{ | |
} | |
static void Initialise(HWND &hwnd); | |
static void Destroy(); | |
static Mouse *instance; | |
// Internal function that updates the mouse variables from a | |
// raw input 'packet' | |
virtual void Update(RAWINPUT *raw); | |
// Updates the holdButtons array. Call once per frame! | |
virtual void UpdateHolds(); | |
// Sends the mouse to sleep (i.e window has been alt-tabbed away etc) | |
virtual void Sleep(); | |
// Updates the doubleclicks array. Call once per frame! | |
void UpdateDoubleClick(float msec); | |
// Set the mouse's current screen position. Maybe should be public? | |
void SetAbsolutePosition(unsigned int x, unsigned int y); | |
// Set the absolute screen bounds (<0 is always assumed dissallowed). Used | |
// by the window resize routine... | |
void SetAbsolutePositionBounds(unsigned int maxX, unsigned int maxY); | |
// Current mouse absolute position | |
Vector2 absolutePosition; | |
// Current mouse absolute position maximum bounds | |
Vector2 absolutePositionBounds; | |
// How much as the mouse moved since the last raw packet? | |
Vector2 relativePosition; | |
// Current button down state for each button | |
bool buttons[MOUSE_MAX]; | |
// Current button held state for each button | |
bool holdButtons[MOUSE_MAX]; | |
// Current doubleClick counter for each button | |
bool doubleClicks[MOUSE_MAX]; | |
// Counter to remember when last mouse click occured | |
float lastClickTime[MOUSE_MAX]; | |
// last mousewheel updated position | |
int lastWheel; | |
// Current mousewheel updated position | |
int frameWheel; | |
// Max amount of ms between clicks count as a 'double click' | |
float clickLimit; | |
// Mouse pointer sensitivity. Set this negative to get a headache! | |
float sensitivity; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "RenderObject.h" | |
RenderObject::RenderObject(void) | |
{ | |
texture = NULL; | |
mesh = NULL; | |
} | |
RenderObject::~RenderObject(void) | |
{ | |
if (texture != NULL) | |
delete texture; | |
if (mesh != NULL) | |
delete mesh; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:RenderObject | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description: Class to represent an object in our basic rendering program | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "Mesh.h" | |
#include "Texture.h" | |
#include "Matrix4.h" | |
class Texture; | |
class RenderObject | |
{ | |
public: | |
RenderObject(void); | |
~RenderObject(void); | |
Mesh *GetMesh() | |
{ | |
return mesh; | |
} | |
Texture *GetTexure() | |
{ | |
return texture; | |
} | |
Matrix4 GetModelMatrix() | |
{ | |
return modelMatrix; | |
} | |
// protected: | |
Matrix4 modelMatrix; | |
Texture *texture; | |
Mesh *mesh; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "SoftwareRasteriser.h" | |
#include <cmath> | |
#include <math.h> | |
/* | |
While less 'neat' than just doing a 'new', like in the tutorials, it's usually | |
possible to render a bit quicker to use direct pointers to the drawing area | |
that the OS gives you. For a bit of a speedup, you can uncomment the define below | |
to switch to using this method. | |
For those of you new to the preprocessor, here's a quick explanation: | |
Preprocessor definitions like #define allow parts of a file to be selectively enabled | |
or disabled at compile time. This is useful for hiding parts of the codebase on a | |
per-platform basis: if you have support for linux and windows in your codebase, obviously | |
the linux platform won't have the windows platform headers available, so compilation will | |
fail. So instead you can hide away all the platform specific stuff: | |
#if PLATFORM_WINDOWS | |
DoSomeWindowsStuff(); | |
#elif PLATFORM_LINUX | |
DoSomeLinuxStuff(); | |
#else | |
#error Unsupported Platform Specified! | |
#endif | |
As in our usage, it also allows you to selectively compile in some different functionality | |
without any 'run time' cost - if it's not enabled by the preprocessor, it won't make it to | |
the compiler, so no assembly will be generated. | |
Also, I've implemented the Resize method for you, in a manner that'll behave itself | |
no matter which method you use. I kinda forgot to do that, so there was a chance you'd | |
get exceptions if you resized to a bigger screen area. Sorry about that. | |
*/ | |
//#define USE_OS_BUFFERS | |
#define MAX_VERTS 16 | |
const int INSIDE_CS = 0; | |
const int LEFT_CS = 1; | |
const int RIGHT_CS = 2; | |
const int BOTTOM_CS = 4; | |
const int TOP_CS = 8; | |
const int FAR_CS = 16; | |
const int NEAR_CS = 32; | |
float SoftwareRasteriser::ScreenAreaOfTri(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2) | |
{ | |
float area = ((v0.x * v1.y) + (v1.x * v2.y) + (v2.x * v0.y)) - | |
((v1.x * v0.y) + (v2.x * v1.y) + (v0.x * v2.y)); | |
return area * 0.5f; | |
} | |
SoftwareRasteriser::SoftwareRasteriser(uint width, uint height) | |
: Window(width, height) | |
{ | |
m_currentDrawBuffer = 0; | |
m_currentTexture = NULL; | |
m_texSampleState = SAMPLE_NEAREST; | |
m_blendState = BLEND_REPLACE; | |
#ifndef USE_OS_BUFFERS | |
// Hi! In the tutorials, it's mentioned that we need to form our front + back buffer like so: | |
for (int i = 0; i < 2; ++i) | |
{ | |
m_buffers[i] = new Colour[screenWidth * screenHeight]; | |
} | |
#else | |
// This works, but we can actually save a memcopy by rendering directly into the memory the | |
// windowing system gives us, which I've added to the Window class as the 'bufferData' pointers | |
for (int i = 0; i < 2; ++i) | |
{ | |
buffers[i] = (Colour *)bufferData[i]; | |
} | |
#endif | |
m_depthBuffer = new unsigned short[screenWidth * screenHeight]; | |
float zScale = (pow(2.0f, 16) - 1) * 0.5f; | |
Vector3 halfScreen = Vector3((screenWidth - 1) * 0.5f, (screenHeight - 1) * 0.5f, zScale); | |
m_portMatrix = Matrix4::Translation(halfScreen) * Matrix4::Scale(halfScreen); | |
} | |
SoftwareRasteriser::~SoftwareRasteriser(void) | |
{ | |
#ifndef USE_OS_BUFFERS | |
for (int i = 0; i < 2; ++i) | |
{ | |
delete[] m_buffers[i]; | |
} | |
#endif | |
delete[] m_depthBuffer; | |
} | |
void SoftwareRasteriser::Resize() | |
{ | |
Window::Resize(); // make sure our base class gets to do anything it needs to | |
#ifndef USE_OS_BUFFERS | |
for (int i = 0; i < 2; ++i) | |
{ | |
delete[] m_buffers[i]; | |
m_buffers[i] = new Colour[screenWidth * screenHeight]; | |
} | |
#else | |
for (int i = 0; i < 2; ++i) | |
{ | |
buffers[i] = (Colour *)bufferData[i]; | |
} | |
#endif | |
delete[] m_depthBuffer; | |
m_depthBuffer = new unsigned short[screenWidth * screenHeight]; | |
float zScale = (pow(2.0f, 16) - 1) * 0.5f; | |
Vector3 halfScreen = Vector3((screenWidth - 1) * 0.5f, (screenHeight - 1) * 0.5f, zScale); | |
m_portMatrix = Matrix4::Translation(halfScreen) * Matrix4::Scale(halfScreen); | |
} | |
Colour *SoftwareRasteriser::GetCurrentBuffer() | |
{ | |
return m_buffers[m_currentDrawBuffer]; | |
} | |
void SoftwareRasteriser::ClearBuffers() | |
{ | |
Colour *buffer = GetCurrentBuffer(); | |
unsigned int clearVal = 0xFF000000; | |
unsigned int depthVal = ~0; | |
for (uint y = 0; y < screenHeight; ++y) | |
{ | |
for (uint x = 0; x < screenWidth; ++x) | |
{ | |
buffer[(y * screenWidth) + x].c = clearVal; | |
m_depthBuffer[(y * screenWidth) + x] = depthVal; | |
} | |
} | |
} | |
void SoftwareRasteriser::SwapBuffers() | |
{ | |
PresentBuffer(m_buffers[m_currentDrawBuffer]); | |
m_currentDrawBuffer = !m_currentDrawBuffer; | |
} | |
void SoftwareRasteriser::DrawObject(RenderObject *o) | |
{ | |
m_currentTexture = o->GetTexure(); | |
switch (o->GetMesh()->GetType()) | |
{ | |
case PRIMITIVE_POINTS: | |
RasterisePointsMesh(o); | |
break; | |
case PRIMITIVE_LINES: | |
RasteriseLinesMesh(o); | |
break; | |
case PRIMITIVE_TRIANGLES: | |
RasteriseTriMesh(o); | |
break; | |
case PRIMITIVE_TRIANGLE_STRIP: | |
RasteriseTriMeshStrip(o); | |
break; | |
case PRIMITIVE_TRIANGLE_FAN: | |
RasteriseTriMeshFan(o); | |
break; | |
} | |
} | |
void SoftwareRasteriser::CalculateWeights(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Vector4 &p, float &alpha, float &beta, float &gamma) | |
{ | |
const float triArea = ScreenAreaOfTri(v0, v1, v2); | |
const float areaRecip = 1.0f / triArea; | |
float subTriArea[3]; | |
subTriArea[0] = abs(ScreenAreaOfTri(v0, p, v1)); | |
subTriArea[1] = abs(ScreenAreaOfTri(v1, p, v2)); | |
subTriArea[2] = abs(ScreenAreaOfTri(v2, p, v0)); | |
alpha = subTriArea[1] * areaRecip; | |
beta = subTriArea[2] * areaRecip; | |
gamma = subTriArea[0] * areaRecip; | |
} | |
bool SoftwareRasteriser::CohenSutherlandLine(Vector4 &inA, Vector4 &inB, Colour &colA, Colour &colB, | |
Vector3 &texA, Vector3 &texB) | |
{ | |
for (int i = 0; i < 6; ++i) | |
{ | |
int planeCode = 1 << i; | |
int outsideA = (HomogeneousOutcode(inA) & planeCode); | |
int outsideB = (HomogeneousOutcode(inB) & planeCode); | |
if (outsideA && outsideB) | |
return false; | |
if (!outsideA && !outsideB) | |
continue; | |
float clipRatio = ClipEdge(inA, inB, planeCode); | |
if (outsideA) | |
{ | |
texA = Vector3::Lerp(texA, texB, clipRatio); | |
colA = Colour::Lerp(colA, colB, clipRatio); | |
inA = Vector4::Lerp(inA, inB, clipRatio); | |
} | |
else | |
{ | |
texB = Vector3::Lerp(texA, texB, clipRatio); | |
colB = Colour::Lerp(colA, colB, clipRatio); | |
inB = Vector4::Lerp(inA, inB, clipRatio); | |
} | |
} | |
return true; | |
} | |
void SoftwareRasteriser::SutherlandHodgmanTri(Vector4 &v0, Vector4 &v1, Vector4 &v2, | |
const Colour &c0, const Colour &c1, const Colour &c2, | |
const Vector2 &t0, const Vector2 &t1, | |
const Vector2 &t2) | |
{ | |
Vector4 posIn[MAX_VERTS]; | |
Colour colIn[MAX_VERTS]; | |
Vector3 texIn[MAX_VERTS]; | |
Vector4 posOut[MAX_VERTS]; | |
Colour colOut[MAX_VERTS]; | |
Vector3 texOut[MAX_VERTS]; | |
posIn[0] = v0; | |
posIn[1] = v1; | |
posIn[2] = v2; | |
colIn[0] = c0; | |
colIn[1] = c1; | |
colIn[2] = c2; | |
texIn[0] = Vector3(t0.x, t0.y, 1); | |
texIn[1] = Vector3(t1.x, t1.y, 1); | |
texIn[2] = Vector3(t2.x, t2.y, 1); | |
int inSize = 3; | |
for (int i = 0; i <= 6; i++) | |
{ | |
int planeCode = 1 << i; | |
Vector4 prevPos = posIn[inSize - 1]; | |
Colour prevCol = colIn[inSize - 1]; | |
Vector3 prevTex = texIn[inSize - 1]; | |
int outSize = 0; | |
for (int j = 0; j < inSize; ++j) | |
{ | |
int outsideA = HomogeneousOutcode(posIn[j]) & planeCode; | |
int outsideB = HomogeneousOutcode(prevPos) & planeCode; | |
if (outsideA ^ outsideB) | |
{ | |
float clipRatio = ClipEdge(posIn[j], prevPos, planeCode); | |
colOut[outSize] = Colour::Lerp(colIn[j], prevCol, clipRatio); | |
texOut[outSize] = Vector3::Lerp(texIn[j], prevTex, clipRatio); | |
posOut[outSize] = Vector4::Lerp(posIn[j], prevPos, clipRatio); | |
outSize++; | |
} | |
if (!outsideA) | |
{ | |
posOut[outSize] = posIn[j]; | |
colOut[outSize] = colIn[j]; | |
texOut[outSize] = texIn[j]; | |
outSize++; | |
} | |
prevPos = posIn[j]; | |
prevCol = colIn[j]; | |
prevTex = texIn[j]; | |
} | |
for (int j = 0; j < outSize; ++j) | |
{ | |
posIn[j] = posOut[j]; | |
colIn[j] = colOut[j]; | |
texIn[j] = texOut[j]; | |
} | |
inSize = outSize; | |
} | |
for (int i = 0; i < inSize; ++i) | |
{ | |
texIn[i] = Vector3(texIn[i].x, texIn[i].y, 1.0f) / posIn[i].w; | |
posIn[i].SelfDivisionByW(); | |
} | |
for (int i = 2; i < inSize; ++i) | |
{ | |
RasteriseTri(posIn[0], posIn[i - 1], posIn[i], colIn[0], colIn[i - 1], colIn[i], texIn[0], | |
texIn[i - 1], texIn[i]); | |
} | |
} | |
float SoftwareRasteriser::ClipEdge(const Vector4 &inA, const Vector4 &inB, int axis) | |
{ | |
float ratio = 0.0f; | |
switch (axis) | |
{ | |
case LEFT_CS: | |
ratio = (-inA.w - inA.x) / ((inB.x - inA.x) + inB.w - inA.w); | |
break; | |
case RIGHT_CS: | |
ratio = (inA.w - inA.x) / ((inB.x - inA.x) + inB.w - inA.w); | |
break; | |
case BOTTOM_CS: | |
ratio = (-inA.w - inA.y) / ((inB.y - inA.y) + inB.w - inA.w); | |
break; | |
case TOP_CS: | |
ratio = (inA.w - inA.y) / ((inB.y - inA.y) + inB.w - inA.w); | |
break; | |
case NEAR_CS: | |
ratio = (-inA.w - inA.z) / ((inB.z - inA.z) + inB.w - inA.w); | |
break; | |
case FAR_CS: | |
ratio = (inA.w - inA.z) / ((inB.z - inA.z) + inB.w - inA.w); | |
break; | |
} | |
return min(1.0f, ratio); | |
} | |
int SoftwareRasteriser::HomogeneousOutcode(const Vector4 &in) | |
{ | |
int outCode = INSIDE_CS; | |
if (in.x < -in.w) | |
outCode |= LEFT_CS; | |
else if (in.x > in.w) | |
outCode |= RIGHT_CS; | |
if (in.y < -in.w) | |
outCode |= BOTTOM_CS; | |
else if (in.y > in.w) | |
outCode |= TOP_CS; | |
if (in.z < -in.w) | |
outCode |= NEAR_CS; | |
else if (in.z > in.w) | |
outCode |= FAR_CS; | |
return outCode; | |
} | |
void SoftwareRasteriser::RasteriseLine(const Vector4 &v0, const Vector4 &v1, const Colour &colA, | |
const Colour &colB, const Vector3 &texA, const Vector3 &texB) | |
{ | |
Vector4 v0p = m_portMatrix * v0; | |
Vector4 v1p = m_portMatrix * v1; | |
Vector4 dir = v1p - v0p; | |
int xDir = (dir.x < 0.0f) ? -1 : 1; | |
int yDir = (dir.y < 0.0f) ? -1 : 1; | |
int x = (int)v0p.x; | |
int y = (int)v0p.y; | |
int *target = NULL; | |
int *scan = NULL; | |
int targetVal = 0; | |
int scanVal = 0; | |
float slope = 0.0f; | |
int range = 0; | |
if (abs(dir.y) > abs(dir.x)) | |
{ | |
slope = dir.x / dir.y; | |
range = (int)abs(dir.y); | |
target = &x; | |
scan = &y; | |
targetVal = xDir; | |
scanVal = yDir; | |
} | |
else | |
{ | |
slope = dir.y / dir.x; | |
range = (int)abs(dir.x); | |
target = &y; | |
scan = &x; | |
targetVal = yDir; | |
scanVal = xDir; | |
} | |
const float absSlope = abs(slope); | |
float error = 0.0f; | |
const float reciprocalRange = 1.0f / range; | |
for (int i = 0; i < range; i++) | |
{ | |
const float t = i * reciprocalRange; | |
const float zVal = v1p.z * (i * reciprocalRange) + v0p.z * (1.0 - (i * reciprocalRange)); | |
Colour c; | |
if (m_currentTexture == NULL) | |
{ | |
c = colB * t + colA * (1.0f - t); | |
} | |
else | |
{ | |
Vector3 subTex = texA * (i * reciprocalRange) + texB * (1.0f - (i * reciprocalRange)); | |
subTex.x /= subTex.z; | |
subTex.y /= subTex.z; | |
c = m_currentTexture->ColourAtPoint((int)subTex.x, (int)subTex.y); | |
} | |
if (DepthFunc((int)x, (int)y, zVal)) | |
BlendPixel(x, y, c); | |
error += absSlope; | |
if (error > 0.5f) | |
{ | |
error -= 1.0f; | |
(*target) += targetVal; | |
} | |
(*scan) += scanVal; | |
} | |
} | |
void SoftwareRasteriser::RasteriseTri(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Colour &c0, const Colour &c1, const Colour &c2, | |
const Vector3 &t0, const Vector3 &t1, const Vector3 &t2) | |
{ | |
Vector4 v0p = m_portMatrix * v0; | |
Vector4 v1p = m_portMatrix * v1; | |
Vector4 v2p = m_portMatrix * v2; | |
const BoundingBox b = CalculateBoxForTri(v0p, v1p, v2p); | |
const float triArea = abs(ScreenAreaOfTri(v0p, v1p, v2p)); | |
const float areaRecip = 1.0f / triArea; | |
float subTriArea[3]; | |
Vector4 screenPos(0, 0, 0, 1); | |
for (float y = b.topLeft.y; y < b.bottomRight.y; ++y) | |
{ | |
for (float x = b.topLeft.x; x < b.bottomRight.x; ++x) | |
{ | |
screenPos.x = x; | |
screenPos.y = y; | |
subTriArea[0] = abs(ScreenAreaOfTri(v0p, screenPos, v1p)); | |
subTriArea[1] = abs(ScreenAreaOfTri(v1p, screenPos, v2p)); | |
subTriArea[2] = abs(ScreenAreaOfTri(v2p, screenPos, v0p)); | |
float triSum = subTriArea[0] + subTriArea[1] + subTriArea[2]; | |
// Check if pixel is outside of triangle | |
if (triSum > (triArea + 1.0f)) | |
continue; | |
// Check if tringle is very small | |
if (triSum < 1.0f) | |
continue; | |
const float alpha = subTriArea[1] * areaRecip; | |
const float beta = subTriArea[2] * areaRecip; | |
const float gamma = subTriArea[0] * areaRecip; | |
float zVal = (v0p.z * alpha) + (v1p.z * beta) + (v2p.z * gamma); | |
if (!DepthFunc((int)x, (int)y, zVal)) | |
continue; | |
// Pixel is in triangle, so shade it | |
if (m_currentTexture) | |
{ | |
Vector3 subTex = (t0 * alpha) + (t1 * beta) + (t2 * gamma); | |
subTex.x /= subTex.z; | |
subTex.y /= subTex.z; | |
switch (m_texSampleState) | |
{ | |
case SAMPLE_BILINEAR: | |
BlendPixel((int)x, (int)y, m_currentTexture->BilinearTexSample(subTex)); | |
break; | |
case SAMPLE_MIPMAP_NEAREST: | |
{ | |
float xAlpha, xBeta, xGamma, yAlpha, yBeta, yGamma; | |
CalculateWeights(v0p, v1p, v2p, screenPos + Vector4(1, 0, 0, 0), xAlpha, xBeta, xGamma); | |
CalculateWeights(v0p, v1p, v2p, screenPos + Vector4(0, 1, 0, 0), yAlpha, yBeta, yGamma); | |
Vector3 xDerivs = (t0 * xAlpha) + (t1 * xBeta) + (t2 * xGamma); | |
Vector3 yDerivs = (t0 * yAlpha) + (t1 * yBeta) + (t2 * yGamma); | |
xDerivs.x /= xDerivs.z; | |
xDerivs.y /= xDerivs.z; | |
yDerivs.x /= yDerivs.z; | |
yDerivs.y /= yDerivs.z; | |
xDerivs = xDerivs - subTex; | |
yDerivs = yDerivs - subTex; | |
const float maxU = max(abs(xDerivs.x), abs(yDerivs.y)); | |
const float maxV = max(abs(xDerivs.y), abs(yDerivs.y)); | |
const float maxChange = abs(max(maxU, maxV)); | |
const int lambda = (int)(abs(log(maxChange) / log(2.0))); | |
BlendPixel((int)x, (int)y, m_currentTexture->NearestTexSample(subTex, lambda)); | |
break; | |
} | |
default: | |
BlendPixel((int)x, (int)y, m_currentTexture->NearestTexSample(subTex)); | |
} | |
} | |
else | |
{ | |
Colour c = ((c0 * alpha) + (c1 * beta) + (c2 * gamma)); | |
BlendPixel((int)x, (int)y, c); | |
} | |
} | |
} | |
} | |
void SoftwareRasteriser::RasteriseTriSpans(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Colour &c0, const Colour &c1, const Colour &c2, | |
const Vector3 &t0, const Vector3 &t1, const Vector3 &t2) | |
{ | |
Vector4 v0p = m_portMatrix * v0; | |
Vector4 v1p = m_portMatrix * v1; | |
Vector4 v2p = m_portMatrix * v2; | |
float edge0y = abs(v0p.y - v1p.y); | |
float edge1y = abs(v1p.y - v2p.y); | |
float edge2y = abs(v2p.y - v0p.y); | |
// Edhe 0 is longest | |
if (edge0y >= edge1y && edge0y >= edge2y) | |
{ | |
RasteriseTriEdgeSpans(v0p, v1p, v2p, v0p); | |
RasteriseTriEdgeSpans(v0p, v1p, v1p, v2p); | |
} | |
// Edge 1 is longest | |
else if (edge1y >= edge0y && edge1y >= edge2y) | |
{ | |
RasteriseTriEdgeSpans(v1p, v2p, v2p, v0p); | |
RasteriseTriEdgeSpans(v1p, v2p, v0p, v1p); | |
} | |
// Edge 2 is longest | |
else | |
{ | |
RasteriseTriEdgeSpans(v2p, v0p, v0p, v1p); | |
RasteriseTriEdgeSpans(v2p, v0p, v1p, v2p); | |
} | |
} | |
void SoftwareRasteriser::RasteriseTriEdgeSpans(const Vector4 &v0, const Vector4 &v1, | |
const Vector4 &v2, const Vector4 &v3) | |
{ | |
Vector4 longEdge0 = v0; | |
Vector4 longEdge1 = v1; | |
Vector4 shortEdge0 = v2; | |
Vector4 shortEdge1 = v3; | |
if (longEdge1.y < longEdge0.y) | |
{ | |
longEdge0 = v1; | |
longEdge1 = v0; | |
} | |
if (shortEdge1.y < shortEdge0.y) | |
{ | |
shortEdge0 = v3; | |
shortEdge1 = v2; | |
} | |
Vector4 longDiff = longEdge1 - longEdge0; | |
Vector4 shortDiff = shortEdge1 - shortEdge0; | |
float longStep = longDiff.x / longDiff.y; | |
float shortStep = shortDiff.x / shortDiff.y; | |
float startOff = (shortEdge0.y - longEdge0.y) / longDiff.y; | |
Vector4 start = longEdge0 + (longDiff * startOff); | |
float endOff = (start.y - shortEdge0.y) / shortDiff.y; | |
Vector4 end = shortEdge0 + (shortDiff * endOff); | |
for (float y = shortEdge0.y; y < shortEdge1.y; ++y) | |
{ | |
float minX = min(start.x, end.x); | |
float maxX = max(start.x, end.x); | |
for (float x = minX; x < maxX; ++x) | |
BlendPixel((int)x, (int)y, Colour::White); | |
start.x += longStep; | |
end.x += shortStep; | |
} | |
} | |
void SoftwareRasteriser::RasterisePointsMesh(RenderObject *o) | |
{ | |
Matrix4 mvp = m_viewProjMatrix * o->GetModelMatrix(); | |
for (uint i = 0; i < o->GetMesh()->numVertices; i++) | |
{ | |
Vector4 vertexPos = mvp * o->GetMesh()->vertices[i]; | |
vertexPos.SelfDivisionByW(); | |
Vector4 screenPos = m_portMatrix * vertexPos; | |
BlendPixel((uint)screenPos.x, (uint)screenPos.y, o->GetMesh()->colours[i]); | |
} | |
} | |
void SoftwareRasteriser::RasteriseLinesMesh(RenderObject *o) | |
{ | |
Matrix4 mvp = m_viewProjMatrix * o->GetModelMatrix(); | |
for (uint i = 0; i < o->GetMesh()->numVertices; i += 2) | |
{ | |
Vector4 v0 = mvp * o->GetMesh()->vertices[i]; | |
Vector4 v1 = mvp * o->GetMesh()->vertices[i + 1]; | |
Colour c0 = o->GetMesh()->colours[0]; | |
Colour c1 = o->GetMesh()->colours[1]; | |
Vector3 t0 = Vector3(o->GetMesh()->textureCoords[i].x, o->GetMesh()->textureCoords[i].y, 1.0f); | |
Vector3 t1 = | |
Vector3(o->GetMesh()->textureCoords[i + 1].x, o->GetMesh()->textureCoords[i + 1].y, 1.0f); | |
if (!CohenSutherlandLine(v0, v1, c0, c1, t0, t1)) | |
continue; | |
t0.z = 1.0f; | |
t1.z = 1.0f; | |
t0 /= v0.w; | |
t1 /= v1.w; | |
v0.SelfDivisionByW(); | |
v1.SelfDivisionByW(); | |
RasteriseLine(v0, v1, c0, c1, t0, t1); | |
} | |
} | |
void SoftwareRasteriser::RasteriseTriMesh(RenderObject *o) | |
{ | |
Matrix4 mvp = m_viewProjMatrix * o->GetModelMatrix(); | |
Mesh *m = o->GetMesh(); | |
for (uint i = 0; i < o->GetMesh()->numVertices; i += 3) | |
{ | |
Vector4 v0 = mvp * m->vertices[i]; | |
Vector4 v1 = mvp * m->vertices[i + 1]; | |
Vector4 v2 = mvp * m->vertices[i + 2]; | |
SutherlandHodgmanTri(v0, v1, v2, m->colours[i], m->colours[i + 1], m->colours[i + 2], | |
m->textureCoords[i], m->textureCoords[i + 1], m->textureCoords[i + 2]); | |
} | |
} | |
void SoftwareRasteriser::RasteriseTriMeshStrip(RenderObject *o) | |
{ | |
Matrix4 mvp = m_viewProjMatrix * o->GetModelMatrix(); | |
Mesh *m = o->GetMesh(); | |
for (uint i = 0; i < o->GetMesh()->numVertices - 2; ++i) | |
{ | |
Vector4 vv0 = m->vertices[i]; | |
Vector4 vv1 = m->vertices[i + 1]; | |
Vector4 vv2 = m->vertices[i + 2]; | |
Vector4 v0 = mvp * m->vertices[i]; | |
Vector4 v1 = mvp * m->vertices[i + 1]; | |
Vector4 v2 = mvp * m->vertices[i + 2]; | |
SutherlandHodgmanTri(v0, v1, v2, m->colours[i], m->colours[i + 1], m->colours[i + 2], | |
m->textureCoords[i], m->textureCoords[i + 1], m->textureCoords[i + 2]); | |
} | |
} | |
void SoftwareRasteriser::RasteriseTriMeshFan(RenderObject *o) | |
{ | |
Matrix4 mvp = m_viewProjMatrix * o->GetModelMatrix(); | |
Mesh *m = o->GetMesh(); | |
Vector4 v0 = mvp * m->vertices[0]; | |
for (uint i = 1; i < o->GetMesh()->numVertices - 1; ++i) | |
{ | |
Vector4 v1 = mvp * m->vertices[i]; | |
Vector4 v2 = mvp * m->vertices[i + 1]; | |
SutherlandHodgmanTri(v0, v1, v2, m->colours[0], m->colours[i], m->colours[i + 1], | |
m->textureCoords[0], m->textureCoords[i], m->textureCoords[i + 1]); | |
} | |
} | |
BoundingBox SoftwareRasteriser::CalculateBoxForTri(const Vector4 &a, const Vector4 &b, | |
const Vector4 &c) | |
{ | |
BoundingBox box; | |
box.topLeft.x = a.x; | |
box.topLeft.x = min(box.topLeft.x, b.x); | |
box.topLeft.x = min(box.topLeft.x, c.x); | |
box.topLeft.x = max(box.topLeft.x, 0.0f); | |
box.topLeft.y = a.y; | |
box.topLeft.y = min(box.topLeft.y, b.y); | |
box.topLeft.y = min(box.topLeft.y, c.y); | |
box.topLeft.y = max(box.topLeft.y, 0.0f); | |
box.bottomRight.x = a.x; | |
box.bottomRight.x = max(box.bottomRight.x, b.x); | |
box.bottomRight.x = max(box.bottomRight.x, c.x); | |
box.bottomRight.x = min(box.bottomRight.x, screenWidth); | |
box.bottomRight.y = a.y; | |
box.bottomRight.y = max(box.bottomRight.y, b.y); | |
box.bottomRight.y = max(box.bottomRight.y, c.y); | |
box.bottomRight.y = min(box.bottomRight.y, screenHeight); | |
return box; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:SoftwareRasteriser | |
Implements:Window | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description: Class to encapsulate the various rasterisation techniques looked | |
at in the course material. | |
This is the class you'll be modifying the most! | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "Matrix4.h" | |
#include "Mesh.h" | |
#include "Texture.h" | |
#include "RenderObject.h" | |
#include "Common.h" | |
#include "Window.h" | |
#include <vector> | |
using std::vector; | |
enum BlendMode | |
{ | |
BLEND_REPLACE, | |
BLEND_ALPHA, | |
BLEND_ADDITIVE | |
}; | |
enum TextureSampleMode | |
{ | |
SAMPLE_NEAREST, | |
SAMPLE_BILINEAR, | |
SAMPLE_MIPMAP_NEAREST | |
}; | |
struct BoundingBox | |
{ | |
Vector2 topLeft; | |
Vector2 bottomRight; | |
}; | |
class RenderObject; | |
class Texture; | |
class SoftwareRasteriser : public Window | |
{ | |
public: | |
static float ScreenAreaOfTri(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2); | |
SoftwareRasteriser(uint width, uint height); | |
~SoftwareRasteriser(void); | |
void DrawObject(RenderObject *o); | |
void ClearBuffers(); | |
void SwapBuffers(); | |
void SetViewMatrix(const Matrix4 &m) | |
{ | |
m_viewMatrix = m; | |
m_viewProjMatrix = m_projectionMatrix * m_viewMatrix; | |
} | |
void SetProjectionMatrix(const Matrix4 &m) | |
{ | |
m_projectionMatrix = m; | |
m_viewProjMatrix = m_projectionMatrix * m_viewMatrix; | |
} | |
inline bool DepthFunc(int x, int y, float depthValue) | |
{ | |
if (y < 0 || x < 0 || y >= screenHeight || x >= screenWidth) | |
return false; | |
int index = (y * screenWidth) + x; | |
unsigned int castVal = (unsigned int)depthValue; | |
if (castVal > m_depthBuffer[index]) | |
return false; | |
m_depthBuffer[index] = castVal; | |
return true; | |
} | |
void SetTextureSamplingMode(TextureSampleMode mode) | |
{ | |
m_texSampleState = mode; | |
} | |
TextureSampleMode GetTextureSamplingMode() | |
{ | |
return m_texSampleState; | |
} | |
void SetBlendMode(BlendMode mode) | |
{ | |
m_blendState = mode; | |
} | |
BlendMode GetBlendMode() | |
{ | |
return m_blendState; | |
} | |
bool CohenSutherlandLine(Vector4 &inA, Vector4 &inB, Colour &colA, Colour &colB, Vector3 &texA, | |
Vector3 &texB); | |
void SutherlandHodgmanTri(Vector4 &v0, Vector4 &v1, Vector4 &v2, const Colour &c0 = Colour(), | |
const Colour &c1 = Colour(), const Colour &c2 = Colour(), | |
const Vector2 &t0 = Vector2(), const Vector2 &t1 = Vector2(), | |
const Vector2 &t2 = Vector2()); | |
float ClipEdge(const Vector4 &inA, const Vector4 &inB, int axis); | |
int HomogeneousOutcode(const Vector4 &in); | |
protected: | |
Colour *GetCurrentBuffer(); | |
void CalculateWeights(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, const Vector4 &p, | |
float &alpha, float &beta, float &gamma); | |
void RasterisePointsMesh(RenderObject *o); | |
void RasteriseLinesMesh(RenderObject *o); | |
void RasteriseTriMesh(RenderObject *o); | |
void RasteriseTriMeshStrip(RenderObject *o); | |
void RasteriseTriMeshFan(RenderObject *o); | |
BoundingBox CalculateBoxForTri(const Vector4 &a, const Vector4 &b, const Vector4 &c); | |
virtual void Resize(); | |
void RasteriseLine(const Vector4 &v0, const Vector4 &v1, | |
const Colour &colA = Colour(255, 255, 255, 255), | |
const Colour &colB = Colour(255, 255, 255, 255), | |
const Vector3 &texA = Vector3(0, 0, 0), | |
const Vector3 &texB = Vector3(1, 1, 1)); | |
void RasteriseTri(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Colour &c0 = Colour(), const Colour &c1 = Colour(), | |
const Colour &c2 = Colour(), const Vector3 &t0 = Vector3(), | |
const Vector3 &t1 = Vector3(), const Vector3 &t2 = Vector3()); | |
void RasteriseTriSpans(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Colour &c0 = Colour(), const Colour &c1 = Colour(), | |
const Colour &c2 = Colour(), const Vector3 &t0 = Vector3(), | |
const Vector3 &t1 = Vector3(), const Vector3 &t2 = Vector3()); | |
void RasteriseTriEdgeSpans(const Vector4 &v0, const Vector4 &v1, const Vector4 &v2, | |
const Vector4 &v3); | |
inline void ShadePixel(uint x, uint y, const Colour &c) | |
{ | |
if (y >= screenHeight) | |
return; | |
if (x >= screenWidth) | |
return; | |
const int index = (y * screenWidth) + x; | |
m_buffers[m_currentDrawBuffer][index] = c; | |
} | |
inline void BlendPixel(uint x, uint y, const Colour &c) | |
{ | |
if (y >= screenHeight) | |
return; | |
if (x >= screenWidth) | |
return; | |
const int index = (y * screenWidth) + x; | |
Colour &dest = m_buffers[m_currentDrawBuffer][index]; | |
switch (m_blendState) | |
{ | |
case BLEND_ALPHA: | |
{ | |
const unsigned char sFactor = c.a; | |
const unsigned char dFactor = (255 - c.a); | |
dest.r = ((c.r * sFactor) + (dest.r * dFactor)) / 255; | |
dest.g = ((c.g * sFactor) + (dest.g * dFactor)) / 255; | |
dest.b = ((c.b * sFactor) + (dest.b * dFactor)) / 255; | |
dest.a = ((c.a * sFactor) + (dest.a * dFactor)) / 255; | |
break; | |
} | |
case BLEND_ADDITIVE: | |
dest = dest + c; | |
break; | |
default: | |
dest = c; | |
} | |
} | |
int m_currentDrawBuffer; | |
Texture *m_currentTexture; | |
Colour *m_buffers[2]; | |
unsigned short *m_depthBuffer; | |
Matrix4 m_viewMatrix; | |
Matrix4 m_projectionMatrix; | |
Matrix4 m_textureMatrix; | |
Matrix4 m_viewProjMatrix; | |
Matrix4 m_portMatrix; | |
TextureSampleMode m_texSampleState; | |
BlendMode m_blendState; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Texture.h" | |
Texture::Texture(void) | |
{ | |
width = 0; | |
height = 0; | |
texels = NULL; | |
CreateMipMaps(); | |
} | |
Texture::~Texture(void) | |
{ | |
delete[] texels; | |
} | |
Texture *Texture::TextureFromTGA(const string &filename) | |
{ | |
Texture *t = new Texture(); | |
std::ifstream file; | |
std::cout << "Loading TGA from(" << filename << ")" << std::endl; | |
file.open(filename.c_str(), std::ios::binary); | |
if (!file.is_open()) | |
{ | |
std::cout << "TextureFromTGA file error" << std::endl; | |
return t; | |
} | |
unsigned char TGAheader[18]; | |
std::cout << "sizeof(TGAheader) is " << sizeof(TGAheader) << std::endl; | |
file.read((char *)TGAheader, sizeof(TGAheader)); | |
t->width = (TGAheader[12] + (TGAheader[13] << 8)); | |
t->height = (TGAheader[14] + (TGAheader[15] << 8)); | |
int size = t->width * t->height * (TGAheader[16] / 8); | |
t->texels = new Colour[t->width * t->height]; | |
file.read((char *)t->texels, size); | |
file.close(); | |
return t; | |
} | |
const Colour &Texture::NearestTexSample(const Vector3 &coords, int miplevel) | |
{ | |
miplevel = min(miplevel, mipLevels.size() - 1); | |
miplevel = (mipLevels.size() - 1) - miplevel; | |
const int texWidth = width >> miplevel; | |
const int texHeight = height >> miplevel; | |
int x = (int)(coords.x * (texWidth - 1)); | |
int y = (int)(coords.y * (texHeight - 1)); | |
return ColourAtPoint(x, y, miplevel); | |
} | |
const Colour &Texture::BilinearTexSample(const Vector3 &coords, int miplevel) | |
{ | |
const int texWidth = width; | |
const int texHeight = height; | |
const int x = (int)(coords.x * texWidth); | |
const int y = (int)(coords.y * texHeight); | |
const Colour &tl = ColourAtPoint(x, y); | |
const Colour &tr = ColourAtPoint(x + 1, y); | |
const Colour &bl = ColourAtPoint(x, y + 1); | |
const Colour &br = ColourAtPoint(x + 1, y + 1); | |
const float fracX = (coords.x * texWidth) - x; | |
const float fracY = (coords.y * texHeight) - y; | |
Colour top = Colour::Lerp(tl, tr, fracX); | |
Colour bottom = Colour::Lerp(bl, br, fracX); | |
return Colour::Lerp(top, bottom, fracY); | |
} | |
void Texture::CreateMipMaps() | |
{ | |
int tempWidth = width; | |
int tempHeight = height; | |
mipLevels.push_back(texels); | |
int numLevels = 0; | |
while (tempWidth > 1 && tempHeight > 1) | |
{ | |
// Half existing values | |
tempWidth = tempWidth >> 1; | |
tempHeight = tempHeight >> 1; | |
Colour * newLevel = new Colour[tempWidth * tempHeight]; | |
GenerateMipLevel(mipLevels.back(), newLevel, numLevels); | |
numLevels++; | |
mipLevels.push_back(newLevel); | |
} | |
} | |
void Texture::GenerateMipLevel(Colour *source, Colour *dest, int mipLevel) | |
{ | |
int sourceWidth = width >> mipLevel; | |
int sourceHeight = height >> mipLevel; | |
int destWidth = width >> (mipLevel + 1); | |
int destHeight = height >> (mipLevel + 1); | |
int outY = 0; | |
for (int y = 0; y < sourceHeight; y += 2) | |
{ | |
int outX = 0; | |
for (int x = 0; x < sourceWidth; x += 2) | |
{ | |
Colour out; | |
out += source[(y * sourceHeight) + x] * 0.25f; | |
out += source[(y * sourceHeight) + x + 1] * 0.25f; | |
out += source[((y + 1) * sourceHeight) + x] * 0.25f; | |
out += source[((y + 1) * sourceHeight) + x + 1] * 0.25f; | |
dest[outY * destHeight + outX] = out; | |
outX++; | |
} | |
outY++; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "SoftwareRasteriser.h" | |
#include "Colour.h" | |
#include <string> | |
/****************************************************************************** | |
Class:Texture | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Simple class to hold texture data for our software rasteriser. | |
Later on in the tutorial series, the ability to performa mipmapping and | |
bilinear filtering is added to this class. | |
The provided TextureFromTGA function is very basic, and will only support | |
uncompressed targa files - you can save images in this format using paint.net, | |
which is free | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#include <iostream> | |
#include <fstream> | |
#include <vector> | |
using std::string; | |
using std::ifstream; | |
using std::vector; | |
class Texture | |
{ | |
public: | |
friend class SoftwareRasteriser; | |
Texture(void); | |
~Texture(void); | |
static Texture *TextureFromTGA(const string &filename); | |
const Colour &NearestTexSample(const Vector3 &coords, int miplevel = 0); | |
const Colour &BilinearTexSample(const Vector3 &coords, int miplevel = 0); | |
const Colour &ColourAtPoint(int x, int y, int mipLevel = 0) | |
{ | |
int texWidth = width >> mipLevel; | |
int texHeight = height >> mipLevel; | |
x = max(0, min(x, (int)texWidth - 1)); | |
y = max(0, min(y, (int)texHeight - 1)); | |
int index = (y * texHeight) + x; | |
return texels[index]; | |
} | |
uint GetWidth() | |
{ | |
return width; | |
} | |
uint GetHeight() | |
{ | |
return height; | |
} | |
protected: | |
void CreateMipMaps(); | |
void GenerateMipLevel(Colour *source, Colour *dest, int mipLevel); | |
vector<Colour *> mipLevels; | |
uint width; | |
uint height; | |
Colour *texels; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Vector2 | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Simple 2-component vector class, with associated math overloads | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include <iostream> | |
class Vector2 | |
{ | |
public: | |
Vector2(void) | |
{ | |
ToZero(); | |
} | |
Vector2(const float x, const float y) | |
{ | |
this->x = x; | |
this->y = y; | |
} | |
~Vector2(void) | |
{ | |
} | |
float x; | |
float y; | |
void ToZero() | |
{ | |
x = 0.0f; | |
y = 0.0f; | |
} | |
static float Dot(const Vector2 &a, const Vector2 &b) | |
{ | |
return (a.x * b.x) + (a.y * b.y); | |
} | |
static float Cross(const Vector2 &a, const Vector2 &b) | |
{ | |
return (a.x * b.y) - (a.y * b.x); | |
} | |
float Length() const | |
{ | |
return sqrt((x * x) + (y * y)); | |
} | |
float LengthSquared() const | |
{ | |
return (x * x) + (y * y); | |
} | |
void Normalise() | |
{ | |
float length = Length(); | |
if (length != 0.0f) | |
{ | |
length = 1.0f / length; | |
x = x * length; | |
y = y * length; | |
} | |
} | |
static Vector2 Lerp(const Vector2 &a, const Vector2 &b, float by) | |
{ | |
Vector2 p; | |
p.x = ((b.x * by) + (a.x * (1.0f - by))); | |
p.y = ((b.y * by) + (a.y * (1.0f - by))); | |
return p; | |
} | |
inline friend std::ostream &operator<<(std::ostream &o, const Vector2 &v) | |
{ | |
o << "Vector2(" << v.x << "," << v.y << ")" << std::endl; | |
return o; | |
} | |
inline Vector2 operator-(const Vector2 &a) const | |
{ | |
return Vector2(x - a.x, y - a.y); | |
} | |
inline Vector2 operator+(const Vector2 &a) const | |
{ | |
return Vector2(x + a.x, y + a.y); | |
} | |
inline Vector2 operator*(const Vector2 &a) const | |
{ | |
return Vector2(x * a.x, y * a.y); | |
} | |
inline Vector2 operator*(const float &a) const | |
{ | |
return Vector2(x * a, y * a); | |
} | |
inline Vector2 operator/(const Vector2 &a) const | |
{ | |
return Vector2(x / a.x, y / a.y); | |
} | |
inline Vector2 operator/(const float &a) const | |
{ | |
return Vector2(x / a, y / a); | |
} | |
inline void operator/=(const float &a) | |
{ | |
x /= a; | |
y /= a; | |
} | |
inline void operator*=(const float &a) | |
{ | |
x *= a; | |
y *= a; | |
} | |
inline void operator+=(const Vector2 &a) | |
{ | |
x += a.x; | |
y += a.y; | |
} | |
inline void operator-=(const Vector2 &a) | |
{ | |
x -= a.x; | |
y -= a.y; | |
} | |
inline void operator*=(const Vector2 &a) | |
{ | |
x *= a.x; | |
y *= a.y; | |
} | |
inline void operator/=(const Vector2 &a) | |
{ | |
x /= a.x; | |
y /= a.y; | |
} | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Vector3.h" | |
#include "Vector4.h" | |
#include "Vector2.h" | |
#include "Common.h" | |
Vector4 Vector3::ToVector4(float w) | |
{ | |
return Vector4(x, y, z, w); | |
} | |
float Vector3::GetMaxElement() | |
{ | |
return max(x, max(y, z)); | |
} | |
float Vector3::GetMinElement() | |
{ | |
return min(x, min(y, z)); | |
} | |
Vector2 Vector3::ToVector2() | |
{ | |
return Vector2(x, y); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Vector3 | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Simple 3-component vector class, with associated math overloads | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include <cmath> | |
#include <iostream> | |
#include "Vector4.h" | |
#include "Vector2.h" | |
class Vector4; | |
class Vector2; | |
class Vector3 | |
{ | |
public: | |
Vector3(void) | |
{ | |
ToZero(); | |
} | |
Vector3(const float x, const float y, const float z) | |
{ | |
this->x = x; | |
this->y = y; | |
this->z = z; | |
} | |
Vector3(const float size) | |
{ | |
this->x = size; | |
this->y = size; | |
this->z = size; | |
} | |
~Vector3(void) | |
{ | |
} | |
float x; | |
float y; | |
float z; | |
void Normalise() | |
{ | |
float length = Length(); | |
if (length != 0.0f) | |
{ | |
length = 1.0f / length; | |
x = x * length; | |
y = y * length; | |
z = z * length; | |
} | |
} | |
void ToZero() | |
{ | |
x = y = z = 0.0f; | |
} | |
float Length() const | |
{ | |
return sqrt((x * x) + (y * y) + (z * z)); | |
} | |
float LengthSquared() const | |
{ | |
return (x * x) + (y * y) + (z * z); | |
} | |
void Invert() | |
{ | |
x = -x; | |
y = -y; | |
z = -z; | |
} | |
Vector3 Inverse() const | |
{ | |
return Vector3(-x, -y, -z); | |
} | |
static inline float Dot(const Vector3 &a, const Vector3 &b) | |
{ | |
return (a.x * b.x) + (a.y * b.y) + (a.z * b.z); | |
} | |
static inline Vector3 Cross(const Vector3 &a, const Vector3 &b) | |
{ | |
return Vector3((a.y * b.z) - (a.z * b.y), (a.z * b.x) - (a.x * b.z), (a.x * b.y) - (a.y * b.x)); | |
} | |
inline friend std::ostream &operator<<(std::ostream &o, const Vector3 &v) | |
{ | |
o << "Vector3(" << v.x << "," << v.y << "," << v.z << ")" << std::endl; | |
return o; | |
} | |
inline Vector3 operator+(const Vector3 &a) const | |
{ | |
return Vector3(x + a.x, y + a.y, z + a.z); | |
} | |
inline Vector3 operator-(const Vector3 &a) const | |
{ | |
return Vector3(x - a.x, y - a.y, z - a.z); | |
} | |
inline Vector3 operator-() const | |
{ | |
return Vector3(-x, -y, -z); | |
} | |
inline void operator+=(const Vector3 &a) | |
{ | |
x += a.x; | |
y += a.y; | |
z += a.z; | |
} | |
inline void operator-=(const Vector3 &a) | |
{ | |
x -= a.x; | |
y -= a.y; | |
z -= a.z; | |
} | |
inline void operator/=(const Vector3 &a) | |
{ | |
x /= a.x; | |
y /= a.y; | |
z /= a.z; | |
} | |
inline Vector3 operator*(const float a) const | |
{ | |
return Vector3(x * a, y * a, z * a); | |
} | |
inline Vector3 operator*(const Vector3 &a) const | |
{ | |
return Vector3(x * a.x, y * a.y, z * a.z); | |
} | |
inline Vector3 operator/(const Vector3 &a) const | |
{ | |
return Vector3(x / a.x, y / a.y, z / a.z); | |
}; | |
inline Vector3 operator/(const float v) const | |
{ | |
return Vector3(x / v, y / v, z / v); | |
}; | |
float GetMaxElement(); | |
float GetMinElement(); | |
static inline Vector3 Lerp(const Vector3 &a, const Vector3 &b, float by) | |
{ | |
return (a * (1.0f - by)) + (b * by); | |
} | |
Vector4 ToVector4(float w = 0.0f); | |
Vector2 ToVector2(); | |
inline bool operator==(const Vector3 &A) const | |
{ | |
return (A.x == x && A.y == y && A.z == z) ? true : false; | |
}; | |
inline bool operator!=(const Vector3 &A) const | |
{ | |
return (A.x == x && A.y == y && A.z == z) ? false : true; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Vector4.h" | |
#include "Vector3.h" | |
#include "Common.h" | |
Vector2 Vector4::ToVector2() | |
{ | |
return Vector2(x, y); | |
} | |
Vector3 Vector4::ToVector3() | |
{ | |
return Vector3(x, y, z); | |
} | |
Vector3 Vector4::DivisionByW() | |
{ | |
return Vector3(x / w, y / w, z / w); | |
} | |
void Vector4::SelfDivisionByW() | |
{ | |
float recip = 1.0f / w; | |
x *= recip; | |
y *= recip; | |
z *= recip; | |
w = 1.0f; | |
} | |
float Vector4::GetMaxElement() | |
{ | |
return max(x, max(max(y, z), w)); | |
} | |
float Vector4::GetMinElement() | |
{ | |
return min(x, min(min(y, z), w)); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/****************************************************************************** | |
Class:Vector4 | |
Implements: | |
Author:Rich Davison <richard.davison4@newcastle.ac.uk> | |
Description:Simple 4-component vector class, with associated math overloads | |
-_-_-_-_-_-_-_,------, | |
_-_-_-_-_-_-_-| /\_/\ NYANYANYAN | |
-_-_-_-_-_-_-~|__( ^ .^) / | |
_-_-_-_-_-_-_-"" "" | |
*/ ///////////////////////////////////////////////////////////////////////////// | |
#pragma once | |
#include "Vector2.h" | |
#include "Vector3.h" | |
class Vector3; | |
class Vector2; | |
class Vector4 | |
{ | |
public: | |
Vector4(void) | |
{ | |
x = y = z = w = 1.0f; | |
} | |
Vector4(float x, float y, float z, float w) | |
{ | |
this->x = x; | |
this->y = y; | |
this->z = z; | |
this->w = w; | |
} | |
Vector2 ToVector2(); | |
Vector3 ToVector3(); | |
Vector3 DivisionByW(); | |
void SelfDivisionByW(); | |
~Vector4(void) | |
{ | |
} | |
inline friend std::ostream &operator<<(std::ostream &o, const Vector4 &v) | |
{ | |
o << "Vector4(" << v.x << "," << v.y << "," << v.z << "," << v.w << ")" << std::endl; | |
return o; | |
} | |
inline Vector4 operator+(const Vector4 &a) const | |
{ | |
return Vector4(x + a.x, y + a.y, z + a.z, w + a.w); | |
} | |
inline Vector4 operator-(const Vector4 &a) const | |
{ | |
return Vector4(x - a.x, y - a.y, z - a.z, w - a.w); | |
} | |
inline Vector4 operator-() const | |
{ | |
return Vector4(-x, -y, -z, -w); | |
} | |
float Length() const | |
{ | |
return sqrt((x * x) + (y * y) + (z * z) + (w * w)); | |
} | |
float LengthSquared() const | |
{ | |
return (x * x) + (y * y) + (z * z) + (w * w); | |
} | |
inline void operator+=(const Vector4 &a) | |
{ | |
x += a.x; | |
y += a.y; | |
z += a.z; | |
w += a.w; | |
} | |
inline void operator-=(const Vector4 &a) | |
{ | |
x -= a.x; | |
y -= a.y; | |
z -= a.z; | |
w -= a.w; | |
} | |
inline Vector4 operator*(const float a) const | |
{ | |
return Vector4(x * a, y * a, z * a, w * a); | |
} | |
inline Vector4 operator*(const Vector4 &a) const | |
{ | |
return Vector4(x * a.x, y * a.y, z * a.z, w * a.w); | |
} | |
inline Vector4 operator/(const Vector4 &a) const | |
{ | |
return Vector4(x / a.x, y / a.y, z / a.z, w / a.w); | |
}; | |
inline Vector4 operator/(const float v) const | |
{ | |
return Vector4(x / v, y / v, z / v, w / v); | |
}; | |
float GetMaxElement(); | |
float GetMinElement(); | |
static inline Vector4 Lerp(const Vector4 &a, const Vector4 &b, float by) | |
{ | |
return (a * (1.0f - by)) + (b * by); | |
} | |
inline bool operator==(const Vector4 &A) const | |
{ | |
return (A.x == x && A.y == y && A.z == z && A.w == w) ? true : false; | |
}; | |
inline bool operator!=(const Vector4 &A) const | |
{ | |
return (A.x == x && A.y == y && A.z == z && A.w == w) ? false : true; | |
}; | |
inline float operator[](const int i) const | |
{ | |
return array[i]; | |
} | |
union | |
{ | |
struct | |
{ | |
float x; | |
float y; | |
float z; | |
float w; | |
}; | |
float array[4]; | |
}; | |
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "Window.h" | |
Window::Window(uint width, uint height) | |
{ | |
hasInit = false; | |
HINSTANCE hInstance = GetModuleHandle(NULL); | |
WNDCLASSEX windowClass; | |
ZeroMemory(&windowClass, sizeof(WNDCLASSEX)); | |
if (!GetClassInfoEx(hInstance, WINDOWCLASS, &windowClass)) | |
{ | |
windowClass.cbSize = sizeof(WNDCLASSEX); | |
windowClass.style = CS_HREDRAW | CS_VREDRAW; | |
windowClass.lpfnWndProc = (WNDPROC)StaticWindowProc; | |
windowClass.hInstance = hInstance; | |
windowClass.hCursor = LoadCursor(NULL, IDC_ARROW); | |
windowClass.hbrBackground = (HBRUSH)COLOR_WINDOW; | |
windowClass.lpszClassName = WINDOWCLASS; | |
if (!RegisterClassEx(&windowClass)) | |
{ | |
return; | |
} | |
} | |
windowHandle = CreateWindowEx(NULL, | |
WINDOWCLASS, // name of the window class | |
"Software Rasteriser!", // title of the window | |
WS_OVERLAPPEDWINDOW | WS_POPUP | WS_VISIBLE | WS_SYSMENU | | |
WS_MAXIMIZEBOX | WS_MINIMIZEBOX, // window style | |
(int)100, // x-position of the window | |
(int)100, // y-position of the window | |
(int)width, // width of the window | |
(int)height, // height of the window | |
NULL, // No parent window! | |
NULL, // No Menus! | |
hInstance, // application handle | |
this); // | |
deviceContext = GetDC(windowHandle); | |
RECT rt; | |
GetClientRect(windowHandle, &rt); | |
screenWidth = rt.right; | |
screenHeight = rt.bottom; | |
BuildBitmap(); | |
Keyboard::Initialise(windowHandle); | |
Mouse::Initialise(windowHandle); | |
hasInit = true; | |
forceQuit = false; | |
} | |
Window::~Window(void) | |
{ | |
DeleteObject(bitBuffers[0]); | |
DeleteObject(bitBuffers[1]); | |
DeleteDC(drawDC); | |
Keyboard::Destroy(); | |
Mouse::Destroy(); | |
} | |
void Window::BuildBitmap() | |
{ | |
DeleteObject(bitBuffers[0]); | |
DeleteObject(bitBuffers[1]); | |
DeleteDC(drawDC); | |
BITMAPINFO bmi; | |
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); | |
bmi.bmiHeader.biWidth = screenWidth; | |
bmi.bmiHeader.biHeight = screenHeight; | |
bmi.bmiHeader.biPlanes = 1; | |
bmi.bmiHeader.biBitCount = 32; // four 8-bit components | |
bmi.bmiHeader.biCompression = BI_RGB; | |
bmi.bmiHeader.biSizeImage = screenWidth * screenHeight * 3; | |
drawDC = CreateCompatibleDC(deviceContext); | |
for (int i = 0; i < 2; ++i) | |
{ | |
bitBuffers[i] = CreateDIBSection(drawDC, &bmi, DIB_RGB_COLORS, &bufferData[i], NULL, 0x0); | |
} | |
} | |
void Window::PresentBuffer(Colour *buffer) | |
{ | |
if ((void *)buffer == bufferData[0]) | |
{ | |
SelectObject(drawDC, bitBuffers[0]); | |
} | |
else | |
{ | |
SelectObject(drawDC, bitBuffers[1]); | |
// Using the student provided mem buffer, must memcopy! | |
if ((void *)buffer != bufferData[0] && ((void *)buffer != bufferData[1])) | |
{ | |
memcpy(bufferData[1], buffer, screenWidth * screenHeight * sizeof(unsigned int)); | |
} | |
} | |
BitBlt(deviceContext, 0, 0, screenWidth, screenHeight, drawDC, 0, 0, SRCCOPY); | |
} | |
LRESULT Window::WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
switch (message) | |
{ | |
case (WM_CREATE): | |
{ | |
Window *w = reinterpret_cast<Window *>(((LPCREATESTRUCT)lParam)->lpCreateParams); | |
SetWindowLongPtr(hWnd, GWL_USERDATA, reinterpret_cast<long>(w)); | |
} | |
break; | |
case (WM_DESTROY): | |
{ | |
PostQuitMessage(0); | |
forceQuit = true; | |
} | |
break; | |
case (WM_ACTIVATE): | |
{ | |
if (LOWORD(wParam) == WA_INACTIVE) | |
{ | |
ReleaseCapture(); | |
ClipCursor(NULL); | |
if (hasInit) | |
{ | |
Mouse::instance->Sleep(); | |
Mouse::instance->Sleep(); | |
} | |
} | |
else | |
{ | |
if (hasInit) | |
{ | |
Mouse::instance->Wake(); | |
Mouse::instance->Wake(); | |
} | |
} | |
return 0; | |
} | |
break; | |
case (WM_LBUTTONDOWN): | |
{ | |
} | |
break; | |
case (WM_MOUSEMOVE): | |
{ | |
TRACKMOUSEEVENT tme; | |
tme.cbSize = sizeof(TRACKMOUSEEVENT); | |
tme.dwFlags = TME_LEAVE; | |
tme.hwndTrack = windowHandle; | |
TrackMouseEvent(&tme); | |
} | |
break; | |
case (WM_SIZE): | |
{ | |
screenWidth = LOWORD(lParam); | |
screenHeight = HIWORD(lParam); | |
BuildBitmap(); | |
Resize(); | |
} | |
break; | |
case (WM_SETFOCUS): | |
{ | |
if (hasInit) | |
{ | |
Mouse::instance->Wake(); | |
Mouse::instance->Wake(); | |
} | |
} | |
break; | |
case (WM_KILLFOCUS): | |
{ | |
if (hasInit) | |
{ | |
Mouse::instance->Sleep(); | |
Mouse::instance->Sleep(); | |
} | |
} | |
break; | |
} | |
return DefWindowProc(hWnd, message, wParam, lParam); | |
} | |
LRESULT CALLBACK Window::StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) | |
{ | |
Window *window = (Window *)GetWindowLongPtr(hWnd, GWL_USERDATA); | |
return window->WindowProc(hWnd, message, wParam, lParam); | |
} | |
bool Window::UpdateWindow() | |
{ | |
Keyboard::instance->UpdateHolds(); | |
Mouse::instance->UpdateHolds(); | |
MSG msg; | |
while (PeekMessage(&msg, windowHandle, 0, 0, PM_REMOVE)) | |
{ | |
CheckMessages(msg); | |
} | |
return !forceQuit; | |
} | |
void Window::CheckMessages(MSG &msg) | |
{ | |
switch (msg.message) | |
{ // Is There A Message Waiting? | |
case (WM_QUIT): | |
case (WM_CLOSE): | |
{ // Have We Received A Quit Message? | |
forceQuit = true; | |
} | |
break; | |
case (WM_INPUT): | |
{ | |
UINT dwSize; | |
GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, NULL, &dwSize, sizeof(RAWINPUTHEADER)); | |
BYTE *lpb = new BYTE[dwSize]; | |
GetRawInputData((HRAWINPUT)msg.lParam, RID_INPUT, lpb, &dwSize, sizeof(RAWINPUTHEADER)); | |
RAWINPUT *raw = (RAWINPUT *)lpb; | |
if (Keyboard::instance && raw->header.dwType == RIM_TYPEKEYBOARD) | |
{ | |
Keyboard::instance->Update(raw); | |
} | |
else if (Mouse::instance && raw->header.dwType == RIM_TYPEMOUSE) | |
{ | |
Mouse::instance->Update(raw); | |
} | |
delete lpb; | |
} | |
break; | |
default: | |
{ // If Not, Deal With Window Messages | |
TranslateMessage(&msg); // Translate The Message | |
DispatchMessage(&msg); // Dispatch The Message | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#pragma once | |
#include "Common.h" | |
#include "Colour.h" | |
#include "Mouse.h" | |
#include "Keyboard.h" | |
#include <windows.h> | |
#include <fcntl.h> | |
// These two defines cut a lot of crap out of the Windows libraries | |
#define WIN32_LEAN_AND_MEAN | |
#define VC_EXTRALEAN | |
#define WINDOWCLASS "WindowClass" | |
// This is the OS-specific crap required to render our pixel blocks on screen | |
class Window | |
{ | |
public: | |
Window(uint width, uint height); | |
~Window(void); | |
void PresentBuffer(Colour *buffer); | |
bool UpdateWindow(); | |
protected: | |
void CheckMessages(MSG &msg); | |
void BuildBitmap(); | |
virtual void Resize() { | |
}; | |
// Windows requires a static callback function to handle certain incoming messages. | |
static LRESULT CALLBACK StaticWindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
LRESULT WindowProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam); | |
HWND windowHandle; // OS handle | |
HDC deviceContext; | |
uint screenWidth; | |
uint screenHeight; | |
HDC drawDC; | |
HBITMAP bitBuffers[2]; | |
void *bufferData[2]; | |
VOID *pvBits; // pointer to DIB section | |
bool forceQuit; | |
bool hasInit; | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment