Kirjautuminen

Haku

Tehtävät

Keskustelu: Koodit: C++: Siirrosefekti

Sivun loppuun

Spirits [03.07.2004 13:03:02]

#

Tuossa eräänä päivänä seurasin sivusta kun serkkuni pelasi jotain PS2 peliä jossa hilluttiin ilmeisesti jollain ketulla ja kerättin omenoita? Noh pelistä jäi kuitenkin mieleen siinä ollut siirrosefekti joka oli kenttien välissä. Efektissä kuva pyöri,zoomasi,'suli' ja feidasi. Jostain syystä kuitenkin sain mieleeni että tuosta täytyy tehdä koodivinkki. Vaikka periaattessahan tämä efekti on aika wanha ja varmasti tuon saisi kasattua kasaan pienen googletus-session jälkeen.

Efektin saa toteutettua yksinkertaisesti siten että ensin pikselin sijainti (x,y) muunnetaan ympyräkoordinaateiksi (r,phi) jonka jälkeen pikselille lasketaan uusi sijainti muuttamalla kulmaa ja sädettä. Kun kulmaa pienennetään saadaan kuva pyörimään kellon suuntaa vastaan ja sädettä pienentämällä kuva lähenee, koska laskettaessa uuden pikselin paikkaa ei lasketa uutta loppusijaintia vaan mistä otetaan vanha pikseli. Kun uusi sijainti on laskettu otetaan sen hetkisestä sijainnista pikselin arvo ja vanha pikseli ja sekoitetaan ne jollain suhteella('sulaminen'). Vielä lopuksi uutta pikselin arvoa piennetään hieman jotta saadaan mukaan feidaus.

Onneksi nuo siirrokset voidaan laskea etukäteen taulukkoon jolloin säästytään paljolta laskemiselta per pikseli. Kuitenkin koodiesimerkissä on mukana MMX-asm-inline versio koska C-versio oli sen verran hidas että oli pakko tehdä sille jotain.

Tuosta MMX-versiosta täytyy mainita sen verran että siinä käytetään fixed-point-aritmetiikka jotta saadaan laskettua nuo 'sulamiset' ja feidaukset. Ongelmaksi muodostui se että käytettäessä 16-bit laskuja vie etumerkki yhden bitin jolloin desim.osalle ei jää kuin 7-bittiä ja tämä vuorostaan aiheuttaa sen että laskut menevät vähän hankaliksi, koska kertolaskun jälkeen desim.osa on 14-bittiä ja MMX-mul-käsky 'katkaisee' luvun 16-bitin kohdalta. Tällöin 2-bittiä jotka kuuluvat pikselille menee desim.osan mukana. Tästä syystä tuli vähän kikkailua siihen koodin, mutta eipä tuo MMX-osa ollut esimerkin aiheenakaan(ja Ext.MMX:ssä on etumerkitön kertolasku).

En kuitenkaan muista enää aivan tarkalleen millainen tuo efekti oikeastaan oli joten täytyy luottaa siihen että se näyttää siltä miltä tämä esimerkki näyttää. Lopuksi vielä muutama esim. kerroinyhdistelmä. Jotta nämä toimivat pitää MMX-versio ottaa pois käytöstä tai muuttaa MMX-versioon liittyviä kertoimia samallalailla. Ohjelma tekee efektin joko kuvalle(raaka-dataa pic.raw-tiedostosta 640*480*24bit) tai jos kuvaa ei ole ladattavissa niin laskee yksinkertaisen kuvan jolle efekti tehdään.


Pelkkä Rotaatio:
#define ROT_MUL 0.045
#define XOOM_MUL 1
#define PIC_MUL1 0
#define PIC_MUL2 1
#define FADE_MUL 1

Pelkkä Zoom:
#define ROT_MUL 0
#define XOOM_MUL 0.9525
#define PIC_MUL1 0
#define PIC_MUL2 1
#define FADE_MUL 1

Pelkkä Fade:
#define ROT_MUL 0
#define XOOM_MUL 1
#define PIC_MUL1 1
#define PIC_MUL2 0
#define FADE_MUL 0.9375

Valmis exe(copy-paste osoite kenttään):

http://www.geocities.com/prlnsop/rtxmfd.zip

#define WIN32_LEAN_AND_MEAN

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

