Browse Source

Merge pull request #308 from rsredsq/RED-ATOMIC-GRAPHIC

Billboards exposed to the JSAPI.
JoshEngebretson 10 years ago
parent
commit
20aa446f5a

+ 1 - 1
Script/Packages/Atomic/Atomic3D.json

@@ -6,7 +6,7 @@
 			      "<Atomic/Scene/ValueAnimation.h>", "<Atomic/Graphics/Material.h>", "<Atomic/Resource/Image.h>"],
 			      "<Atomic/Scene/ValueAnimation.h>", "<Atomic/Graphics/Material.h>", "<Atomic/Resource/Image.h>"],
 	"classes" : ["Model",
 	"classes" : ["Model",
 				 "StaticModel",
 				 "StaticModel",
-				 "Animation", "AnimatedModel", "AnimationController", "AnimationState", "BillboardSet", "CustomGeometry",
+				 "Animation", "AnimatedModel", "AnimationController", "AnimationState", "Billboard", "BillboardSet", "CustomGeometry",
 				 "DecalSet", "ParticleEffect", "ParticleEmitter",
 				 "DecalSet", "ParticleEffect", "ParticleEmitter",
 				 "Skybox", "StaticModelGroup", "Terrain", "TerrainPatch"],
 				 "Skybox", "StaticModelGroup", "Terrain", "TerrainPatch"],
 	"overloads" : {
 	"overloads" : {

+ 63 - 47
Source/Atomic/Atomic3D/BillboardSet.cpp

@@ -58,7 +58,17 @@ const char* faceCameraModeNames[] =
 
 
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 inline bool CompareBillboards(Billboard* lhs, Billboard* rhs)
 {
 {
-    return lhs->sortDistance_ > rhs->sortDistance_;
+    return lhs->GetSortDistance() > rhs->GetSortDistance();
+}
+
+Billboard::Billboard() 
+{
+
+}
+
+Billboard::~Billboard() 
+{
+
 }
 }
 
 
 BillboardSet::BillboardSet(Context* context) :
 BillboardSet::BillboardSet(Context* context) :
@@ -134,12 +144,12 @@ void BillboardSet::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQue
 
 
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i].enabled_)
+        if (!billboards_[i]->enabled_)
             continue;
             continue;
 
 
         // Approximate the billboards as spheres for raycasting
         // Approximate the billboards as spheres for raycasting
-        float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_);
-        Vector3 center = billboardTransform * billboards_[i].position_;
+        float size = INV_SQRT_TWO * (billboards_[i]->size_.x_ * billboardScale.x_ + billboards_[i]->size_.y_ * billboardScale.y_);
+        Vector3 center = billboardTransform * billboards_[i]->position_;
         Sphere billboardSphere(center, size);
         Sphere billboardSphere(center, size);
 
 
         float distance = query.ray_.HitDistance(billboardSphere);
         float distance = query.ray_.HitDistance(billboardSphere);
@@ -240,12 +250,14 @@ void BillboardSet::SetNumBillboards(unsigned num)
     // Set default values to new billboards
     // Set default values to new billboards
     for (unsigned i = oldNum; i < num; ++i)
     for (unsigned i = oldNum; i < num; ++i)
     {
     {
-        billboards_[i].position_ = Vector3::ZERO;
-        billboards_[i].size_ = Vector2::ONE;
-        billboards_[i].uv_ = Rect::POSITIVE;
-        billboards_[i].color_ = Color(1.0f, 1.0f, 1.0f);
-        billboards_[i].rotation_ = 0.0f;
-        billboards_[i].enabled_ = false;
+        SharedPtr<Billboard> bb(new Billboard());
+        bb->position_ = Vector3::ZERO;
+        bb->size_ = Vector2::ONE;
+        bb->uv_ = Rect::POSITIVE;
+        bb->color_ = Color(1.0f, 1.0f, 1.0f);
+        bb->rotation_ = 0.0f;
+        bb->enabled_ = false;
+        billboards_[i] = bb;
     }
     }
 
 
     bufferSizeDirty_ = true;
     bufferSizeDirty_ = true;
