Capricorn 76
3D Sound example.
ScriptENGINE supports 2D/3D sound.
Sounds can be loaded and played as background music or sound fx.
Sounds can be streamed from disk (for large files) or loaded entirely in memory (for smaller, frequently used fx).
Sounds can be attached to 3D entities, and automatically moved around the scene with the entity.

---------------------------
---------------------------
-- 3D Sound example
-- Copyright (c) 2007-09 Capricorn 76 Pty. Ltd.
--
-- ScriptENGINE supports 2D/3D sound.
-- Sounds can be loaded and played as background music or sound fx.
-- Sounds can be streamed from disk (for large files) or loaded entirely in memory (for smaller, frequently used fx).
-- Sounds can be attached to 3D entities, and automatically moved around the scene with the entity.
--
-- NOTE:
-- The ./Demos/runDemo.e76script is a utility script used to run all the examples.
-- It loads the world, calls the OnWorldLoad(), OnWorldUnload() functions, and runs the graphics engine loop, waiting for the user to press escape.
---------------------------
---------------------------


local subTick = nil;
local subGui = nil;
local moveCube = false;
local cubeAngle     = 0;
local cubeRadius    = 0;

---------------------------
-- runDemo.e76script calls this function when the world is loaded
---------------------------
function OnWorldLoad(worldName)
    --print('OnWorldLoad');

    ---------------------------
    -- The SDK world editor creates a worldIds.e76script when the world is saved. It contains all the entity ids.
    -- We load the IDs so that we can refer to the world entities using friendly names.
    ---------------------------
    IApp:loadScript(IWorld:getNameLong() .. '/worldIds');

    -- Load this example's GUI
    OnWorldLoadGui();

    -- Precalculate how big the cube's circle path will be based upon its starting position
    cubeRadius = IMisc:vectorGetLength(IWorld:getEntityPosition(idUnit_box));

    ---------------------------
    -- Subscribe to the graphics engine 'tick' event.
    -- Our callback will be periodically fired by the graphics engine,
    -- so we can do some extra processing.
    --
    -- Could create a thread to manage this,
    -- but we're showing how this can be done from the graphics engine thread.
    --
    -- Note that you should do minimal processing this way,
    -- because the graphics can become jerky as the processing load increases.
    --
    -- The returned identifier is later used to unsubscribe.
    ---------------------------
    subTick = IEvents:subscribeTickPeriodic('OnGraphicsTick', 100);
    subGui = IEvents:subscribeGui('OnGui');

    ---------------------------
    -- Play some music and attach it to Agent.
    -- If he moves, the sound will follow.
    -- The engine will search its filesystem for the matching file.
    -- NB: 3D sounds must have only one channel (ie. mono)
    ---------------------------
    ISound:load('agentMusic', './Resource/music3d.ogg', false);
    if (ISound:soundExists('agentMusic')) then
        print('Loaded sound. Setting properties...');

        -- Set the 3D position of the sound.
        -- Attached the sound to Agent
        ISound:setMaxDistance('agentMusic', 1000);
        ISound:setReferenceDistance('agentMusic', 10);
        ISound:setLooping('agentMusic', true);
        ISound:setVolume('agentMusic', 0.5);
        ISound:attachEntitySound(idAgent_Mech, 'agentMusic');
        ISound:play('agentMusic');
    end

    ---------------------------
    -- Attach a 3D sound to cube.
    -- Change the default minimum/maximum distances for the cube's 3D sounds.
    -- These control how close/far a sound can be heard from at max/min volume, depending on the sound engine's distance model.
    -- NB: 3D sounds must have only one channel (ie. mono)
    ---------------------------
    if (not(ISound:soundExists('sfx'))) then
        ISound:load('sfx', './Resource/sfx3d.wav', false);
    end

    if (ISound:soundExists('sfx')) then
        ISound:setMaxDistance('sfx', 1000);
        ISound:setReferenceDistance('sfx', 5);
        ISound:setVolume('sfx', 0.5);
        ISound:setLooping('sfx', true);
        ISound:attachEntitySound(idUnit_box, 'sfx');
        ISound:play('sfx');
    end

    ---------------------------
    -- Turn on auto camera movement.
    -- The active camera is 'fps' type, so it responds to mouse/key input in a first person shooter style
    ---------------------------
    IWorld:setActiveCameraAutoProcessed(true);

