неразрешенных внешних элементов: 2
неразрешенный внешний символ "public: __cdecl Vec3<float>::Vec3<float><int>(struct Vec3<int> const &)" (??$?0H@?$Vec3@M@@QEAA@AEBU?$Vec3@H@@@Z).
неразрешенный внешний символ "public: __cdecl Vec3<int>::Vec3<int><float>(struct Vec3<float> const &)" (??$?0M@?$Vec3@H@@QEAA@AEBU?$Vec3@M@@@Z).
#include <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cmath>
#include "png.h"
using namespace std;

template <class t> struct Vec2 {
	t x, y;
	Vec2<t>() : x(t()), y(t()) {}
	Vec2<t>(t _x, t _y) : x(_x), y(_y) {}
	Vec2<t>(const Vec2<t>& v) : x(t()), y(t()) { *this = v; }
	Vec2<t>& operator =(const Vec2<t>& v) {
		if (this != &v) {
			x = v.x;
			y = v.y;
		}
		return *this;
	}
	Vec2<t> operator +(const Vec2<t>& V) const { return Vec2<t>(x + V.x, y + V.y); }
	Vec2<t> operator -(const Vec2<t>& V) const { return Vec2<t>(x - V.x, y - V.y); }
	Vec2<t> operator *(float f)          const { return Vec2<t>(x * f, y * f); }
	t& operator[](const int i) { if (x <= 0) return x; else return y; }
	template <class > friend std::ostream& operator<<(std::ostream& s, Vec2<t>& v);
};

template <class t> struct Vec3 {
	t x, y, z;
	Vec3<t>() : x(t()), y(t()), z(t()) {}
	Vec3<t>(t _x, t _y, t _z) : x(_x), y(_y), z(_z) {}
	template <class u> Vec3<t>(const Vec3<u>& v);
	Vec3<t>(const Vec3<t>& v) : x(t()), y(t()), z(t()) { *this = v; }
	Vec3<t>& operator =(const Vec3<t>& v) {
		if (this != &v) {
			x = v.x;
			y = v.y;
			z = v.z;
		}
		return *this;
	}
	Vec3<t> operator ^(const Vec3<t>& v) const { return Vec3<t>(y * v.z - z * v.y, z * v.x - x * v.z, x * v.y - y * v.x); }
	Vec3<t> operator +(const Vec3<t>& v) const { return Vec3<t>(x + v.x, y + v.y, z + v.z); }
	Vec3<t> operator -(const Vec3<t>& v) const { return Vec3<t>(x - v.x, y - v.y, z - v.z); }
	Vec3<t> operator *(float f)          const { return Vec3<t>(x * f, y * f, z * f); }
	t       operator *(const Vec3<t>& v) const { return x * v.x + y * v.y + z * v.z; }
	float norm() const { return std::sqrt(x * x + y * y + z * z); }
	Vec3<t>& normalize(t l = 1) { *this = (*this) * (l / norm()); return *this; }
	t& operator[](const int i) { if (i <= 0) return x; else if (i == 1) return y; else return z; }
	template <class > friend std::ostream& operator<<(std::ostream& s, Vec3<t>& v);
};

typedef Vec2<float> Vec2f;
typedef Vec2<int>   Vec2i;
typedef Vec3<float> Vec3f;
typedef Vec3<int>   Vec3i;

template <> template <> Vec3<int>::Vec3(const Vec3<float>& v);
template <> template <> Vec3<float>::Vec3(const Vec3<int>& v);


template <class t> std::ostream& operator<<(std::ostream& s, Vec2<t>& v) {
	s << "(" << v.x << ", " << v.y << ")\n";
	return s;
}

template <class t> std::ostream& operator<<(std::ostream& s, Vec3<t>& v) {
	s << "(" << v.x << ", " << v.y << ", " << v.z << ")\n";
	return s;
}