@@ -295,7 +307,7 @@ Material* BillboardSet::GetMaterial() const
 
 
 Billboard* BillboardSet::GetBillboard(unsigned index)
 Billboard* BillboardSet::GetBillboard(unsigned index)
 {
 {
-    return index < billboards_.Size() ? &billboards_[index] : (Billboard*)0;
+    return index < billboards_.Size() ? billboards_[index] : (Billboard*)0;
 }
 }
 
 
 void BillboardSet::SetMaterialAttr(const ResourceRef& value)
 void BillboardSet::SetMaterialAttr(const ResourceRef& value)
@@ -310,15 +322,16 @@ void BillboardSet::SetBillboardsAttr(const VariantVector& value)
     unsigned numBillboards = index < value.Size() ? value[index++].GetUInt() : 0;
     unsigned numBillboards = index < value.Size() ? value[index++].GetUInt() : 0;
     SetNumBillboards(numBillboards);
     SetNumBillboards(numBillboards);
 
 
-    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End() && index < value.Size(); ++i)
+    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End() && index < value.Size(); ++i)
     {
     {
-        i->position_ = value[index++].GetVector3();
-        i->size_ = value[index++].GetVector2();
+        Billboard *bb = i->Get();
+        bb->position_ = value[index++].GetVector3();
+        bb->size_ = value[index++].GetVector2();
         Vector4 uv = value[index++].GetVector4();
         Vector4 uv = value[index++].GetVector4();
-        i->uv_ = Rect(uv.x_, uv.y_, uv.z_, uv.w_);
-        i->color_ = value[index++].GetColor();
-        i->rotation_ = value[index++].GetFloat();
-        i->enabled_ = value[index++].GetBool();
+        bb->uv_ = Rect(uv.x_, uv.y_, uv.z_, uv.w_);
+        bb->color_ = value[index++].GetColor();
+        bb->rotation_ = value[index++].GetFloat();
+        bb->enabled_ = value[index++].GetBool();
     }
     }
 
 
     Commit();
     Commit();
@@ -330,14 +343,15 @@ void BillboardSet::SetNetBillboardsAttr(const PODVector<unsigned char>& value)
     unsigned numBillboards = buf.ReadVLE();
     unsigned numBillboards = buf.ReadVLE();
     SetNumBillboards(numBillboards);
     SetNumBillboards(numBillboards);
 
 
-    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        i->position_ = buf.ReadVector3();
-        i->size_ = buf.ReadVector2();
-        i->uv_ = buf.ReadRect();
-        i->color_ = buf.ReadColor();
-        i->rotation_ = buf.ReadFloat();
-        i->enabled_ = buf.ReadBool();
+        Billboard *bb = i->Get();
+        bb->position_ = buf.ReadVector3();
+        bb->size_ = buf.ReadVector2();
+        bb->uv_ = buf.ReadRect();
+        bb->color_ = buf.ReadColor();
+        bb->rotation_ = buf.ReadFloat();
+        bb->enabled_ = buf.ReadBool();
     }
     }
 
 
     Commit();
     Commit();
@@ -354,14 +368,15 @@ VariantVector BillboardSet::GetBillboardsAttr() const
     ret.Reserve(billboards_.Size() * 6 + 1);
     ret.Reserve(billboards_.Size() * 6 + 1);
     ret.Push(billboards_.Size());
     ret.Push(billboards_.Size());
 
 
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        ret.Push(i->position_);
-        ret.Push(i->size_);
-        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
-        ret.Push(i->color_);
-        ret.Push(i->rotation_);
-        ret.Push(i->enabled_);
+        Billboard *bb = i->Get();
+        ret.Push(bb->position_);
+        ret.Push(bb->size_);
+        ret.Push(Vector4(bb->uv_.min_.x_, bb->uv_.min_.y_, bb->uv_.max_.x_, bb->uv_.max_.y_));
+        ret.Push(bb->color_);
+        ret.Push(bb->rotation_);
+        ret.Push(bb->enabled_);
     }
     }
 
 
     return ret;
     return ret;
