EGE纹理映射

承接上次的EGE光栅化三角形,这次实现了纹理映射

#include <graphics.h>
#include <algorithm>
#include <cmath>
#include <stdio.h>
#include <map>

using namespace std;

//判断点v是否在[a,b]中

//C#与JAVa的后遗症,EGEAPP
namespace EA {
	using namespace std;
#define ct color_t
	//几个全局变量
	//屏幕buffer
	color_t* wBuffer=nullptr;
	//纹理buffer
	PIMAGE img;
	color_t* tBuffer=nullptr;
	//屏幕长宽
	int W=0;
	int H=0;
	//纹理宽
	int tW=0;
	int tH=0;

	inline float F2I(float f) {
		return floor(f);
	}
	//把浮点数位数强定为3位(担心爆精度
	inline float F2F3(float f) {
		return floor(f*1000.0f)/1000.0f;
	}
	template<class T>
	inline bool IsCovered(T a,T b,T v) {
		if(a>b) swap(a,b);
		return (a<=v&&v<=b);
	}

	//数据类型
	class Vertex2D {
		public:
			float x;
			float y;
			ct c;
			inline Vertex2D() {
				x=y=0.0f;
				c=0;
			}
			inline Vertex2D(float _x,float _y,ct _c) {
				x=_x,y=_y,c=_c;
			}
			inline void Format() {
				x=EA::F2I(x);
				y=EA::F2I(y);
			}
			inline VECTOR3D ToVec3D() {
				return VECTOR3D(x,y,0);
			}
	};
	typedef struct Vertex2D Ver2D;
	class Point2D{
		public:
			Vertex2D xy;//位置
			Vertex2D uv;//纹理坐标

			Point2D(float x,float y,float u,float v){
				xy.x=x;xy.y=y;
				uv.x=u;uv.y=v;
			}
			Point2D(){
				
			}
	};
	//函数
	//初始化绘图环境
	void InitGraph(int w,int h,string image) {
		initgraph(w,h,0);
		ege_enable_aa(1);
		W=getwidth(),H=getheight();
		img=newimage();
		getimage(img,image.c_str());
		ege_enable_aa(1,img);
		tW=getwidth(img);
		tH=getheight(img);
		wBuffer=getbuffer(NULL);
		tBuffer=getbuffer(img);
	}
	//向量插值,0<=v<=1
	inline Point2D VerLerp(Point2D c1,Point2D c2,float v);
	//高速画点
	inline void PutPixel_f(Point2D v);
	//高速画水平线
	//这个会渐变
	inline void DrawLine_f(float x1,float x2,float y,float u1,float u2,float v);
	//绘制简单三角,要求至少两个点y一样
	inline void DrawSimpleTrangle(Point2D v1,Point2D v2,Point2D v3);
	//绘制复杂三角
	inline void DrawTrangle(Point2D v1,Point2D v2,Point2D v3);
#undef ct
}

using EA::Point2D;

#define ct color_t
//程序入口
int main() {
	EA::InitGraph(1000,800,"此文件涉嫌违规问题.gif");
	//setbkcolor(RED);
	EA::DrawTrangle(Point2D(0,0,0,0),Point2D(0,EA::H,0,EA::tH),Point2D(EA::W,0,EA::tW,0));
	delay_fps(60);
	getch();
	closegraph();
	return 0;
}

Point2D EA::VerLerp(Point2D c1,Point2D c2,float v) {
	return Point2D((c1.xy.x-c2.xy.x)*v+c2.xy.x,(c1.xy.y-c2.xy.y)*v+c2.xy.y,(c1.uv.x-c2.uv.x)*v+c2.uv.x,(c1.uv.y-c2.uv.y)*v+c2.uv.y);
}
void EA::PutPixel_f(Point2D v) {
	if(EA::IsCovered(0,W,int(v.xy.x))&&EA::IsCovered(0,H,int(v.xy.y))) {
	        //☆☆☆这一点很坑,如果写成wBuffer坐标一样的话会出大乱子...引以为戒☆☆☆
		wBuffer[int(v.xy.x+v.xy.y*W)]=tBuffer[int(v.uv.x)+int(v.uv.y)*tW];	
	}
}
void EA::DrawLine_f(float x1,float x2,float y,float u1,float u2,float v) {
	if(x1>x2) swap(x1,x2);
	Point2D pt;
	float k=(u1-u2)/(x1-x2);
	for(float i=x1; i<=x2; ++i) {
		PutPixel_f(Point2D(i,y,k*(i-x1)+u1,v));
	}
}
//v1.y==v2.y
void EA::DrawSimpleTrangle(Point2D v1,Point2D v2,Point2D v3) {
	//Format
	if(v2.xy.y==v3.xy.y&&v1.xy.y!=v2.xy.y) {
		swap(v1,v2);
		swap(v2,v3);
	} else if(v1.xy.y==v3.xy.y&&v1.xy.y!=v2.xy.y) {
		swap(v2,v3);
	}
	if(v1.xy.x>v2.xy.x) {
		swap(v1,v2);
	}
	//解一次方程
	float h=v3.xy.y-v1.xy.y;
	float kL=(v3.xy.x-v1.xy.x)/h;
	float kR=(v3.xy.x-v2.xy.x)/h;

	float xL=0;
	float xR=0;

	xL=v1.xy.x;
	xR=v2.xy.x;
	Point2D p1,p2;
	if(h>0) {
		for(float i=v1.xy.y; i<=v3.xy.y; ++i) {
			float percent=1.0f-(i-v1.xy.y)/(v3.xy.y-v1.xy.y);
			p1=EA::VerLerp(v1,v3,percent);
			p2=EA::VerLerp(v2,v3,percent);
			EA::DrawLine_f(xL,xR,i,p1.uv.x,p2.uv.x,p1.uv.y);
			xL+=kL;
			xR+=kR;
		}
	} else {
		for(float i=v1.xy.y; i>=v3.xy.y; --i) {
			float percent=(i-v3.xy.y)/(v1.xy.y-v3.xy.y);
			p1=EA::VerLerp(v1,v3,percent);
			p2=EA::VerLerp(v2,v3,percent);
			EA::DrawLine_f(xL,xR,i,p1.uv.x,p2.uv.x,p1.uv.y);
			xL-=kL;
			xR-=kR;
		}
	}
}
void EA::DrawTrangle(Point2D v1,Point2D v2,Point2D v3) {
	if(v1.xy.y==v2.xy.y||v1.xy.y==v3.xy.y||v2.xy.y==v3.xy.y) {
		EA::DrawSimpleTrangle(v1,v2,v3);
	} else {
		//找出y中间的顶点,并放在v3的位置
		if(v2.xy.y<v1.xy.y&&v1.xy.y<v3.xy.y) {
			swap(v1,v3);
		} else if(v1.xy.y<v2.xy.y&&v2.xy.y<v3.xy.y) {
			swap(v2,v3);
		}
		//切成两半
		float h=v1.xy.y-v2.xy.y;
		float w=v1.xy.x-v2.xy.x;
		Point2D pt=EA::VerLerp(v2,v1,(v3.xy.y-v2.xy.y)/h);
		Point2D vm((w/h)*(v3.xy.y-v2.xy.y)+v2.xy.x,v3.xy.y,pt.uv.x,pt.uv.y);
		//画他
		DrawSimpleTrangle(v1,vm,v3);
		DrawSimpleTrangle(v2,vm,v3);
	}
}
#undef ct