mirror of
https://github.com/OpenTrespasser/JurassicParkTrespasser.git
synced 2024-12-18 14:41:56 +00:00
464 lines
17 KiB
Plaintext
464 lines
17 KiB
Plaintext
-- **********************************************************************************************
|
|
-- *
|
|
-- * Copyright © DreamWorks Interactive, 1997
|
|
-- *
|
|
-- * Contents:
|
|
-- * implementation of caustics.ms
|
|
-- *
|
|
-- * Bugs:
|
|
-- *
|
|
-- * To do:
|
|
-- *
|
|
-- * Notes:
|
|
-- * Caustics is a simple script raytracer that generates bitmaps from a water object,
|
|
-- * a terrain object and a light
|
|
-- *
|
|
-- **********************************************************************************************
|
|
fileIn "SortFns.ms"
|
|
fn MaxComp N =
|
|
(
|
|
m = (abs N.x)
|
|
i = 1
|
|
if (abs N.y) > m do
|
|
(
|
|
m = (abs N.y)
|
|
i = 2
|
|
)
|
|
if (abs N.z) > m do
|
|
(
|
|
m = (abs N.z)
|
|
i = 3
|
|
)
|
|
return i
|
|
)
|
|
|
|
fn Intersect r oMesh =
|
|
(
|
|
local nf = oMesh.numfaces
|
|
local debug = false
|
|
|
|
local rPoint, rNormal, rIndex, rUV
|
|
fi = false
|
|
-- first get t (where t > 0) for all faces in the object
|
|
TmpArray = #()
|
|
TmpIndices = #()
|
|
TArray = #()
|
|
TIndices = #()
|
|
for i = 1 to nf do
|
|
(
|
|
-- get the vertex indices of the current face
|
|
CFace = getFace oMesh i
|
|
-- get the vertex positions of the vace
|
|
V0 = (getVert oMesh CFace.x)
|
|
-- get the normal of the face
|
|
N = getFaceNormal oMesh i
|
|
d = -(dot V0 N)
|
|
t = -((d + (dot N r.pos)) / (dot N r.dir))
|
|
if t > 0.0 do
|
|
(
|
|
append TmpArray t
|
|
append TmpIndices i
|
|
)
|
|
)
|
|
|
|
if TmpArray.count != 0 do
|
|
(
|
|
aTemp = (SortUp2 TmpArray TmpIndices)
|
|
TArray = aTemp[1]
|
|
TIndices = aTemp[2]
|
|
|
|
for i = 1 to TIndices.count do
|
|
(
|
|
t = TArray[i]
|
|
if debug do format "\n\nFace:%\n" TIndices[i]
|
|
-- get the vertex indices of the current face
|
|
CFace = getFace oMesh TIndices[i]
|
|
-- get the vertex positions of the vace
|
|
V0 = (getVert oMesh CFace.x)
|
|
V0 = #(V0.x, V0.y, V0.z)
|
|
V1 = (getVert oMesh CFace.y)
|
|
V1 = #(V1.x, V1.y, V1.z)
|
|
V2 = (getVert oMesh CFace.z)
|
|
V2 = #(V2.x, V2.y, V2.z)
|
|
local V = #(V0, V1, V2)
|
|
-- First Rejection test (t>0)
|
|
if t > 0.0 then
|
|
(
|
|
N = getFaceNormal oMesh TIndices[i]
|
|
-- second rejection test, are the normals parallel?
|
|
if (dot N r.dir) != 0 do
|
|
(
|
|
inter = false
|
|
P0 = r.pos.x + r.dir.x * t
|
|
P1 = r.pos.y + r.dir.y * t
|
|
P2 = r.pos.z + r.dir.z * t
|
|
P = #(P0, P1, P2)
|
|
if debug do
|
|
(
|
|
format "Intersection Point = %\n" [p0,p1,p2]
|
|
s = sphere()
|
|
s.radius = .25
|
|
s.pos = [p0,p1,p2]
|
|
)
|
|
-- determine the max component of N
|
|
i0 = MaxComp N
|
|
if debug do format "Maximum component:%\n" i0
|
|
if i0 == 1 do (i1 = 2;i2 = 3)
|
|
if i0 == 2 do (i1 = 3;i2 = 1)
|
|
if i0 == 3 do (i1 = 1;i2 = 2)
|
|
u0 = P[i1] - V[1][i1]
|
|
v0 = P[i2] - V[1][i2]
|
|
u1 = V[2][i1] - V[1][i1]
|
|
u2 = V[3][i1] - V[1][i1]
|
|
v1 = V[2][i2] - V[1][i2]
|
|
v2 = V[3][i2] - V[1][i2]
|
|
|
|
if debug do format "u0:%\t\tu1:%\t\tu2:%\nv0:%\t\tv1:%\t\tv2:%\n" u0 u1 u2 v0 v1 v2
|
|
if u1 == 0 then
|
|
(
|
|
beta = u0/u2 as float
|
|
if debug do format "beta:%\n" beta
|
|
if (beta >= 0.0) and (beta <= 1.0) do
|
|
(
|
|
alpha = (v0 - beta * v2) / v1
|
|
if debug do format "alpha:%\n" alpha
|
|
if ((alpha >= 0) and ((alpha + beta) <=1)) do
|
|
(
|
|
CTVFace = getTVFace oMesh TIndices[i]
|
|
-- get the TVerts of the face
|
|
tv0 = (getTVert oMesh CTVFace.x)
|
|
tv1 = (getTVert oMesh CTVFace.y)
|
|
tv2 = (getTVert oMesh CTVFace.z)
|
|
local tv = #(tv0, tv1, tv2)
|
|
rUV = ((tv0 * (1 - (alpha + beta))) + (tv1 * alpha) + (tv2 * beta))
|
|
rPoint = [P[1], P[2], P[3]]
|
|
-- get the normal of the face
|
|
rN = N
|
|
rIndex = TIndices[i]
|
|
fi = true
|
|
if debug do format "FOUND INTERSECTION\n"
|
|
return #(rPoint, rN, rIndex, rUV)
|
|
)
|
|
)
|
|
) else (
|
|
beta = ((v0 * u1) - (u0 * v1)) / ((v2 * u1) - (u2 * v1))
|
|
if debug do format "Beta:%\n" beta
|
|
if ((beta >= 0.0) and (beta <= 1.0)) do
|
|
(
|
|
alpha = (u0 - (beta * u2)) / u1
|
|
if debug do format "Alpha:%\n" alpha
|
|
if ((alpha >= 0) and ((alpha + beta) <=1)) do
|
|
(
|
|
CTVFace = getTVFace oMesh TIndices[i]
|
|
-- get the TVerts of the face
|
|
tv0 = (getTVert oMesh CTVFace.x)
|
|
tv1 = (getTVert oMesh CTVFace.y)
|
|
tv2 = (getTVert oMesh CTVFace.z)
|
|
local tv = #(tv0, tv1, tv2)
|
|
rUV = ((tv0 * (1 - (alpha + beta))) + (tv1 * alpha) + (tv2 * beta))
|
|
rPoint = [P[1], P[2], P[3]]
|
|
-- get the normal of the face
|
|
rN = N
|
|
rIndex = TIndices[i]
|
|
fi = true
|
|
if debug do format "FOUND INTERSECTION\n"
|
|
return #(rPoint, rN, rIndex, rUV)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
)
|
|
if fi == false do
|
|
return undefined
|
|
)
|
|
|
|
-- returns the magnitude of a vector
|
|
fn MagV vec = (sqrt((vec.x)^2 + (vec.y)^2 + (vec.z)^2))
|
|
-- returns a normalized vector between two objects
|
|
fn GetNormalDirVec o1 o2 = normalize (o2.pos - o1.pos)
|
|
-- returns a non-normalized vector between two objects
|
|
fn GetDirVec o1 o2 = (o2.pos - o1.pos)
|
|
-- returns a ray from one object to another
|
|
fn CreateRay o1 o2 =
|
|
return (ray o1.pos (GetNormalDirVec o2 o1))
|
|
|
|
Utility Caustics "Caustics"
|
|
(
|
|
|
|
local WaterObject, L, Terrain, CTexture,
|
|
BrightenValue = 0.1 -- value that get's added to each pixel each time a ray hits it
|
|
|
|
group "Debug Options"
|
|
(
|
|
checkbox debugMode "Debug Mode" checked:true
|
|
checkbox GenerateRays "Generate Rays"
|
|
checkbox GenerateTerrainInts "Generate Terrain Ints"
|
|
)
|
|
|
|
Group "New Bitmap Options"
|
|
(
|
|
spinner Width "Width:" range:[0,256,32] type:#integer
|
|
spinner Height "Height:" range:[0,256,32] type:#integer
|
|
)
|
|
|
|
group "Refraction Parameters"
|
|
(
|
|
spinner sampleFreqX "Sample Freq X:" range:[1,2048,16] fieldwidth:35 type:#integer
|
|
spinner sampleFreqY "Sample Freq Y:" range:[1,2048,16] fieldwidth:35 type:#integer
|
|
spinner SourceRefraction "Source IoR:" range:[0,4,1.0] fieldwidth:35
|
|
spinner DestRefraction "Destination IoR:" range:[0,4,1.33] fieldwidth:35
|
|
)
|
|
|
|
group "Image Filtering Options"
|
|
(
|
|
radiobuttons FilterMethod labels:#("No Filtering", "Barlett Window")
|
|
)
|
|
|
|
group "Animation"
|
|
(
|
|
checkbox AnimateCaustic "Animate" checked:true
|
|
spinner StartFrame "Start Frame: " type:#integer range:[0,30,0]
|
|
spinner EndFrame "End Frame: " type:#integer range:[0,30,30]
|
|
spinner FrameStep "Frame Step: " type:#integer range:[1,100,1]
|
|
editText SubStr "SubStr: " text:"MyBitmap"
|
|
)
|
|
|
|
label TerrainLabel "Terrain:" align:#left offset:[-10,0]
|
|
pickbutton GetTerrain width:80 align:#right offset:[5,-20]
|
|
label waterLabel "Water Object:" align:#left offset:[-10,0]
|
|
pickbutton GetWater width:80 align:#right offset:[5,-20]
|
|
label LightLabel "Light:" align:#left offset:[-10,0]
|
|
pickbutton GetLight width:80 align:#right offset:[5,-20]
|
|
|
|
label blank00
|
|
button RenderBitmap "Render Caustic Map"
|
|
|
|
label status1
|
|
label status2
|
|
|
|
on GetTerrain picked obj do
|
|
(
|
|
Terrain = obj
|
|
GetTerrain.text = Terrain.name
|
|
)
|
|
|
|
on GetLight picked obj do
|
|
(
|
|
L = -(obj.dir) -- lights shine in the negative Z direction
|
|
GetLight.text = obj.name
|
|
format "Light Direction: %\n" L
|
|
)
|
|
|
|
on GetWater picked obj do
|
|
(
|
|
WaterObject = obj
|
|
GetWater.text = WaterObject.name
|
|
)
|
|
|
|
on RenderBitmap pressed do
|
|
(
|
|
-- determine the size of the water object
|
|
if WaterObject != undefined then
|
|
(
|
|
if Terrain != undefined then
|
|
(
|
|
if animateCaustic.checked do
|
|
(
|
|
if (SavePath = (GetSavePath caption:"Give me path...now!") + "\\") != undefined do
|
|
(
|
|
if classof terrain.mat == standardMaterial do
|
|
(
|
|
if classof terrain.mat.diffusemap == bitmaptexture do
|
|
CTexture = (openbitmap terrain.mat.diffusemap.filename)
|
|
)
|
|
-- format the start time to the listener window
|
|
format "Start:%\n" localTime
|
|
|
|
for t = StartFrame.value to EndFrame.value by FrameStep.value do
|
|
(
|
|
status2.text = ("t = " + t as string)
|
|
gc()
|
|
-- Generate a new bitmap of the size specified in the interface
|
|
CausticMap = bitmap width.value height.value
|
|
|
|
if debugMode.checked do
|
|
format "t = %\n" t
|
|
|
|
-- Determine the scale of the terrain
|
|
TerrainSize = (Terrain.max - Terrain.min)
|
|
at time t
|
|
(
|
|
-- Determine the scale of the water
|
|
WaterSize = (WaterObject.max - WaterObject.min)
|
|
|
|
-- get the Caustic map array ready and filled with zeros
|
|
CausticMapArray = #()
|
|
for i = 1 to Height.value do
|
|
(
|
|
CausticMapArray[i] = #()
|
|
for j = 1 to width.value do
|
|
CausticMapArray[i][j] = 0.0
|
|
)
|
|
YLimit = CausticMapArray.count
|
|
XLimit = CausticMapArray[1].count
|
|
CausticIndexSize = [(TerrainSize.x / width.value as float), (TerrainSize.y / Height.value as float)]
|
|
InvCausticIndexSize = [(1 / CausticIndexSize.x), (1 / CausticIndexSize.y)]
|
|
-- precalculate the index of refraction (n1/n2)
|
|
Index = (SourceRefraction.value / DestRefraction.value)
|
|
|
|
-- Create a ray
|
|
r = ray [0,0,0] [0,0,-1]
|
|
|
|
-- the PosZ value is precalculated for the z position of the ray, as it gets moved around
|
|
PosZ = (WaterObject.max.z + 1.0)
|
|
|
|
-- determine the pixel size and sampling width and height for
|
|
-- stepping over the water object
|
|
pixelsize = [(WaterSize.x / sampleFreqX.value), (WaterSize.y / sampleFreqY.value)]
|
|
StartPosition = [(WaterObject.min.x + (pixelsize.x * 0.5)), (WaterObject.max.y - (pixelsize.x * 0.5))]
|
|
|
|
-- print out some debug information to the Listener
|
|
if debugMode.checked do
|
|
(
|
|
format "Water Scale: % % %\n" WaterSize.x WaterSize.y WaterSize.z
|
|
format "Pixel Size: % %\n" pixelsize.x pixelsize.y
|
|
format "Start Point: % %\n" StartPosition.x StartPosition.y
|
|
format "x:%\ny:%\n" (sampleFreqX.value - 1) (sampleFreqY.value - 1)
|
|
)
|
|
-- now we start stepping through the water, calculating everything
|
|
ProgressStart "Rendering Caustic Texture Map..."
|
|
for x = 0 to (sampleFreqX.value - 1) do
|
|
(
|
|
status1.text = (x as string + " / " + (sampleFreqX.value - 1) as string)
|
|
ProgValue = (x / sampleFreqX.value) * 100
|
|
if (progressUpdate ProgValue) == false then Exit
|
|
for y = (sampleFreqY.value - 1) to 0 by -1 do
|
|
(
|
|
-- find the new position to look at
|
|
NewPos = [(StartPosition.x + (x * pixelsize.x)), (StartPosition.y - (y * pixelsize.y))]
|
|
-- move the ray there
|
|
r.pos = [NewPos.x, NewPos.y, PosZ]
|
|
-- find the intersect ray between the ray and the water
|
|
------------------------------------------------
|
|
if (ir = IntersectRay WaterObject r) != undefined then
|
|
-- if (ir = Intersect r WaterObject) != undefined then
|
|
(
|
|
N = ir.dir
|
|
-- test to see if ir is facing away from the lightsource
|
|
-- if it is, we skip the rest of this and go on to the
|
|
-- next casting position
|
|
if (DotLN = (dot L N)) < 0.0 then
|
|
(
|
|
-- Index = n1/n2
|
|
sinTheta = Index * (sqrt (1 - (dot L N)^2))
|
|
-- q = (n1/n2 * L)
|
|
q = [(Index * L.x), (Index * L.y), (Index * L.z)]
|
|
Stuff = (sqrt(1 - sinTheta^2) + Index * DotLN)
|
|
TransRay = ray ir.pos (q - [(Stuff * N.x), (Stuff * N.y), (Stuff * N.z)])
|
|
if GenerateRays.checked do
|
|
(
|
|
c = cylinder()
|
|
c.pos = TransRay.pos
|
|
c.dir = TransRay.dir
|
|
c.height = 20
|
|
c.sides = 3
|
|
c.radius = 0.01
|
|
)
|
|
|
|
-- now that we have the transmitted ray,we intersect it with
|
|
-- the terrain to find out where it's hitting. If it's undefined
|
|
-- we'll assume that it's refracting off the terrain surface.
|
|
-----------------------------------------------
|
|
if (tr = intersect TransRay Terrain) != undefined then
|
|
(
|
|
p3IntersectUV = tr[4]
|
|
iFaceIndex = tr[3]
|
|
iMatID = getFaceMatID terrain iFaceIndex
|
|
-- determine the pixel in the source texture we hit
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
-- ************************************************
|
|
) else (
|
|
if debugMode.checked do
|
|
format "Trans Ray undefined!!!\n"
|
|
)
|
|
) else (
|
|
if DebugMode.checked do
|
|
format "Skipped...\n"
|
|
)
|
|
) else (
|
|
format "undefined intersectRay!\n"
|
|
)
|
|
)
|
|
)
|
|
ProgressEnd()
|
|
for y = 1 to CausticMapArray.count do
|
|
(
|
|
Brightpoint = 0.0
|
|
|
|
for y = 1 to CausticMapArray.count do
|
|
for x = 1 to CausticMapArray[y].count do
|
|
if CausticMapArray[y][x] > BrightPoint do
|
|
BrightPoint = CausticMapArray[y][x]
|
|
|
|
BrightnessScalar = 1 / BrightPoint
|
|
|
|
CRow = #()
|
|
for x = 1 to CausticMapArray[y].count do
|
|
(
|
|
c = (CausticMapArray[y][x] * BrightnessScalar) * 255
|
|
if c > 255 do c = 255
|
|
c = (color c c c)
|
|
append CRow c
|
|
)
|
|
setpixels CausticMap [0,(y - 1)] CRow
|
|
)
|
|
NewFileName = (SavePath + Substr.text + t as string + ".bmp")
|
|
CausticMap.filename = NewFileName
|
|
save CausticMap
|
|
status1.text = ""
|
|
|
|
-- if debugMode.checked do
|
|
display CausticMap
|
|
)
|
|
)
|
|
format "End:%\n" localTime
|
|
status1.text = ""
|
|
)
|
|
)
|
|
) else (
|
|
messageBox "Terrain object is not defined!!!"
|
|
)
|
|
) else (
|
|
messageBox "Water object is not defined!!!"
|
|
)
|
|
)
|
|
) -- end Utility |