class Model
{
private:
	vector<Vec3f> verts_;
	vector<vector<int>> faces_;

public:
	Model(const std::string& buffer) : verts_(), faces_() {
		std::istringstream in(buffer);
		std::string line;

		while (std::getline(in, line)) {
			std::istringstream iss(line);
			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, 2, "f ")) {
				std::vector<int> f;
				int itrash, idx;
				iss >> trash;
				while (iss >> idx >> trash >> itrash >> trash >> itrash) {
					idx--; // In Wavefront obj, all indices start at 1
					f.push_back(idx);
				}
				faces_.push_back(f);
			}
		}
		std::cerr << "# v# " << verts_.size() << " f# " << faces_.size() << std::endl;
	}
	~Model() {
	}
	int nverts()
	{
		return (int)verts_.size();
	}

	int nfaces()
	{
		return (int)faces_.size();
	}
	vector<int> face(int idx)
	{
		return faces_[idx];
	}

	Vec3f vert(int i)
	{
		return verts_[i];
	}
};

struct color
{
	int r, g, b, a;
};

const int w = 800, h = 800;
const int depth = 255;
color image[w][h];

void triangle(Vec3i t0, Vec3i t1, Vec3i t2, color(&image)[w][h], color c, int* zbuffer) {
	if (t0.y == t1.y && t0.y == t2.y) return; // i dont care about degenerate triangles
	if (t0.y > t1.y) std::swap(t0, t1);
	if (t0.y > t2.y) std::swap(t0, t2);
	if (t1.y > t2.y) std::swap(t1, t2);
	int total_height = t2.y - t0.y;
	for (int i = 0; i < total_height; i++) {
		bool second_half = i > t1.y - t0.y || t1.y == t0.y;
		int segment_height = second_half ? t2.y - t1.y : t1.y - t0.y;
		float alpha = (float)i / total_height;
		float beta = (float)(i - (second_half ? t1.y - t0.y : 0)) / segment_height; // be careful: with above conditions no division by zero here
		Vec3i A = t0 + Vec3f(t2 - t0) * alpha;
		Vec3i B = second_half ? t1 + Vec3f(t2 - t1) * beta : t0 + Vec3f(t1 - t0) * beta;
		if (A.x > B.x) std::swap(A, B);
		for (int j = A.x; j <= B.x; j++) {
			float phi = B.x == A.x ? 1. : (float)(j - A.x) / (float)(B.x - A.x);
			Vec3i P = Vec3f(A) + Vec3f(B - A) * phi;
			int idx = P.x + P.y * w;
			if (zbuffer[idx] < P.z) {
				zbuffer[idx] = P.z;
				image[P.x][P.y] = c;
			}
		}
	}
}
int main()
{
	int* zbuffer = new int[w * h];
	for (int i = 0; i < w * h; i++) {
		zbuffer[i] = std::numeric_limits<int>::min();
	}
	Vec3f light_dir(0, 0, -1);
	
	const char* buffer;
	// draw model
	Model* model = new Model(buffer);
	for (int i = 0; i < model->nfaces(); i++) {
		vector<int> face = model->face(i);
		Vec3i screen_coords[3];
		Vec3f world_coords[3];
		for (int j = 0; j < 3; j++) {
			Vec3f v = model->vert(face[j]);
			screen_coords[j] = Vec3i((v.x + 1.) * w / 2., (v.y + 1.) * h / 2., (v.z + 1.) * depth / 2.);
			world_coords[j] = v;
		}
		Vec3f n = (world_coords[2] - world_coords[0]) ^ (world_coords[1] - world_coords[0]);
		n.normalize();
		float intensity = n * light_dir;
		if (intensity > 0) {
			triangle(screen_coords[0], screen_coords[1], screen_coords[2], image, 
				{ int(intensity*255), int(intensity*255), int(intensity*255),255 }, zbuffer);
		}
	}
	png::PngWriter writer;
	writer.initialize(w, h);
	for (uint32_t y = 0; y < h; ++y) {
		for (uint32_t x = 0; x < w; ++x) {
			uint32_t color = (image[x][y].r << 24) | \
							 (image[x][y].g << 16) | \
							 (image[x][y].b << 8)| 255;
			writer.setPixel(x, y, color);
		}
	}
	writer.save("output.png");

	delete model;
	delete[] zbuffer;

	return 0;
 }