#define MY_PI 3.14159265358979323846264338327950288419716939937510582097

#define ROT_MUL 0.045
#define XOOM_MUL 0.9525
#define PIC_MUL1 0.75
#define PIC_MUL2 0.25
#define FADE_MUL 0.9375

#define CPUID_STD_MMX			0x00800000
#define FEATURE_CPUID			0x00000001
#define FEATURE_STD_FEATURES	0x00000002
#define FEATURE_MMX				0x00000020

HINSTANCE hInst;
HWND hMainWindow;

char clzName[]="RotoXoomFeider";
unsigned char *DrawBuffer;
unsigned char *CopyBuffer;
int *xbuff;
int *ybuff;

unsigned int shift1[]={1,0};
unsigned int shift2[]={7,0};
unsigned int shift3[]={2,0};
unsigned int shift4[]={14,0};
//0.75,0.25,0.75,0.25
unsigned short muller[]={0x0060,0x0020,0x0060,0x0020};
//0.9375,0.9375,0.9375,0.9375
unsigned short muller2[]={0x0078,0x0078,0x0078,0x0078};

void RotoXoom(void);
void RotoXoom_MMX(void);
void (*TheRotoXoom)(void);

/*AMD Processor Recognition(AppNote:20734)*/
unsigned int get_feature_flags()
{
	unsigned int result = 0;
	/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
	  ;; Step 1: Check if processor has CPUID support. The processor faults
	  ;; with an illegal instruction exception if the instruction is not
	  ;; supported. This step catches the exception and immediately returns
	  ;; with feature string bits with all 0s, if the exception occurs.
	  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

	__try {
		__asm{
			xor eax, eax
			xor ebx, ebx
			xor ecx, ecx
			xor edx, edx
			cpuid
			}
		}

	__except (EXCEPTION_EXECUTE_HANDLER) {
		return (0);
		}

	result |= FEATURE_CPUID;
	__asm {
		/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		  ;; Step 2: Check if CPUID supports function 1 (signature/std features)
		  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/
		xor eax, eax						// CPUID function #0
		cpuid								// largest std func/vendor string
		test eax, eax						// largest standard function==0?
		jz no_standard_features				// yes, no standard features func
		or [result], FEATURE_STD_FEATURES	// does have standard features

		/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		  ;; Step 3: Get standard feature flags and signature
		  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

		mov eax, 1							// CPUID function #1
		cpuid								// get signature/std feature flgs
		/*;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
		  ;; Step 4: Extract desired features from standard feature flags
		  ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;*/

		/*;; Check for MMX support*/
		mov ecx, CPUID_STD_MMX				// bit 23 indicates MMX support
		and ecx, edx						// supports MMX ? CPUID_STD_MMX:0
		neg ecx								// supports MMX ? CY : NC
		sbb ecx, ecx						// supports MMX ? 0xffffffff:0
		and ecx, FEATURE_MMX				// supports MMX ? FEATURE_MMX:0
		or [result], ecx					// merge into feature flags

		/* Extract features specific to non AMD CPUs */
no_standard_features:
		}
	return result;
}

int SetRXFunc()
{
	if(get_feature_flags()&FEATURE_MMX){
		TheRotoXoom=RotoXoom_MMX;
		}
	else {
		TheRotoXoom=RotoXoom;
		}

	return true;
}

