The test scene runs at 14-15 ms (66 FPS) per iteration on my end, while the original code runs at 70-71 (14 FPS).
The most critical point I see is to reduce the amount of ray test. Currently, there are 4 test for most of the vertices, when we only need 1.
So for the plane (225 faces) we are casting 900 rays per light, but we only need to cast 256. That will give you a much better performance.
Other point was that SetFaceColor() is much slower than SetMapVert().
I don’t think there will be a huge performance gain using the SDK.
Things that could be improved:
- Cast only 1 ray per light and vertex
- If the lit object is static, then caching the values (normal, vertex positions, etc.)
- If the “blockers” objects (the ones that cast the shadows) are spheres, planes or boxes, then there might be a chance to improve the ray casting by using a custom routine. This should be better to implement using the SDK.
- If you will just send an array of integers via telnet, then there should be some speed up since you don’t need to actually lit the object.
At the moment I can’t think of any other thing that could have a big impact in the performance.
Here is a different approach, casting only one ray per vertex, but only works with 1 light. It could be modified though to work with more lights.
As you can see it performs around 2 times faster.
(
/* SETUP TEST SCENE ####################################################################################################### */
delete objects
master = converttopoly (plane length:300 width:300 pos:[0,0,0] lengthsegs:15 widthsegs:15 name:"master" wirecolor:gray)
max zoomext sel
b1 = converttopoly (box lengthsegs:2 widthsegs:2 heightsegs:2 length:20 width:20 height:20 pos:[ 50, 0, 50] wirecolor:red)
b2 = converttopoly (box lengthsegs:2 widthsegs:2 heightsegs:2 length:20 width:20 height:20 pos:[-50, 0, 50] wirecolor:red)
b3 = converttopoly (box lengthsegs:2 widthsegs:2 heightsegs:2 length:20 width:20 height:20 pos:[ 0, 50, 50] wirecolor:red)
b4 = converttopoly (box lengthsegs:2 widthsegs:2 heightsegs:2 length:20 width:20 height:20 pos:[ 0, -50, 50] wirecolor:red)
converttopoly (box lengthsegs:1 widthsegs:1 heightsegs:1 length:20 width:20 height:75 pos:[0, 0, 0] wirecolor:orange)
l1 = omnilight rgb:(color 255 255 255) pos:[50,0,110] multiplier:1.0
l2 = omnilight rgb:(color 255 255 255) pos:[ 0,0,150] multiplier:0.0
d1 = dummy()
d2 = dummy()
b1.parent = b2.parent = b3.parent = b4.parent = d1
l1.parent = d2
with animate on
(
at time 100
(
rotate d1 (angleaxis 90 [0,0, 1])
rotate d2 (angleaxis 360 [0,0,-1])
)
)
/* END SETUP TEST SCENE ################################################################################################### */
try destroydialog ::RO_DISPLAY_FACES_COLORS catch()
rollout RO_DISPLAY_FACES_COLORS "Faces Colors" width:172 height:100
(
checkbutton bt_start "Start" pos:[8,8] width:154 height:32
spinner sp_l1 "Light 1 Multiplier: " pos:[8,54] fieldwidth:48 range:[0,1,1.0] scale:0.01
spinner sp_l2 "Light 2 Multiplier: " pos:[8,76] fieldwidth:48 range:[0,1,0.0] scale:0.01 enabled:false
global GW_DisplayFacesColors
local node = $master
local GetfaceNormal = polyop.getfacenormal
local GetFaceCenter = polyop.getfacecenter
local GetFaceVerts = polyop.getfaceverts
local GetVert = polyop.getvert
local SetMapVert = polyop.setmapvert
local MRIntersect = #()
local facesVerts = #()
local mapFaces = #()
local sourceLights = #()
local colour = [0,0,0]
fn CalculateFacesColors = with undo off
(
MRIntersect = for j in geometry where j != node collect
(
rm = RayMeshGridIntersect()
rm.Initialize 5
rm.addNode j
rm.buildGrid()
#(rm, rm.intersectRay)
)
vertsHits = #()
for j = 1 to node.numverts do
(
vpos = GetVert node j
hits = 0
for k in sourceLights do
(
lightPos = k.center
for rm in MRIntersect where (rm[2] vpos (lightPos-vpos) false) > 0 do hits += 1
)
vertsHits[j] = hits
)
for f = 1 to node.numfaces do
(
value = 0
faceNormal = GetfaceNormal node f
faceCenter = GetFaceCenter node f
vertsPos = for k in facesVerts[f] collect GetVert node k
strength = 1.0/vertsPos.count
for k in sourceLights do
(
lightPos = k.center
lightDir = normalize (lightPos - faceCenter)
shadowStrength = 0
for i in facesVerts[f] do
(
for v = 1 to vertsHits[i] do shadowStrength += strength
)
diffuse = amax ((dot lightDir faceNormal)*k.multiplier) 0
value += diffuse * (1.0-shadowStrength)
)
colour.x = colour.y = colour.z = amax 0.0 (amin value 1.0)
for k in mapFaces[f] do SetMapVert node 0 k colour
)
update node
for rm in MRIntersect do rm[1].free() -- Prevent memory leaking
)
fn GW_DisplayFacesColors =
(
clearlistener()
st = timestamp(); sh = heapfree
CalculateFacesColors()
format "time:% heap:%\n" (timestamp()-st) (sh-heapfree)
)
fn SetupScene =
(
polyop.applyuvwmap node #face channel:0
sourceLights = for j in lights where classof j != targetobject collect j
mapFaces = for j = 1 to node.numfaces collect polyop.getmapface node 0 j
facesVerts = for j = 1 to node.numfaces collect GetFaceVerts node j
registerredrawviewscallback GW_DisplayFacesColors
node.vertexColorType = 0
node.showVertexColors = on
completeredraw()
)
on bt_start changed arg do
(
unregisterredrawviewscallback GW_DisplayFacesColors
if arg then
(
SetupScene()
playanimation()
bt_start.text = "Stop"
)else(
bt_start.text = "Start"
stopanimation()
)
)
on RO_DISPLAY_FACES_COLORS open do
(
unregisterredrawviewscallback GW_DisplayFacesColors
gc()
)
on RO_DISPLAY_FACES_COLORS close do unregisterredrawviewscallback GW_DisplayFacesColors
on sp_l1 changed arg do l1.multiplier = arg
on sp_l2 changed arg do l2.multiplier = arg
)
createdialog RO_DISPLAY_FACES_COLORS
)
This code with just 1 light, runs at 8 ms per iteration (125 FPS).