Browse Source

Selectable axes for BillboardSet & Text3D camera facing. Default all axes (follow camera rotation fully). Fix ParticleEmitter faceCamera property not being exposed to AngelScript. Closes #325.

Lasse Öörni 11 years ago
parent
commit
00e9dc0a79

+ 14 - 4
Source/Engine/Graphics/BillboardSet.cpp

@@ -53,6 +53,7 @@ inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 
 BillboardSet::BillboardSet(Context* context) :
     Drawable(context, DRAWABLE_GEOMETRY),
+    faceCameraAxes_(Vector3::ONE),
     animationLodBias_(1.0f),
     animationLodTimer_(0.0f),
     relative_(true),
@@ -90,9 +91,10 @@ void BillboardSet::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Position", IsRelative, SetRelative, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
-    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Face Camera", GetFaceCamera, SetFaceCamera, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ATTRIBUTE(BillboardSet, VAR_BOOL, "Cast Shadows", castShadows_, false, AM_DEFAULT);
+    ACCESSOR_ATTRIBUTE(BillboardSet, VAR_BOOL, "Face Camera", GetFaceCamera, SetFaceCamera, bool, true, AM_DEFAULT);
+    ATTRIBUTE(BillboardSet, VAR_VECTOR3, "Face Camera Axes", faceCameraAxes_, Vector3::ONE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_FLOAT, "Shadow Distance", GetShadowDistance, SetShadowDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(BillboardSet, VAR_FLOAT, "Animation LOD Bias", GetAnimationLodBias, SetAnimationLodBias, float, 1.0f, AM_DEFAULT);
@@ -175,10 +177,12 @@ void BillboardSet::UpdateBatches(const FrameInfo& frame)
         lodDistance_ = 0.0f;
     
     batches_[0].distance_ = distance_;
-    batches_[0].numWorldTransforms_ = faceCamera_ ? 1 : 2;
+    batches_[0].numWorldTransforms_ = 2;
+    // Billboard positioning
     transforms_[0] = relative_ ? node_->GetWorldTransform() : Matrix3x4::IDENTITY;
-    if (!faceCamera_)
-        transforms_[1] = node_->GetWorldTransform();
+    // Billboard rotation
+    transforms_[1] = Matrix3x4(Vector3::ZERO, faceCamera_ ? frame.camera_->GetFaceCameraRotation(
+        node_->GetWorldRotation(), faceCameraAxes_) : node_->GetWorldRotation(), Vector3::ONE);
 }
 
 void BillboardSet::UpdateGeometry(const FrameInfo& frame)
@@ -255,6 +259,12 @@ void BillboardSet::SetFaceCamera(bool enable)
     MarkNetworkUpdate();
 }
 
+void BillboardSet::SetFaceCameraAxes(const Vector3& axes)
+{
+    faceCameraAxes_ = axes;
+    MarkNetworkUpdate();
+}
+
 void BillboardSet::SetAnimationLodBias(float bias)
 {
     animationLodBias_ = Max(bias, 0.0f);

+ 6 - 0
Source/Engine/Graphics/BillboardSet.h

@@ -89,6 +89,8 @@ public:
     void SetSorted(bool enable);
     /// Set whether billboards face the camera automatically. Default true.
     void SetFaceCamera(bool enable);
+    /// Set on which coordinate axes the billboards face the camera. Default all axes (1,1,1). Negative axes imply facing away.
+    void SetFaceCameraAxes(const Vector3& axes);
     /// Set animation LOD bias.
     void SetAnimationLodBias(float bias);
     /// Mark for bounding box and vertex buffer update. Call after modifying the billboards.
@@ -110,6 +112,8 @@ public:
     bool IsSorted() const { return sorted_; }
     /// Return whether faces the camera automatically.
     bool GetFaceCamera() const { return faceCamera_; }
+    /// Return on which coordinate axes camera facing is done.
+    const Vector3& GetFaceCameraAxes() const { return faceCameraAxes_; }
     /// Return animation LOD bias.
     float GetAnimationLodBias() const { return animationLodBias_; }
     
@@ -134,6 +138,8 @@ protected:
     
     /// Billboards.
     PODVector<Billboard> billboards_;
+    /// Coordinate axes on which camera facing is done.
+    Vector3 faceCameraAxes_;
     /// Animation LOD bias.
     float animationLodBias_;
     /// Animation LOD timer.

+ 36 - 0
Source/Engine/Graphics/Camera.cpp

@@ -527,6 +527,42 @@ float Camera::GetLodDistance(float distance, float scale, float bias) const
         return orthoSize_ / d;
 }
 