void InitBuff()
{
	int i,j;
	float x,y;
	int ax,ay;
	float ang,rad;
	FILE *data;
	static int start=1;

	if(start){
		//turha tarkistaa samaa asiaa useaan kertaan
		SetRXFunc();
		start=0;
		}

	data=fopen("pic.raw","rb");
	if(data!=NULL){
		for(i=0;i<480;i++)
			for(j=0;j<640;j++){
				DrawBuffer[i*640*3+j*3+2]=fgetc(data);
				DrawBuffer[i*640*3+j*3+1]=fgetc(data);
				DrawBuffer[i*640*3+j*3+0]=fgetc(data);
				}
		fclose(data);
		}
	else {
		//lasketaan kuva kun ei ollut tiedostoa mistä ladata
		for(i=0;i<480;i++)
			for(j=0;j<640;j++){
				DrawBuffer[i*640*3+j*3+2]=sqrt((j-320)*(j-320)+(i-240)*(i-240));
				DrawBuffer[i*640*3+j*3+1]=i&j;
				DrawBuffer[i*640*3+j*3+0]=i^j;
				}
		}

	//käännetään kuva
	for(i=0;i<480;i++)
		for(j=0;j<640;j++){
			CopyBuffer[i*640*3+j*3+0]=DrawBuffer[(479-i)*640*3+j*3+0];
			CopyBuffer[i*640*3+j*3+1]=DrawBuffer[(479-i)*640*3+j*3+1];
			CopyBuffer[i*640*3+j*3+2]=DrawBuffer[(479-i)*640*3+j*3+2];
			}
	for(i=0;i<480;i++)
		for(j=0;j<640;j++){
			DrawBuffer[i*640*3+j*3+0]=CopyBuffer[i*640*3+j*3+0];
			DrawBuffer[i*640*3+j*3+1]=CopyBuffer[i*640*3+j*3+1];
			DrawBuffer[i*640*3+j*3+2]=CopyBuffer[i*640*3+j*3+2];
			}

	//lasketaan siirrokset etukäteen
	for(i=0;i<480;i++){
		for(j=0;j<640;j++){
			x=(float)j-320.0;
			y=(float)i-240.001;

			//muunnetaan (x,y)->(r,phi)

			//korjataan arkustangentti
			if(y<0)
				ang=MY_PI*1.5-atan(x/y);
			else
				ang=MY_PI*0.5-atan(x/y);

			rad=sqrt(x*x+y*y);

			//lasketaan mistä uusi pikseli otetaan
			//pyöritetään cw koska kuvan pitää pyöriä ccw
			ang-=ROT_MUL;
			//piennetään sädettä
			rad*=XOOM_MUL;

			//lasketaan pikselin uusi sijainti
			ax=(int)(320+cos(ang)*rad);
			ay=(int)(240+sin(ang)*rad);
			//ei yli kuvan
			if(ax<0)ax=0;
			if(ax>639)ax=639;
			if(ay<0)ay=0;
			if(ay>479)ay=479;

			xbuff[i*640+j]=ax;
			ybuff[i*640+j]=ay;
			}
		}

}

void RotoXoom()
{
	int i,off;
	int ax,ay;

	for(i=0;i<307200;i++){
		//haetaan siirros
		ax=xbuff[i];
		ay=ybuff[i];

		//lasketaan uusi sijainti
		off=ay*1920+ax*3;

		//sotketaan uutta ja vanhaa sopivasti yhteen ja samalla feidataan
		DrawBuffer[i*3]=(int)(((float)DrawBuffer[i*3]*PIC_MUL1+(float)CopyBuffer[off]*PIC_MUL2)*FADE_MUL);
		DrawBuffer[i*3+1]=(int)(((float)DrawBuffer[i*3+1]*PIC_MUL1+(float)CopyBuffer[off+1]*PIC_MUL2)*FADE_MUL);
		DrawBuffer[i*3+2]=(int)(((float)DrawBuffer[i*3+2]*PIC_MUL1+(float)CopyBuffer[off+2]*PIC_MUL2)*FADE_MUL);
		}
	memcpy(CopyBuffer,DrawBuffer,640*480*3);
}

