DX Neuling



  • Stimmts so? Oder mach ich irgendwelche dummen Fehler, unnötige Dinge, unschöne Dinge (...)?
    Ich mein, ich bin interessenmäßig grad erst vorgestern über DX gestolpert, hab' mir gedacht "das wär mal ganz nett" und angefangen zu coden und möcht mir nicht schon am Anfang blödsinn angewöhnen.

    Mein 'Starfield Demo':

    #include <windows.h>
    #include <mmsystem.h>
    #include <stdlib.h>
    #include <stdio.h>
    #include <time.h>
    #include <d3d9.h>
    #include <d3d9types.h>
    #include <d3dx9math.h>
    
    typedef DWORD(CALLBACK* lpfntimeGetTime)(void);
    typedef HRESULT(CALLBACK *lpfnD3DCreateFont)(LPDIRECT3DDEVICE9, INT, UINT, UINT, UINT, BOOL, DWORD, DWORD, DWORD, DWORD, LPCTSTR, LPD3DXFONT*);
    typedef D3DXMATRIX*(CALLBACK *lpfnD3DMatrixLookAtLH)(D3DXMATRIX*, D3DXVECTOR3*, D3DXVECTOR3*, D3DXVECTOR3*);
    typedef IDirect3D9*(CALLBACK *lpfnD3DCreate9)(UINT);
    typedef D3DXMATRIX*(CALLBACK *lpfnD3DMatrixPerspectiveFovLH)(D3DXMATRIX*, FLOAT, FLOAT, FLOAT, FLOAT);
    typedef D3DXMATRIX*(CALLBACK *lpfnD3DMatrixRotationZ)(D3DXMATRIX*, FLOAT);
    
    lpfntimeGetTime			GetTime;
    lpfnD3DCreateFont			D3DCreateFont;
    lpfnD3DMatrixLookAtLH		D3DMatrixLookAtLH;
    lpfnD3DCreate9			D3DCreate9;
    lpfnD3DMatrixPerspectiveFovLH	D3DMatrixPerspectiveFovLH;
    lpfnD3DMatrixRotationZ		D3DMatrixRotationZ;
    
    // Some defines...
    
    #define NUM_STARS		 8000	// Number of stars to be calculated
    #define ORIGIN_WIDTH	  200 // Width and hight of origin where the stars appear
    #define FIELD_DEPTH	  500 // Depth of the starfield
    #define D3DFVF_VERTEX	(D3DFVF_XYZ|D3DFVF_DIFFUSE)
    #define BRIGHTNES(x)	D3DCOLOR_XRGB(x, x, x) // Returns a grayscale color based upon x
    #define random(x)		((float)((float)x * 10.0f) * rand() / ((RAND_MAX + 1.0f) * 10.0f) )
    #define srandom()		srand((unsigned)time(NULL))
    
    // My vertex
    
    struct VERTEX {
    	float x, y, z;
    	D3DCOLOR color;
    };
    
    VERTEX			Stars[NUM_STARS];
    float				Speed[NUM_STARS];	// Speed parameter for each star[i]
    HINSTANCE			hInstance;		// Current instance
    HMODULE			hdllD3D9;
    HMODULE			hdllD3DX9D;
    HMODULE			hdllWinMM;
    LPDIRECT3D9			lpD3D;
    LPDIRECT3DDEVICE9		lpD3DDevice;
    LPD3DXFONT			lpFont;
    LPDIRECT3DVERTEXBUFFER9	lpVertexBuffer;
    DWORD				InitialFrameTime;
    DWORD				CurrentFrameTime;
    
    // Forward declarations
    
    LRESULT CALLBACK MainWindowProc(HWND hWindow, UINT Message, WPARAM wParam, LPARAM lParam);
    HWND InitApplication(HINSTANCE hInstance);
    LPDIRECT3DDEVICE9 CreateD3DDevice(LPDIRECT3D9 lpDirect3D, HWND hMainWindow);
    bool GetDirectXVersion(LPSTR lpDirectXVersion);
    void ExitApplication(HINSTANCE hInstance, int nStatus);
    inline void DoTransformations();
    inline void UpdateVertexBuffer();
    inline void Render(HWND hWindow);
    void InitStarfield();
    inline void MoveStarfield();
    
    // Application main entry
    
    int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCommand)
    {
    	if(!(hdllD3D9 = LoadLibrary("d3d9.dll"))) {
    		MessageBox(NULL, "Fehler beim Laden! (d3d9.dll)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	if(!(hdllD3DX9D = LoadLibrary("d3dx9d.dll"))) {
    		MessageBox(NULL, "Fehler beim Laden! (d3d9.dll)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	if(!(hdllWinMM = LoadLibrary("winmm.dll"))) {
    		MessageBox(NULL, "Fehler beim Laden! (WinMM.dll)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    
    	GetTime = (lpfntimeGetTime) GetProcAddress(hdllWinMM, "timeGetTime");
    	if(!GetTime) {
    		MessageBox(NULL, "Fehler beim Laden! (timeGetTime)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	D3DCreateFont = (lpfnD3DCreateFont) GetProcAddress(hdllD3DX9D, "D3DXCreateFontA");
    	if(!D3DCreateFont) {
    		MessageBox(NULL, "Fehler beim Laden! (D3DXCreateFont)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	D3DMatrixLookAtLH = (lpfnD3DMatrixLookAtLH) GetProcAddress(hdllD3DX9D, "D3DXMatrixLookAtLH");
    	if(!D3DMatrixLookAtLH) {
    		MessageBox(NULL, "Fehler beim Laden! (D3DXMatrixLookAt)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	D3DCreate9 = (lpfnD3DCreate9) GetProcAddress(hdllD3D9, "Direct3DCreate9");
    	if(!D3DCreate9) {
    		MessageBox(NULL, "Fehler beim Laden! (Direct3DCreate9)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	D3DMatrixPerspectiveFovLH = (lpfnD3DMatrixPerspectiveFovLH) GetProcAddress(hdllD3DX9D, "D3DXMatrixPerspectiveFovLH");
    	if(!D3DMatrixPerspectiveFovLH) {
    		MessageBox(NULL, "Fehler beim Laden! (D3DMatrixPerspectiveFovLH)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	D3DMatrixRotationZ = (lpfnD3DMatrixRotationZ) GetProcAddress(hdllD3DX9D, "D3DXMatrixRotationZ");
    	if(!D3DMatrixRotationZ) {
    		MessageBox(NULL, "Fehler beim Laden! (D3DMatrixRotationZ)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	::hInstance = hInstance;
    
    	HWND	hMainWindow;
    	char	lpDirectXVersion[15];
    
    	srandom();
    
    	if(!GetDirectXVersion(lpDirectXVersion))
    	{
    		MessageBox(NULL, "DirectX scheint nicht ordnungsgemäß installiert zu sein!\n", "Starfield Demo:", MB_OK|MB_ICONEXCLAMATION);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    
    	MessageBox(NULL, lpDirectXVersion, "I am running on DirectX version...", MB_OK);
    
    	if(!(lpD3D = D3DCreate9(D3D_SDK_VERSION)))
    		ExitApplication(hInstance, EXIT_FAILURE);
    
    	if(!(hMainWindow = InitApplication(hInstance)))
    		ExitApplication(hInstance, EXIT_FAILURE);
    
    	if(!(lpD3DDevice = CreateD3DDevice(lpD3D, hMainWindow)))
    		ExitApplication(hInstance, EXIT_FAILURE);
    
    	InitStarfield();
    	MoveStarfield();
    	UpdateVertexBuffer();
    
    	UpdateWindow(hMainWindow);
    	ShowWindow(hMainWindow, nShowCommand);
    
    	// Message loop
    
    	MSG Message;
    	do {
    		if(PeekMessage(&Message, NULL, 0U, 0U, PM_REMOVE))
    		{
    			TranslateMessage(&Message);
    			DispatchMessage(&Message);
    		}
    		else Render(hMainWindow);
    	} while(Message.message!=WM_QUIT);
    
    	ExitApplication(hInstance, EXIT_SUCCESS);
    }
    
    void InitStarfield()
    {
    	for(register UINT i = 0; i < NUM_STARS; i++)
    	{
    		Stars[i].color = BRIGHTNES(0);
    		Stars[i].x = (ORIGIN_WIDTH / 2) - random(ORIGIN_WIDTH);
    		Stars[i].y = (ORIGIN_WIDTH / 2) - random(ORIGIN_WIDTH);
    		Stars[i].z = FIELD_DEPTH-random(100);
    		Speed[i] = random(6);
    	}
    }
    
    inline void MoveStarfield()
    {
    	for(register UINT i = 0; i < NUM_STARS; i++)
    	{
    		Stars[i].z -= Speed[i];
    		Speed[i] += 0.005f;
    		Stars[i].color = BRIGHTNES((int)(255 * (1-Stars[i].z / FIELD_DEPTH)));
    		if(Stars[i].z < 0.0)
    		{
    			Stars[i].color = BRIGHTNES(0);
    			Stars[i].x = (ORIGIN_WIDTH / 2) - random(ORIGIN_WIDTH);
    			Stars[i].y = (ORIGIN_WIDTH / 2) - random(ORIGIN_WIDTH);
    			Stars[i].z = FIELD_DEPTH-random(100);
    			Speed[i]	= random(6);
    		}
    	}
    }
    
    HWND InitApplication(HINSTANCE hInstance)
    {
    	WNDCLASS wcStarfieldDemo;
    	HWND		hMainWindow;
    	wcStarfieldDemo.style			= CS_SAVEBITS | CS_HREDRAW | CS_VREDRAW;
    	wcStarfieldDemo.lpfnWndProc	= MainWindowProc;
    	wcStarfieldDemo.cbClsExtra		= 0;
    	wcStarfieldDemo.cbWndExtra		= 0;
    	wcStarfieldDemo.hInstance		= hInstance;
    	wcStarfieldDemo.hIcon			= LoadIcon(NULL, IDI_APPLICATION);
    	wcStarfieldDemo.hCursor			= LoadCursor(NULL, IDC_ARROW);
    	wcStarfieldDemo.hbrBackground	= (HBRUSH)(COLOR_WINDOW+1);
    	wcStarfieldDemo.lpszMenuName	= NULL;
    	wcStarfieldDemo.lpszClassName	= "StarfieldDemo";
    
    	if(!RegisterClass(&wcStarfieldDemo))
    		return 0;
    
    	hMainWindow = CreateWindow("StarfieldDemo", "Starfield Demo (V 1.0 Alpha)", WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, 300, 300, NULL, NULL, hInstance, NULL);
    	if(!hMainWindow)
    		return 0;
    
    	return hMainWindow;
    }
    
    LPDIRECT3DDEVICE9 CreateD3DDevice(LPDIRECT3D9 lpDirect3D, HWND hMainWindow)
    {
    	HRESULT result = 0;
    	D3DPRESENT_PARAMETERS D3DParameters;
    	ZeroMemory(&D3DParameters, sizeof(D3DParameters));
    	D3DParameters.Windowed = TRUE;
    	D3DParameters.SwapEffect = D3DSWAPEFFECT_DISCARD;
    	D3DParameters.BackBufferFormat = D3DFMT_UNKNOWN;
    	LPDIRECT3DDEVICE9 D3DDevice;
    	result = lpDirect3D->CreateDevice(D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hMainWindow,
    		D3DCREATE_HARDWARE_VERTEXPROCESSING, &D3DParameters, &D3DDevice);
    	D3DDevice->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
    	D3DDevice->SetRenderState(D3DRS_LIGHTING, FALSE);
    	D3DDevice->CreateVertexBuffer(NUM_STARS * sizeof(VERTEX), 0, D3DFVF_VERTEX, D3DPOOL_DEFAULT, &lpVertexBuffer, NULL);
    	D3DCreateFont(D3DDevice, 12, 0, FW_BOLD, 1, false, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH | FF_DONTCARE, "Arial", &lpFont);
    
    	D3DXMATRIX matWorld;
    	D3DMatrixRotationZ(&matWorld, (float)cos(GetTime() / 2000.0f));
    	D3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
    
    	D3DXVECTOR3 vEyePoint		(0.0f, 0.0f, -1.0f);
    	D3DXVECTOR3 vLookAt		(0.0f, 0.0f, 0.0f);
    	D3DXVECTOR3 vUpDirection	(0.0f, 1.0f, 0.0f);
    
    	D3DXMATRIXA16 matView;
    	D3DMatrixLookAtLH(&matView, &vEyePoint, &vLookAt, &vUpDirection);
    	D3DDevice->SetTransform(D3DTS_VIEW, &matView);
    
    	D3DXMATRIX matProjection;
    	D3DMatrixPerspectiveFovLH(&matProjection, D3DX_PI/4, 1.0f, 1.0f, FIELD_DEPTH);
    	D3DDevice->SetTransform(D3DTS_PROJECTION, &matProjection);
    
    	InitialFrameTime = GetTime();
    
    	return D3DDevice;
    }
    
    bool GetDirectXVersion(LPSTR lpDirectXVersion)
    {
    	HKEY hRegKey;
    	DWORD dwValueType;
    	DWORD dwValueLength = 13;
    	DWORD dwResult;
    	char lpValue[13];
    	if(RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\DirectX", 0, KEY_READ, &hRegKey) != ERROR_SUCCESS) return false;
    	dwResult = RegQueryValueEx(hRegKey, "Version", 0, &dwValueType, (LPBYTE)lpValue, &dwValueLength);
    	RegCloseKey(hRegKey);
    	if((dwResult != ERROR_SUCCESS)||(dwValueType != REG_SZ)) return false;
    	strcpy(lpDirectXVersion, lpValue);
    	return true;
    }
    
    void ExitApplication(HINSTANCE hInstance, int nStatus)
    {
    	if(lpVertexBuffer)
    		lpVertexBuffer->Release();
    	if(lpFont)
    		lpFont->Release();
    	if(lpD3DDevice)
    		lpD3DDevice->Release();
    	if(lpD3D)
    		lpD3D->Release();
    
    	if(hdllD3D9)
    		FreeLibrary(hdllD3D9);
    	if(hdllWinMM)
    		FreeLibrary(hdllWinMM);
    	if(hdllD3DX9D)
    		FreeLibrary(hdllD3DX9D);
    
    	UnregisterClass("StarfieldDemo", hInstance);
    
    	if(nStatus == EXIT_FAILURE)
    		MessageBox(NULL, "Es ist ein unbehebbarer Fehler in der Anwengung aufgetreten!", "Starfield Demo:", MB_OK|MB_ICONEXCLAMATION);
    
    	exit(nStatus);
    }
    
    LRESULT CALLBACK MainWindowProc(HWND hWindow, UINT uMessage, WPARAM wParam, LPARAM lParam)
    {
    	switch(uMessage)
    	{
    		case WM_CLOSE:
    		case WM_DESTROY:
    			PostQuitMessage(0);
    			return 0;
    
    		case WM_MOVE:
    		case WM_SIZING:
    		case WM_PAINT:
    			Render(hWindow);
    			return 0;
    	}
    	return DefWindowProc(hWindow, uMessage, wParam, lParam);
    }
    
    inline void UpdateVertexBuffer()
    {
    	void *pVertices;
    
    	if(FAILED(lpVertexBuffer->Lock(0, sizeof(Stars), (void**)&pVertices, 0 ) ) )
    	{
    		MessageBox(NULL, "Lock auf Vertexbuffer fehlgeschlagen!", "Starfield Demo:", MB_OK|MB_ICONEXCLAMATION);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    
    	memcpy(pVertices, Stars, NUM_STARS * sizeof(VERTEX));
    	lpVertexBuffer->Unlock();
    }
    
    inline void DoTransformations()
    {
    	D3DXMATRIX matWorld;
    	D3DMatrixRotationZ(&matWorld, (float)cos(GetTime() / 2000.0f));
    	lpD3DDevice->SetTransform(D3DTS_WORLD, &matWorld);
    }
    
    inline void Render(HWND hWindow)
    {
    	static	char		strFrameRate[100] = "N/A";
    	static	DWORD		FrameCounter = 1;
    	static	RECT		rc = {2, 2, 0, 0};
    
    	FrameCounter++;
    
    	CurrentFrameTime = GetTime();
    
    	if( (CurrentFrameTime - InitialFrameTime) > 1000 )
    	{
    		InitialFrameTime = GetTime();
    		sprintf(strFrameRate, "FPS: %u", FrameCounter);
    		FrameCounter = 0;
    	}
    
    	lpD3DDevice->Clear(0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB(0,0,0), 1.0f, 0);
    
    	MoveStarfield();
    	UpdateVertexBuffer();
    	DoTransformations();
    
    	lpD3DDevice->BeginScene();
    
    	lpD3DDevice->SetStreamSource(0, lpVertexBuffer, 0, sizeof(VERTEX));
    	lpD3DDevice->SetFVF(D3DFVF_VERTEX);
    	lpD3DDevice->DrawPrimitive(D3DPT_POINTLIST, 0, NUM_STARS);
    
    	SetRect(&rc, 2, 2, 0, 0);
    	lpFont->DrawText( NULL, strFrameRate, -1, &rc, DT_NOCLIP, D3DXCOLOR( 1.0f, 1.0f, 0.0f, 1.0f ));
    
    	lpD3DDevice->EndScene();
    	lpD3DDevice->Present(NULL, NULL, hWindow, NULL);
    
    }
    

    Ich hoff es findet sich wer der kritisiert...
    THXIA

    Swordfish

    [edit]
    Jetzt mit dynamic linkage...
    So erkenn ich (und nicht Windows) wenn die libraries nicht forhanden sind. 😃
    [/edit]



  • Swordfish schrieb:

    if(!GetDirectXVersion(lpDirectXVersion))
    	{
    		MessageBox(NULL, "DirectX scheint nicht ordnungsgemäß installiert zu sein!\n", "Man Hunt:", MB_OK|MB_ICONEXCLAMATION);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    	
    	MessageBox(NULL, lpDirectXVersion, "I am running on DirectX version...", MB_OK);
    

    Da Du doch sowieso statisch linkst, nützt das nicht sehr viel.
    Falls DX < 9 installiert ist, wird's soweit gar nicht kommen, und Windows meldet vorher "d3d9.dll konnte nicht gefunden werden"...



  • Haste recht Sgt.!
    Dumm von mir...

    Hab's jetzt dynamisch Geladen (Code im Post ^ abgeändert).

    Zur Benutzung von DX irgendwelche Kommentare??
    Kommt schon leute, ich kann doch nicht perfekt sein... 😉

    [EDIT]
    Ich hab' grad ne Anzeige für die FPS eingebaut und komm aus dem Staunen nicht 'raus: <=75 FPS
    Das kanns doch bei nem Athlon XP 2600+ und 'ner GeForce 4 Ti 4800 SE nicht sein oder??
    Zudem bei der simplen Anwendung. Hat dafür jemand eine Erklärung?
    [/EDIT]

    THXIA
    GreeTz,

    Swordfish



  • Reicht Vertical Syncronisation / V-Sync als Antwort? 🙂

    DX wartet bis der Monitor den Backbuffer einmal durchläuft und flippt dann erst zwischen den beiden Backbuffern.

    Edit:
    (Genauer gesagt: DX wartet auf den Elektronenstrahl des Monitors bis der am unteren Bildschirmrand angekommen ist 😉 )



  • Swordfish schrieb:

    Haste recht Sgt.!
    Dumm von mir...

    Hab's jetzt dynamisch Geladen (Code im Post ^ abgeändert).

    ROFL - willst mich wohl verscheissern, was?!? 😃

    Zuerst wolltest Du bei Nicht-Erfolg die schöne Meldung "DirectX scheint nicht ordnungsgemäß installiert zu sein!" ausgeben, damit kann auch ein DAU der null Ahnung vom Programmieren hat etwas anfangen.
    Das funzte in Deiner Implementation aber nicht, weil Dir Windows mit dem nichts-sagenden Fehler "Die benötigte DLL d3d9.dll konnte nicht gefunden werden." zuvor kam.

    Jetzt lädst Du dynamisch, gibst bei Nicht-Erfolg aber wieder die gleich-beschissene Meldung an den User weiter (und beendest dann):

    Swordfish schrieb:

    if(!(hdllD3D9 = LoadLibrary("d3d9.dll"))) {
    		MessageBox(NULL, "Fehler beim Laden! (d3d9.dll)", "Starfield demo:", MB_OK);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    

    Zu folgendem kommt es wieder nicht:

    Swordfish schrieb:

    if(!GetDirectXVersion(lpDirectXVersion))
    	{
    		MessageBox(NULL, "DirectX scheint nicht ordnungsgemäß installiert zu sein!\n", "Starfield Demo:", MB_OK|MB_ICONEXCLAMATION);
    		ExitApplication(hInstance, EXIT_FAILURE);
    	}
    


  • Sgt. Nukem schrieb:

    Swordfish schrieb:

    Haste recht Sgt.!
    Dumm von mir...

    Hab's jetzt dynamisch Geladen (Code im Post ^ abgeändert).

    ROFL - willst mich wohl verscheissern, was?!? 😃

    Vielleicht... :p

    Sgt. Nukem schrieb:

    Zuerst wolltest Du bei Nicht-Erfolg die schöne Meldung "DirectX scheint nicht ordnungsgemäß installiert zu sein!" ausgeben, damit kann auch ein DAU der null Ahnung vom Programmieren hat etwas anfangen.
    Das funzte in Deiner Implementation aber nicht, weil Dir Windows mit dem nichts-sagenden Fehler "Die benötigte DLL d3d9.dll konnte nicht gefunden werden." zuvor kam.

    Nö, war nur so, daß ich das implementiert hab' ohne das Ziel im Auge zu behalten...

    Zum Rest des Codes (Anwendung des DX SDK) irgendwas?



  • Swordfish schrieb:

    Vielleicht... :p

    😃

    Swordfish schrieb:

    Nö, war nur so, daß ich das implementiert hab' ohne das Ziel im Auge zu behalten...

    Zum Rest des Codes (Anwendung des DX SDK) irgendwas?

    *hattugutgemacht* *tätschel* 🕶 👍

    Schön, daß Du die D3DX Funktionen nutzt und nicht alles selber machst. 😮



  • Sgt. Nukem schrieb:

    Schön, daß Du die D3DX Funktionen nutzt und nicht alles selber machst. 😮

    Wie darf ich das verstehen, ironie? Was soll ich denn selber machen? Matrixen selber baun -> boah scheiße Mathematik... 🕶

    [edit] Mit der Rechtschreibung hab' ich größere Probleme als mit der Mathematik... 😉 [/edit]



  • Swordfish schrieb:

    Sgt. Nukem schrieb:

    Schön, daß Du die D3DX Funktionen nutzt und nicht alles selber machst. 😮

    Wie darf ich das verstehen, ironie? Was soll ich denn selber machen? Matrixen selber baun -> boah scheiße Mathematik... 🕶

    [edit] Mit der Rechtschreibung hab' ich größere Probleme als mit der Mathematik... 😉 [/edit]

    Es war zwar sarkastisch aber nicht ironisch gemeint. 🤡


Anmelden zum Antworten