Oculus VR template: /flow_callbacks.lua — code sample - Stingray Lua API Reference

Oculus VR template: /flow_callbacks.lua — code sample

Code

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