bate's blog

調べたこと実装したことなどを取りとめもなく書きます。

三角形の走査変換もどき

三角形を塗ることができた。
とにかく実装してみたかった。今は反省している
後処理が怪しい。

// main.cpp

#include <stdlib.h>
#include <stdio.h>
#include <float.h>
#include <math.h>

#include <SDL/SDL.h>

//-----------------------------------------------
// 構造体の定義
//-----------------------------------------------
struct Vector4
{
	float x, y, z, w;
};

//-----------------------------------------------
// 関数の宣言
//-----------------------------------------------
void init(int w, int h, int bpp);
void render(int w, int h);
void putPixel(SDL_Surface * surface, int x, int y, Uint32 pixel);
bool PollEvent();

//-----------------------------------------------
// 大域変数
//-----------------------------------------------
SDL_Surface* g_screen = NULL;


//-----------------------------------------------
// エントリーポイント
//-----------------------------------------------
int main(int argc, char** argv)
{
	const int width = 640;
	const int height = 480;
	const int bpp = 8;
	
	init(width, height, bpp);
	
	// SDL 終了時のコールバックを設定
	atexit(SDL_Quit);
	
	while(1)
	{
		// 描画
		render(width, height);
		
		// 終了イベントがあれば終わる
		if(!PollEvent())
		{
			break;
		}
	}
	
	return 0;
}


//-----------------------------------------------
// 関数の実装
//-----------------------------------------------

/*!
	@brief	SDL 関係の初期化
*/
void init(int w, int h, int bpp)
{
	// SDL の初期化
	if(SDL_Init(SDL_INIT_VIDEO) != 0)
	{
		fprintf(stderr, "Init Failed : %s", SDL_GetError());
		exit(1);
	}
	
	// スクリーンのサイズ、BPPを設定
	g_screen = SDL_SetVideoMode(w, h, bpp, SDL_HWSURFACE);
	if(g_screen == NULL)
	{
		fprintf(stderr, "640x480x8 Video mode failed: %s\n", SDL_GetError());
		exit(1);
	}
}


/*!
	@brief サーフェイスの(x,y)にピクセルを設定
*/
void putPixel(SDL_Surface * surface, int x, int y, Uint32 pixel)
{
	int bpp = surface->format->BytesPerPixel;
	Uint8 *p = (Uint8*)surface->pixels + y * surface->pitch + x * bpp;

	switch(bpp)
	{
	case 1:
		*p = pixel;
		break;
	case 2:
		*(Uint16*)p = pixel;
		break;
	case 3:
		if(SDL_BYTEORDER == SDL_BIG_ENDIAN)
		{
			p[0] = (pixel >> 16) & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = pixel & 0xff;
		}
		else
		{
			p[0] = pixel & 0xff;
			p[1] = (pixel >> 8) & 0xff;
			p[2] = (pixel >> 16) & 0xff;
		}
		break;
	case 4:
		*(Uint32*)p = pixel;
		break;
	}
}


/*!
	@brief	三角形を描く
	@note	走査変換は増分法を使用
*/
void render(int w, int h)
{
	// 黄色で塗る
	Uint32 yellow;
	yellow = SDL_MapRGB(g_screen->format, 0xff, 0xff, 0x00);
	
	// スクリーンのピクセルにアクセスするためにロック
	if(SDL_MUSTLOCK(g_screen))
	{
		if(SDL_LockSurface(g_screen)<0)
		{
			fprintf(stderr, "Cant lock screen : %s\n", SDL_GetError());
			exit(1);
		}
	}
	
	// 三角形の頂点
	Vector4 t[3] = {
		{100.0, 200.0, 0.0, 1.0},
		{300.0, 400.0, 0.0, 1.0},
		{200.0, 100.0, 0.0, 1.0},
	};
	
	// x, y の最大値と最小値を保存する用
	Vector4 mm = { 0.0, 0.0, 0.0, 0.0 };
	
	// y 座標値でソート
	for(int i = 0; i < 3-1; ++i)
	{
		for(int j = 3-1; j > i; --j)
		{
			if(t[j-1].y > t[j].y)
			{
				mm = t[j];
				t[j] = t[j-1];
				t[j-1] = mm;
			}
		}
	}
	
#if 0
	for(int i = 0; i < 3; ++i)
	{
		printf("%d : (x, y)=(%lf, %lf)\n", i, t[i].x, t[i].y);
	}
#endif
	
	// x のスパン保存
	mm.x = t[0].x;
	mm.y = t[2].x;
	for(int i = 0; i < 3; ++i)
	{
		if(mm.x > t[i].x) {
			mm.x = t[i].x;
		}
		if(mm.y < t[i].x) {
			mm.y = t[i].x;
		}
	}
	// y のスパン保存
	mm.z = t[0].y;
	mm.w = t[2].y;
	
#if 0
	printf("x : [%lf, %lf]\n", mm.x, mm.y);
	printf("y : [%lf, %lf]\n", mm.z, mm.w);
#endif
	
	// 三角形を2分割した時にできる頂点
	Vector4 divMid;
	divMid.y = t[1].y;
	divMid.x = (t[1].y-t[0].y)/(t[2].y-t[0].y)*(t[2].x-t[0].x) + t[0].x;
#if 0
	printf("divMid (%lf,%lf)\n", divMid.x, divMid.y);
#endif
	
	//---------------------------------
	// 上三角形
	
	// 傾きがマイナスになるかプラスになるか
	Vector4* small;
	Vector4* large;
	if(t[1].x > divMid.x) {
		large = &t[1];
		small = &divMid;
	} else {
		large = &divMid;
		small = &t[1];
	}
	double m_nega = (small->x - t[0].x)/(small->y - t[0].y);
	double m_posi = (large->x - t[0].x)/(large->y - t[0].y);
	
	double min_x = t[0].x;
	double max_x = t[0].x;
	
	for(int y = static_cast<int>(mm.z); y < static_cast<int>(divMid.y); ++y)
	{
//		min_x += m_nega;
//		max_x += m_posi;
		for(int x = static_cast<int>(min_x); x < static_cast<int>(max_x); ++x)
		{
			putPixel(g_screen,
			 x,
			 y,
			 yellow);
		}
		min_x += m_nega;
		max_x += m_posi;
	}
	
	//---------------------------------
	// 下三角形
	
	small = NULL;
	large = NULL;
	if(t[1].x > divMid.x) {
		large = &t[1];
		small = &divMid;
	} else {
		large = &divMid;
		small = &t[1];
	}
	m_nega = (t[2].x - small->x)/(t[2].y - small->y);
	m_posi = (t[2].x - large->x)/(t[2].y - large->y);

	for(int y = static_cast<int>(divMid.y); y < static_cast<int>(mm.w); ++y)
	{
//		min_x += m_nega;
//		max_x += m_posi;
		for(int x = static_cast<int>(min_x); x < static_cast<int>(max_x); ++x)
		{
			putPixel(g_screen,
			 x,
			 y,
			 yellow);
		}
		min_x += m_nega;
		max_x += m_posi;
	}
	
	// サーフェイスのロックを解除
	if(SDL_MUSTLOCK(g_screen))
	{
		SDL_UnlockSurface(g_screen);
	}
	
	// スクリーンのアップデート
	SDL_UpdateRect(g_screen, 0, 0, 0, 0);
}


/*!
	@brief	終了イベント判定
*/
bool PollEvent()
{
	SDL_Event event;
	while(SDL_PollEvent(&event))
	{
		if(event.type == SDL_QUIT)
		{
			return false;
		}
	}
	return true;
}