@@ -372,14 +387,15 @@ const PODVector<unsigned char>& BillboardSet::GetNetBillboardsAttr() const
     attrBuffer_.Clear();
     attrBuffer_.Clear();
     attrBuffer_.WriteVLE(billboards_.Size());
     attrBuffer_.WriteVLE(billboards_.Size());
 
 
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        attrBuffer_.WriteVector3(i->position_);
-        attrBuffer_.WriteVector2(i->size_);
-        attrBuffer_.WriteRect(i->uv_);
-        attrBuffer_.WriteColor(i->color_);
-        attrBuffer_.WriteFloat(i->rotation_);
-        attrBuffer_.WriteBool(i->enabled_);
+        Billboard *bb = i->Get();
+        attrBuffer_.WriteVector3(bb->position_);
+        attrBuffer_.WriteVector2(bb->size_);
+        attrBuffer_.WriteRect(bb->uv_);
+        attrBuffer_.WriteColor(bb->color_);
+        attrBuffer_.WriteFloat(bb->rotation_);
+        attrBuffer_.WriteBool(bb->enabled_);
     }
     }
 
 
     return attrBuffer_.GetBuffer();
     return attrBuffer_.GetBuffer();
@@ -395,11 +411,11 @@ void BillboardSet::OnWorldBoundingBoxUpdate()
 
 
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i].enabled_)
+        if (!billboards_[i]->enabled_)
             continue;
             continue;
 
 
-        float size = INV_SQRT_TWO * (billboards_[i].size_.x_ * billboardScale.x_ + billboards_[i].size_.y_ * billboardScale.y_);
-        Vector3 center = billboardTransform * billboards_[i].position_;
+        float size = INV_SQRT_TWO * (billboards_[i]->size_.x_ * billboardScale.x_ + billboards_[i]->size_.y_ * billboardScale.y_);
+        Vector3 center = billboardTransform * billboards_[i]->position_;
         Vector3 edge = Vector3::ONE * size;
         Vector3 edge = Vector3::ONE * size;
         worldBox.Merge(BoundingBox(center - edge, center + edge));
         worldBox.Merge(BoundingBox(center - edge, center + edge));
 
 
@@ -476,7 +492,7 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     // First check number of enabled billboards
     // First check number of enabled billboards
     for (unsigned i = 0; i < numBillboards; ++i)
     for (unsigned i = 0; i < numBillboards; ++i)
     {
     {
-        if (billboards_[i].enabled_)
+        if (billboards_[i]->enabled_)
             ++enabledBillboards;
             ++enabledBillboards;
     }
     }
 
 
@@ -486,12 +502,12 @@ void BillboardSet::UpdateVertexBuffer(const FrameInfo& frame)
     // Then set initial sort order and distances
     // Then set initial sort order and distances
     for (unsigned i = 0; i < numBillboards; ++i)
     for (unsigned i = 0; i < numBillboards; ++i)
     {
     {
-        Billboard& billboard = billboards_[i];
-        if (billboard.enabled_)
+        SharedPtr<Billboard> billboard = billboards_[i];
+        if (billboard->enabled_)
         {
         {
-            sortedBillboards_[index++] = &billboard;
+            sortedBillboards_[index++] = billboard;
             if (sorted_)
             if (sorted_)
-                billboard.sortDistance_ = frame.camera_->GetDistanceSquared(billboardTransform * billboards_[i].position_);
+                billboard->sortDistance_ = frame.camera_->GetDistanceSquared(billboardTransform * billboards_[i]->position_);
         }
         }
     }
     }
 
 

+ 33 - 3
Source/Atomic/Atomic3D/BillboardSet.h

@@ -35,8 +35,38 @@ class IndexBuffer;
 class VertexBuffer;
 class VertexBuffer;
 
 
 /// One billboard in the billboard set.
 /// One billboard in the billboard set.