void RotoXoom_MMX()
{
	int tmp;

	__asm {
		emms
		push edi
		push esi
		xor ecx,ecx

		mov ebx,DrawBuffer
		mov edx,CopyBuffer

		mov edi,xbuff
		mov esi,ybuff

daluup:
		mov eax,[esi+ecx*4]
		imul eax,eax,640
		mov tmp,eax
		mov eax,[edi+ecx*4]
		add eax,tmp
		imul eax,eax,3
		//eax=(ybuff[off]*640+xbuff[off])*3


		//0=draw
		//1=copy
		//2=d*m1+c*m2
		pxor mm0,mm0
		pxor mm1,mm1
		punpcklbw mm0,[ebx]
		//mm0=X0R0G0B0
		movq mm2,mm0
		punpcklbw mm1,[edx+eax]
		movq mm3,mm1
		//mm1=X1R1G1B1
		punpcklwd mm0,mm1
		//mm0=G1G0B1B0
		punpckhwd mm2,mm3
		//mm2=X1X0R1R0
		psrlw mm0,shift1	//1
		psrlw mm2,shift1	//15=etum.,14-7 pixel,6-0 desim.

		movq mm1,muller
		pmaddwd mm0,mm1		//kerrotaan ja summataan ja saadaan
		pmaddwd mm2,mm1		//31=etum.,30-14=pixel,13-0=desim.
		psrld mm0,shift2	//7
		psrld mm2,shift2	//säilytetään 7bit desim.

		packssdw mm0,mm2	//kaikki(R,G,B) samaan rekisteriin
		//mm0=X2R2G2B2
		movq mm2,mm0
		pmulhw mm0,muller2	//kikkaillaan jotta saadaan
		psllw mm0,shift3	//alemmista 16bitistä 2 ylintä
		pmullw mm2,muller2	//bittiä talteen(jotka kuuluvat pikselille)
		psrlw mm2,shift4
		por mm0,mm2

		packuswb mm0,mm0

		movd eax,mm0

		mov [ebx],ax
		shr eax,16
		mov [ebx+2],al

		add ebx,3

		inc ecx
		cmp ecx,307200
		jnz daluup

		pop esi
		pop edi
		emms
		}

	memcpy(CopyBuffer,DrawBuffer,640*480*3);
}

LRESULT CALLBACK WindowProc(HWND hWnd,UINT uMsg,WPARAM wParam,LPARAM lParam)
{
	static UINT	uTimer;
	static HDC hdcDIBSection,hdcScreen;
	static BITMAPINFO *pbmiDIB;
	static HBITMAP hDIBSection;
	HBITMAP holdbitmap;
	HDC hDC;
	static int count=0;

	switch(uMsg){
		case WM_CREATE:
			DrawBuffer=new unsigned char[640*481*3];
			CopyBuffer=new unsigned char[640*480*3];
			xbuff=new int[640*480];
			ybuff=new int[640*480];

			if(DrawBuffer==NULL||CopyBuffer==NULL||xbuff==NULL||ybuff==NULL){
				MessageBox(NULL,"ERROR","Memory alloc failed(2400 kBytes)?\n",MB_OK|MB_ICONWARNING);
				PostQuitMessage(0);
				}

			pbmiDIB = (BITMAPINFO* )new BITMAPINFO[1];

			if (pbmiDIB == NULL){
				MessageBox(NULL,"ERROR","Mem alloc failed!\n",MB_OK|MB_ICONWARNING);
				PostQuitMessage(0);
				}

			pbmiDIB->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
			pbmiDIB->bmiHeader.biWidth = 640;
			//MMX menee viimeisellä pikselillä yhden
			//tavun yli joten sitä varten pitää
			//olla yksi rivi 'liikaa'
			pbmiDIB->bmiHeader.biHeight = 481;
			pbmiDIB->bmiHeader.biPlanes = 1;
			pbmiDIB->bmiHeader.biBitCount = 24;
			pbmiDIB->bmiHeader.biCompression = BI_RGB;
			pbmiDIB->bmiHeader.biSizeImage = 0;
			pbmiDIB->bmiHeader.biXPelsPerMeter = 0;
			pbmiDIB->bmiHeader.biYPelsPerMeter = 0;
			pbmiDIB->bmiHeader.biClrUsed = 0;
			pbmiDIB->bmiHeader.biClrImportant = 0;

			hDC=GetDC(hWnd);

			hDIBSection = CreateDIBSection (hDC, pbmiDIB, DIB_RGB_COLORS,(void**)&DrawBuffer, NULL, 0);

			if(!hDIBSection) {
				delete DrawBuffer;
				delete CopyBuffer;
				delete xbuff;
				delete ybuff;
				delete pbmiDIB;
				MessageBox(NULL,"ERROR","Call to CreateDIBSection failed!\n",MB_OK|MB_ICONWARNING);
				PostQuitMessage(0);
				}
			hdcScreen = GetDC(hWnd);
			hdcDIBSection = CreateCompatibleDC(hdcScreen);
			holdbitmap = (HBITMAP)SelectObject(hdcDIBSection, hDIBSection);

			InitBuff();
			//n. 25 kertaa sekunnissa
			uTimer = SetTimer(hWnd, 1, 40, NULL);
			break;
		case WM_TIMER:
			//aloitetaan alusta melkein 2 sekunnin välein
			if(count++>40){InitBuff();count=0;}

			TheRotoXoom();

			BitBlt(hdcScreen, 0, 0,640,480, hdcDIBSection,0, 0, SRCCOPY);

			break;
		case WM_DESTROY:
			delete DrawBuffer;
			delete CopyBuffer;
			delete xbuff;
			delete ybuff;

			delete pbmiDIB;
	        DeleteObject(hDIBSection);

			KillTimer(hWnd,uTimer);
			break;
		case WM_SIZE:
			//ei anneta käyttäjän säätää ikkunaa
			SetWindowPos(hWnd,HWND_TOPMOST,0,0,648,508,SWP_NOMOVE);
			break;
		case WM_CLOSE:
			PostQuitMessage(0);
			break;
		default:
			return DefWindowProc(hWnd,uMsg,wParam,lParam);
			break;
		}
	return 1;
}


