瀏覽代碼

Added Sound Component

Areloch 7 年之前
父節點
當前提交
35c0860418
共有 2 個文件被更改,包括 550 次插入0 次删除
  1. 421 0
      Engine/source/T3D/components/audio/SoundComponent.cpp
  2. 129 0
      Engine/source/T3D/components/audio/SoundComponent.h

+ 421 - 0
Engine/source/T3D/components/audio/SoundComponent.cpp

@@ -0,0 +1,421 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+#include "T3D/components/audio/SoundComponent.h"
+#include "core/stream/bitStream.h"
+#include "sim/netConnection.h"
+
+#include "sfx/sfxSystem.h"
+#include "sfx/sfxSource.h"
+#include "sfx/sfxTrack.h"
+#include "sfx/sfxDescription.h"
+#include "T3D/sfx/sfx3DWorld.h"
+
+#include "sfx/sfxTrack.h"
+#include "sfx/sfxTypes.h"
+
+#include "renderInstance/renderPassManager.h"
+#include "gfx/gfxDrawUtil.h"
+
+// Timeout for non-looping sounds on a channel
+static SimTime sAudioTimeout = 500;
+
+extern bool gEditingMission;
+
+//////////////////////////////////////////////////////////////////////////
+// Constructor/Destructor
+//////////////////////////////////////////////////////////////////////////
+SoundComponent::SoundComponent() : Component()
+{
+   //These flags inform that, in this particular component, we network down to the client, which enables the pack/unpackData functions to operate
+   mNetworked = true;
+
+   mFriendlyName = "Sound(Component)";
+   mComponentType = "Sound";
+   mDescription = getDescriptionText("Stores up to 4 sounds for playback.");
+
+   for (U32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++) {
+      mSoundThread[slotNum].play = false;
+      mSoundThread[slotNum].profile = 0;
+      mSoundThread[slotNum].sound = 0;
+
+      mSoundFile[slotNum] = NULL;
+      mPreviewSound[slotNum] = false;
+      mPlay[slotNum] = false;
+   }
+}
+
+SoundComponent::~SoundComponent()
+{
+}
+
+IMPLEMENT_CO_NETOBJECT_V1(SoundComponent);
+
+//Standard onAdd function, for when the component is created
+bool SoundComponent::onAdd()
+{
+   if (!Parent::onAdd())
+      return false;
+
+   for (U32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+      mPreviewSound[slotNum] = false;
+
+   return true;
+}
+
+//Standard onRemove function, when the component object is deleted
+void SoundComponent::onRemove()
+{
+   Parent::onRemove();
+}
+
+//This is called when the component has been added to an entity
+void SoundComponent::onComponentAdd()
+{
+   Parent::onComponentAdd();
+
+   Con::printf("We were added to an entity! SoundComponent reporting in for owner entity %i", mOwner->getId());
+}
+
+//This is called when the component has been removed from an entity
+void SoundComponent::onComponentRemove()
+{
+   Con::printf("We were removed from our entity! SoundComponent signing off for owner entity %i", mOwner->getId());
+   Parent::onComponentRemove();
+}
+
+//This is called any time a component is added to an entity. Every component currently owned by the entity is informed of the event. 
+//This allows you to do dependency behavior, like collisions being aware of a mesh component, etc
+void SoundComponent::componentAddedToOwner(Component *comp)
+{
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+   {
+      if (mPlay[slotNum])
+      {
+         playAudio(slotNum, mSoundFile[slotNum]);
+      }
+   }
+   Con::printf("Our owner entity has a new component being added! SoundComponent welcomes component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
+}
+
+//This is called any time a component is removed from an entity. Every component current owned by the entity is informed of the event.
+//This allows cleanup and dependency management.
+void SoundComponent::componentRemovedFromOwner(Component *comp)
+{
+   Con::printf("Our owner entity has a removed a component! SoundComponent waves farewell to component %i of type %s", comp->getId(), comp->getClassRep()->getNameSpace());
+}
+
+//Regular init persist fields function to set up static fields.
+void SoundComponent::initPersistFields()
+{
+   //addArray("Sounds", MaxSoundThreads);
+   addField("mSoundFile", TypeSFXTrackName, Offset(mSoundFile, SoundComponent), MaxSoundThreads, "If the text will not fit in the control, the deniedSound is played.");
+   addProtectedField("mPreviewSound", TypeBool, Offset(mPreviewSound, SoundComponent),
+      &_previewSound, &defaultProtectedGetFn, MaxSoundThreads, "Preview Sound", AbstractClassRep::FieldFlags::FIELD_ComponentInspectors);
+   addProtectedField("play", TypeBool, Offset(mPlay, SoundComponent),
+      &_autoplay, &defaultProtectedGetFn, MaxSoundThreads, "Whether playback of the emitter's sound should start as soon as the emitter object is added to the level.\n"
+      "If this is true, the emitter will immediately start to play when the level is loaded.");
+   //endArray("Sounds");
+   Parent::initPersistFields();
+}
+
+bool SoundComponent::_previewSound(void *object, const char *index, const char *data)
+{
+   U32 slotNum = (index != NULL) ? dAtoui(index) : 0;
+   SoundComponent* component = reinterpret_cast< SoundComponent* >(object);
+   if (!component->mPreviewSound[slotNum])
+      component->playAudio(slotNum, component->mSoundFile[slotNum]);
+   else
+      component->stopAudio(slotNum);
+   component->mPreviewSound[slotNum] = !component->mPreviewSound[slotNum];
+
+   return false;
+}
+
+bool SoundComponent::_autoplay(void *object, const char *index, const char *data)
+{
+   U32 slotNum = (index != NULL) ? dAtoui(index) : 0;
+   SoundComponent* component = reinterpret_cast< SoundComponent* >(object);
+   component->mPlay[slotNum] = dAtoui(data);
+   if (component->mPlay[slotNum])
+      component->playAudio(slotNum, component->mSoundFile[slotNum]);
+   else
+      component->stopAudio(slotNum);
+
+   return false;
+}
+
+U32 SoundComponent::packUpdate(NetConnection *con, U32 mask, BitStream *stream)
+{
+   U32 retMask = Parent::packUpdate(con, mask, stream);
+
+   if (mask & InitialUpdateMask)
+   {
+      // mask off sounds that aren't playing
+      S32 slotNum;
+      for (slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+         if (!mSoundThread[slotNum].play)
+            mask &= ~(SoundMaskN << slotNum);
+   }
+
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+      stream->writeFlag(mPreviewSound[slotNum]);
+
+   if (stream->writeFlag(mask & SoundMask)) 
+   {
+      for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++) 
+      {
+         Sound& st = mSoundThread[slotNum];
+
+         if (stream->writeFlag(mask & (SoundMaskN << slotNum)))
+         {
+            if (stream->writeFlag(st.play))
+               //stream->writeRangedU32(st.profile->getId(), DataBlockObjectIdFirst,
+               //   DataBlockObjectIdLast);
+               stream->writeString(st.profile->getName());
+
+         }
+      }
+   }
+
+   return retMask;
+}
+
+void SoundComponent::unpackUpdate(NetConnection *con, BitStream *stream)
+{
+   Parent::unpackUpdate(con, stream);
+
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+      mPreviewSound[slotNum] = stream->readFlag();
+
+   if (stream->readFlag())
+   {
+      for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+      {
+         if (stream->readFlag())
+         {
+            Sound& st = mSoundThread[slotNum];
+            st.play = stream->readFlag();
+            if (st.play)
+            {
+               //st.profile = (SFXTrack*)stream->readRangedU32(DataBlockObjectIdFirst,
+               //   DataBlockObjectIdLast);
+               char profileName[255];
+               stream->readString(profileName);
+
+               if (!Sim::findObject(profileName, st.profile))
+                  Con::errorf("Could not find SFXTrack");
+            }
+
+            //if (isProperlyAdded())
+               updateAudioState(st);
+         }
+      }
+   }
+}
+
+//This allows custom behavior in the event the owner is being edited
+void SoundComponent::onInspect()
+{
+}
+
+//This allows cleanup of the custom editor behavior if our owner stopped being edited
+void SoundComponent::onEndInspect()
+{
+}
+
+//Process tick update function, natch
+void SoundComponent::processTick()
+{
+   Parent::processTick();
+}
+
+//Client-side advance function
+void SoundComponent::advanceTime(F32 dt)
+{
+
+}
+
+//Client-side interpolation function
+void SoundComponent::interpolateTick(F32 delta)
+{
+
+}
+
+void SoundComponent::prepRenderImage(SceneRenderState *state)
+{
+   if (!mEnabled || !mOwner || !gEditingMission)
+      return;
+   ObjectRenderInst* ri = state->getRenderPass()->allocInst< ObjectRenderInst >();
+
+   ri->renderDelegate.bind(this, &SoundComponent::_renderObject);
+   ri->type = RenderPassManager::RIT_Editor;
+   ri->defaultKey = 0;
+   ri->defaultKey2 = 0;
+
+   state->getRenderPass()->addInst(ri);
+}
+
+void SoundComponent::_renderObject(ObjectRenderInst *ri,
+   SceneRenderState *state,
+   BaseMatInstance *overrideMat)
+{
+   if (overrideMat)
+      return;
+
+   GFXStateBlockDesc desc;
+   desc.setBlend(true);
+
+   MatrixF camera = GFX->getWorldMatrix();
+   camera.inverse();
+   Point3F pos = mOwner->getPosition();
+
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+   {
+      if (mPreviewSound[slotNum])
+      {
+         Sound& st = mSoundThread[slotNum];
+         if (st.sound && st.sound->getDescription())
+         {
+            F32 minRad = st.sound->getDescription()->mMinDistance;
+            F32 falloffRad = st.sound->getDescription()->mMaxDistance;
+            SphereF sphere(pos, falloffRad);
+            if (sphere.isContained(camera.getPosition()))
+               desc.setCullMode(GFXCullNone);
+
+            GFX->getDrawUtil()->drawSphere(desc, minRad, pos, ColorI(255, 0, 255, 64));
+            GFX->getDrawUtil()->drawSphere(desc, falloffRad, pos, ColorI(128, 0, 128, 64));
+         }
+      }
+   }
+}
+
+void SoundComponent::playAudio(U32 slotNum, SFXTrack* _profile)
+{
+   AssertFatal(slotNum < MaxSoundThreads, "ShapeBase::playAudio() bad slot index");
+   SFXTrack* profile = (_profile != NULL) ? _profile : mSoundFile[slotNum];
+   Sound& st = mSoundThread[slotNum];
+   if (profile && (!st.play || st.profile != profile))
+   {
+      setMaskBits(SoundMaskN << slotNum);
+      st.play = true;
+      st.profile = profile;
+      updateAudioState(st);
+   }
+}
+
+void SoundComponent::stopAudio(U32 slotNum)
+{
+   AssertFatal(slotNum < MaxSoundThreads, "ShapeBase::stopAudio() bad slot index");
+
+   Sound& st = mSoundThread[slotNum];
+   if (st.play)
+   {
+      st.play = false;
+      setMaskBits(SoundMaskN << slotNum);
+      updateAudioState(st);
+   }
+}
+
+void SoundComponent::updateServerAudio()
+{
+   // Timeout non-looping sounds
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++) 
+   {
+      Sound& st = mSoundThread[slotNum];
+      if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) 
+      {
+         //clearMaskBits(SoundMaskN << slotNum);
+         st.play = false;
+      }
+   }
+}
+
+void SoundComponent::updateAudioState(Sound& st)
+{
+   SFX_DELETE(st.sound);
+
+   if (st.play && st.profile)
+   {
+      if (isClientObject())
+      {
+         //if (Sim::findObject(SimObjectId((uintptr_t)st.profile), st.profile))
+        // {
+            st.sound = SFX->createSource(st.profile, &mOwner->getTransform());
+            if (st.sound)
+               st.sound->play();
+         //}
+         else
+            st.play = false;
+      }
+      else
+      {
+         // Non-looping sounds timeout on the server
+         st.timeout = 0;
+         if (!st.profile->getDescription()->mIsLooping)
+            st.timeout = Sim::getCurrentTime() + sAudioTimeout;
+      }
+   }
+   else
+      st.play = false;
+}
+
+void SoundComponent::updateAudioPos()
+{
+   for (S32 slotNum = 0; slotNum < MaxSoundThreads; slotNum++)
+   {
+      SFXSource* source = mSoundThread[slotNum].sound;
+      if (source)
+         source->setTransform(mOwner->getTransform());
+   }
+}
+
+//----------------------------------------------------------------------------
+DefineEngineMethod(SoundComponent, playAudio, bool, (S32 slot, SFXTrack* track), (0, nullAsType<SFXTrack*>()),
+   "@brief Attach a sound to this shape and start playing it.\n\n"
+
+   "@param slot Audio slot index for the sound (valid range is 0 - 3)\n" // 3 = ShapeBase::MaxSoundThreads-1
+   "@param track SFXTrack to play\n"
+   "@return true if the sound was attached successfully, false if failed\n\n"
+
+   "@see stopAudio()\n")
+{
+   if (track && slot >= 0 && slot < SoundComponent::MaxSoundThreads) {
+      object->playAudio(slot, track);
+      return true;
+   }
+   return false;
+}
+
+DefineEngineMethod(SoundComponent, stopAudio, bool, (S32 slot), ,
+   "@brief Stop a sound started with playAudio.\n\n"
+
+   "@param slot audio slot index (started with playAudio)\n"
+   "@return true if the sound was stopped successfully, false if failed\n\n"
+
+   "@see playAudio()\n")
+{
+   if (slot >= 0 && slot < SoundComponent::MaxSoundThreads) {
+      object->stopAudio(slot);
+      return true;
+   }
+   return false;
+}

