ProjectFlowCallbacks = ProjectFlowCallbacks or {}
-- Example custom project flow node callback. Prints a message.
-- The parameter t contains the node inputs, and node outputs can
-- be set on t. See documentation for details.
local Unit = stingray.Unit
local Matrix4x4 = stingray.Matrix4x4
local Quaternion = stingray.Quaternion
local Vector3 = stingray.Vector3
local Color = stingray.Color
local Material = stingray.Material
local Mesh = stingray.Mesh
local _selection_mask_counter = 0
function ProjectFlowCallbacks.example(t)
local message = t.Text or ""
print("Example Node Message: " .. message)
end
function ProjectFlowCallbacks.update_look_at_constraint(t)
local unit = t.unit
if not Unit.has_data(unit, "saved_rotation") then
print ("no saved_rotation")
return
end
if not Unit.has_data(unit, "grab_location") then
print ("no grab_location")
return
end
local saved_rotation = Unit.get_data(unit, "saved_rotation")
local grab_location = Unit.get_data(unit, "grab_location")
-- Rotate around this up axis
local up_axis = Vector3(0, 1, 0)
local target_unit = unit
local world_target_pos = t.world_look_at_position
local node_to_rotate = t.node_to_rotate
local rotate_id = Unit.node(unit, node_to_rotate)
local parent = Unit.scene_graph_parent(unit, rotate_id)
local parent_pose = Unit.world_pose(unit, parent)
local inverse_parent_pose = Matrix4x4.inverse(parent_pose)
local local_target_pos = Matrix4x4.transform(inverse_parent_pose, world_target_pos)
local target_angle = math.atan2(local_target_pos.x, local_target_pos.z) / (2 * math.pi)
local local_grab_pos = Matrix4x4.transform(inverse_parent_pose, grab_location)
local grab_angle = math.atan2(local_grab_pos.x, local_grab_pos.z) / (2 * math.pi)
local delta = target_angle - grab_angle
delta = delta % 1.0
if delta > 0.5 then delta = delta - 1 end
local delta_rotation = Quaternion.axis_angle(up_axis, delta * (math.pi * 2))
local rotation = Quaternion.multiply(saved_rotation, delta_rotation)
Unit.set_local_rotation(unit, rotate_id, rotation)
end
function ProjectFlowCallbacks.set_kinematic(t)
local actor = t.Actor
local bool = t.Bool
if actor ~= nil then
stingray.Actor.set_kinematic(actor, bool)
end
end
function get_mesh_by_name_or_all(unit, mesh_name)
local meshes = {}
if mesh_name and Unit.has_mesh(unit, mesh_name) then
table.insert(meshes, Unit.mesh(unit, mesh_name))
elseif mesh_name == nil or mesh_name == "" then
local num_meshes = Unit.num_meshes(unit)
for i=1, num_meshes do
local mesh = Unit.mesh(unit, i)
table.insert(meshes, mesh)
end
end
return meshes
end
function get_material_by_slot_or_all(mesh, slot_name)
local materials = {}
if slot_name and Mesh.has_material(mesh, slot_name) then
table.insert(materials, Mesh.material(mesh, slot_name))
elseif slot_name == nil or slot_name == "" then
local num_materials = Mesh.num_materials(mesh)
for i=1, num_materials do
local material = Mesh.material(mesh, i)
table.insert(materials, material)
end
end
return materials
end
function ProjectFlowCallbacks.setUnitHighlight(t)
-- the selection_mask for a unit or mesh should usually not be 0.
-- if it is zero, it means no outlines will be rendered for that mesh at all.
-- it should loop between 1-255, never go above 255
-- what matters is that the mask is fairly unique between meshes/units
-- if 2 meshes are both selected and overlapping each other and they both have the same mask,
-- their outlines will merge as if they are one mesh.
-- nothing bad will happen if two units by pure chance get the same mask
-- value other then what is mentioned above.
_selection_mask_counter = _selection_mask_counter + 1
local _selection_mask = t.Mask ~= nil and t.Mask % 255 or _selection_mask_counter % 255 + 1
-- You can supply both a selection color RGB as well as an alpha.
-- The alpha value will render a highlight over the object with the alpha amount
local input_color = t.Color or Vector3(67, 255, 163)
local alpha = t.Alpha or 16
local unit = t.Unit or nil
local value = t.Enable
if value == nil then
value = true
end
if unit then
local color = Color(alpha, input_color[1], input_color[2], input_color[3])
local meshes = get_mesh_by_name_or_all(unit, t.Mesh)
if next(meshes) == nil then
print_warning("Warning: setUnitHighlight: No mesh found for "..Unit.debug_name(unit).." named "..tostring(t.Mesh))
return
end
local _match_found = false
for i=1, #meshes do
local mesh = meshes[i]
local materials = get_material_by_slot_or_all(mesh, t.MaterialSlot)
if next(materials) == nil and t.MaterialSlot and t.Mesh then
print_warning("Warning: setUnitHighlight: No material slot found for "..Unit.debug_name(unit).." named "..tostring(t.MaterialSlot).." for mesh " .. tostring(t.Mesh))
return
end
for j=1, #materials do
_match_found = true
local material = materials[j]
Material.set_color(material, "dev_selection_color", color)
Material.set_scalar(material, "dev_selection_mask", (_selection_mask/255))
Material.set_shader_pass_flag(material, "dev_selection", value)
end
end
if not _match_found then
print_warning("Warning: setUnitHighlight: No material slot found for "..Unit.debug_name(unit).." named "..tostring(t.MaterialSlot))
end
end
end
function ProjectFlowCallbacks.caculateRotationOffset(t)
local inverse_parent_rot = Quaternion.inverse(t.parent_rotation)
--local unit_rot = Unit.world_rotation(unit,unit_node)
t.offset_rotation = Quaternion.multiply(inverse_parent_rot, t.child_rotation)
return t
end
function ProjectFlowCallbacks.caculatePositionOffset(t)
local parent_node = Unit.node(t.parent_unit, t.parent_object)
local child_node = Unit.node(t.child_unit, t.child_object)
local parent_transform = Matrix4x4.inverse(Unit.world_pose(t.parent_unit, parent_node))
local unit_pos = Unit.world_position(t.child_unit, child_node)
t.position = Matrix4x4.transform(parent_transform, unit_pos)
return t
end