/* Copyright 2012 Wolfgang Thaller. This file is part of Retro68. Retro68 is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Retro68 is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Retro68. If not, see . */ #ifdef __APPLE__ #include #include #else #ifdef RETRO_CARBON //#define __FP__ #include #include #else #include #include #include #include #include #include #endif #endif #include "fixed.h" #include #include #include #include #include #include using std::sqrt; using std::floor; inline int floor_to_int(float f) { return static_cast(std::floor(f)); } template class vec3 { public: T x,y,z; vec3(T x, T y, T z) : x(x), y(y), z(z) {} vec3() : x(0), y(0), z(0) {} vec3 operator+(const vec3& other) const { return vec3(x + other.x, y + other.y, z + other.z); } vec3 operator-(const vec3& other) const { return vec3(x - other.x, y - other.y, z - other.z); } vec3 operator*(T a) const { return vec3(x*a, y*a, z*a); } T length() const { return sqrt(x*x + y*y + z*z); } T operator*(const vec3& other) const { return x*other.x + y*other.y + z*other.z; } #if 1 vec3 normalize() const { T l = length(); //if(l == 0) // return *this; //else return (*this) * (T(1) / length()); } #else vec3 normalize() const { T l = length(); if(l == 0) return *this; else return vec3(x/l, y/l, z/l); } #endif }; template bool hitSphere(vec3 p0, vec3 dir, T& t) { const vec3 center(0.0f, 1.0f, -6.0f); const T r = 2.0f; vec3 p0c(p0 - center); /* (x-xc)^2 + (y-yc)^2 + (z-zc)^2 = r^2; (x0c + dx * t)^2 + (y0c + dy * t)^2 + (z0c + dz * t)^2 = r^2; x0c^2 + 2*x0c*dx*t + dx^2*t^2 + y0c^2 + 2*y0c*dy*t + dy^2*t^2 + z0c^2 + 2 * z0c*dz*t + dz^2*t^2 = r^2 (dx^2 + dy^2 + dz^2)*t^2 + (2*x0c*dx + 2*y0c&dy + 2*z0c*dz) * t + x0c^2+y0c^2+z0c^2-r^2 */ T a = dir*dir; T b = 2*(p0c*dir); T c = p0c*p0c - r*r; T D = b*b - 4 * a * c; if(D >= 0) { t = (-b - sqrt(D)) / (2*a); return t >= 0; } return false; } template T ray(int n, vec3 p0, vec3 dir) { #if 1 static const vec3 light = vec3(-2,4,3).normalize(); if(1){ const vec3 center(0.0f, 1.0f, -6.0f); const T r = 2.0f; vec3 p0c(p0 - center); /* (x-xc)^2 + (y-yc)^2 + (z-zc)^2 = r^2; (x0c + dx * t)^2 + (y0c + dy * t)^2 + (z0c + dz * t)^2 = r^2; x0c^2 + 2*x0c*dx*t + dx^2*t^2 + y0c^2 + 2*y0c*dy*t + dy^2*t^2 + z0c^2 + 2 * z0c*dz*t + dz^2*t^2 = r^2 (dx^2 + dy^2 + dz^2)*t^2 + (2*x0c*dx + 2*y0c&dy + 2*z0c*dz) * t + x0c^2+y0c^2+z0c^2-r^2 */ T a = dir*dir; T b = 2*(p0c*dir); T c = p0c*p0c - r*r; T D = b*b - 4 * a * c; if(D >= 0) { T t = (-b - sqrt(D)) / (2*a); if(t > 0) { vec3 p = p0 + dir * t; vec3 dir2 = (p - center) * (T(1)/r); T l = dir2*dir; T reflected; if(n) reflected = ray(n-1, p, dir - dir2*(l*2)); else reflected = 0.0f; T lambert = dir2 * light; return T(0.2f) + T(0.4f) * std::max(T(0),lambert) + T(0.4f) * reflected; } } } if(dir.y < 0) { T t = (T(-1.5f) - p0.y) / dir.y; vec3 p = p0 + dir*t; T color; if( (floor_to_int(p.x) + floor_to_int(p.z)) % 2 ) color = 0.8f; else color = 0.1f; T ts; if(hitSphere(p, light, ts)) color *= T(0.2f); return std::min(T(1), color + T(0.5f) * ray(n-1, p,vec3(dir.x, -dir.y, dir.z))); } #endif return std::max(T(0), dir.y * T(0.3f)); } typedef fixed numtype; template struct rand1 { static T get() { return T(std::rand()) / T(32767.0f * 65536.0f); } }; template<> struct rand1 { static fixed get() { return fixed(std::rand() >> 15, fixed::raw()); } }; int main() { WindowPtr win; #if !TARGET_API_MAC_CARBON InitGraf(&qd.thePort); InitFonts(); InitWindows(); InitMenus(); Rect r = qd.screenBits.bounds; #else BitMap bm; GetQDGlobalsScreenBits(&bm); Rect r = bm.bounds; #endif SetRect(&r, r.left + 5, r.top + 45, r.right - 5, r.bottom -5); win = NewWindow(NULL, &r, "\pRaytracer (C++ Version)", true, 0, (WindowPtr)-1, false, 0); #if !TARGET_API_MAC_CARBON SetPort(win); r = win->portRect; #else SetPortWindowPort(win); GetPortBounds(GetWindowPort(win), &r); #endif EraseRect(&r); numtype accum = 0.0f; short cx = r.right /2; short cy = r.bottom / 2; long startTime = TickCount(); std::vector accumV(r.right); BitMap line; std::vector bits(((r.right + 31) / 8) & ~0x3); SetRect(&line.bounds, 0,0,r.right,1); line.rowBytes = bits.size(); line.baseAddr = (char*)(&bits[0]); numtype preRandoms[29*31]; for(int i = 0; i < 29*31; i++) { numtype thresh = rand1::get(); thresh = numtype(0.5f) + numtype(0.4f) * (thresh - numtype(0.5f)); preRandoms[i] = thresh; } int randIdx = 0; for(int y = 0; y < r.bottom; y++) { std::fill(bits.begin(),bits.end(), 0); for(int x = 0; x < r.right; x++) { numtype pixel; // cam = (0,0,0) // ray = t * (x-r.right/2, - (y-r.bottom/2), -1) // plane: y = -2 pixel = ray(1,vec3(),vec3(numtype(x-cx)/numtype(cx),-numtype(y-cy)/numtype(cx),-1).normalize()); numtype thresh = preRandoms[randIdx++]; if(randIdx == 29*31) randIdx = 0; accum += pixel; accum += accumV[x]; if(accum >= thresh) accum -= 1; else { //MoveTo(x,y); //Line(0,0); bits[x / 8] |= (0x80 >> (x%8)); } accumV[x] = accum = accum / numtype(2); } Rect r2; SetRect(&r2,0,y,r.right,y+1); #if TARGET_API_MAC_CARBON CopyBits(&line, GetPortBitMapForCopyBits(GetWindowPort(win)), &line.bounds, &r2, srcCopy, NULL); #else CopyBits(&line, &win->portBits, &line.bounds, &r2, srcCopy, NULL); #endif if(Button()) return 0; #if TARGET_API_MAC_CARBON // QDFlushPortBuffer(GetWindowPort(win),NULL); #endif } long endTime = TickCount(); char buf[256]; unsigned char* pstr = (unsigned char*)buf; std::sprintf(buf+1, "pps = %d %ld %ld %ld %ld", (int)( (long)r.right * r.bottom * 60 / (endTime - startTime) ), fixed::nMul, fixed::nIMul, fixed::nDiv, fixed::nSqrt); buf[0] = std::strlen(buf+1); SetRect(&r, 10, 10, 10 + StringWidth(pstr) + 10, 30); PaintRect(&r); PenMode(patXor); FrameRect(&r); MoveTo(15,25); TextMode(srcBic); DrawString(pstr); #if TARGET_API_MAC_CARBON QDFlushPortBuffer(GetWindowPort(win),NULL); #endif while(!Button()) ; FlushEvents(everyEvent, -1); return 0; }