end

---------------------------
-- Load this example's GUI
---------------------------
function OnWorldLoadGui()
    ---------------------------
    -- Let's show some help text
    ---------------------------
    IGraphics:loadGui('help');
end

---------------------------
-- runDemo.e76script calls this function when the world is unloaded
---------------------------
function OnWorldUnload(worldName)
    --print('OnWorldUnload');

    ---------------------------
    -- Unsubscribe to events
    -- We will no longer be informed of the events.
    ---------------------------
    IEvents:unsubscribe(subTick);
    IEvents:unsubscribe(subGui);

    ---------------------------
    -- Stop sounds
    ---------------------------
    ISound:unload('agentMusic');
    ISound:unload('sfx');

end

------------------------
-- Graphics tick event callback function.
-- The graphics engine will periodically call this function based upon our subscription settings.
-- We rotate the cube about the world's centre a little bit each time this callback is fired.
------------------------
function OnGraphicsTick(currentTime, lastTime, isWindowActive, userData)
     IWorld:manipulateEntity(idUnit_box, 'rotate', 0, 1, 1);

    if (moveCube) then
        cubeAngle = math.mod(cubeAngle + 2, 360);
        --print('cubeAngle:', cubeAngle);

        ------------------------
        -- Calculate the cube's x/y/z position based upon
        -- our current angle and radius. We are basically
        -- rotating the cube in a circle around the world's center.
        ------------------------
        X   = math.cos(math.rad(cubeAngle)) * cubeRadius;
        Y   = 0;    -- The vertical dimension.
        Z   = math.sin(math.rad(cubeAngle)) * cubeRadius;
        IWorld:setEntityPosition(idUnit_box, IMisc:vectorCreate(X, 0, Z));
    end

    IWorld:manipulateEntity(idAgent_Mech, 'rotate', 0, 1, 0);
end

------------------------
-- Gui handler
------------------------
function OnGui(callerId, eventType, userData)
    -----------------------
    -- Change bell pitch
    -----------------------
    if (IGraphics:scrollBarChanged('scrollBellPitch', callerId, eventType)) then

        -- Scale scrollbar value to between -1 and 1, then we take the exponential to make the pitch scale more user-friendly
        local pitch = math.exp((IGraphics:getScrollBarPos('scrollBellPitch') - 5) / 5);

        print('Changing bell pitch:', pitch);

        ISound:setPitch('sfx', pitch);  -- 1 is the default, scale is logorithmic, so (2 = an octave up) and (0.5 = an octave down)

    -----------------------
    -- Make directional sound
    -----------------------
    elseif (IGraphics:checkboxClicked('checkboxDirectional', callerId, eventType)) then
        local enable = IGraphics:getCheckbox('checkboxDirectional');

        if (enable) then
            -- The volume will be reduced outside the outer angle
            -- The sound's position and direction is updated with the entity it's attached to (ie. the box)
            ISound:setDirectionConeAngleParams('agentMusic', 90, 180, 0);
            ISound:setDirection('agentMusic', '1 0 0');
            print('Enabling directional sound...');
        else
            ISound:setDirectionConeAngleParams('agentMusic', 360, 360, 1);
            print('Disabling directional sound...');
        end

    -----------------------
    -- Move cube?
    -----------------------
    elseif (IGraphics:checkboxClicked('checkboxMoveCube', callerId, eventType)) then
        moveCube = IGraphics:getCheckbox('checkboxMoveCube');
    end
end

Copyright © 2006-23 Sep 2009 Capricorn 76 Pty. Ltd. (created on Wed Sep 23 16:49:12 2009)