+Quaternion Camera::GetFaceCameraRotation(const Quaternion& rotation, const Vector3& faceCameraAxes)
+{
+    if (!node_)
+        return rotation;
+    
+    // No facing
+    if (faceCameraAxes == Vector3::ZERO)
+        return rotation;
+    // Facing on all axes
+    else if (faceCameraAxes.x_ > 0.0f && faceCameraAxes.y_ > 0.0f && faceCameraAxes.z_ > 0.0f)
+        return node_->GetWorldRotation();
+    // Selective facing based on Euler angle rewriting
+    else
+    {
+        Vector3 euler = rotation.EulerAngles();
+        Vector3 cameraEuler = node_->GetWorldRotation().EulerAngles();
+        
+        if (faceCameraAxes.x_ > 0.0f)
+            euler.x_ = cameraEuler.x_;
+        else if (faceCameraAxes.x_ < 0.0f)
+            euler.x_ = cameraEuler.x_ + 180.0f;
+        
+        if (faceCameraAxes.y_ > 0.0f)
+            euler.y_ = cameraEuler.y_;
+        else if (faceCameraAxes.y_ < 0.0f)
+            euler.y_ = -cameraEuler.y_ + 180.0f;
+        
+        if (faceCameraAxes.z_ > 0.0f)
+            euler.z_ = cameraEuler.z_;
+        else if (faceCameraAxes.z_ < 0.0f)
+            euler.z_ = -cameraEuler.z_ + 180.0f;
+
+        return Quaternion(euler.x_, euler.y_, euler.z_);
+    }
+}
+
 Matrix3x4 Camera::GetEffectiveWorldTransform() const
 {
     Matrix3x4 worldTransform = node_ ? Matrix3x4(node_->GetWorldPosition(), node_->GetWorldRotation(), 1.0f) : Matrix3x4::IDENTITY;

+ 2 - 0
Source/Engine/Graphics/Camera.h

@@ -163,6 +163,8 @@ public:
     float GetDistanceSquared(const Vector3& worldPos) const;
     /// Return a scene node's LOD scaled distance.
     float GetLodDistance(float distance, float scale, float bias) const;
+    /// Return a world rotation for facing a camera on certain axes based on the existing world rotation.
+    Quaternion GetFaceCameraRotation(const Quaternion& rotation, const Vector3& faceCameraAxes);
     /// Get effective world transform for matrix and frustum calculations including reflection but excluding node scaling.
     Matrix3x4 GetEffectiveWorldTransform() const;
     /// Return if projection parameters are valid for rendering and raycasting.

+ 1 - 0
Source/Engine/Graphics/ParticleEmitter.cpp

@@ -119,6 +119,7 @@ void ParticleEmitter::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Relative Scale", IsScaled, SetScaled, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Sort By Distance", IsSorted, SetSorted, bool, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(ParticleEmitter, VAR_BOOL, "Face Camera", GetFaceCamera, SetFaceCamera, bool, true, AM_DEFAULT);
+    ATTRIBUTE(ParticleEmitter, VAR_VECTOR3, "Face Camera Axes", faceCameraAxes_, Vector3::ONE, AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Time To Live Min", timeToLiveMin_, DEFAULT_TIME_TO_LIVE, AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_FLOAT, "Time To Live Max", timeToLiveMax_, DEFAULT_TIME_TO_LIVE, AM_DEFAULT);
     ATTRIBUTE(ParticleEmitter, VAR_VECTOR2, "Particle Size Min", sizeMin_, DEFAULT_PARTICLE_SIZE, AM_DEFAULT);

+ 3 - 0
Source/Engine/LuaScript/pkgs/Graphics/BillboardSet.pkg

@@ -19,6 +19,7 @@ class BillboardSet : public Drawable
     void SetScaled(bool enable);
     void SetSorted(bool enable);
     void SetFaceCamera(bool enable);
+    void SetFaceCameraAxes(const Vector3& axes);
     void SetAnimationLodBias(float bias);
 
     void Commit();
@@ -30,6 +31,7 @@ class BillboardSet : public Drawable
     bool IsScaled() const;
     bool IsSorted() const;
     bool GetFaceCamera() const;
+    const Vector3& GetFaceCameraAxes() const;
     float GetAnimationLodBias() const;
     
     tolua_property__get_set Material* material;
@@ -38,5 +40,6 @@ class BillboardSet : public Drawable
     tolua_property__is_set bool scaled;
     tolua_property__is_set bool sorted;
     tolua_property__get_set bool faceCamera;
+    tolua_property__get_set Vector3& faceCameraAxes;
     tolua_property__get_set float animationLodBias;
 };

+ 3 - 0
Source/Engine/LuaScript/pkgs/UI/Text3D.pkg

@@ -26,6 +26,7 @@ class Text3D : public Drawable
     void SetColor(Corner corner, const Color& color);
     void SetOpacity(float opacity);
     void SetFaceCamera(bool enable);
+    void SetFaceCameraAxes(const Vector3& axes);
     
     Font* GetFont() const;
     Material* GetMaterial() const;
@@ -49,6 +50,7 @@ class Text3D : public Drawable
     const Color& GetColor(Corner corner) const;
     float GetOpacity() const;
     bool GetFaceCamera() const;
+    const Vector3& GetFaceCameraAxes() const;
     
     tolua_property__get_set Font* font;
     tolua_property__get_set Material* material;
@@ -69,6 +71,7 @@ class Text3D : public Drawable
     tolua_readonly tolua_property__get_set unsigned numChars;
     tolua_property__get_set float opacity;
     tolua_property__get_set bool faceCamera;
+    tolua_property__get_set Vector3& faceCameraAxes;
 };
 
 ${

+ 6 - 0
Source/Engine/Script/GraphicsAPI.cpp

@@ -1012,6 +1012,8 @@ static void RegisterBillboardSet(asIScriptEngine* engine)
     engine->RegisterObjectMethod("BillboardSet", "bool get_scaled() const", asMETHOD(BillboardSet, IsScaled), asCALL_THISCALL);
     engine->RegisterObjectMethod("BillboardSet", "void set_faceCamera(bool)", asMETHOD(BillboardSet, SetFaceCamera), asCALL_THISCALL);
     engine->RegisterObjectMethod("BillboardSet", "bool get_faceCamera() const", asMETHOD(BillboardSet, GetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("BillboardSet", "void set_faceCameraAxes(const Vector3&)", asMETHOD(BillboardSet, SetFaceCameraAxes), asCALL_THISCALL);
+    engine->RegisterObjectMethod("BillboardSet", "const Vector3& get_faceCameraAxes() const", asMETHOD(BillboardSet, GetFaceCameraAxes), asCALL_THISCALL);
     engine->RegisterObjectMethod("BillboardSet", "void set_animationLodBias(float)", asMETHOD(BillboardSet, SetAnimationLodBias), asCALL_THISCALL);
     engine->RegisterObjectMethod("BillboardSet", "float get_animationLodBias() const", asMETHOD(BillboardSet, GetAnimationLodBias), asCALL_THISCALL);
     engine->RegisterObjectMethod("BillboardSet", "Billboard@+ get_billboards(uint)", asMETHOD(BillboardSet, GetBillboard), asCALL_THISCALL);
@@ -1054,6 +1056,10 @@ static void RegisterParticleEmitter(asIScriptEngine* engine)
     engine->RegisterObjectMethod("ParticleEmitter", "bool get_sorted() const", asMETHOD(ParticleEmitter, IsSorted), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEmitter", "void set_scaled(bool)", asMETHOD(ParticleEmitter, SetScaled), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEmitter", "bool get_scaled() const", asMETHOD(ParticleEmitter, IsScaled), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ParticleEmitter", "void set_faceCamera(bool)", asMETHOD(ParticleEmitter, SetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ParticleEmitter", "bool get_faceCamera() const", asMETHOD(ParticleEmitter, GetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ParticleEmitter", "void set_faceCameraAxes(const Vector3&)", asMETHOD(ParticleEmitter, SetFaceCameraAxes), asCALL_THISCALL);
+    engine->RegisterObjectMethod("ParticleEmitter", "const Vector3& get_faceCameraAxes() const", asMETHOD(ParticleEmitter, GetFaceCameraAxes), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEmitter", "void set_updateInvisible(bool)", asMETHOD(ParticleEmitter, SetUpdateInvisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEmitter", "bool get_updateInvisible() const", asMETHOD(ParticleEmitter, GetUpdateInvisible), asCALL_THISCALL);
     engine->RegisterObjectMethod("ParticleEmitter", "void set_animationLodBias(float)", asMETHOD(ParticleEmitter, SetAnimationLodBias), asCALL_THISCALL);

+ 2 - 0
Source/Engine/Script/UIAPI.cpp

@@ -394,6 +394,8 @@ static void RegisterText3D(asIScriptEngine* engine)
     engine->RegisterObjectMethod("Text3D", "float get_opacity() const", asMETHOD(Text3D, GetOpacity), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text3D", "void set_faceCamera(bool)", asMETHOD(Text3D, SetFaceCamera), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text3D", "bool get_faceCamera() const", asMETHOD(Text3D, GetFaceCamera), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "void set_faceCameraAxes(const Vector3&)", asMETHOD(Text3D, SetFaceCameraAxes), asCALL_THISCALL);
+    engine->RegisterObjectMethod("Text3D", "const Vector3& get_faceCameraAxes() const", asMETHOD(Text3D, GetFaceCameraAxes), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text3D", "uint get_numRows() const", asMETHOD(Text3D, GetNumRows), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text3D", "uint get_numChars() const", asMETHOD(Text3D, GetNumChars), asCALL_THISCALL);
     engine->RegisterObjectMethod("Text3D", "int get_rowWidths(uint) const", asMETHOD(Text3D, GetRowWidth), asCALL_THISCALL);

+ 9 - 1
Source/Engine/UI/Text3D.cpp

@@ -50,6 +50,7 @@ Text3D::Text3D(Context* context) :
     text_(context),
     vertexBuffer_(new VertexBuffer(context_)),
     customWorldTransform_(Matrix3x4::IDENTITY),
+    faceCameraAxes_(Vector3::ONE),
     faceCamera_(false),
     textDirty_(true),
     geometryDirty_(true)
@@ -75,6 +76,7 @@ void Text3D::RegisterObject(Context* context)
     ATTRIBUTE(Text3D, VAR_BOOL, "Word Wrap", text_.wordWrap_, false, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Text3D, VAR_BOOL, "Can Be Occluded", IsOccludee, SetOccludee, bool, true, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Text3D, VAR_BOOL, "Face Camera", GetFaceCamera, SetFaceCamera, bool, false, AM_DEFAULT);
+    ATTRIBUTE(Text3D, VAR_VECTOR3, "Face Camera Axes", faceCameraAxes_, Vector3::ONE, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Text3D, VAR_FLOAT, "Draw Distance", GetDrawDistance, SetDrawDistance, float, 0.0f, AM_DEFAULT);
     ACCESSOR_ATTRIBUTE(Text3D, VAR_INT, "Width", GetWidth, SetWidth, int, 0, AM_DEFAULT);
     ENUM_ACCESSOR_ATTRIBUTE(Text3D, "Horiz Alignment", GetHorizontalAlignment, SetHorizontalAlignment, HorizontalAlignment, horizontalAlignments, HA_LEFT, AM_DEFAULT);
@@ -105,7 +107,8 @@ void Text3D::UpdateBatches(const FrameInfo& frame)
     
     if (faceCamera_)
     {
-        customWorldTransform_ = Matrix3x4(node_->GetWorldPosition(), frame.camera_->GetNode()->GetWorldRotation(), node_->GetWorldScale());
+        customWorldTransform_ = Matrix3x4(node_->GetWorldPosition(), frame.camera_->GetFaceCameraRotation(
+            node_->GetWorldRotation(), faceCameraAxes_), node_->GetWorldScale());
         worldBoundingBoxDirty_ = true;
     }
     
@@ -291,6 +294,11 @@ void Text3D::SetFaceCamera(bool enable)
     }
 }
 
+void Text3D::SetFaceCameraAxes(const Vector3& axes)
+{
+    faceCameraAxes_ = axes;
+}
+
 Material* Text3D::GetMaterial() const
 {
     return material_;

+ 6 - 0
Source/Engine/UI/Text3D.h

@@ -90,6 +90,8 @@ public:
     void SetOpacity(float opacity);
     /// Set whether to face the camera automatically.
     void SetFaceCamera(bool enable);
+    /// Set on which axes the text faces the camera. Default all axes (1,1,1). Negative axes imply facing away.
+    void SetFaceCameraAxes(const Vector3& axes);
     
     /// Return font.
     Font* GetFont() const;
@@ -135,6 +137,8 @@ public:
     float GetOpacity() const;
     /// Return whether faces the camera automatically.
     bool GetFaceCamera() const { return faceCamera_; }
+    /// Return on which coordinate axes camera facing is done.
+    const Vector3& GetFaceCameraAxes() const { return faceCameraAxes_; }
     
     /// Set font attribute.
     void SetFontAttr(ResourceRef value);
@@ -177,6 +181,8 @@ private:
     PODVector<float> uiVertexData_;
     /// Custom world transform for facing the camera automatically.
     Matrix3x4 customWorldTransform_;
+    /// Coordinate axes on which camera facing is done.
+    Vector3 faceCameraAxes_;
     /// Face camera flag.
     bool faceCamera_;
     /// Text needs update flag.