ATOM myRegClass()
{
	WNDCLASS wc;

	memset(&wc,0,sizeof(WNDCLASS));

	wc.style=CS_HREDRAW|CS_VREDRAW;
	wc.lpfnWndProc=(WNDPROC)WindowProc;
	wc.hInstance=hInst;
	wc.hIcon=LoadIcon(NULL,IDI_WINLOGO);
	wc.hCursor=LoadCursor(NULL,IDC_ARROW);
	wc.hbrBackground=(HBRUSH)(COLOR_GRAYTEXT+1);
	wc.lpszMenuName=NULL;
	wc.lpszClassName=clzName;
	wc.lpszMenuName=NULL;

	return (RegisterClass(&wc));
}

int WINAPI WinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,LPSTR lpCmdLine,int nCmdShow)
{
	bool Running=true;;
	MSG Msg;

	hInst=hInstance;

	if(!myRegClass()){
		MessageBox(NULL,"Call to RegisterClass failed","ERROR",MB_OK|MB_ICONWARNING);
		return -1;
		}

	hMainWindow=CreateWindow(clzName,"RotoXoomerFeideri",WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,CW_USEDEFAULT,648,508,NULL,NULL,hInstance,NULL);

	if(!hMainWindow){
		MessageBox(NULL,"Call to CreateWindow failed","ERROR",MB_OK|MB_ICONWARNING);
		return -1;
		}

	ShowWindow(hMainWindow,SW_NORMAL);
	UpdateWindow(hMainWindow);

	while(Running){
		if(PeekMessage(&Msg,NULL,0,0,PM_REMOVE)){
			if(Msg.message==WM_QUIT){
				Running=false;
				}
			else {
				TranslateMessage(&Msg);
				DispatchMessage(&Msg);
				}
			}
		}

	return 0;
}

Dual [03.07.2004 14:59:24]

#

Spirits on kova jätkä;)

kenkku [03.07.2004 15:03:40]

#

Taitaapi olla Crash Bandicoot :)

P.S: se on koira ;D

Juice [03.07.2004 23:05:58]

#

Itse asiassa bandicoot tarkoittaa muistaakseni pussimäyrää.

rndprogy [03.07.2004 23:14:01]

#

yhteen kohtaan Spirits kirjoitti:

PS2 peliä jossa hilluttiin ilmeisesti jollain ketulla ja kerättin omenoita?

Apua(!) ;D ;)
Luulin, että kaikki tietää tuon pelin. >_<

Spirits [04.07.2004 11:56:04]

#

Siis no kyllähän minä periaattessa tuon pelin tiesin. En vain sillä hetkellä tunnistanut kun sitä ohimennen seurasin.

Se elukka on pussieläimiin(Marsupialia) kuuluva Eastern Barred Bandicoot(Perameles gunni). Piti tämäkin sitten selvittää.

http://www.parks.tas.gov.au/wildlife/mammals/ebband.html

Antti Laaksonen [04.07.2004 11:59:46]

#

Hienosti tehty efekti!

sooda [05.07.2004 16:44:37]

#

Anteeks mitäh? Vähän sä oot pro!

C++Amatööri [11.08.2005 14:44:02]

#

Hyvä jatka samaan malliin.


Sivun alkuun

Vastaus

Aihe on jo aika vanha, joten et voi enää vastata siihen.

Tietoa sivustosta