/* -*- compile-command: "msdev trace.dsp /make \"Trace - Win32 Debug\"" -*- */ #include "engine.h" #include "read.h" #include "scene.h" #include "general.h" #include "trace.h" #include extern "C" { int __declspec( dllexport ) Trace_Init( Tcl_Interp* interp ); BOOL __declspec( dllexport ) WINAPI DllMain( HINSTANCE, DWORD, LPVOID ); int GetScene( ClientData, Tcl_Interp*, int, Tcl_Obj* const [] ); int Trace_ResizeCmd( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ); int Trace_RedrawCmd( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ); int Start_Trace_Render( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ); int SaveBitmap(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]); int StopThread(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]); unsigned int __stdcall Trace_Render( void *dummy); } enum Status_e {RUNNING, STOPPED, HALT}; HINSTANCE g_hInstance = NULL; Tcl_Interp* g_interp = NULL; int g_height; int g_width; Tcl_Obj* g_pWidth = NULL; Tcl_Obj* g_pHeight = NULL; unsigned char* g_picture; static Status_e threadStatus = STOPPED; static HANDLE threadMutex; #define _countof(x) (sizeof(x) / sizeof(x[0])) const UINT MAX_CPUs = 32; const int AMBIENT_ID = 537; const int DIFFUSE_ID = 538; const int SPECULAR_ID = 539; const int REFLECTION_ID = 540; const int REFRACTION_ID = 541; const int SHADOWS_ID = 542; const int ATTENUATION_ID = 543; const int OPACITY_ID = 544; class BandLock { public: void Create(int cProcs) { m_cProcessors = min(cProcs, _countof(m_rgcsGuard)); for (int idx = 0; idx < m_cProcessors; idx++) { InitializeCriticalSection(&m_rgcsGuard[idx]); } } inline int GetCount() const { return m_cProcessors; } inline void LockAll() { for (int idx = 0; idx < m_cProcessors; idx++) { EnterCriticalSection(&m_rgcsGuard[idx]); } } inline void UnlockAll() { for (int idx = 0; idx < m_cProcessors; idx++) { LeaveCriticalSection(&m_rgcsGuard[idx]); } } inline void Lock(int idxProc) { EnterCriticalSection(&m_rgcsGuard[idxProc]); } inline void Unlock(int idxProc) { LeaveCriticalSection(&m_rgcsGuard[idxProc]); } protected: int m_cProcessors; CRITICAL_SECTION m_rgcsGuard[MAX_CPUs]; }; BandLock g_BandLock; char command_buffer[1024]; char convert_buffer[1024]; Scene *scene; /* DllMain * * every DLL must provide DllMain. ours just stores the instance handle, * so we can use it to create a window later. */ BOOL WINAPI DllMain( HINSTANCE hInst, DWORD dwReason, LPVOID reserved ) { g_hInstance = hInst; return 1; } void refreshScreen(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); g_BandLock.LockAll(); if (g_picture != NULL) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glDrawPixels(g_width, g_height, GL_RGB, GL_UNSIGNED_BYTE, g_picture); glFlush(); } g_BandLock.UnlockAll(); Sleep(500); } int StopThread(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { g_BandLock.LockAll(); if(threadStatus == RUNNING) { threadStatus = HALT; while(threadStatus != STOPPED) Sleep(10); } g_BandLock.UnlockAll(); return TCL_OK; } int Start_Trace_Render( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { if(scene != NULL) { g_BandLock.LockAll(); if( Tcl_GetIntFromObj( interp, Tcl_ObjGetVar2( interp, g_pWidth, NULL, TCL_GLOBAL_ONLY), &g_width) != TCL_OK) { g_BandLock.UnlockAll(); return TCL_OK; } if( Tcl_GetIntFromObj( interp, Tcl_ObjGetVar2( interp, g_pHeight, NULL, TCL_GLOBAL_ONLY), &g_height) != TCL_OK) { g_BandLock.UnlockAll(); return TCL_OK; } threadStatus = RUNNING; delete [] g_picture; g_picture = new GLubyte [g_width * g_height * 3]; memset(g_picture, 0, g_width*g_height*3); for (int idx = 0; idx < g_BandLock.GetCount(); idx++) { UINT nID; HANDLE h = (HANDLE) _beginthreadex(NULL, 0, &Trace_Render, (void *) idx, 0, &nID); if (h != NULL) { CloseHandle(h); } } g_BandLock.UnlockAll(); } return TCL_OK; } unsigned int __stdcall Trace_Render( void *dummy ) { int idxProcessor = (int) dummy; char buff[] = {".gl redraw"}; RayTracer ray_tracer; /* ray_tracer.do_ambient = ( get_control_b( AMBIENT_ID ) != 0 ); ray_tracer.do_diffuse = ( get_control_b( DIFFUSE_ID ) != 0 ); ray_tracer.do_specular = ( get_control_b( SPECULAR_ID ) != 0 ); ray_tracer.do_reflection = ( get_control_b( REFLECTION_ID ) != 0 ); ray_tracer.do_refraction = ( get_control_b( REFRACTION_ID ) != 0 ); ray_tracer.do_shadows = ( get_control_b( SHADOWS_ID ) != 0 ); ray_tracer.do_attenuation = ( get_control_b( ATTENUATION_ID ) != 0 ); ray_tracer.do_opacity = ( get_control_b( OPACITY_ID ) != 0 ); */ const band = 16; int xStart = idxProcessor * g_height / g_BandLock.GetCount(); int xEnd = (idxProcessor + 1) * g_height / g_BandLock.GetCount(); for (unsigned int height_strip = xStart; height_strip < xEnd; height_strip += band) { g_BandLock.Lock(idxProcessor); for (unsigned int offset = 0; offset < band; offset++) { unsigned int height_index = offset + height_strip; if (height_index >= xEnd) { threadStatus = STOPPED; //We allow this because we know the mutex is held in StopThread g_BandLock.Unlock(idxProcessor); return 0; } unsigned char * pbPicture = &g_picture[(g_width * height_index) * 3]; for (unsigned int width_index = 0; width_index < g_width; width_index++) { if(threadStatus == HALT) { threadStatus = STOPPED; //We allow this because we know the mutex is held in StopThread g_BandLock.Unlock(idxProcessor); return 0; } double offset_x = 0.5*((static_cast(g_height) - static_cast(g_width))/static_cast(g_height)); double normalized_y = static_cast(height_index) / static_cast(g_height); double normalized_x = offset_x + (static_cast(width_index) / static_cast(g_height)); vec3 color = ray_tracer.trace(scene, normalized_x, normalized_y); *pbPicture++ = (BYTE) (color[0] * 255.0);//red; *pbPicture++ = (BYTE) (color[1] * 255.0);//green; *pbPicture++ = (BYTE) (color[2] * 255.0);//blue; } } g_BandLock.Unlock(idxProcessor); } threadStatus = STOPPED; return 0; } int Trace_ResizeCmd( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ) { refreshScreen(); return TCL_OK; } int Trace_RedrawCmd( ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[] ) { refreshScreen(); return TCL_OK; } /* Trace_Init * * Tcl calls this function when the DLL is first loaded, to initialize * the package(s) contained in the DLL. */ int Trace_Init( Tcl_Interp* interp ) { Tcl_CreateObjCommand( interp, "trace::getscene", GetScene, NULL, NULL); Tcl_CreateObjCommand( interp, "trace::resize", Trace_ResizeCmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "trace::redraw", Trace_RedrawCmd, NULL, NULL ); Tcl_CreateObjCommand( interp, "trace::render", Start_Trace_Render, NULL, NULL ); Tcl_CreateObjCommand( interp, "trace::save", SaveBitmap, NULL, NULL ); Tcl_CreateObjCommand( interp, "trace::stoprender", StopThread, NULL, NULL ); /* initialize the UI control package. */ if ( Controls_Init( interp ) != TCL_OK ) return TCL_ERROR; /* keep a pointer to the interpreter. */ g_interp = interp; int cProcessors = 1; SYSTEM_INFO si; ZeroMemory(&si, sizeof(si)); GetSystemInfo(&si); if ((si.dwNumberOfProcessors > 0) && (si.dwNumberOfProcessors < 128)) { cProcessors = (int) si.dwNumberOfProcessors; } g_BandLock.Create(cProcessors); g_pWidth = Tcl_NewStringObj("g_width", -1); g_pHeight = Tcl_NewStringObj("g_height", -1); /* if ( checkbox( interp, AMBIENT_ID, "Ambient", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, DIFFUSE_ID, "Diffuse", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, SPECULAR_ID, "Specular", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, REFLECTION_ID, "Reflection", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, REFRACTION_ID, "Refraction", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, SHADOWS_ID, "Shadows", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, ATTENUATION_ID, "Distance Attenuation", 1 ) != TCL_OK ) return TCL_ERROR; if ( checkbox( interp, OPACITY_ID, "Attenuate Amb/Dif by 1-kt", 1 ) != TCL_OK ) return TCL_ERROR; */ return TCL_OK; } /* Debug * * Debug works just like printf(), except that it prints output in the * MSVC++ "output" window. don't try to print more than 1K bytes at * once. */ void Debug( char* fmt, ... ) { char buffer[1024]; va_list list; va_start( list, fmt ); vsprintf( buffer, fmt, list ); va_end( list ); OutputDebugString( buffer ); } int SaveBitmap(ClientData clientData, Tcl_Interp* interp, int objc, Tcl_Obj* const objv[]) { FILE* file; OPENFILENAME open; char fname[100]; BITMAPFILEHEADER bmfh; BITMAPINFOHEADER bmih; fname[0] = '\0'; open.lStructSize = sizeof(OPENFILENAME); open.hwndOwner = NULL; open.lpstrFilter = "Bitmap (*.bmp)\0*.bmp\0\0"; open.nFilterIndex = 0; open.lpstrCustomFilter = NULL; open.nFilterIndex = NULL; open.lpstrFile = fname; open.nMaxFile = 2048000; open.lpstrFileTitle = NULL; open.lpstrInitialDir = NULL; open.lpstrTitle = "Bmp loader"; open.Flags = NULL; open.nFileOffset = NULL; open.nFileExtension = NULL; open.lpstrDefExt = "bmp"; open.lCustData = NULL; open.lpfnHook = NULL; open.lpTemplateName = NULL; GetSaveFileName(&open); int bytes, pad; bytes = g_width * 3; pad = (bytes%4) ? 4-(bytes%4) : 0; bytes += pad; bytes *= g_height; bmfh.bfType = 0x4d42; // "BM" bmfh.bfSize = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) + bytes; bmfh.bfReserved1 = 0; bmfh.bfReserved2 = 0; bmfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); bmih.biSize = sizeof(BITMAPINFOHEADER); bmih.biWidth = g_width; bmih.biHeight = g_height; bmih.biPlanes = 1; bmih.biBitCount = 24; bmih.biCompression = BI_RGB; bmih.biSizeImage = 0; bmih.biXPelsPerMeter = (int)(100 / 2.54 * 72); bmih.biYPelsPerMeter = (int)(100 / 2.54 * 72); bmih.biClrUsed = 0; bmih.biClrImportant = 0; file = fopen(open.lpstrFile, "wb"); fwrite( &bmfh, sizeof(BITMAPFILEHEADER), 1, file ); fwrite( &bmih, sizeof(BITMAPINFOHEADER), 1, file ); bytes /= g_height; unsigned char* scanline = new unsigned char [bytes]; for ( int j = 0; j < g_height; ++j ) { memcpy( scanline, g_picture + j*3*g_width, bytes ); for ( int i = 0; i < g_width; ++i ) { unsigned char temp = scanline[i*3]; scanline[i*3] = scanline[i*3+2]; scanline[i*3+2] = temp; } fwrite( scanline, bytes, 1, file ); } delete [] scanline; fclose(file); return TCL_OK; }