+ 129 - 0
Engine/source/T3D/components/audio/SoundComponent.h

@@ -0,0 +1,129 @@
+//-----------------------------------------------------------------------------
+// Copyright (c) 2012 GarageGames, LLC
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy
+// of this software and associated documentation files (the "Software"), to
+// deal in the Software without restriction, including without limitation the
+// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
+// sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in
+// all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
+// IN THE SOFTWARE.
+//-----------------------------------------------------------------------------
+
+#ifndef EXAMPLE_COMPONENT_H
+#define EXAMPLE_COMPONENT_H
+#pragma once
+
+#ifndef COMPONENT_H
+#include "T3D/components/component.h"
+#endif
+#ifndef RENDER_COMPONENT_INTERFACE_H
+#include "T3D/components/render/renderComponentInterface.h"
+#endif
+
+class SFXSource;
+
+//SoundComponent
+//A basic example of the various functions you can utilize to make your own component!
+//This example doesn't really DO anything, persay, but you can readily copy it as a base
+//and use it as a starting point for your own.
+class SoundComponent : public Component, public RenderComponentInterface, public EditorInspectInterface
+{
+   typedef Component Parent;
+
+public:
+   enum PublicConstants
+   {
+      MaxSoundThreads = 4,            ///< Should be a power of 2
+   };
+
+   /// @name Network state masks
+   /// @{
+
+   ///
+   enum SoundComponentMasks
+   {
+      SoundMaskN = Parent::NextFreeMask << 6,       ///< Extends + MaxSoundThreads bits
+   };
+
+   enum BaseMaskConstants
+   {
+      SoundMask = (SoundMaskN << MaxSoundThreads) - SoundMaskN,
+   };
+   /// @name Scripted Sound
+   /// @{
+   struct Sound {
+      bool play;                    ///< Are we playing this sound?
+      SimTime timeout;              ///< Time until we stop playing this sound.
+      SFXTrack* profile;            ///< Profile on server
+      SFXSource* sound;             ///< Sound on client
+      Sound::Sound()
+      {
+         play = false;
+         timeout = 0;
+         profile = NULL;
+         sound = NULL;
+      }
+   };
+   Sound mSoundThread[MaxSoundThreads];
+   SFXTrack* mSoundFile[MaxSoundThreads];
+   bool mPreviewSound[MaxSoundThreads];
+   bool mPlay[MaxSoundThreads];
+   /// @}
+
+   SoundComponent();
+   virtual ~SoundComponent();
+   DECLARE_CONOBJECT(SoundComponent);
+
+   virtual bool onAdd();
+   virtual void onRemove();
+   static void initPersistFields();
+   static bool _previewSound(void *object, const char *index, const char *data);
+   static bool _autoplay(void *object, const char *index, const char *data);
+
+   virtual U32 packUpdate(NetConnection *con, U32 mask, BitStream *stream);
+   virtual void unpackUpdate(NetConnection *con, BitStream *stream);
+
+   virtual void onComponentRemove();
+   virtual void onComponentAdd();
+
+   virtual void componentAddedToOwner(Component *comp);
+   virtual void componentRemovedFromOwner(Component *comp);
+
+   virtual void onInspect();
+   virtual void onEndInspect();
+
+   virtual void processTick();
+   virtual void advanceTime(F32 dt);
+   virtual void interpolateTick(F32 delta);
+
+   void prepRenderImage(SceneRenderState* state);
+   void _renderObject(ObjectRenderInst *ri, SceneRenderState *state, BaseMatInstance *overrideMat);
+
+   virtual void playAudio(U32 slotNum, SFXTrack* profile = NULL);
+   virtual void stopAudio(U32 slot);
+   virtual void updateServerAudio();
+   virtual void updateAudioState(Sound& st);
+   virtual void updateAudioPos();
+
+   //why god why
+   virtual TSShape* getShape() { return NULL; };
+   Signal< void(RenderComponentInterface*) > onShapeChanged;
+   virtual TSShapeInstance* getShapeInstance() { return NULL; };
+   Signal< void(RenderComponentInterface*) > onShapeInstanceChanged;
+   virtual MatrixF getNodeTransform(S32 nodeIdx) { return MatrixF::Identity; };
+   virtual Vector<MatrixF> getNodeTransforms() { return NULL; };
+   virtual void setNodeTransforms(Vector<MatrixF> transforms) {};
+};
+
+#endif