diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..2f1229f
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,350 @@
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+##
+## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
+
+# User-specific files
+*.rsuser
+*.suo
+*.user
+*.userosscache
+*.sln.docstates
+
+# User-specific files (MonoDevelop/Xamarin Studio)
+*.userprefs
+
+# Mono auto generated files
+mono_crash.*
+
+# Build results
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+[Rr]eleases/
+x64/
+x86/
+[Aa][Rr][Mm]/
+[Aa][Rr][Mm]64/
+bld/
+[Bb]in/
+[Oo]bj/
+[Ll]og/
+[Ll]ogs/
+
+# Visual Studio 2015/2017 cache/options directory
+.vs/
+# Uncomment if you have tasks that create the project's static files in wwwroot
+#wwwroot/
+
+# Visual Studio 2017 auto generated files
+Generated\ Files/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+# NUnit
+*.VisualState.xml
+TestResult.xml
+nunit-*.xml
+
+# Build Results of an ATL Project
+[Dd]ebugPS/
+[Rr]eleasePS/
+dlldata.c
+
+# Benchmark Results
+BenchmarkDotNet.Artifacts/
+
+# .NET Core
+project.lock.json
+project.fragment.lock.json
+artifacts/
+
+# StyleCop
+StyleCopReport.xml
+
+# Files built by Visual Studio
+*_i.c
+*_p.c
+*_h.h
+*.ilk
+*.meta
+*.obj
+*.iobj
+*.pch
+*.pdb
+*.ipdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*_wpftmp.csproj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.svclog
+*.scc
+
+# Chutzpah Test files
+_Chutzpah*
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opendb
+*.opensdf
+*.sdf
+*.cachefile
+*.VC.db
+*.VC.VC.opendb
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+*.sap
+
+# Visual Studio Trace Files
+*.e2e
+
+# TFS 2012 Local Workspace
+$tf/
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+*.DotSettings.user
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# AxoCover is a Code Coverage Tool
+.axoCover/*
+!.axoCover/settings.json
+
+# Visual Studio code coverage results
+*.coverage
+*.coveragexml
+
+# NCrunch
+_NCrunch_*
+.*crunch*.local.xml
+nCrunchTemp_*
+
+# MightyMoose
+*.mm.*
+AutoTest.Net/
+
+# Web workbench (sass)
+.sass-cache/
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.[Pp]ublish.xml
+*.azurePubxml
+# Note: Comment the next line if you want to checkin your web deploy settings,
+# but database connection strings (with potential passwords) will be unencrypted
+*.pubxml
+*.publishproj
+
+# Microsoft Azure Web App publish settings. Comment the next line if you want to
+# checkin your Azure Web App publish settings, but sensitive information contained
+# in these scripts will be unencrypted
+PublishScripts/
+
+# NuGet Packages
+*.nupkg
+# NuGet Symbol Packages
+*.snupkg
+# The packages folder can be ignored because of Package Restore
+**/[Pp]ackages/*
+# except build/, which is used as an MSBuild target.
+!**/[Pp]ackages/build/
+# Uncomment if necessary however generally it will be regenerated when needed
+#!**/[Pp]ackages/repositories.config
+# NuGet v3's project.json files produces more ignorable files
+*.nuget.props
+*.nuget.targets
+
+# Microsoft Azure Build Output
+csx/
+*.build.csdef
+
+# Microsoft Azure Emulator
+ecf/
+rcf/
+
+# Windows Store app package directories and files
+AppPackages/
+BundleArtifacts/
+Package.StoreAssociation.xml
+_pkginfo.txt
+*.appx
+*.appxbundle
+*.appxupload
+
+# Visual Studio cache files
+# files ending in .cache can be ignored
+*.[Cc]ache
+# but keep track of directories ending in .cache
+!?*.[Cc]ache/
+
+# Others
+ClientBin/
+~$*
+*~
+*.dbmdl
+*.dbproj.schemaview
+*.jfm
+*.pfx
+*.publishsettings
+orleans.codegen.cs
+
+# Including strong name files can present a security risk
+# (https://github.com/github/gitignore/pull/2483#issue-259490424)
+#*.snk
+
+# Since there are multiple workflows, uncomment next line to ignore bower_components
+# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
+#bower_components/
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file
+# to a newer Visual Studio version. Backup files are not needed,
+# because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+ServiceFabricBackup/
+*.rptproj.bak
+
+# SQL Server files
+*.mdf
+*.ldf
+*.ndf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+*.rptproj.rsuser
+*- [Bb]ackup.rdl
+*- [Bb]ackup ([0-9]).rdl
+*- [Bb]ackup ([0-9][0-9]).rdl
+
+# Microsoft Fakes
+FakesAssemblies/
+
+# GhostDoc plugin setting file
+*.GhostDoc.xml
+
+# Node.js Tools for Visual Studio
+.ntvs_analysis.dat
+node_modules/
+
+# Visual Studio 6 build log
+*.plg
+
+# Visual Studio 6 workspace options file
+*.opt
+
+# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
+*.vbw
+
+# Visual Studio LightSwitch build output
+**/*.HTMLClient/GeneratedArtifacts
+**/*.DesktopClient/GeneratedArtifacts
+**/*.DesktopClient/ModelManifest.xml
+**/*.Server/GeneratedArtifacts
+**/*.Server/ModelManifest.xml
+_Pvt_Extensions
+
+# Paket dependency manager
+.paket/paket.exe
+paket-files/
+
+# FAKE - F# Make
+.fake/
+
+# CodeRush personal settings
+.cr/personal
+
+# Python Tools for Visual Studio (PTVS)
+__pycache__/
+*.pyc
+
+# Cake - Uncomment if you are using it
+# tools/**
+# !tools/packages.config
+
+# Tabs Studio
+*.tss
+
+# Telerik's JustMock configuration file
+*.jmconfig
+
+# BizTalk build output
+*.btp.cs
+*.btm.cs
+*.odx.cs
+*.xsd.cs
+
+# OpenCover UI analysis results
+OpenCover/
+
+# Azure Stream Analytics local run output
+ASALocalRun/
+
+# MSBuild Binary and Structured Log
+*.binlog
+
+# NVidia Nsight GPU debugger configuration file
+*.nvuser
+
+# MFractors (Xamarin productivity tool) working folder
+.mfractor/
+
+# Local History for Visual Studio
+.localhistory/
+
+# BeatPulse healthcheck temp database
+healthchecksdb
+
+# Backup folder for Package Reference Convert tool in Visual Studio 2017
+MigrationBackup/
+
+# Ionide (cross platform F# VS Code tools) working folder
+.ionide/
\ No newline at end of file
diff --git a/OpenWindow.sln b/OpenWindow.sln
new file mode 100644
index 0000000..35fcdee
--- /dev/null
+++ b/OpenWindow.sln
@@ -0,0 +1,31 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 15
+VisualStudioVersion = 15.0.28307.852
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "OpenWindow", "OpenWindow\OpenWindow.vcxproj", "{42971E68-F861-4D45-9DA6-F5E163705584}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x64 = Debug|x64
+ Debug|x86 = Debug|x86
+ Release|x64 = Release|x64
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Debug|x64.ActiveCfg = Debug|x64
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Debug|x64.Build.0 = Debug|x64
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Debug|x86.ActiveCfg = Debug|Win32
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Debug|x86.Build.0 = Debug|Win32
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Release|x64.ActiveCfg = Release|x64
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Release|x64.Build.0 = Release|x64
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Release|x86.ActiveCfg = Release|Win32
+ {42971E68-F861-4D45-9DA6-F5E163705584}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+ GlobalSection(ExtensibilityGlobals) = postSolution
+ SolutionGuid = {E8F2DE00-C3E8-4801-B6A2-84B81D8359A9}
+ EndGlobalSection
+EndGlobal
diff --git a/OpenWindow/OpenWindow.vcxproj b/OpenWindow/OpenWindow.vcxproj
new file mode 100644
index 0000000..6ec45ac
--- /dev/null
+++ b/OpenWindow/OpenWindow.vcxproj
@@ -0,0 +1,135 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ x64
+
+
+
+ 15.0
+ {42971E68-F861-4D45-9DA6-F5E163705584}
+ OpenWindow
+ 10.0.18362.0
+
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+ Application
+ true
+ v141
+ MultiByte
+
+
+ Application
+ false
+ v141
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+
+
+
+
+ Level3
+ Disabled
+ true
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+
+
+
+
+ Level3
+ MaxSpeed
+ true
+ true
+ true
+ true
+
+
+ true
+ true
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/OpenWindow/OpenWindow.vcxproj.filters b/OpenWindow/OpenWindow.vcxproj.filters
new file mode 100644
index 0000000..53f3b63
--- /dev/null
+++ b/OpenWindow/OpenWindow.vcxproj.filters
@@ -0,0 +1,54 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;ipp;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+ Source Files
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+ Header Files
+
+
+
\ No newline at end of file
diff --git a/OpenWindow/african_head_diffuse.tga b/OpenWindow/african_head_diffuse.tga
new file mode 100644
index 0000000..bdd085f
Binary files /dev/null and b/OpenWindow/african_head_diffuse.tga differ
diff --git a/OpenWindow/african_head_nm_tangent.tga b/OpenWindow/african_head_nm_tangent.tga
new file mode 100644
index 0000000..46ee251
Binary files /dev/null and b/OpenWindow/african_head_nm_tangent.tga differ
diff --git a/OpenWindow/geometry.cpp b/OpenWindow/geometry.cpp
new file mode 100644
index 0000000..3b6b2f2
--- /dev/null
+++ b/OpenWindow/geometry.cpp
@@ -0,0 +1,7 @@
+#include "geometry.h"
+
+template <> template <> vec<3,int> ::vec(const vec<3,float> &v) : x(int(v.x+.5f)),y(int(v.y+.5f)),z(int(v.z+.5f)) {}
+template <> template <> vec<3,float>::vec(const vec<3,int> &v) : x(v.x),y(v.y),z(v.z) {}
+template <> template <> vec<2,int> ::vec(const vec<2,float> &v) : x(int(v.x+.5f)),y(int(v.y+.5f)) {}
+template <> template <> vec<2,float>::vec(const vec<2,int> &v) : x(v.x),y(v.y) {}
+
diff --git a/OpenWindow/geometry.h b/OpenWindow/geometry.h
new file mode 100644
index 0000000..630234a
--- /dev/null
+++ b/OpenWindow/geometry.h
@@ -0,0 +1,237 @@
+#ifndef __GEOMETRY_H__
+#define __GEOMETRY_H__
+#include
+#include
+#include
+#include
+
+template class mat;
+
+template struct vec {
+ vec() { for (size_t i=DIM; i--; data_[i] = T()); }
+ T& operator[](const size_t i) { assert(i struct vec<2,T> {
+ vec() : x(T()), y(T()) {}
+ vec(T X, T Y) : x(X), y(Y) {}
+ template vec<2,T>(const vec<2,U> &v);
+ T& operator[](const size_t i) { assert(i<2); return i<=0 ? x : y; }
+ const T& operator[](const size_t i) const { assert(i<2); return i<=0 ? x : y; }
+
+ T x,y;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+template struct vec<3,T> {
+ vec() : x(T()), y(T()), z(T()) {}
+ vec(T X, T Y, T Z) : x(X), y(Y), z(Z) {}
+ vec(vec<4, T> v) : x(v.x/v.w), y(v.y/v.w), z(v.z/v.w) {}
+ template vec<3,T>(const vec<3,U> &v);
+ T& operator[](const size_t i) { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
+ const T& operator[](const size_t i) const { assert(i<3); return i<=0 ? x : (1==i ? y : z); }
+ float norm() { return std::sqrt(x*x+y*y+z*z); }
+ vec<3,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
+
+ T x,y,z;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+template struct vec<4,T> {
+ vec() : x(T()), y(T()), z(T()), w(T()) {}
+ vec(T X, T Y, T Z, T W) : x(X), y(Y), z(Z), w(W) {}
+ vec(vec<3, T> v) : x(v.x), y(v.y), z(v.z), w(1) {}
+ template vec<4,T>(const vec<4,U> &v);
+ T& operator[](const size_t i) { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }
+ const T& operator[](const size_t i) const { assert(i<4); return i<=0 ? x : (1==i ? y : (2==i ? z : w)); }
+ float norm() { return std::sqrt(x*x+y*y+z*z+w*w); }
+ vec<4,T> & normalize(T l=1) { *this = (*this)*(l/norm()); return *this; }
+
+ T x,y,z,w;
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+template T operator*(const vec& lhs, const vec& rhs) {
+ T ret = T();
+ for (size_t i=DIM; i--; ret+=lhs[i]*rhs[i]);
+ return ret;
+}
+
+
+templatevec operator+(vec lhs, const vec& rhs) {
+ for (size_t i=DIM; i--; lhs[i]+=rhs[i]);
+ return lhs;
+}
+
+templatevec operator-(vec lhs, const vec& rhs) {
+ for (size_t i=DIM; i--; lhs[i]-=rhs[i]);
+ return lhs;
+}
+
+template vec operator*(vec lhs, const U& rhs) {
+ for (size_t i=DIM; i--; lhs[i]*=rhs);
+ return lhs;
+}
+
+template vec operator/(vec lhs, const U& rhs) {
+ for (size_t i=DIM; i--; lhs[i]/=rhs);
+ return lhs;
+}
+
+template vec embed(const vec &v, T fill=1) {
+ vec ret;
+ for (size_t i=LEN; i--; ret[i]=(i vec proj(const vec &v) {
+ vec ret;
+ for (size_t i=LEN; i--; ret[i]=v[i]);
+ return ret;
+}
+
+template vec<3,T> cross(vec<3,T> v1, vec<3,T> v2) {
+ return vec<3,T>(v1.y*v2.z - v1.z*v2.y, v1.z*v2.x - v1.x*v2.z, v1.x*v2.y - v1.y*v2.x);
+}
+
+template std::ostream& operator<<(std::ostream& out, vec& v) {
+ for(unsigned int i=0; i struct dt {
+ static T det(const mat& src) {
+ T ret=0;
+ for (size_t i=DIM; i--; ret += src[0][i]*src.cofactor(0,i));
+ return ret;
+ }
+};
+
+template struct dt<1,T> {
+ static T det(const mat<1,1,T>& src) {
+ return src[0][0];
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+template class mat {
+ vec rows[DimRows];
+public:
+ mat() {}
+
+ vec& operator[] (const size_t idx) {
+ assert(idx& operator[] (const size_t idx) const {
+ assert(idx col(const size_t idx) const {
+ assert(idx ret;
+ for (size_t i=DimRows; i--; ret[i]=rows[i][idx]);
+ return ret;
+ }
+
+ void set_col(size_t idx, vec v) {
+ assert(idx identity() {
+ mat ret;
+ for (size_t i=DimRows; i--; )
+ for (size_t j=DimCols;j--; ret[i][j]=(i==j));
+ return ret;
+ }
+
+ T det() const {
+ return dt::det(*this);
+ }
+
+ mat get_minor(size_t row, size_t col) const {
+ mat ret;
+ for (size_t i=DimRows-1; i--; )
+ for (size_t j=DimCols-1;j--; ret[i][j]=rows[i adjugate() const {
+ mat ret;
+ for (size_t i=DimRows; i--; )
+ for (size_t j=DimCols; j--; ret[i][j]=cofactor(i,j));
+ return ret;
+ }
+
+ mat invert_transpose() {
+ mat ret = adjugate();
+ T tmp = ret[0]*rows[0];
+ return ret/tmp;
+ }
+
+ mat invert() {
+ return invert_transpose().transpose();
+ }
+
+ mat transpose() {
+ mat ret;
+ for (size_t i=DimCols; i--; ret[i]=this->col(i));
+ return ret;
+ }
+};
+
+/////////////////////////////////////////////////////////////////////////////////
+
+template vec operator*(const mat& lhs, const vec& rhs) {
+ vec ret;
+ for (size_t i=DimRows; i--; ret[i]=lhs[i]*rhs);
+ return ret;
+}
+
+templatemat operator*(const mat& lhs, const mat& rhs) {
+ mat result;
+ for (size_t i=R1; i--; )
+ for (size_t j=C2; j--; result[i][j]=lhs[i]*rhs.col(j));
+ return result;
+}
+
+templatemat operator/(mat lhs, const T& rhs) {
+ for (size_t i=DimRows; i--; lhs[i]=lhs[i]/rhs);
+ return lhs;
+}
+
+template std::ostream& operator<<(std::ostream& out, mat& m) {
+ for (size_t i=0; i Vec2f;
+typedef vec<2, int> Vec2i;
+typedef vec<3, float> Vec3f;
+typedef vec<3, int> Vec3i;
+typedef vec<4, float> Vec4f;
+typedef mat<4,4,float> Matrix;
+#endif //__GEOMETRY_H__
+
diff --git a/OpenWindow/main.cpp b/OpenWindow/main.cpp
new file mode 100644
index 0000000..85583cc
--- /dev/null
+++ b/OpenWindow/main.cpp
@@ -0,0 +1,53 @@
+//#include
+#include "util_window.h"
+#include "renderer.h"
+#include "ctime"
+
+const int width = 800;
+const int height = 800;
+
+int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
+{
+ HWND hwnd;
+ MSG Msg;
+
+ hwnd = create_window(width, height, hInstance);
+ ShowWindow(hwnd, nCmdShow);
+
+ char debug_str[100];
+
+ clock_t first_time = clock();
+ render();
+ clock_t end_time = clock();
+ Update();
+
+ sprintf_s(debug_str, "%f\n", (double)(end_time - first_time) / CLOCKS_PER_SEC);
+ OutputDebugString(debug_str);
+
+ while (GetMessage(&Msg, NULL, 0, 0))
+ {
+ TranslateMessage(&Msg);
+ DispatchMessage(&Msg);
+ }
+ return Msg.wParam;
+}
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch (message)
+ {
+ case WM_RBUTTONDOWN:
+ break;
+ case WM_LBUTTONDOWN:
+ Update();
+ break;
+ case WM_CLOSE:
+ DestroyWindow(hwnd);
+ break;
+ case WM_DESTROY:
+ PostQuitMessage(0);
+ break;
+ default:
+ return DefWindowProc(hwnd, message, wParam, lParam);
+ }
+}
diff --git a/OpenWindow/model.cpp b/OpenWindow/model.cpp
new file mode 100644
index 0000000..2a327b5
--- /dev/null
+++ b/OpenWindow/model.cpp
@@ -0,0 +1,143 @@
+#include
+#include
+#include
+#include "model.h"
+
+#define PI 3.14159265358979323846
+#define DEG2RAD PI/180
+
+Model::Model(const char *filename) : verts_(), faces_(), norms_(), uv_(), diffusemap_(), normalmap_(), specularmap_() {
+ std::ifstream in;
+ Transform = Matrix::identity();
+ Rotation = Matrix::identity();
+ Scale = Matrix::identity();
+ Translation = Matrix::identity();
+ in.open (filename, std::ifstream::in);
+ if (in.fail()) return;
+ std::string line;
+ while (!in.eof()) {
+ std::getline(in, line);
+ std::istringstream iss(line.c_str());
+ char trash;
+ if (!line.compare(0, 2, "v ")) {
+ iss >> trash;
+ Vec3f v;
+ for (int i=0;i<3;i++) iss >> v[i];
+ verts_.push_back(v);
+ } else if (!line.compare(0, 3, "vn ")) {
+ iss >> trash >> trash;
+ Vec3f n;
+ for (int i=0;i<3;i++) iss >> n[i];
+ norms_.push_back(n);
+ } else if (!line.compare(0, 3, "vt ")) {
+ iss >> trash >> trash;
+ Vec2f uv;
+ for (int i=0;i<2;i++) iss >> uv[i];
+ uv_.push_back(uv);
+ } else if (!line.compare(0, 2, "f ")) {
+ std::vector f;
+ Vec3i tmp;
+ iss >> trash;
+ while (iss >> tmp[0] >> trash >> tmp[1] >> trash >> tmp[2]) {
+ for (int i=0; i<3; i++) tmp[i]--; // in wavefront obj all indices start at 1, not zero
+ f.push_back(tmp);
+ }
+ faces_.push_back(f);
+ }
+ }
+ std::cerr << "# v# " << verts_.size() << " f# " << faces_.size() << " vt# " << uv_.size() << " vn# " << norms_.size() << std::endl;
+ load_texture(filename, "_diffuse.tga", diffusemap_);
+ load_texture(filename, "_nm_tangent.tga", normalmap_);
+ //load_texture(filename, "_spec.tga", specularmap_);
+}
+
+Model::~Model() {}
+
+void Model::ApplyTransform() {
+ Transform = Translation * Scale * Rotation;
+}
+
+void Model::translate(Vec3f tr) {
+ Translation[0][3] += tr.x;
+ Translation[1][3] += tr.y;
+ Translation[2][3] += tr.z;
+}
+void Model::rotate(Vec3f rot) {
+ rot = rot * DEG2RAD;
+
+ Rotation[0][0] = cosf(rot.y) * cosf(rot.z);
+ Rotation[0][1] = -cosf(rot.y) * sinf(rot.z);
+ Rotation[0][2] = sinf(rot.y);
+ Rotation[1][0] = sinf(rot.x)*sinf(rot.y)*cosf(rot.z) + cosf(rot.x) * sinf(rot.z);
+ Rotation[1][1] = -sinf(rot.x)*sinf(rot.y)*sinf(rot.z) + cosf(rot.x) * cosf(rot.z);
+ Rotation[1][2] = -sinf(rot.x)*cosf(rot.y);
+ Rotation[2][0] = -cosf(rot.x)*sinf(rot.y)*cosf(rot.z) + sinf(rot.x) * sinf(rot.z);
+ Rotation[2][1] = cosf(rot.x)*sinf(rot.y)*sinf(rot.z) + sinf(rot.x) * cosf(rot.z);
+ Rotation[2][2] = cosf(rot.x)*cosf(rot.y);
+}
+void Model::scale(Vec3f scl) {
+ Scale[0][0] = scl.x;
+ Scale[1][1] = scl.y;
+ Scale[2][2] = scl.z;
+}
+
+int Model::nverts() {
+ return (int)verts_.size();
+}
+
+int Model::nfaces() {
+ return (int)faces_.size();
+}
+
+std::vector Model::face(int idx) {
+ std::vector face;
+ for (int i=0; i<(int)faces_[idx].size(); i++) face.push_back(faces_[idx][i][0]);
+ return face;
+}
+
+Vec3f Model::vert(int i) {
+ return verts_[i];
+}
+
+Vec3f Model::vert(int iface, int nthvert) {
+ return verts_[faces_[iface][nthvert][0]];
+}
+
+void Model::load_texture(std::string filename, const char *suffix, TGAImage &img) {
+ std::string texfile(filename);
+ size_t dot = texfile.find_last_of(".");
+ if (dot!=std::string::npos) {
+ texfile = texfile.substr(0,dot) + std::string(suffix);
+ std::cerr << "texture file " << texfile << " loading " << (img.read_tga_file(texfile.c_str()) ? "ok" : "failed") << std::endl;
+ img.flip_vertically();
+ }
+}
+
+TGAColor Model::diffuse(Vec2f uvf) {
+ Vec2i uv(uvf[0]*diffusemap_.get_width(), uvf[1]*diffusemap_.get_height());
+ return diffusemap_.get(uv[0], uv[1]);
+}
+
+Vec3f Model::normal(Vec2f uvf) {
+ Vec2i uv(uvf[0]*normalmap_.get_width(), uvf[1]*normalmap_.get_height());
+ TGAColor c = normalmap_.get(uv[0], uv[1]);
+ Vec3f res;
+ for (int i=0; i<3; i++)
+ res[2-i] = (float)c[i]/255.f*2.f - 1.f;
+ return res;
+}
+
+Vec2f Model::uv(int iface, int nthvert) {
+ return uv_[faces_[iface][nthvert][1]];
+}
+
+float Model::specular(Vec2f uvf) {
+ Vec2i uv(uvf[0]*specularmap_.get_width(), uvf[1]*specularmap_.get_height());
+ return specularmap_.get(uv[0], uv[1])[0]/1.f;
+}
+
+Vec3f Model::normal(int iface, int nthvert) {
+ int idx = faces_[iface][nthvert][2];
+ return norms_[idx].normalize();
+}
+
diff --git a/OpenWindow/model.h b/OpenWindow/model.h
new file mode 100644
index 0000000..93951ef
--- /dev/null
+++ b/OpenWindow/model.h
@@ -0,0 +1,41 @@
+#ifndef __MODEL_H__
+#define __MODEL_H__
+#include
+#include
+#include "geometry.h"
+#include "tgaimage.h"
+
+class Model {
+private:
+ std::vector verts_;
+ std::vector > faces_; // attention, this Vec3i means vertex/uv/normal
+ std::vector norms_;
+ std::vector uv_;
+ TGAImage diffusemap_;
+ TGAImage normalmap_;
+ TGAImage specularmap_;
+ void load_texture(std::string filename, const char *suffix, TGAImage &img);
+public:
+ Model(const char *filename);
+ ~Model();
+ int nverts();
+ int nfaces();
+ Vec3f normal(int iface, int nthvert);
+ Vec3f normal(Vec2f uv);
+ Vec3f vert(int i);
+ Vec3f vert(int iface, int nthvert);
+ Vec2f uv(int iface, int nthvert);
+ Matrix Transform;
+ Matrix Rotation;
+ Matrix Scale;
+ Matrix Translation;
+ void translate(Vec3f tr);
+ void rotate(Vec3f rot);
+ void scale(Vec3f scl);
+ void ApplyTransform();
+ TGAColor diffuse(Vec2f uv);
+ float specular(Vec2f uv);
+ std::vector face(int idx);
+};
+#endif //__MODEL_H__
+
diff --git a/OpenWindow/output.tga b/OpenWindow/output.tga
new file mode 100644
index 0000000..9eb084b
Binary files /dev/null and b/OpenWindow/output.tga differ
diff --git a/OpenWindow/renderer.cpp b/OpenWindow/renderer.cpp
new file mode 100644
index 0000000..09f8a03
--- /dev/null
+++ b/OpenWindow/renderer.cpp
@@ -0,0 +1,232 @@
+#define _USE_MATH_DEFINES
+#include
+#include
+#include
+#include "tgaimage.h"
+#include "model.h"
+#include "geometry.h"
+#include "renderer.h"
+#include "util_window.h"
+#include
+
+const TGAColor white = TGAColor(255, 255, 255, 255);
+const TGAColor red = TGAColor(255, 0, 0, 255);
+const TGAColor green = TGAColor(0, 255, 0, 255);
+const TGAColor blue = TGAColor(0, 0, 255, 255);
+
+const int depth = 255;
+
+float* z_buffer;
+Vec3f light_dir = Vec3f(0, 0, 1).normalize();
+Vec3f eye(0, 0, 3);
+Vec3f center(0, 0, 0);
+
+Matrix viewport(int x, int y, int w, int h) {
+ Matrix m = Matrix::identity();
+ m[0][3] = x + w / 2.f;
+ m[1][3] = y + h / 2.f;
+ m[2][3] = depth / 2.f;
+
+ m[0][0] = w / 2.f;
+ m[1][1] = h / 2.f;
+ m[2][2] = depth / 2.f;
+ return m;
+}
+
+void line(Vec2i p0, Vec2i p1, TGAImage &image, TGAColor color)
+{
+ bool steep = false;
+
+ if (std::abs(p0[0] - p1[0]) < std::abs(p0[1] - p1[1])) {
+ std::swap(p0[0], p0[1]);
+ std::swap(p1[0], p1[1]);
+ steep = true;
+
+ }
+
+ if (p0[0] > p1[0]) {
+ std::swap(p0[0], p1[0]);
+ std::swap(p0[1], p1[1]);
+ }
+
+ int dx = p1[0] - p0[0];
+ int dy = p1[1] - p0[1];
+ int derror2 = std::abs(dy) * 2;
+ int error2 = 0;
+ int y = p0[1];
+ int y_step = p1[1] > p0[1] ? 1 : -1;
+ int dx_2 = 2 * dx;
+
+ for (int x = p0[0]; x <= p1[0]; x++) {
+ if (steep) {
+ image.set(y, x, color);
+ }
+ else {
+ image.set(x, y, color);
+ }
+ error2 += derror2;
+ if (error2 > dx) {
+ y += (y_step);
+ error2 -= dx_2;
+ }
+ }
+}
+
+Vec3f barycentric(Vec3f* pts, Vec3f P)
+{
+ Vec3f u = cross(
+ Vec3f(pts[2][0] - pts[0][0], pts[1][0] - pts[0][0], pts[0][0] - P[0]), // AC_x, AB_x, distance_x
+ Vec3f(pts[2][1] - pts[0][1], pts[1][1] - pts[0][1], pts[0][1] - P[1]) // AC_y, AB_y, distance_y
+ );
+
+ if (std::abs(u[2]) < 1) return Vec3f(-1, 1, 1);
+ return Vec3f(1.f - (u.x + u.y) / u.z, u.y / u.z, u.x / u.z);
+}
+
+
+void triangle(
+ Vec3f* pts, // Needed
+ Model* model, // Should be removed
+ Vec2f* diff_pts, // Should be removed
+ float* intensities,
+ Vec3f camera_pos) // Not really sure yet
+{
+
+ if (pts[0].y == pts[1].y && pts[0].y == pts[2].y) return; // i dont care about degenerate triangles
+ if (pts[0].y > pts[1].y) {
+ std::swap(pts[0], pts[1]);
+ std::swap(diff_pts[0], diff_pts[1]);
+ std::swap(intensities[0], intensities[1]);
+ }
+ if (pts[0].y > pts[2].y) {
+ std::swap(pts[0], pts[2]);
+ std::swap(diff_pts[0], diff_pts[2]);
+ std::swap(intensities[0], intensities[2]);
+ }
+ if (pts[1].y > pts[2].y) {
+ std::swap(pts[1], pts[2]);
+ std::swap(diff_pts[1], diff_pts[2]);
+ std::swap(intensities[1], intensities[2]);
+ }
+ Vec2i bounding_box_min(screen_width - 1, screen_height - 1);
+ Vec2i bounding_box_max(0, 0);
+ Vec2i clamp(screen_width - 1, screen_height - 1);
+ TGAColor color = white;
+
+ for(int i = 0; i < 3; i++) {
+ for(int j =0; j < 2; j++) {
+ bounding_box_min[j] = std::fmax(0, std::fmin(bounding_box_min[j], (int)pts[i][j]));
+ bounding_box_max[j] = std::fmin(clamp[j], std::fmax(bounding_box_max[j], (int)pts[i][j]));
+ }
+ }
+
+ Vec3f P;
+ for(P.x = bounding_box_min.x; P.x <= bounding_box_max.x; P.x++) {
+ for(P.y = bounding_box_min.y; P.y <= bounding_box_max.y; P.y++) {
+ Vec3f bc_coord = barycentric(pts, P);
+ if(bc_coord.x < 0 || bc_coord.y < 0 || bc_coord.z < 0) continue;
+
+
+ float intensity =
+ intensities[0]
+ + (intensities[1] - intensities[0]) * bc_coord[1]
+ + (intensities[2] - intensities[0]) * bc_coord[2];
+
+
+ // Interpolating Z using the barycentric coordinates
+ P.z = 0;
+ for(int i = 0; i < 3; i++) P.z += pts[i][2] * bc_coord[i];
+
+ // Coloring according to the Z-Buffer
+ if (P.z > z_buffer[(int)(P.x + P.y * screen_width)])
+ {
+ z_buffer[(int)(P.x + P.y * screen_width)] = P.z;
+
+ // If diff_pts (Diffusemap Points) were passed, then find the
+ // color of the current pixel
+ if(diff_pts) {
+ Vec2f diff_pt =
+ diff_pts[0]
+ + (diff_pts[1] - diff_pts[0]) * bc_coord[1]
+ + (diff_pts[2] - diff_pts[0]) * bc_coord[2];
+
+ color = model->diffuse(diff_pt);
+ }
+ color = color * intensity;
+ set_pixel(P.x, P.y, color_to_int(color));
+ }
+ }
+ }
+}
+
+int color_to_int(TGAColor col) {
+ return (col[2] << 16) | (col[1] << 8) | col[0];
+}
+
+void init_zbuffer()
+{
+ z_buffer = new float[screen_width*screen_height];
+ for (int i = 0; i < screen_width * screen_height; i++)
+ z_buffer[i] = INT_MIN;
+}
+
+Matrix lookat(Vec3f eye, Vec3f center, Vec3f up) {
+ Vec3f z = (eye - center).normalize();
+ Vec3f x = cross(up, z).normalize();
+ Vec3f y = cross(z, x).normalize();
+ Matrix Minv = Matrix::identity();
+ Matrix Tr = Matrix::identity();
+ for (int i = 0; i < 3; i++) {
+ Minv[0][i] = x[i];
+ Minv[1][i] = y[i];
+ Minv[2][i] = z[i];
+ Tr[i][3] = -center[i];
+ }
+ return Minv * Tr;
+}
+
+void render()
+{
+ Model* model = new Model("african_head.obj");
+
+ Matrix ViewPort = viewport(screen_width / 8, screen_height / 8, screen_width * 3 / 4, screen_height * 3 / 4);
+ Matrix Projection = Matrix::identity();
+ Matrix ModelView = lookat(eye, center, Vec3f(0, 1, 0));
+
+ Projection[3][2] = -1.f / (eye - center).norm();
+
+ model->rotate(Vec3f(0, 0, 90));
+ model->scale(Vec3f(0.5, 0.5, 0.5));
+ model->translate(Vec3f(0.5, 0.5, -1));
+
+ model->ApplyTransform();
+
+ Matrix z = ViewPort * Projection * ModelView * model->Transform;
+
+ init_zbuffer();
+ for (int i = 0; i < model->nfaces(); i++)
+ {
+ std::vector face = model->face(i);
+ Vec3f screen_coords[3];
+ Vec3f world_coords[3];
+ Vec2f diffuse_coords[3];
+ float intensities[3];
+
+ for (int j = 0; j < 3; j++)
+ {
+ Vec3f v = model->vert(face[j]);
+ Vec4f v4(v);
+ Vec3f coord(z * v4);
+
+ screen_coords[j] = coord;
+ world_coords[j] = v;
+ diffuse_coords[j] = model->uv(i, j);
+ intensities[j] = model->normal(i, j) * light_dir;
+ }
+
+ triangle(screen_coords, model, diffuse_coords, intensities, Vec3f(0, 0, 5));
+ }
+
+ delete model;
+}
+
diff --git a/OpenWindow/renderer.h b/OpenWindow/renderer.h
new file mode 100644
index 0000000..f92dacb
--- /dev/null
+++ b/OpenWindow/renderer.h
@@ -0,0 +1,7 @@
+#ifndef RENDERER_HEADER
+#define RENDERER_HEADER
+#include "tgaimage.h"
+
+void render();
+int color_to_int(TGAColor col);
+#endif
\ No newline at end of file
diff --git a/OpenWindow/tgaimage.cpp b/OpenWindow/tgaimage.cpp
new file mode 100644
index 0000000..47e6ff8
--- /dev/null
+++ b/OpenWindow/tgaimage.cpp
@@ -0,0 +1,356 @@
+#include
+#include
+#include
+#include
+#include
+#include "tgaimage.h"
+
+TGAImage::TGAImage() : data(NULL), width(0), height(0), bytespp(0) {}
+
+TGAImage::TGAImage(int w, int h, int bpp) : data(NULL), width(w), height(h), bytespp(bpp) {
+ unsigned long nbytes = width*height*bytespp;
+ data = new unsigned char[nbytes];
+ memset(data, 0, nbytes);
+}
+
+TGAImage::TGAImage(const TGAImage &img) : data(NULL), width(img.width), height(img.height), bytespp(img.bytespp) {
+ unsigned long nbytes = width*height*bytespp;
+ data = new unsigned char[nbytes];
+ memcpy(data, img.data, nbytes);
+}
+
+TGAImage::~TGAImage() {
+ if (data) delete [] data;
+}
+
+TGAImage & TGAImage::operator =(const TGAImage &img) {
+ if (this != &img) {
+ if (data) delete [] data;
+ width = img.width;
+ height = img.height;
+ bytespp = img.bytespp;
+ unsigned long nbytes = width*height*bytespp;
+ data = new unsigned char[nbytes];
+ memcpy(data, img.data, nbytes);
+ }
+ return *this;
+}
+
+bool TGAImage::read_tga_file(const char *filename) {
+ if (data) delete [] data;
+ data = NULL;
+ std::ifstream in;
+ in.open (filename, std::ios::binary);
+ if (!in.is_open()) {
+ std::cerr << "can't open file " << filename << "\n";
+ in.close();
+ return false;
+ }
+ TGA_Header header;
+ in.read((char *)&header, sizeof(header));
+ if (!in.good()) {
+ in.close();
+ std::cerr << "an error occured while reading the header\n";
+ return false;
+ }
+ width = header.width;
+ height = header.height;
+ bytespp = header.bitsperpixel>>3;
+ if (width<=0 || height<=0 || (bytespp!=GRAYSCALE && bytespp!=RGB && bytespp!=RGBA)) {
+ in.close();
+ std::cerr << "bad bpp (or width/height) value\n";
+ return false;
+ }
+ unsigned long nbytes = bytespp*width*height;
+ data = new unsigned char[nbytes];
+ if (3==header.datatypecode || 2==header.datatypecode) {
+ in.read((char *)data, nbytes);
+ if (!in.good()) {
+ in.close();
+ std::cerr << "an error occured while reading the data\n";
+ return false;
+ }
+ } else if (10==header.datatypecode||11==header.datatypecode) {
+ if (!load_rle_data(in)) {
+ in.close();
+ std::cerr << "an error occured while reading the data\n";
+ return false;
+ }
+ } else {
+ in.close();
+ std::cerr << "unknown file format " << (int)header.datatypecode << "\n";
+ return false;
+ }
+ if (!(header.imagedescriptor & 0x20)) {
+ flip_vertically();
+ }
+ if (header.imagedescriptor & 0x10) {
+ flip_horizontally();
+ }
+ std::cerr << width << "x" << height << "/" << bytespp*8 << "\n";
+ in.close();
+ return true;
+}
+
+bool TGAImage::load_rle_data(std::ifstream &in) {
+ unsigned long pixelcount = width*height;
+ unsigned long currentpixel = 0;
+ unsigned long currentbyte = 0;
+ TGAColor colorbuffer;
+ do {
+ unsigned char chunkheader = 0;
+ chunkheader = in.get();
+ if (!in.good()) {
+ std::cerr << "an error occured while reading the data\n";
+ return false;
+ }
+ if (chunkheader<128) {
+ chunkheader++;
+ for (int i=0; ipixelcount) {
+ std::cerr << "Too many pixels read\n";
+ return false;
+ }
+ }
+ } else {
+ chunkheader -= 127;
+ in.read((char *)colorbuffer.bgra, bytespp);
+ if (!in.good()) {
+ std::cerr << "an error occured while reading the header\n";
+ return false;
+ }
+ for (int i=0; ipixelcount) {
+ std::cerr << "Too many pixels read\n";
+ return false;
+ }
+ }
+ }
+ } while (currentpixel < pixelcount);
+ return true;
+}
+
+bool TGAImage::write_tga_file(const char *filename, bool rle) {
+ unsigned char developer_area_ref[4] = {0, 0, 0, 0};
+ unsigned char extension_area_ref[4] = {0, 0, 0, 0};
+ unsigned char footer[18] = {'T','R','U','E','V','I','S','I','O','N','-','X','F','I','L','E','.','\0'};
+ std::ofstream out;
+ out.open (filename, std::ios::binary);
+ if (!out.is_open()) {
+ std::cerr << "can't open file " << filename << "\n";
+ out.close();
+ return false;
+ }
+ TGA_Header header;
+ memset((void *)&header, 0, sizeof(header));
+ header.bitsperpixel = bytespp<<3;
+ header.width = width;
+ header.height = height;
+ header.datatypecode = (bytespp==GRAYSCALE?(rle?11:3):(rle?10:2));
+ header.imagedescriptor = 0x20; // top-left origin
+ out.write((char *)&header, sizeof(header));
+ if (!out.good()) {
+ out.close();
+ std::cerr << "can't dump the tga file\n";
+ return false;
+ }
+ if (!rle) {
+ out.write((char *)data, width*height*bytespp);
+ if (!out.good()) {
+ std::cerr << "can't unload raw data\n";
+ out.close();
+ return false;
+ }
+ } else {
+ if (!unload_rle_data(out)) {
+ out.close();
+ std::cerr << "can't unload rle data\n";
+ return false;
+ }
+ }
+ out.write((char *)developer_area_ref, sizeof(developer_area_ref));
+ if (!out.good()) {
+ std::cerr << "can't dump the tga file\n";
+ out.close();
+ return false;
+ }
+ out.write((char *)extension_area_ref, sizeof(extension_area_ref));
+ if (!out.good()) {
+ std::cerr << "can't dump the tga file\n";
+ out.close();
+ return false;
+ }
+ out.write((char *)footer, sizeof(footer));
+ if (!out.good()) {
+ std::cerr << "can't dump the tga file\n";
+ out.close();
+ return false;
+ }
+ out.close();
+ return true;
+}
+
+// TODO: it is not necessary to break a raw chunk for two equal pixels (for the matter of the resulting size)
+bool TGAImage::unload_rle_data(std::ofstream &out) {
+ const unsigned char max_chunk_length = 128;
+ unsigned long npixels = width*height;
+ unsigned long curpix = 0;
+ while (curpix=width || y>=height) {
+ return TGAColor();
+ }
+ return TGAColor(data+(x+y*width)*bytespp, bytespp);
+}
+
+bool TGAImage::set(int x, int y, TGAColor &c) {
+ if (!data || x<0 || y<0 || x>=width || y>=height) {
+ return false;
+ }
+ memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
+ return true;
+}
+
+bool TGAImage::set(int x, int y, const TGAColor &c) {
+ if (!data || x<0 || y<0 || x>=width || y>=height) {
+ return false;
+ }
+ memcpy(data+(x+y*width)*bytespp, c.bgra, bytespp);
+ return true;
+}
+
+int TGAImage::get_bytespp() {
+ return bytespp;
+}
+
+int TGAImage::get_width() {
+ return width;
+}
+
+int TGAImage::get_height() {
+ return height;
+}
+
+bool TGAImage::flip_horizontally() {
+ if (!data) return false;
+ int half = width>>1;
+ for (int i=0; i>1;
+ for (int j=0; j=(int)width) {
+ errx -= width;
+ nx += bytespp;
+ memcpy(tdata+nscanline+nx, data+oscanline+ox, bytespp);
+ }
+ }
+ erry += h;
+ oscanline += olinebytes;
+ while (erry>=(int)height) {
+ if (erry>=(int)height<<1) // it means we jump over a scanline
+ memcpy(tdata+nscanline+nlinebytes, tdata+nscanline, nlinebytes);
+ erry -= height;
+ nscanline += nlinebytes;
+ }
+ }
+ delete [] data;
+ data = tdata;
+ width = w;
+ height = h;
+ return true;
+}
+
diff --git a/OpenWindow/tgaimage.h b/OpenWindow/tgaimage.h
new file mode 100644
index 0000000..63a944b
--- /dev/null
+++ b/OpenWindow/tgaimage.h
@@ -0,0 +1,98 @@
+#ifndef __IMAGE_H__
+#define __IMAGE_H__
+
+#include
+
+#pragma pack(push,1)
+struct TGA_Header {
+ char idlength;
+ char colormaptype;
+ char datatypecode;
+ short colormaporigin;
+ short colormaplength;
+ char colormapdepth;
+ short x_origin;
+ short y_origin;
+ short width;
+ short height;
+ char bitsperpixel;
+ char imagedescriptor;
+};
+#pragma pack(pop)
+
+struct TGAColor {
+ unsigned char bgra[4];
+ unsigned char bytespp;
+
+ TGAColor() : bgra(), bytespp(1) {
+ for (int i=0; i<4; i++) bgra[i] = 0;
+ }
+
+ TGAColor(unsigned char R, unsigned char G, unsigned char B, unsigned char A=255) : bgra(), bytespp(4) {
+ bgra[0] = B;
+ bgra[1] = G;
+ bgra[2] = R;
+ bgra[3] = A;
+ }
+
+ TGAColor(unsigned char v) : bgra(), bytespp(1) {
+ for (int i=0; i<4; i++) bgra[i] = 0;
+ bgra[0] = v;
+ }
+
+
+ TGAColor(const unsigned char *p, unsigned char bpp) : bgra(), bytespp(bpp) {
+ for (int i=0; i<(int)bpp; i++) {
+ bgra[i] = p[i];
+ }
+ for (int i=bpp; i<4; i++) {
+ bgra[i] = 0;
+ }
+ }
+
+ unsigned char& operator[](const int i) { return bgra[i]; }
+
+ TGAColor operator *(float intensity) const {
+ TGAColor res = *this;
+ intensity = (intensity>1.f?1.f:(intensity<0.f?0.f:intensity));
+ for (int i=0; i<4; i++) res.bgra[i] = bgra[i]*intensity;
+ return res;
+ }
+};
+
+class TGAImage {
+protected:
+ unsigned char* data;
+ int width;
+ int height;
+ int bytespp;
+
+ bool load_rle_data(std::ifstream &in);
+ bool unload_rle_data(std::ofstream &out);
+public:
+ enum Format {
+ GRAYSCALE=1, RGB=3, RGBA=4
+ };
+
+ TGAImage();
+ TGAImage(int w, int h, int bpp);
+ TGAImage(const TGAImage &img);
+ bool read_tga_file(const char *filename);
+ bool write_tga_file(const char *filename, bool rle=true);
+ bool flip_horizontally();
+ bool flip_vertically();
+ bool scale(int w, int h);
+ TGAColor get(int x, int y);
+ bool set(int x, int y, TGAColor &c);
+ bool set(int x, int y, const TGAColor &c);
+ ~TGAImage();
+ TGAImage & operator =(const TGAImage &img);
+ int get_width();
+ int get_height();
+ int get_bytespp();
+ unsigned char *buffer();
+ void clear();
+};
+
+#endif //__IMAGE_H__
+
diff --git a/OpenWindow/util_window.cpp b/OpenWindow/util_window.cpp
new file mode 100644
index 0000000..5821618
--- /dev/null
+++ b/OpenWindow/util_window.cpp
@@ -0,0 +1,112 @@
+#include "util_window.h"
+#include
+
+const char* wnd_class_name = "Renderer To-Be";
+void* pixels;
+char* pixel_data;
+BITMAPINFO info;
+HBITMAP hbm;
+const int BITCOUNT_PER_PIXEL = 24;
+const int title_height = 39;
+int screen_width = 0;
+int screen_height = 0;
+long long bytes_per_row;
+bool screen_changed = false;
+HDC hdc;
+HDRAWDIB hdd;
+HDC bitmap_dc;
+HGDIOBJ old_obj;
+
+HWND create_window(int width, int height, HINSTANCE &hInstance) {
+ HWND hWnd;
+ WNDCLASSEX wnd_class = { 0 };
+ init_wnd_class(wnd_class, hInstance);
+
+ RegisterClassEx(&wnd_class);
+
+ screen_width = width;
+ screen_height = height;
+ create_hwnd(hWnd, hInstance);
+ init(hWnd);
+
+ return hWnd;
+}
+
+void init(HWND &hWnd) {
+ memset(&info, 0, sizeof(info));
+ info.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
+ info.bmiHeader.biWidth = screen_width;
+ info.bmiHeader.biHeight = screen_height;
+ info.bmiHeader.biPlanes = 1;
+ info.bmiHeader.biBitCount = BITCOUNT_PER_PIXEL;
+ info.bmiHeader.biCompression = BI_RGB;
+
+ hdc = GetDC(hWnd);
+ pixels = NULL;
+ hbm = CreateDIBSection(hdc, &info, DIB_RGB_COLORS, &pixels, NULL, 0);
+ pixel_data = (char*)pixels;
+
+ hdd = DrawDibOpen();
+
+ bitmap_dc = CreateCompatibleDC(hdc);
+ old_obj = SelectObject(bitmap_dc, hbm);
+ bytes_per_row = ceil(screen_width * 0.03) * 100;
+}
+
+void create_hwnd(HWND &hwnd, HINSTANCE &hInstance)
+{
+ hwnd = CreateWindowEx(
+ WS_EX_CLIENTEDGE,
+ TEXT(wnd_class_name),
+ TEXT("Renderer To-be"), // window caption
+ WS_OVERLAPPEDWINDOW, // window style
+ 0, // initial x position
+ 0, // initial y position
+ screen_width + 16, // Some weird horizontal dead pixels
+ screen_height + title_height,
+ NULL, // parent window handle
+ NULL, // window menu handle
+ hInstance, // program instance handle
+ NULL); // creation parameters
+}
+
+void init_wnd_class(WNDCLASSEX &wndClass, HINSTANCE &hInstance) {
+ wndClass.cbSize = sizeof(WNDCLASSEX);
+ wndClass.style = 0;
+ wndClass.lpfnWndProc = WndProc;
+ wndClass.cbClsExtra = 0;
+ wndClass.cbWndExtra = 0;
+ wndClass.hInstance = hInstance;
+ wndClass.hIcon = LoadIcon(NULL, IDI_APPLICATION);
+ wndClass.hCursor = LoadCursor(NULL, IDC_ARROW);
+ wndClass.hbrBackground = (HBRUSH)(COLOR_WINDOW + 1);
+ wndClass.lpszMenuName = NULL;
+ wndClass.lpszClassName = TEXT(wnd_class_name);
+ wndClass.hIconSm = LoadIcon(NULL, IDI_APPLICATION);
+}
+
+void destroy_window() {
+ DrawDibEnd(hdd);
+ DrawDibClose(hdd);
+ SelectObject(bitmap_dc, old_obj);
+ DeleteDC(hdc);
+ DeleteDC(bitmap_dc);
+ DeleteObject(hbm);
+ DeleteObject(old_obj);
+}
+
+
+void set_pixel(unsigned int x, unsigned int y, unsigned int color) {
+ unsigned long pixel_index = y * bytes_per_row + x * 3;
+
+ pixel_data[pixel_index + 0] = (char)(color >> 0);
+ pixel_data[pixel_index + 1] = (char)(color >> 8);
+ pixel_data[pixel_index + 2] = (char)(color >> 16);
+
+ if (!screen_changed)
+ screen_changed = true;
+}
+
+void Update() {
+ DrawDibDraw(hdd, hdc, 0, 0, screen_width, screen_height, &info.bmiHeader, pixels, 0, 0, screen_width, screen_height, 0);
+}
diff --git a/OpenWindow/util_window.h b/OpenWindow/util_window.h
new file mode 100644
index 0000000..c9f5776
--- /dev/null
+++ b/OpenWindow/util_window.h
@@ -0,0 +1,35 @@
+#ifndef UTIL_HEADER
+#define UTIL_HEADER
+
+#include
+#include
+#pragma comment(lib, "Vfw32.lib")
+
+extern const char* wnd_class_name;
+extern void* pixels;
+extern char* pixel_data;
+extern BITMAPINFO info;
+extern HBITMAP hbm;
+extern const int BITCOUNT_PER_PIXEL;
+extern const int title_height;
+extern long long bytes_per_row;
+extern bool screen_changed;
+extern HDC hdc;
+extern HDRAWDIB hdd;
+extern HDC bitmap_dc;
+extern HGDIOBJ old_obj;
+extern int screen_width;
+extern int screen_height;
+
+
+void init(HWND &hWnd);
+void destroy_window();
+HWND create_window(int width, int height, HINSTANCE &hInstance);
+void create_hwnd(HWND &hwnd, HINSTANCE &hInstance);
+void init_wnd_class(WNDCLASSEX &wndClass, HINSTANCE &hInstance);
+void set_pixel(unsigned int x, unsigned int y, unsigned int color);
+void Update();
+
+LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+
+#endif