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