-struct ATOMIC_API Billboard
+struct ATOMIC_API Billboard : public RefCounted
 {
 {
+    friend class BillboardSet;
+    friend class ParticleEmitter;
+    REFCOUNTED(Billboard);
+
+public:
+    Billboard();
+    virtual ~Billboard();
+
+    Vector3 GetPosition() { return position_; }
+    void SetPosition(Vector3 &position) { position_ = position; }
+
+    Vector3 GetSize() { return size_; }
+    void SetSize(Vector2 &size) { size_ = size; }
+
+    Rect GetUV() { return uv_; }
+    void SetUV(Rect &uv) { uv_ = uv; }
+
+    Color GetColor() { return color_; }
+    void SetColor(Color &color) { color_ = color; }
+
+    float GetRotation() { return rotation_; }
+    void SetRotation(float rotation) { rotation_ = rotation; }
+
+    bool IsEnabled() { return enabled_; }
+    void SetEnabled(bool enabled) { enabled_ = enabled; }
+
+    float GetSortDistance() { return sortDistance_; }
+    void SetSortDistance(float sortDistance) { sortDistance_ = sortDistance; }
+
+private:
     /// Position.
     /// Position.
     Vector3 position_;
     Vector3 position_;
     /// Two-dimensional size.
     /// Two-dimensional size.
@@ -101,7 +131,7 @@ public:
     unsigned GetNumBillboards() const { return billboards_.Size(); }
     unsigned GetNumBillboards() const { return billboards_.Size(); }
 
 
     /// Return all billboards.
     /// Return all billboards.
-    PODVector<Billboard>& GetBillboards() { return billboards_; }
+    Vector<SharedPtr<Billboard>>& GetBillboards() { return billboards_; }
 
 
     /// Return billboard by index.
     /// Return billboard by index.
     Billboard* GetBillboard(unsigned index);
     Billboard* GetBillboard(unsigned index);
@@ -141,7 +171,7 @@ protected:
     void MarkPositionsDirty();
     void MarkPositionsDirty();
 
 
     /// Billboards.
     /// Billboards.
-    PODVector<Billboard> billboards_;
+    Vector<SharedPtr<Billboard>> billboards_;
     /// Coordinate axes on which camera facing is done.
     /// Coordinate axes on which camera facing is done.
     Vector3 faceCameraAxes_;
     Vector3 faceCameraAxes_;
     /// Animation LOD bias.
     /// Animation LOD bias.

+ 27 - 26
Source/Atomic/Atomic3D/ParticleEmitter.cpp

@@ -169,16 +169,16 @@ void ParticleEmitter::Update(const FrameInfo& frame)
     for (unsigned i = 0; i < particles_.Size(); ++i)
     for (unsigned i = 0; i < particles_.Size(); ++i)
     {
     {
         Particle& particle = particles_[i];
         Particle& particle = particles_[i];
-        Billboard& billboard = billboards_[i];
+        SharedPtr<Billboard> billboard = billboards_[i];
 
 
-        if (billboard.enabled_)
+        if (billboard->enabled_)
         {
         {
             needCommit = true;
             needCommit = true;
 
 
             // Time to live
             // Time to live
             if (particle.timer_ >= particle.timeToLive_)
             if (particle.timer_ >= particle.timeToLive_)
             {
             {
-                billboard.enabled_ = false;
+                billboard->enabled_ = false;
                 continue;
                 continue;
             }
             }
             particle.timer_ += lastTimeStep_;
             particle.timer_ += lastTimeStep_;
@@ -199,10 +199,10 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                 Vector3 force = -dampingForce * particle.velocity_;
                 Vector3 force = -dampingForce * particle.velocity_;
                 particle.velocity_ += lastTimeStep_ * force;
                 particle.velocity_ += lastTimeStep_ * force;
             }
             }
-            billboard.position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
+            billboard->position_ += lastTimeStep_ * particle.velocity_ * scaleVector;
 
 
             // Rotation
             // Rotation
-            billboard.rotation_ += lastTimeStep_ * particle.rotationSpeed_;
+            billboard->rotation_ += lastTimeStep_ * particle.rotationSpeed_;
 
 
             // Scaling
             // Scaling
             float sizeAdd = effect_->GetSizeAdd();
             float sizeAdd = effect_->GetSizeAdd();
@@ -214,7 +214,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                     particle.scale_ = 0.0f;
                     particle.scale_ = 0.0f;
                 if (sizeMul != 1.0f)
                 if (sizeMul != 1.0f)
                     particle.scale_ *= (lastTimeStep_ * (sizeMul - 1.0f)) + 1.0f;
                     particle.scale_ *= (lastTimeStep_ * (sizeMul - 1.0f)) + 1.0f;
-                billboard.size_ = particle.size_ * particle.scale_;
+                billboard->size_ = particle.size_ * particle.scale_;
             }
             }
 
 
             // Color interpolation
             // Color interpolation
@@ -228,9 +228,9 @@ void ParticleEmitter::Update(const FrameInfo& frame)
                         ++index;
                         ++index;
                 }
                 }
                 if (index < colorFrames_.Size() - 1)
                 if (index < colorFrames_.Size() - 1)
