Ok, here’s the modified Jorge’s version that uses c# function to speed up setting vertex colors

```
(
/* SETUP TEST SCENE ####################################################################################################### */
delete objects
master = convertToMesh (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:50 width:50 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 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
)
hits = dotnet.ValueToDotNetObject vertsHits (dotNetClass "system.int32[]")
lights_n_mults = #()
for l in lights do
(
append lights_n_mults l.pos.x
append lights_n_mults l.pos.y
append lights_n_mults l.pos.z
append lights_n_mults l.multiplier
)
lights_n_mults = dotnet.ValueToDotNetObject lights_n_mults (dotNetClass "system.single[]")
SetMeshFaceLigthness node.inode.handle hits lights_n_mults
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 =
(
meshop.setMapSupport node 0 true
sourceLights = for j in lights where classof j != targetobject collect 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
)
```

and c# code

```
static public void SetMeshFaceLigthness( uint handle, int[] vert_hits, float[] light_pos_and_multipliers )
{
IGlobal ip = GlobalInterface.Instance;
IINode node = ip.COREInterface7.GetINodeByHandle( handle );
if ( node == null ) return;
//var TM_src = node.GetObjectTM( ip.COREInterface7.Time, null );
//TM_src.Invert();
IObject obj = node.EvalWorldState( ip.COREInterface7.Time, true ).Obj;
node.Dispose();
ITriObject tri = (ITriObject)obj;
IMesh mesh = tri.Mesh;
tri.Dispose();
obj.Dispose();
IList<ITVFace> map_faces = mesh.MapFaces( 0 );
var map_verts = mesh.MapVerts( 0 );
for ( int f = 0; f < mesh.NumFaces; f++ )
{
double value = 0f;
var meshfaceverts = mesh.Faces[ f ].V;
IPoint3 N = mesh.FaceNormal( (uint)f, true );
IPoint3 C = mesh.FaceCenter( (uint)f );
double strength = 1.0 / 3;
for ( int i = 0; i < light_pos_and_multipliers.Length; i += 4 )
{
//IPoint3 light_pos = ip.Point3.Create( light_pos_and_multipliers[ i ], light_pos_and_multipliers[ i + 1 ], light_pos_and_multipliers[ i + 2 ] );
//IPoint3 light_dir = light_pos.Subtract( C ).FNormalize;
double x = light_pos_and_multipliers[ i ] - C.X;
double y = light_pos_and_multipliers[ i + 1 ] - C.Y;
double z = light_pos_and_multipliers[ i + 2 ] - C.Z;
double len = Math.Sqrt( x * x + y * y + z * z );
x = x / len;
y = y / len;
z = z / len;
double shadowStrength = 0;
for ( int v = 0; v < 3; v++ )
{
for ( int j = 0; j < vert_hits[ meshfaceverts[ v ] ]; j++ )
{
shadowStrength += strength;
}
}
//double diffuse = Math.Max( (light_dir.X * N.X + light_dir.Y * N.Y + light_dir.Z * N.Z) * light_pos_and_multipliers[ i + 3 ], 0 );
double diffuse = Math.Max( (x * N.X + y * N.Y + z * N.Z) * light_pos_and_multipliers[ i + 3 ], 0 );
value += diffuse * (1.0 - shadowStrength);
}
float avg_value = (float)(Math.Max( Math.Min( value, 1 ), 0 ));
map_verts[ (int)map_faces[ f ].T[ 0 ] ].Set( avg_value, avg_value, avg_value );
map_verts[ (int)map_faces[ f ].T[ 1 ] ].Set( avg_value, avg_value, avg_value );
map_verts[ (int)map_faces[ f ].T[ 2 ] ].Set( avg_value, avg_value, avg_value );
N.Dispose();
C.Dispose();
}
mesh.Dispose();
}
```

it can further be heavily optimized using unsafe for direct memory reads/writes

and don’t forget to dispose everything that needs to be