#lang "fb"
#include once "crt.bi"
#include once "fbgfx.bi"
using FB
' not used here so the compiled result should be the same
' #define USE_DOUBLE
#ifndef USE_DOUBLE
type REAL as single
#define rAtan atanf
#define rCeil ceilf
#define rCos cosf
#define rExp expf
#define rFloor floorf
#define rPow powf
#define rSin sinf
#define rSqr sqrtf
#define rTan tanf
#else
type REAL as double
#define rAtan atan_
#define rCos cos_
#define rCeil ceil
#define rExp exp_
#define rFloor floor
#define rPow pow
#define rSin sin_
#define rSqr sqrt
#define rTan tan_
#endif
const as REAL rPI = M_PI
const as REAL rPI2 = M_PI*2
const as REAL rDeg2Rad = rPI/180
const as REAL rRad2Deg = 180/rPI
const as REAL rSQR2 = M_SQRT2
const as REAL rLightAngleRatio = 0.06992681194351041
#define rRadians(x_) ((x_)*rDeg2Rad)
#define rDegree(x_) ((x_)*rRad2Deg)
#define rAbs(x_) iif( (x_)< 0 ,-(x_), (x_) )
#define rMin(x_,y_) iif( (x_)<(y_), (x_), (y_) )
#define rMax(x_,y_) iif( (x_)>(y_), (x_), (y_) )
'#define rMax(x_,y_) iif( (x_)<(y_), (y_), (x_) )
#define rClamp(x_,y_,z_) iif( (x_)<(y_), (y_), iif( (x_)>(z_),(z_),(x_) ) )
#define rLerp(a_,b_,t_) (a_)+((b_)-(a_))*(t_)
'#define rSmoothStep(r_,edge0_,edge1_,x_) (r_) = rMin(rMax(((x_)-(edge0_)) / ((edge1_)-(edge0_)),0),1) : (r_) = (r_)*(r_)* (3-2*(r_))
function rSmoothstep (edge0 as REAL, edge1 as REAL, x as REAL) as REAL
x = rMin(rMax((x - edge0) / (edge1 - edge0),0.0),1.0)
return x*x*(3-2*x)
end function
#define rPow4(a_) ((a_)*(a_)*(a_) )
#define rPow16(a_) rPow4(rPow4((a_)))
type PVector
declare constructor
declare constructor(_x as REAL,_y as REAL,_z as REAL)
as REAL x=any,y=any,z=any
end type
constructor PVector
x=0 :y=0 : z=0
end constructor
constructor PVector(_x as REAL,_y as REAL,_z as REAL)
x=_x : y=_y : z=_z
end constructor
#define v3Cross(a_,b_) PVector( (a_).y*b_.z - (a_).z*(b_).y, (a_).z*(b_).x - (a_).x*(b_).z, (a_).x*(b_).y - (a_).y*(b_).x)
#define v3Dot(a_,b_) (a_).x*(b_).x + (a_).y*(b_).y + (a_).z*(b_).z
#define v3LengthSquared(a_) (a_).x*(a_).x + (a_).y*(a_).y + (a_).z*(a_).z
#define v3Lerp(a_,b_,t_) PVector( rLerp((a_).x, (b_).x , (t_)), rLerp((a_).y, (b_).y , (t_)), rLerp((a_).z, (b_).z, (t_)) )
#define v3Clamp(v_,a_,b_) PVector( rClamp((v_).x,(a_),(b_)), rClamp((v_).y,(a_),(b_)), rClamp((v_).z,(a_),(b_)) )
#define v3Smoothblend(a_,b_,x_) v3Lerp( (a_),(b_),(x_)*(x_)*(3-2*(x_)) )
#macro v3Normalize(v_)
scope
dim as REAL l_ = (v_).x*(v_).x + (v_).y*(v_).y + (v_).z*(v_).z
if l_ then
l_=1/rSqr(l_)
(v_).x*=l_ : (v_).y*=l_ : (v_).z*=l_
end if
end scope
#endmacro
type PImage as PUT_HEADER ptr
dim shared as boolean g_HiQuality = false
dim shared as boolean g_usePost = false
dim shared as boolean g_useClouds = false
dim shared as boolean g_UseExpensiveAO = false
dim shared as boolean g_doCloudShadows = false
dim shared as boolean g_UseRefinement = true
dim shared as boolean g_useGamma = true
dim shared as integer g_PhotoSizeX
dim shared as integer g_PhotoSizeY
dim shared as integer g_NoiseSeed
dim shared as integer g_SceneIdx
dim shared as integer g_CurrentPhotograph
dim shared as integer g_numHeightSamples
dim shared as integer g_numHeightShadowSamples
dim shared as REAL g_FovRatio
dim shared as REAL g_eps = 0.001
dim shared as PVector g_sunDir
type PERLINNOISE
declare constructor
declare sub noiseSeed(byval seed as double)
declare sub noiseDetail(byval lod as integer)
declare sub noiseDetail(byval lod as integer, byval falloff as REAL)
declare function noise1D(byval x as REAL) as REAL
declare function noise2D(byval x as REAL,byval y as REAL) as REAL
declare function noise3D(byval x as REAL,byval y as REAL,byval z as REAL) as REAL
private:
const as REAL SINCOS_PRECISION = 0.5
const as integer SINCOS_LENGTH = (360 / SINCOS_PRECISION)
const as integer PERLIN_YWRAPB = 4
const as integer PERLIN_YWRAP = 1 shl PERLIN_YWRAPB
const as integer PERLIN_ZWRAPB = 8
const as integer PERLIN_ZWRAP = 1 shl PERLIN_ZWRAPB
const as integer PERLIN_SIZE = 4095
const as integer PERLIN_TWOPI = SINCOS_LENGTH
const as integer PERLIN_PI = PERLIN_TWOPI shr 1
declare sub reInit
declare function noise_fsc(byval i as REAL) as REAL
as REAL perlin_cosTable(SINCOS_LENGTH-1)
as REAL perlin(PERLIN_SIZE)
as integer perlin_octaves = 4 ' default to medium smooth
as REAL perlin_amp_falloff = 0.5 ' 50% reduction/octave
end type
constructor PERLINNOISE
for i as integer = 0 to SINCOS_LENGTH-1
perlin_cosTable(i) = rCos(i * rDEG2RAD * SINCOS_PRECISION)
next
reInit
end constructor
sub PERLINNOISE.reInit
for i as integer = 0 to PERLIN_SIZE
perlin(i) = rnd()
next
end sub
function PERLINNOISE.noise_fsc(byval i as REAL) as REAL
dim as integer index = int(i*PERLIN_PI)
return 0.5*(1.0-perlin_cosTable(index mod SINCOS_LENGTH))
end function
sub PERLINNOISE.noiseSeed(byval seed as double)
randomize(0) ' !!!
' randomize(seed) : reInit
end sub
sub PERLINNOISE.noiseDetail(byval lod as integer)
if (lod>0) then perlin_octaves = lod
end sub
sub PERLINNOISE.noiseDetail(byval lod as integer,byval falloff as REAL)
if (lod>0) then perlin_octaves = lod
if (falloff>0) then perlin_amp_falloff = falloff
end sub
function PERLINNOISE.noise1D(byval x as REAL) as REAL
return noise3D(x,0,0)
end function
function PERLINNOISE.noise2D(byval x as REAL,byval y as REAL) as REAL
return noise3D(x,y,0)
end function
function PERLINNOISE.noise3D(byval x as REAL,byval y as REAL,byval z as REAL) as REAL
x=rAbs(x) : y=rAbs(y) : z=rAbs(z)
dim as integer xi=int(x), yi=int(y), zi=int(z)
dim as REAL xf = x - xi, yf = y - yi, zf = z - zi
dim as REAL r, ampl=0.5
for i as integer = 0 to perlin_octaves-1
dim as integer of= xi + (yi shl PERLIN_YWRAPB) + (zi shl PERLIN_ZWRAPB)
dim as REAL rxf=noise_fsc(xf)
dim as REAL ryf=noise_fsc(yf)
dim as REAL n1 = perlin(of and PERLIN_SIZE)
n1 += rxf * (perlin((of+1) and PERLIN_SIZE)-n1)
dim as REAL n2 = perlin((of+PERLIN_YWRAP) and PERLIN_SIZE)
n2 += rxf * (perlin((of+PERLIN_YWRAP+1) and PERLIN_SIZE)-n2)
n1 += ryf * (n2-n1)
of += PERLIN_ZWRAP
n2 = perlin(of and PERLIN_SIZE)
n2 += rxf * (perlin((of+1) and PERLIN_SIZE)-n2)
dim as REAL n3 = perlin((of+PERLIN_YWRAP) and PERLIN_SIZE)
n3 += rxf * (perlin((of+PERLIN_YWRAP+1) and PERLIN_SIZE)-n3)
n2 += ryf * (n3-n2)
n1 += noise_fsc(zf) * (n2-n1)
r += n1 * ampl
ampl *= perlin_amp_falloff
xi shl =1: xf*=2
yi shl =1: yf*=2
zi shl =1: zf*=2
if (xf>=1) then xi+=1 : xf-=1
if (yf>=1) then yi+=1 : yf-=1
if (zf>=1) then zi+=1 : zf-=1
next
return r
end function
dim shared as PERLINNOISE g_Noise
type GammaTable
declare constructor
declare function get(v as REAL) as ulong
private:
as ulong g_tab(2047)
end type
dim shared as GammaTable g_GTable
constructor GammaTable
for i as integer = 0 to 2047
g_tab(i) = rPow(i/2048,1.0/2.2)*255
next
end constructor
function GammaTable.Get(v as REAL) as ulong
dim as integer i = v*2047 ' !!! 2048
if i>2046 then return 255
if i<1 then return 0
return g_tab(i)
end function
' Fractional_BrownianMotion
' Ambient_Occlusion
type GradientNoise
declare constructor (wS as integer=7)
declare destructor
declare function Get(x as REAL,y as REAL) as REAL
declare sub GetWithNormal(x as REAL, y as REAL, r as REAL ptr)
declare function FBM(x as REAL, y as REAL, iOct as integer, p as REAL, l as REAL) as REAL
declare function dFBM(x as REAL, y as REAL, iOct as integer, g as REAL, l as REAL, r as REAL ptr) as REAL
declare function FbmAmbientOcclusion (x as REAL, z as REAL, iOct as integer, g as REAL, l as REAL) as REAL
declare function TurbAmbientOcclusion(x as REAL, z as REAL, iOct as integer, g as REAL, l as REAL) as REAL
declare function Turbulence(x as REAL, y as REAL, iOct as integer, g as REAL, l as REAL) as REAL
declare sub dTurbulence(x as REAL, y as REAL, iOct as integer, g as REAL, l as REAL, r as REAL ptr)
declare sub init(w as integer)
private:
declare function genTab(w as integer) as REAL ptr
as REAL ptr ranGradTable
as integer wMask
as integer wShift
end type
dim shared as GradientNoise g_NoiseGen
destructor GradientNoise
if ranGradTable then deallocate(ranGradTable)
end destructor
function GradientNoise.genTab(w as integer) as REAL ptr
dim as REAL gradTable(15) = { _
1 ,1,-1 ,1,1,-1 ,-1,-1, _
rSqr2,0,-rSqr2,0,0,rSqr2, 0,-rSqr2 }
dim as integer w2=w*w
dim as integer w22=w2*2
dim as REAL ptr rTab = allocate(w22*SizeOf(REAL))
for i as integer=0 to w2-1
dim as integer idx = rnd()*8
if idx>7 then idx=7
rTab[i*2+0] = gradTable(idx*2 )
rTab[i*2+1] = gradTable(idx*2+1)
next
dim as REAL ptr ptab = allocate(w22*4*SizeOf(REAL))
for y as integer=0 to w-1
for x as integer=0 to w-1
dim as integer i = x + y*w
dim as integer j = i
ptab[i*8+0] = rTab[j*2+0]
ptab[i*8+1] = rTab[j*2+1]
j = ((x+1) and wMask) + y*w
ptab[i*8+2] = rTab[j*2+0]
ptab[i*8+3] = rTab[j*2+1]
j = ((x) and wMask) + ((y+1) and wMask)*w
ptab[i*8+4] = rTab[j*2+0]
ptab[i*8+5] = rTab[j*2+1]
j = ((x+1) and wMask) + ((y+1) and wMask)*w
ptab[i*8+6] = rTab[j*2+0]
ptab[i*8+7] = rTab[j*2+1]
next
next
deallocate rTab
return ptab
end function
constructor GradientNoise(wS as integer)
init(ws)
end constructor
sub GradientNoise.init(wS as integer)
if ranGradTable then deallocate ranGradTable
dim as integer w = 1 shl wS
wShift = wS : wMask = w-1
ranGradTable = genTab(w)
end sub
function GradientNoise.Get(x as REAL, y as REAL) as REAL
x = rAbs(x) : y = rAbs(y)
dim as integer i = int(x), j = int(y)
dim as REAL u = x - i
dim as REAL v = y - j
dim as REAL iu = u-1
dim as REAL iv = v-1
i and = wMask
j and = wMask
dim as integer idx = (((j shl wShift) + i) shl 3)
dim as REAL xg0=ranGradTable[idx+0] * u
dim as REAL yg0=ranGradTable[idx+1] * v
dim as REAL xg1=ranGradTable[idx+2] * iu
dim as REAL yg1=ranGradTable[idx+3] * v
dim as REAL xg2=ranGradTable[idx+4] * u
dim as REAL yg2=ranGradTable[idx+5] * iv
dim as REAL xg3=ranGradTable[idx+6] * iu
dim as REAL yg3=ranGradTable[idx+7] * iv
dim as REAL a = xg0+yg0
dim as REAL b = xg1+yg1
dim as REAL c = xg2+yg2
dim as REAL d = xg3+yg3
u = u*u*u*(u*(u*6.0-15.0)+10.0)
v = v*v*v*(v*(v*6.0-15.0)+10.0)
dim as REAL k0 = a
dim as REAL k1 = b - a
dim as REAL k2 = c - a
dim as REAL k4 = a - b - c + d
return k0 + k1*u + k2*v + k4*u*v
end function
sub GradientNoise.GetWithNormal(x as REAL, y as REAL,r as REAL ptr)
x = abs(x) : y = abs(y)
dim as integer i = int(x), j = int(y)
dim as REAL u = x - i
dim as REAL v = y - j
dim as REAL iu = u - 1
dim as REAL iv = v - 1
i and = wMask : j and = wMask
dim as integer idx = (((j shl wShift) + i) shl 3)
dim as REAL xga=ranGradTable[idx+0]
dim as REAL yga=ranGradTable[idx+1]
dim as REAL xgb=ranGradTable[idx+2]
dim as REAL ygb=ranGradTable[idx+3]
dim as REAL xgc=ranGradTable[idx+4]
dim as REAL ygc=ranGradTable[idx+5]
dim as REAL xgd=ranGradTable[idx+6]
dim as REAL ygd=ranGradTable[idx+7]
dim as REAL pa = xga * u +yga * v
dim as REAL pb = xgb * iu+ygb * v
dim as REAL pc = xgc * u +ygc * iv
dim as REAL pd = xgd * iu+ygd * iv
dim as REAL du = 30.0*u*u*(u*(u-2.0)+1.0)
dim as REAL dv = 30.0*v*v*(v*(v-2.0)+1.0)
u = u*u*u*(u*(u*6.0-15.0)+10.0)
v = v*v*v*(v*(v*6.0-15.0)+10.0)
dim as REAL k0 = pa
dim as REAL k1 = pb - pa
dim as REAL k2 = pc - pa
dim as REAL k4 = pa - pb - pc + pd
dim as REAL dk1x = xgb - xga
dim as REAL dk1y = ygb - yga
dim as REAL dk2x = xgc - xga
dim as REAL dk2y = ygc - yga
dim as REAL dk4x = xga - xgb - xgc + xgd
dim as REAL dk4y = yga - ygb - ygc + ygd
dim as REAL dx = xga + u*dk1x + k1*du + dk2x*v + dk4x*u*v + k4*v*du
dim as REAL dy = yga + u*dk1y + k2*dv + dk2y*v + k4*u*dv + dk4y*u*v
r[0] = k0 + k1*u + k2*v + k4*u*v
r[1] = dx
r[2] = dy
end sub
function GradientNoise.FBM(x as REAL, y as REAL, ioct as integer, p as REAL, l as REAL) as REAL
dim as REAL s, amp=1
for i as integer=0 to ioct-1
s+=amp*this.Get(x,y) : x*=l : y*=l : amp *= p
next
return s
end function
function GradientNoise.dFBM(x as REAL, y as REAL, ioct as integer, g as REAL, l as REAL, r as REAL ptr) as REAL
dim as REAL s, amp=1, t(2)
r[0]=0 : r[1]=0 : r[2]=0
for i as integer=0 to ioct-1
this.GetWithNormal( x,y,@t(0))
r[0]+= amp*t(0) : r[1]+= t(1) : r[2]+= t(2) : x*=l : y*=l : amp *= g
next
return s
end function
function GradientNoise.FbmAmbientOcclusion(x as REAL, z as REAL, ioct as integer, g as REAL, l as REAL) as REAL
dim as REAL r = 1, am= 1
x*=l : z*=l
for i as integer=1 to ioct-1
dim as REAL localAmbientOcclusion = am*(this.Get(x,z)*.5+.5)+.45
dim as REAL n = rClamp( localAmbientOcclusion,0,1)
n = rPow4(n) : r*=n : x*=l : z*=l : am*=0.96
next
r = rSqr(r)
return rClamp(r,0,1)
end function
function GradientNoise.TurbAmbientOcclusion(x as REAL, z as REAL, ioct as integer, g as REAL, l as REAL) as REAL
dim as REAL r =1, am=1
x*=l : z*=l
for i as integer =1 to ioct-1
dim as REAL tmp = this.Get(x,z)
tmp=rAbs(tmp)
dim as REAL n = rMin(tmp +.6,1)
r*=n : x*=l : z*=l
next
r = rSqr(r)
return rClamp(r,0,1)
end function
function GradientNoise.Turbulence(x as REAL, y as REAL, ioct as integer, g as REAL, l as REAL) as REAL
dim as REAL s
dim as REAL amp=1
dim as REAL sc
for i as integer=0 to ioct-1
dim as REAL tmp=this.Get(x,y):tmp=rAbs(tmp)
s+= amp*tmp
sc+=amp
x*=l
y*=l
amp*=g
next
return s/sc
end function
sub GradientNoise.dTurbulence(x as REAL, y as REAL, ioct as integer, g as REAL, l as REAL, r as REAL ptr)
dim as REAL s
dim as REAL amp=1
dim as REAL sc
dim as REAL t(2)
r[0]=0 : r[1]=0 : r[2]=0
for i as integer=0 to ioct-1
this.GetWithNormal(x,y,@t(0))
if (t(0) < 0) then
t(0)=-t(0) : t(1)=-t(1) : t(2)=-t(2)
end if
r[0]+= amp*t(0) : r[1]+= t(1) : r[2]+= t(2)
sc+=amp : x*=l : y*=l : amp*=g
next
r[0]/=sc
end sub
function CalcNoiseOctaves(t as REAL, s as REAL, octSize as REAL, lac as REAL) as integer
dim as REAL pixSize = t*g_FovRatio*octSize
dim as REAL si = 1/s
dim as integer numOctaves
while( si > pixSize)
si*=lac : numOctaves+=1
if (numOctaves >12) then return numOctaves
wend
return numOctaves
end function
function detailNoiseLod(x as REAL, z as REAL, t as REAL) as REAL
return g_NoiseGen.FBM( x*4.,z*4.0,CalcNoiseOctaves(t,4.0,2.0,1/2.189),0.9,2.189)
end function
type Ray
declare constructor(byref o as const PVector, byref d as const PVector)
as PVector origin
as PVector direction
end type
constructor Ray(byref o as const PVector, byref d as const PVector)
origin=o
direction=d
end constructor
type PolarCamera
declare constructor
declare constructor(cpos as PVector, angX as REAL, angY as REAL)
declare sub update(dy as REAL, dz as REAL)
declare sub forward(v as REAL)
declare sub side(v as REAL)
as PVector m_camPos
as REAL m_CamAngleX
as REAL m_CamAngleY
as PVector m_camDir
end type
dim shared as PolarCamera g_camControl
constructor PolarCamera
m_camPos = PVector(0.0,2.4,0.0)
m_camDir = PVector(0.0,0,1.0)
m_CamAngleY = -0.3
update(0,0)
end constructor
constructor PolarCamera(cpos as PVector, angX as REAL, angY as REAL)
m_camPos = cpos
m_CamAngleX = angX
m_CamAngleY = angY
update(0,0)
end constructor
sub PolarCamera.update(dy as REAL, dz as REAL)
m_CamAngleX += dy
m_CamAngleY += -dz
m_camDir = PVector(rSin(m_CamAngleX)*rCos(m_CamAngleY), _
rSin(m_CamAngleY), _
rCos(m_CamAngleX)*rCos(m_CamAngleY))
end sub
sub PolarCamera.forward(v as REAL)
m_camPos.x+= m_camDir.x * v
m_camPos.y+= m_camDir.y * v
m_camPos.z+= m_camDir.z * v
end sub
sub PolarCamera.side(v as REAL)
dim as PVector f = v3Cross(m_camDir,PVector(0,1,0))
v3Normalize(f)
v*=0.25
m_camPos.x+= f.x*v
m_camPos.y+= f.y*v
m_camPos.z+= f.z*v
end sub
type Camera
declare constructor (eye as PVector, up as PVector, gaze as PVector, resX as REAL, resY as REAL, d as REAL)
declare function generateRay(screenCoordX as REAL, screenCoordY as REAL) as RAY
private:
as PVector position
as PVector lower_left_corner
as PVector uvw_u
as PVector uvw_v
as PVector uvw_w
end type
constructor Camera(eye as PVector, up as PVector, gaze as PVector, resX as REAL, resY as REAL, d as REAL)
position = eye
uvw_v = up
uvw_w = gaze
uvw_u = v3Cross(uvw_v,uvw_w)
v3Normalize(uvw_u)
uvw_v = v3Cross(uvw_w,uvw_u)
v3Normalize(uvw_v)
v3Normalize(uvw_w)
dim as REAL sX = -resX/2
dim as REAL sY = -resY/2
dim as REAL eX = resX/2
dim as REAL eY = resY/2
dim as PVector t1=uvw_w
dim as PVector t2=uvw_v
t1.x*=d
t1.y*=d
t1.z*=d
t2.x*=sY
t2.y*=sY
t2.z*=sY
lower_left_corner = uvw_u
lower_left_corner.x*=sX
lower_left_corner.y*=sX
lower_left_corner.z*=sX
lower_left_corner.x+=t1.x
lower_left_corner.y+=t1.y
lower_left_corner.z+=t1.z
lower_left_corner.x+=t2.x
lower_left_corner.y+=t2.y
lower_left_corner.z+=t2.z
end constructor
function Camera.generateRay(screenCoordX as REAL, screenCoordY as REAL) as RAY
dim as PVector direction=PVector(lower_left_corner.x + uvw_v.x * screenCoordY + uvw_u.x * screenCoordX, _
lower_left_corner.y + uvw_v.y * screenCoordY + uvw_u.y * screenCoordX, _
lower_left_corner.z + uvw_v.z * screenCoordY + uvw_u.z * screenCoordX)
v3Normalize(direction)
return Ray(position, direction)
end function
' called by CreateStrip()
sub SetFovRatio(fov as REAL, w as integer)
g_FovRatio = rTan(rRadians(fov)/2) / (w/2)
end sub
type Material
declare constructor (surfcol as PVector, bs as REAL)
declare constructor (surfcol as PVector, bs as REAL, btile as REAL, rimAmount as REAL, specAmount as REAL)
as REAL bs=any
as REAL btile=any
as REAL rimAmount=any
as REAL specAmount=any
as PVector surfcol=any
end type
constructor Material(_surfcol as PVector, _bs as REAL)
bs = _bs
btile = 32.0
rimAmount = 0
specAmount = 0
surfcol = _surfcol
end constructor
constructor Material(_surfcol as PVector, _bs as REAL, _btile as REAL, _rimAmount as REAL, _specAmount as REAL)
bs = _bs
btile = _btile
rimAmount = _rimAmount
specAmount = _specAmount
surfcol = _surfcol
end constructor
function mLerp(a as Material, b as Material, t as REAL) as Material
'dim as PVector surfcol=
return Material(v3Lerp(a.surfcol ,b.surfcol,t), _
rLerp(a.bs , b.bs ,t), _
rLerp(a.btile , b.btile ,t), _
rLerp(a.rimAmount , b.rimAmount ,t), _
rLerp(a.specAmount, b.specAmount,t))
end function
' color ,bs ,btile, rim, spec
dim shared as Material g_MatLightGrass = Material(PVector(0.75,0.9 ,0.3 ), 0.01 , 32*4, 0.5, 0.1 )
dim shared as Material g_MatYellowGrass = Material(PVector(0.75,0.8 ,0.3 ), 0.01 , 32*4, 0.2, 0.01)
dim shared as Material g_MatDarkGrass = Material(PVector(0.45,0.6 ,0.2 ), 0.01 , 32*4, 0.3, 0.1 )
dim shared as Material g_MatDarkTrees = Material(PVector(0.35,0.5 ,0.3 ), 0.2 , 32*4, 0.0, 0.01)
dim shared as Material g_MatSand = Material(PVector(0.7 ,0.7 ,0.3 ), 0.0 , 32*5, 0.0, 0.1 )
dim shared as Material g_MatDarkSand = Material(PVector(0.6 ,0.6 ,0.4 ), 0.0 , 32*5, 0.0, 0.1 )
dim shared as Material g_MatRock = Material(PVector(0.35,0.35,0.35), 0.0 , 32*4, 0.0, 0.3 )
dim shared as Material g_MatDarkRock = Material(PVector(0.15,0.15,0.15), 0.1 , 32 , 0.0, 0.3 )
dim shared as Material g_MatDirt = Material(PVector(0.55,0.55,0.55), 0.01 , 32*4, 0.0, 0.1 )
dim shared as Material g_MatDarkDirt = Material(PVector(0.45,0.4 ,0.2 ), 0.01 , 32*3, 0.0, 0.01)
dim shared as Material g_MatSnow = Material(PVector(1.0 ,1.0 ,1.0 ), 0.005, 32*4, 0.0, 0.6 )
dim shared as Material g_MatBlank = Material(PVector(0.5 ,0.5 ,0.5 ), 0.1 , 16 , 0.0, 0.0 )
type HeightField extends Object
declare abstract function getY (x as REAL, z as REAL, t as REAL) as REAL
declare abstract function getFinalY(x as REAL, z as REAL, t as REAL) as REAL
declare abstract function getAmbOcc(x as REAL, y as REAL, z as REAL, t as REAL) as REAL
declare abstract function GetMaterial(p as PVector, sn as PVector, t as REAL) as Material
declare abstract function getWaterY() as REAL
end type
type QualitySettings
declare constructor
as boolean useSubSampling
as REAL shadowRayDeltaRatio
as REAL shadowRayStartRatio
as REAL rayDeltaRatio
as boolean useReflection
as REAL maxHgt
declare sub SetFast
end type
constructor QualitySettings
useSubSampling = false
shadowRayDeltaRatio = 0.025
shadowRayStartRatio = 0.008
rayDeltaRatio = 0.05
useReflection = true
maxHgt = 1.5
end constructor
sub QualitySettings.SetFast
shadowRayDeltaRatio*= 4.0
rayDeltaRatio *= 4.0
useReflection = true
maxHgt = 1.0
shadowRayStartRatio*= 4.0
end sub
type rayRes
as REAL curT
as REAL lastT
end type
function interp(t as REAL, dt as REAL, height as REAL, lastHeight as REAL, pointY as REAL, lastPointY as REAL) as rayRes
dim as rayRes rr
rr.lastT = t - dt
rr.curT = rr.lastT + dt * (lastHeight-lastPointY) / ((pointY-lastPointY-height)+lastHeight)
return rr
end function
function castRay(qs as QualitySettings, _
hgf as HeightField, _
rayOrigin as PVector, _
rayDir as PVector, _
minT as REAL, _
maxT as REAL, _
erratio as REAL) as rayRes
dim as REAL dt = rMax(qs.rayDeltaRatio*mint, 0.001)
dim as REAL lh
dim as REAL ly
dim as PVector p
dim as PVector rp
dim as REAL rErr = erratio
dim as REAL t = minT
erratio = erratio *qs.rayDeltaRatio
while (t < maxT)
p.x=rayOrigin.x + rayDir.x*t
p.y=rayOrigin.y + rayDir.y*t
p.z=rayOrigin.z + rayDir.z*t
dim as REAL h = hgf.getY(p.x, p.z, t*rErr)
g_numHeightSamples+=1
if (p.y < h) then
if ( g_UseRefinement ) then
dim as REAL dt2 = dt*.25
t = t - dt*.75
for i as integer=0 to 2
rp.x=rayOrigin.x + rayDir.x*t
rp.y=rayOrigin.y + rayDir.y*t
rp.z=rayOrigin.z + rayDir.z*t
dim as REAL h0 = hgf.getY(rp.x, rp.z, t)
if (rp.y1) then return 0
sratio *=sratioM
dt = sratio*t
lh = h
ly = p.y
t += dt
wend
return 1 - rClamp(sdist,0,1)
end function
function Lighting.CalcAmbientOcclusion(hgf as HeightField, _
p as PVector, _
nor as PVector, _
sampleRadius as REAL, _
numSamples as integer, _
horizonSteps as integer, _
t as REAL) as REAL
dim as PVector n
dim as PVector l
dim as REAL tAmbientOcclusion
dim as REAL off = rnd()
for i as integer = 0 to numSamples-1
dim as REAL ang = 2.0*rPI*((i+off)/numSamples)
n.x = rCos(ang)
n.y = 0
n.z = rSin(ang)
dim as REAL d = sampleRadius
dim as REAL dm = sampleRadius
dim as PVector tang = nor
dim as REAL dot = -v3Dot(nor,n)
tang.x*=dot
tang.y*=dot
tang.z*=dot
tang.x+=n.x
tang.y+=n.y
tang.z+=n.z
dim as REAL mgrad =-1000.0
dim as REAL mag=tang.x*tang.x+tang.z*tang.z
if mag then mag=rSqr(mag) else mag=0.0001 ' !!! div/0
dim as REAL tanGrad = tang.y/mag
dim as REAL tangentAngle = rSin(rAtan(tanGrad))
for j as integer = 0 to horizonSteps-1
dim as REAL hl=hgf.getY(p.x+n.x*d,p.z+n.z*d, t)
dim as REAL grad = (hl-p.y)/d
grad = rMax(tanGrad, grad)
mgrad = rMax(grad ,mgrad)
d += dm
dm*=3
next
dim as REAL horizonAngle = rSin(rAtan(mgrad))
tAmbientOcclusion +=rClamp(horizonAngle-tangentAngle,0,1)
next
dim as REAL AmbientOcclusion = 1 - rClamp(tAmbientOcclusion/(numSamples),0,1)
return AmbientOcclusion*AmbientOcclusion
end function
function Lighting.getShading(qs as QualitySettings, _
hgf as HeightField, _
p as PVector, _
n as PVector, _
I as PVector, _
mat as Material, _
t as REAL, _
depth as integer, _
localAmbientOcclusion as REAL) as PVector
dim as PVector amb = ambCol
dim as REAL normFactor = n.y*.5 +.5
dim as REAL abFactor
if ( g_UseExpensiveAO) then
abFactor = CalcAmbientOcclusion( hgf, p, n,0.01, 6,4, t)
else
abFactor = hgf.getAmbOcc(p.x, p.y, p.z ,t )
end if
amb.x*=abFactor
amb.y*=abFactor
amb.z*=abFactor
dim as REAL shadow
dim as REAL nd = rMax( v3LengthSquared(sunDir),0)
if ( nd>0) then shadow = castShadowRay( qs, hgf, p, sunDir, t*qs.shadowRayStartRatio, t )
dim as REAL bounceStrength = 1
dim as PVector bounceCol = mat.surfcol
bounceCol.x*=subCol.x
bounceCol.y*=subCol.y
bounceCol.z*=subCol.z
dim as REAL bshadFactor = 1
dim as REAL tmp = bshadFactor*bounceStrength
bounceCol.x*=tmp
bounceCol.y*=tmp
bounceCol.z*=tmp
amb = v3Lerp(bounceCol, amb, normFactor)
dim as PVector sunLight = subCol
tmp=nd* shadow
sunLight.x*=tmp
sunLight.y*=tmp
sunLight.z*=tmp
sunLight.x+=amb.x
sunLight.y+=amb.y
sunLight.z+=amb.z
dim as PVector hangle=I
hangle.x*=-1
hangle.y*=-1
hangle.z*=-1
hangle.x+=sunDir.x
hangle.y+=sunDir.y
hangle.z+=sunDir.z
v3Normalize(hangle)
dim as REAL spec = rPow16(rMax(v3Dot(hangle,n),0)) * mat.specAmount * shadow
dim as PVector speccol = subCol
speccol.x*=spec
speccol.y*=spec
speccol.z*=spec
dim as REAL edgelight = rPow4(1.- rMax(-v3Dot(I,n),0))
edgelight*=mat.rimAmount * shadow * nd
dim as PVector edgecol = subCol
edgecol.x*=edgelight
edgecol.y*=edgelight
edgecol.z*=edgelight
sunLight.x*=mat.surfcol.x
sunLight.y*=mat.surfcol.y
sunLight.z*=mat.surfcol.z
sunLight.x+=speccol.x
sunLight.y+=speccol.y
sunLight.z+=speccol.z
sunLight.x+=edgecol.x
sunLight.y+=edgecol.y
sunLight.z+=edgecol.z
return sunLight
end function
' called by renderImage, reflection
function skyColor(lgt as Lighting, rayDir as PVector) as PVector
dim as REAL blendv = rayDir.y+0.03
blendv = rMax(blendv,0.000001)
if blendv then blendv = rSqr(blendv)
dim as REAL tmp = blendv*2
dim as REAL blendv2 = rClamp(tmp-1,0,1)
blendv = rClamp(tmp,0,1)
dim as PVector sky = v3Lerp( lgt.skybot, lgt.skymid,blendv)
sky = v3Lerp( sky, lgt.skytop,blendv2)
dim as PVector sun = lgt.subCol
tmp = v3Dot(rayDir,lgt.sunDir)
dim as REAL sf = rPow( rMax(tmp,0), 64)
sun.x*=sf
sun.y*=sf
sun.z*=sf
sky.x+=sun.x
sky.y+=sun.y
sky.z+=sun.z
return sky
end function
' called by renderImage, reflection
function applyClouds(colIn as PVector, _
lgt as Lighting, _
rayOrigin as PVector, _
rayDir as PVector, _
maxT as REAL, _
blnLowQuality as boolean) as PVector
const as REAL cloudsSize = 0.7
const as REAL cloudHeight = 4.5
const as REAL cloudEnd = cloudHeight-cloudsSize
const as REAL slabSize = 1/(cloudHeight-cloudEnd)
const as REAL cloudSharpness = 0.75
if (lgt.useClouds=false) then return colIn
dim as integer numSteps = iif(blnLowQuality=true, 4,12)
dim as REAL cloudCover = iif(blnLowQuality=true,.6,.7)
dim as REAL invCloudCover = 1./cloudCover
dim as REAL t0 = rMax((cloudHeight-rayOrigin.y)/rayDir.y,0)
dim as REAL t1 = (cloudEnd -rayOrigin.y)/rayDir.y
dim as REAL tMin = rMax(rMin(t0,t1),0)
dim as REAL tMax = rMax(t0,t1)
if ( tMin > maxT) or (tMax <0.0) then return colIn
dim as PVector np
dim as REAL ccv
dim as REAL tmp
dim as PVector cCol = PVector(1,1,1)
dim as REAL dt = (tMin-tMax)/numSteps
tMin = rMax(rMin(maxT,tmin),0)
tMax = rMax(rMin(maxT,tMax),0)
dim as integer numShadSamples
dim as PVector sunRay
dim as PVector snp
snp.x=rayOrigin.x + rayDir.x*tMin
snp.y=rayOrigin.y + rayDir.y*tMin
snp.z=rayOrigin.z + rayDir.z*tMin
sunRay.x=lgt.sunDir.x*10000.0
sunRay.y=lgt.sunDir.y*10000.0
sunRay.z=lgt.sunDir.z*10000.0
sunRay.x-=snp.x
sunRay.y-=snp.y
sunRay.z-=snp.z
v3Normalize(sunRay)
sunRay.x*=0.35
sunRay.y*=0.5
sunRay.z*=0.35
sunRay.x*=dt
sunRay.y*=dt
sunRay.z*=dt
dim as PVector nd = PVector(rayDir.x*0.35 , rayDir.y*.5,rayDir.z*0.35)
dim as PVector nsp = PVector(rayOrigin.x*0.35+13.5, rayOrigin.y*.5,rayOrigin.z*0.35+5.7)
g_Noise.noiseDetail(7,.7)
dim as REAL t = tMax+dt*.5
while (t>tMin)
np=nd
np.x*=t : np.y*=t : np.z*=t
np.x+=nsp.x : np.y+=nsp.y : np.z+=nsp.z
dim as REAL cv = rMax( g_noise.noise3D(np.x, np.y, np.z)-cloudCover, 0.0)*invCloudCover
cv = rMin(cv,1)
tmp = 1-cv
cCol.x*=tmp : cCol.y*=tmp : cCol.z*=tmp
dim as REAL nc
if ( g_doCloudShadows) then
dim as REAL shad
dim as PVector ss = np
for i as integer = 0 to numShadSamples-1
ss.x+=sunRay.x : ss.y+=sunRay.y : ss.z+=sunRay.z
tmp = g_noise.noise3D(ss.x, ss.y, ss.z)-cloudCover
shad += rMax(tmp,0)*invCloudCover*.5
next
tmp=1-shad
nc = rClamp(tmp,0,1)
numShadSamples+=1
else
dim as REAL py = rayOrigin.y + rayDir.y*t
nc = (py-cloudEnd)*slabSize
end if
nc = nc*0.9+.1
nc*=cv
cCol.x+=nc : cCol.y+=nc : cCol.z+=nc
ccv = (1.-cv)*ccv + cv
t+= dt
wend
cCol.x*=1.3 : cCol.y*=1.3 : cCol.z*=1.3
tmp = rPow( ccv, cloudSharpness)
dim as REAL cd = rMin(tmp,1)
dim as PVector colCol = cCol
dim as PVector sunTint = PVector(1,1,1)
sunTint.x+=lgt.subCol.x : sunTint.y+=lgt.subCol.y : sunTint.z+=lgt.subCol.z
sunTint.x*=0.5 : sunTint.y*=0.5 : sunTint.z*=0.5
colCol.x*=sunTint.x : colCol.y*=sunTint.y : colCol.z*=sunTint.z
cd *= rExp(-tmin * 1/50)
return v3Lerp( colIn, colCol,cd)
end function
declare function terrainColor(qs as QualitySettings, _
hgf as HeightField, _
_ray as Ray, _
lgt as Lighting, _
t as REAL, _
depth as integer) as PVector
' called by GetWaterSurface
function reflection(qs as QualitySettings, _
hgf as HeightField, _
lgt as Lighting, _
d as PVector, _
p as PVector, _
n as PVector, _
oldt as REAL, _
errFactor as REAL) as PVector
dim as PVector RayDir=any
dim as REAL r2 = 2 * v3Dot(d,n)
r2=-r2
RayDir.x=d.x + n.x * r2
RayDir.y=d.y + n.y * r2
RayDir.z=d.z + n.z * r2
v3Normalize(RayDir)
dim as PVector RayOrigin = p
dim as Ray r = Ray(RayOrigin,RayDir)
dim as PVector c
dim as REAL oldt2=oldt*2 : oldt2=rMax(oldt2,1)
dim as rayRes t = castRay( qs,hgf, RayOrigin, RayDir, 0.05,30-oldt,oldt2)
if( t.curT>0.0 ) then
c= terrainColor( qs, hgf, r, lgt, t.curT,1 )
else
c= skyColor( lgt, r.direction)
end if
c = applyClouds( c, lgt, r.origin, r.direction, iif(t.curT>0.0, t.curT,1000.0), true )
return c
end function
' called by GetWaterSurface
function fresnel(i as PVector, n as PVector) as REAL
dim as REAL f = v3Dot(i,n)
f = rAbs(f)
f=rPow(1-f,5)
dim as REAL r=0.05
return r+(1-r)*f
end function
' called by terrainColor
function GetWaterSurface(qs as QualitySettings, _
hgf as HeightField, _
r as Ray, _
lgt as Lighting, _
t as REAL, _
depth as integer, _
fogLength as REAL, _
p as PVector, _
n as PVector) as PVector
dim as PVector m
dim as REAL wfd = 1/0.05
dim as REAL fm = rExp(-fogLength*wfd)
if ( fm >0.001) then
dim as Material mat = hgf.getMaterial( p, n,t )
m = lgt.getShading( qs, hgf, p, n, r.direction, mat, t, depth,0 )
m = v3Lerp(PVector(0,0.12,0.05), m, fm)
else
m = PVector(0,0.1,0.05)
end if
dim as PVector np = any
fogLength-=fogLength
np.x=p.x + r.direction.x*fogLength
np.y=p.y + r.direction.y*fogLength
np.z=p.z + r.direction.z*fogLength
dim as REAL wd = hgf.getWaterY() - hgf.getY( np.x, np.z,t)
dim as integer ioct = CalcNoiseOctaves( t, 32., 2.,1./2.189)
dim as REAL d(2)
g_NoiseGen.dFBM( np.x*32,np.z*32,ioct,0.5,2.189,@d(0))
dim as REAL waterBumpStrength = 12
dim as PVector waterN = PVector( d(1), 32*waterBumpStrength, d(2))
v3Normalize(waterN)
dim as PVector ref = reflection( qs, hgf, lgt, r.direction, np, waterN, t , wd)
m = v3Lerp( m, ref, fresnel( r.direction, waterN ))
return m
end function
' called by terrainColor()
function applyFog(lgt as Lighting, _
d as PVector, _
c as PVector, _
t as REAL) as PVector
dim as REAL b =1./35
dim as REAL tmp = t-10
t = rMax(tmp,0)
dim as REAL fogAmount = rExp( -t*b )
tmp=v3Dot(d,lgt.sunDir)
dim as REAL sunAmount = rPow(rMax(tmp,0.0), 6)
dim as PVector fogColor = v3Lerp( lgt.fogcol, lgt.subCol, sunAmount*.75)
return v3Lerp( fogColor, c, fogAmount )
end function
'called by terrainColor()
sub getSurfaceCDS(ps as REAL ptr, hgf as HeightField, p as PVector, t as REAL)
ps[0]=hgf.getFinalY(p.x-g_eps,p.z ,t)
ps[1]=hgf.getFinalY(p.x+g_eps,p.z ,t)
ps[2]=hgf.getFinalY(p.x ,p.z-g_eps,t)
ps[3]=hgf.getFinalY(p.x ,p.z+g_eps,t)
end sub
' called by terrainColor()
function genNormal(h as REAL ptr) as PVector
dim as PVector n = PVector( h[0]-h[1], 2.0*g_eps, h[2]-h[3] )
v3Normalize(n)
return n
end function
' called by renderImage , reflection
function terrainColor(qs as QualitySettings, _
hgf as HeightField, _
_ray as Ray, _
lgt as Lighting, _
t as REAL, _
depth as integer) as PVector
dim as REAL surfhgts(3)
dim as PVector p,m,sn,f
p.x = _ray.origin.x + _ray.direction.x*t
p.y = _ray.origin.y + _ray.direction.y*t
p.z = _ray.origin.z + _ray.direction.z*t
getSurfaceCDS(@surfhgts(0), hgf, p, t )
sn = genNormal(@surfhgts(0))
if cbool(p.y < hgf.getWaterY()) andalso cbool(depth=0) andalso (qs.useReflection=true) then
dim as REAL fogLength = -(hgf.getWaterY()-p.y)/_ray.direction.y
m = GetWaterSurface( qs, hgf, _ray, lgt, t, depth, fogLength,p,sn )
t -= fogLength
else
dim as Material mat = hgf.getMaterial( p, sn,t)
m = lgt.getShading( qs, hgf, p, sn, _ray.direction, mat, t, depth, 0)
end if
f = applyFog( lgt, _ray.direction, m, t )
return f
end function
' called by renderImage
function postEffect(u as REAL, v as REAL, c as PVector) as PVector
u=(u-.5)*0.8 :v=(v-.5)*0.8
dim as REAL tmp = u*u + v*v
if tmp then tmp = rSqr(tmp)
tmp = 1.-tmp
dim as REAL vignetting = rMax(tmp,0)*1.2
c.x*=vignetting : c.y*=vignetting : c.z*=vignetting
return c
end function
' called by CreateStrip
sub renderImage(qs as QualitySettings, _
cam as Camera, _
img as PImage, _
lgt as Lighting, _
hgf as HeightField, _
usePost as boolean, _
useGamma as boolean)
static as REAL sampleoffsetx4(3) => {0.85,0.3, 0.15,0.6 }
static as REAL sampleoffsety4(3) => {0.15,0.3, 0.6 ,0.85}
static as REAL sampleoffsetx1=0.5
static as REAL sampleoffsety1=0.5
dim as ulong r,g,b
dim as integer xres,yres
dim as boolean blnLock
if img then
imageinfo img,xres,yres
else
blnLock = true
screeninfo xres,yres
end if
g_numHeightSamples = 0
g_numHeightShadowSamples =0
dim as PVector c,finalC
dim as integer numSamples = iif(qs.useSubSampling=true,4,1)
dim as REAL subSampleScale = 1./numSamples
dim as REAL ptr pSampleoffsetx = iif(qs.useSubSampling=true,@sampleoffsetx4(0),@sampleoffsetx1)
dim as REAL ptr pSampleoffsety = iif(qs.useSubSampling=true,@sampleoffsety4(0),@sampleoffsety1)
const as REAL MAX_T = 70
for x as integer = 0 to xres-1
dim as REAL startT = 0.01
' render a y span
dim as integer yImg = yres-1
if blnLock then screenlock
for y as integer = 0 to yres-1
finalC.x=0
finalC.y=0
finalC.z=0
for k as integer = 0 to numSamples-1
dim as Ray r = cam.generateRay( x+pSampleoffsetx[k], y+pSampleoffsety[k])
dim as rayRes rr
dim as integer flag=0
if (startT<>-1) then
flag=1
rr = castRay( qs, hgf, r.origin, r.direction,startT,MAX_T, 1)
end if
if ( flag=1) andalso (rr.curT>0.0) then
c = terrainColor( qs,hgf, r, lgt, rr.curT ,0)
startT = rr.lastT
else
c = skyColor( lgt, r.direction)
startT=-1
end if
c = applyClouds(c, lgt, r.origin, r.direction, iif((flag=1) andalso (rr.curT>0),rr.curT,50.0), false)
finalC.x+=c.x
finalC.y+=c.y
finalC.z+=c.z
next
if numSamples>1 then
c.x = finalC.x*subSampleScale
c.y = finalC.y*subSampleScale
c.z = finalC.z*subSampleScale
else
c=finalC
end if
'if (usePost) then c = postEffect(x/xres, y/yres,c)
'if (useGamma ) then
' r=g_GTable.Get(c.x) : g=g_GTable.Get(c.y) : b=g_GTable.Get(c.z)
'else
c=v3Clamp(c,0,1) : r=c.x*255 : g=c.y*255 : b=c.z*255
'end if
pset img,(x,yImg),RGB(r,g,b)
yImg-=1
next
if blnLock then screenunlock : sleep 1
if asc(inkey())=27 then end
next
'windowtitle "nSamples: " & g_numHeightSamples & " nShadowSamples: " & g_numHeightShadowSamples
end sub
' caled by Draw()
sub CreateStrip(sc as PImage)
dim as integer w,h
if sc then
ImageInfo sc,w,h
else
screeninfo w,h
end if
dim as Camera cam = Camera(g_camControl.m_camPos, PVector(0,1,0), g_camControl.m_camDir, w, h, w)
SetFovRatio( 45, w)
dim as Lighting lgt = Lighting( g_sunDir, rClamp(g_sunDir.y*4,0,1), g_useClouds)
dim as QualitySettings qs
dim as integer screenw
screeninfo screenw
if (g_HiQuality=true) andalso cbool(screenw=w) then
qs.useSubSampling = true
else
qs.useSubSampling = false
end if
if (wset()
images(i)=imagecreate(g_PhotoSizeX,g_PhotoSizeY)
windowtitle "render photo: " & *g_photographs(i)->Title & " [ESC]=abort ..."
CreateStrip images(i)
'ScaledImageToScreen images(i)
put (x*g_PhotoSizeX,y*g_PhotoSizeY),images(i),PSET
if asc(inkey())=27 then end
i+=1
next
next
end sub
sub ShowPhotos
dim as integer i
for y as integer =0 to 2
for x as integer =0 to 2
put (x*g_PhotoSizeX, y*g_PhotoSizeY),images(i),PSET
i+=1
next
next
end sub
function SelectScene as integer
dim as integer mx,my,mb,n=-1
dim as boolean quit
while quit=false
if getMouse(mx,my,,mb)=0 then
dim as integer row = my \ g_PhotoSizeY
dim as integer col = mx \ g_PhotoSizeX
n=row*3+col
end if
if n>=0 andalso n0 then
quit=true
else
windowtitle "[ESC]=abort click to render scene: " & *g_photographs(n)->Title
end if
else
windowtitle "[ESC]=abort select a photo"
end if
if asc(inkey())=27 then end
sleep 10
wend
return n
end function
'
' main
'
chdir(exepath())
setup()
dim as integer SelectedPhoto = SelectScene()
init()
windowtitle "[ESC] abort render scene: " & *g_photographs(SelectedPhoto)->Title & " please wait ..."
g_HiQuality = true
g_photographs(SelectedPhoto)->set()
CreateStrip NULL
windowtitle "that's all for now :-)"
sleep