-                    billboard.color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
+                    billboard->color_ = colorFrames_[index].Interpolate(colorFrames_[index + 1], particle.timer_);
                 else
                 else
-                    billboard.color_ = colorFrames_[index].color_;
+                    billboard->color_ = colorFrames_[index].color_;
             }
             }
 
 
             // Texture animation
             // Texture animation
@@ -240,7 +240,7 @@ void ParticleEmitter::Update(const FrameInfo& frame)
             {
             {
                 if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
                 if (particle.timer_ >= textureFrames_[texIndex + 1].time_)
                 {
                 {
-                    billboard.uv_ = textureFrames_[texIndex + 1].uv_;
+                    billboard->uv_ = textureFrames_[texIndex + 1].uv_;
                     ++texIndex;
                     ++texIndex;
                 }
                 }
             }
             }
@@ -308,8 +308,8 @@ void ParticleEmitter::ResetEmissionTimer()
 
 
 void ParticleEmitter::RemoveAllParticles()
 void ParticleEmitter::RemoveAllParticles()
 {
 {
-    for (PODVector<Billboard>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
-        i->enabled_ = false;
+    for (Vector<SharedPtr<Billboard>>::Iterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+        i->Get()->enabled_ = false;
 
 
     Commit();
     Commit();
 }
 }
@@ -400,14 +400,15 @@ VariantVector ParticleEmitter::GetParticleBillboardsAttr() const
     ret.Reserve(billboards_.Size() * 6 + 1);
     ret.Reserve(billboards_.Size() * 6 + 1);
     ret.Push(billboards_.Size());
     ret.Push(billboards_.Size());
 
 
-    for (PODVector<Billboard>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
+    for (Vector<SharedPtr<Billboard>>::ConstIterator i = billboards_.Begin(); i != billboards_.End(); ++i)
     {
     {
-        ret.Push(i->position_);
-        ret.Push(i->size_);
-        ret.Push(Vector4(i->uv_.min_.x_, i->uv_.min_.y_, i->uv_.max_.x_, i->uv_.max_.y_));
-        ret.Push(i->color_);
-        ret.Push(i->rotation_);
-        ret.Push(i->enabled_);
+        Billboard *bb = i->Get();
+        ret.Push(bb->position_);
+        ret.Push(bb->size_);
+        ret.Push(Vector4(bb->uv_.min_.x_, bb->uv_.min_.y_, bb->uv_.max_.x_, bb->uv_.max_.y_));
+        ret.Push(bb->color_);
+        ret.Push(bb->rotation_);
+        ret.Push(bb->enabled_);
     }
     }
 
 
     return ret;
     return ret;
@@ -430,7 +431,7 @@ bool ParticleEmitter::EmitNewParticle()
         return false;
         return false;
     assert(index < particles_.Size());
     assert(index < particles_.Size());
     Particle& particle = particles_[index];
     Particle& particle = particles_[index];
-    Billboard& billboard = billboards_[index];
+    SharedPtr<Billboard> billboard = billboards_[index];
 
 
     Vector3 startPos;
     Vector3 startPos;
     Vector3 startDir;
     Vector3 startDir;
@@ -480,14 +481,14 @@ bool ParticleEmitter::EmitNewParticle()
     particle.colorIndex_ = 0;
     particle.colorIndex_ = 0;
     particle.texIndex_ = 0;
     particle.texIndex_ = 0;
 
 
-    billboard.position_ = startPos;
-    billboard.size_ = particles_[index].size_;
+    billboard->position_ = startPos;
+    billboard->size_ = particles_[index].size_;
     const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
     const Vector<TextureFrame>& textureFrames_ = effect_->GetTextureFrames();
-    billboard.uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
-    billboard.rotation_ = effect_->GetRandomRotation();
+    billboard->uv_ = textureFrames_.Size() ? textureFrames_[0].uv_ : Rect::POSITIVE;
+    billboard->rotation_ = effect_->GetRandomRotation();
     const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
     const Vector<ColorFrame>& colorFrames_ = effect_->GetColorFrames();
-    billboard.color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
-    billboard.enabled_ = true;
+    billboard->color_ = colorFrames_.Size() ? colorFrames_[0].color_ : Color();
+    billboard->enabled_ = true;
 
 
     return true;
     return true;
 }
 }
@@ -496,7 +497,7 @@ unsigned ParticleEmitter::GetFreeParticle() const
 {
 {
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     for (unsigned i = 0; i < billboards_.Size(); ++i)
     {
     {
-        if (!billboards_[i].enabled_)
+        if (!billboards_[i]->enabled_)
             return i;
             return i;
     }
     }
 
 

+ 21 - 0
Source/AtomicJS/Javascript/JSAtomic3D.cpp

@@ -22,6 +22,7 @@
 
 
 #include <Atomic/Atomic3D/StaticModel.h>
 #include <Atomic/Atomic3D/StaticModel.h>
 #include <Atomic/Atomic3D/CustomGeometry.h>
 #include <Atomic/Atomic3D/CustomGeometry.h>
+#include <Atomic/Atomic3D/BillboardSet.h>
 
 
 #include "JSAtomic3D.h"
 #include "JSAtomic3D.h"
 
 
@@ -62,6 +63,21 @@ static int CustomGeometry_SetMaterialIndex(duk_context* ctx) {
     return 0;
     return 0;
 }
 }
 
 
+static int BillboardSet_GetBillboard(duk_context* ctx) 
+{
+    duk_push_this(ctx);
+
+    BillboardSet* billboardSet = js_to_class_instance<BillboardSet>(ctx, -1, 0);
+
+    unsigned index = (unsigned)duk_to_number(ctx, 0);
+    
+    Billboard* billboard = billboardSet->GetBillboard(index);
+
+    js_push_class_object_instance(ctx, billboard, "Billboard");
+
+    return 1;
+}
+
 void jsapi_init_atomic3d(JSVM* vm)
 void jsapi_init_atomic3d(JSVM* vm)
 {
 {
     duk_context* ctx = vm->GetJSContext();
     duk_context* ctx = vm->GetJSContext();
@@ -75,6 +91,11 @@ void jsapi_init_atomic3d(JSVM* vm)
     duk_push_c_function(ctx, CustomGeometry_SetMaterialIndex, 2);
     duk_push_c_function(ctx, CustomGeometry_SetMaterialIndex, 2);
     duk_put_prop_string(ctx, -2, "setMaterialIndex");
     duk_put_prop_string(ctx, -2, "setMaterialIndex");
     duk_pop(ctx); // pop AObject prototype
     duk_pop(ctx); // pop AObject prototype
+
+    js_class_get_prototype(ctx, "Atomic", "BillboardSet");
+    duk_push_c_function(ctx, BillboardSet_GetBillboard, 1);
+    duk_put_prop_string(ctx, -2, "getBillboard");
+    duk_pop(ctx); // pop AObject prototype
 }
 }
 
 
 }
 }