Browse Source

Initial work on scripted inspector fields that can hold resources

Josh Engebretson 10 years ago
parent
commit
346f006317

+ 48 - 0
Script/AtomicEditor/ui/frames/inspector/DataBinding.ts

@@ -1,3 +1,6 @@
+import InspectorUtils = require("./InspectorUtils");
+import EditorUI = require("ui/EditorUI");
+
 class DataBinding {
 
     constructor(object: Atomic.Serializable, attrInfo: Atomic.AttributeInfo, widget: Atomic.UIWidget) {
@@ -145,6 +148,42 @@ class DataBinding {
                 layout.addChild(select);
             }
 
+        } else if (attrInfo.type == Atomic.VAR_RESOURCEREF && attrInfo.resourceTypeName) {
+
+            var importerName = ToolCore.assetDatabase.getResourceImporterName(attrInfo.resourceTypeName);
+
+            if (importerName) {
+
+                var parent = new Atomic.UILayout();
+                var o = InspectorUtils.createAttrEditFieldWithSelectButton("", parent);
+
+                var lp = new Atomic.UILayoutParams();
+                lp.width = 140;
+                o.editField.layoutParams = lp;
+                o.editField.readOnly = true;
+
+                // stuff editfield in so can be reference
+                parent["editField"] = o.editField;
+
+                var selectButton = o.selectButton;
+
+                selectButton.onClick = () => {
+
+                    EditorUI.getModelOps().showResourceSelection("Select " + attrInfo.resourceTypeName + " Resource", importerName, function(asset: ToolCore.Asset) {
+
+                        var resource = Atomic.cache.getResource(attrInfo.resourceTypeName, asset.path);
+
+                        object.setAttribute(attrInfo.name, resource);
+                        if (resource)
+                            o.editField.text = resource.name;
+
+                    });
+
+                }
+
+                widget = parent;
+            }
+
         }
 
         if (widget) {
@@ -238,6 +277,15 @@ class DataBinding {
                     select.value = value[i];
             }
 
+        } else if (attrInfo.type == Atomic.VAR_RESOURCEREF && attrInfo.resourceTypeName) {
+
+            var resource = <Atomic.Resource> object.getAttribute(attrInfo.name);
+
+            if (resource)
+                widget["editField"].text = resource.name;
+            else
+                widget["editField"].text = "";
+
         }
 
         this.widgetLocked = false;

+ 4 - 2
Script/AtomicEditor/ui/frames/inspector/InspectorUtils.ts

@@ -84,8 +84,10 @@ class InspectorUtils {
     var attrLayout = new Atomic.UILayout();
     attrLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP;
 
-    var _name = InspectorUtils.createAttrName(name);
-    attrLayout.addChild(_name);
+    if (name) {
+      var _name = InspectorUtils.createAttrName(name);
+      attrLayout.addChild(_name);
+    }
 
     var fieldLayout = new Atomic.UILayout();
     fieldLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_POSITION_LEFT_TOP;

+ 1 - 0
Script/TypeScript/AtomicWork.d.ts

@@ -160,6 +160,7 @@ declare module Atomic {
         mode: number; // AM_*
         defaultValue: string;
         enumNames: string[];
+        resourceTypeName: string;
 
     }
 

+ 1 - 0
Script/TypeScript/ToolCore.d.ts

@@ -299,6 +299,7 @@ declare module ToolCore {
       getCachePath(): string;
       deleteAsset(asset: Asset): void;
       scan(): void;
+      getResourceImporterName(resourceTypeName: string): string;
       getDotAssetFilename(path: string): string;
       getFolderAssets(folder:string):ToolCore.Asset[];
       getAssetsByImporterType(type:string):ToolCore.Asset[];

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

@@ -2,6 +2,9 @@
 // Please see LICENSE.md in repository root for license information
 // https://github.com/AtomicGameEngine/AtomicGameEngine
 
+#include <Atomic/Core/Context.h>
+#include <Atomic/Resource/ResourceCache.h>
+
 #include "JSAPI.h"
 #include "JSVM.h"
 
@@ -246,6 +249,14 @@ void js_to_variant(duk_context* ctx, int variantIdx, Variant &v)
         return;
     }
 
+    if (duk_is_object(ctx, variantIdx))
+    {
+        RefCounted* o = js_to_class_instance<RefCounted>(ctx, variantIdx, 0);
+        if (o)
+            v = o;
+        return;
+    }
+
     if (duk_is_array(ctx, variantIdx))
     {
         if (duk_get_length(ctx, variantIdx) == 2)
@@ -380,6 +391,9 @@ void js_push_variant(duk_context *ctx, const Variant& v)
     Vector3 vector3 = Vector3::ZERO;
     Vector4 vector4 = Vector4::ZERO;
     Color color = Color::BLACK;
+    Resource* resource = NULL;
+    ResourceCache* cache = NULL;
+    ResourceRef resourceRef;
 
     switch (type)
     {
@@ -419,6 +433,13 @@ void js_push_variant(duk_context *ctx, const Variant& v)
 
         break;
 
+    case VAR_RESOURCEREF:
+        resourceRef = v.GetResourceRef();
+        cache = JSVM::GetJSVM(ctx)->GetContext()->GetSubsystem<ResourceCache>();
+        resource = cache->GetResource(resourceRef.type_, resourceRef.name_);
+        js_push_class_object_instance(ctx, resource);
+        break;
+
     case VAR_BOOL:
         duk_push_boolean(ctx, v.GetBool() ? 1 : 0);
         break;

+ 48 - 17
Source/AtomicJS/Javascript/JSComponentFile.cpp

@@ -203,8 +203,8 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
     if (!InitModule())
         return false;
 
-    // TODO: cache these for player builds
-    // FIXME: this won't work with obfusication or minimization
+    // TODO: move the inspector field parsing to the JavascriptImporter
+    // which will work with obfusication, minimization, or bytecode dumps
 
     unsigned dataSize = source.GetSize();
     if (!dataSize && !source.GetName().Empty())
@@ -234,7 +234,7 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
 
             if (line.StartsWith("inspectorFields"))
             {
-                eval = line;
+                eval = line + "\n";
                 if (line.Contains("}"))
                 {
                     valid = true;
@@ -243,7 +243,7 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
             }
             else if (line.StartsWith("this.inspectorFields"))
             {
-                eval = line.Substring(5);
+                eval = line.Substring(5) + "\n";
                 if (line.Contains("}"))
                 {
                     valid = true;
@@ -252,7 +252,7 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
             }
             else if (line.StartsWith("var inspectorFields"))
             {
-                eval = line.Substring(4);
+                eval = line.Substring(4) + "\n";
                 if (line.Contains("}"))
                 {
                     valid = true;
@@ -263,7 +263,7 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
         }
         else
         {
-            eval += line;
+            eval += line + "\n";
         }
 
         if (line.Contains("}") && eval.Length())
@@ -321,23 +321,54 @@ bool JSComponentFile::BeginLoad(Deserializer& source)
                     }
                     else if (duk_is_array(ctx, -1))
                     {
-                        if (duk_get_length(ctx, -1) > 0)
+                        int length = duk_get_length(ctx, -1);
+
+                        if (length > 0)
                         {
+
                             duk_get_prop_index(ctx, -1, 0);
 
-                            // TODO: class types
-                            variantType = (VariantType) ((int)duk_require_number(ctx, -1));
+                            // resource ref detection
+                            if (duk_is_string(ctx, -1))
+                            {
+                                const char* classname = duk_to_string(ctx, -1);
 
-                            duk_pop(ctx);
-                        }
+                                duk_pop(ctx);
 
-                        if (duk_get_length(ctx, -1) > 1)
-                        {
-                            duk_get_prop_index(ctx, -1, 1);
-                            // default value
-                            js_to_variant(ctx, -1, defaultValue);
+                                const char* name = NULL;
+
+                                if (length > 1)
+                                {
+                                    duk_get_prop_index(ctx, -1, 1);
+                                    name = duk_require_string(ctx, -1);
+                                    duk_pop(ctx);
+
+                                }
+
+                                ResourceRef resourceRef(classname);
+                                if (name)
+                                    resourceRef.name_ = name;
+
+                                variantType = VAR_RESOURCEREF;
+                                defaultValue = resourceRef;
+
+                            }
+                            else
+                            {
+                                variantType = (VariantType) ((int)duk_require_number(ctx, -1));
+
+                                duk_pop(ctx);
+
+                                if (length > 1)
+                                {
+                                    duk_get_prop_index(ctx, -1, 1);
+                                    // default value
+                                    js_to_variant(ctx, -1, defaultValue);
+                                    duk_pop(ctx);
+                                }
+
+                            }
 
-                            duk_pop(ctx);
                         }
 
                     }

+ 1 - 0
Source/AtomicJS/Javascript/JSComponentFile.h

@@ -44,6 +44,7 @@ public:
     static void RegisterObject(Context* context);
 
     const HashMap<String, VariantType>& GetFields() const { return fields_; }
+    const VariantMap& GetDefaultFieldValues() const { return defaultFieldValues_; }
 
     /// Load resource from stream. May be called from a worker thread. Return true if successful.
     virtual bool BeginLoad(Deserializer& source);

+ 69 - 0
Source/AtomicJS/Javascript/JSSceneSerializable.cpp

@@ -108,6 +108,22 @@ static int Serializable_SetAttribute(duk_context* ctx)
     {
         v = (int) v.GetFloat();
     }
+    else if (variantType == VAR_RESOURCEREF)
+    {
+        RefCounted* ref = v.GetPtr();
+
+        if (ref && ref->IsObject())
+        {
+            Object* o = (Object*) ref;
+
+            // TODO: calling code must ensure we are a resource, can this be done here?
+            Resource* resource = (Resource*) o;
+
+            v = ResourceRef(resource->GetType(), resource->GetName());
+
+        }
+
+    }
 
     if (isAttr)
     {
@@ -182,6 +198,25 @@ static int Serializable_GetAttribute(duk_context* ctx)
     return 1;
 }
 
+static const String& GetResourceRefClassName(Context* context, const ResourceRef& ref)
+{
+    const HashMap<StringHash, SharedPtr<ObjectFactory>>& factories = context->GetObjectFactories();
+
+    HashMap<StringHash, SharedPtr<ObjectFactory>>::ConstIterator itr = factories.Begin();
+
+    while (itr != factories.End())
+    {
+        if (itr->first_ == ref.type_)
+        {
+            return itr->second_->GetTypeName();
+        }
+
+        itr++;
+    }
+
+    return String::EMPTY;
+}
+
 
 static int Serializable_GetAttributes(duk_context* ctx)
 {
@@ -218,6 +253,22 @@ static int Serializable_GetAttributes(duk_context* ctx)
             duk_push_number(ctx, (double) attr->type_);
             duk_put_prop_string(ctx, -2, "type");
 
+            if (attr->type_ == VAR_RESOURCEREF)
+            {
+                if (attr->defaultValue_.GetType() == VAR_RESOURCEREF)
+                {
+                    const ResourceRef& ref = attr->defaultValue_.GetResourceRef();
+                    const String& typeName = GetResourceRefClassName(serial->GetContext(), ref);
+
+                    if (typeName.Length())
+                    {
+                        duk_push_string(ctx, typeName.CString());
+                        duk_put_prop_string(ctx, -2, "resourceTypeName");
+
+                    }
+                }
+            }
+
             duk_push_string(ctx, attr->name_.CString());
             duk_put_prop_string(ctx, -2, "name");
 
@@ -260,6 +311,8 @@ static int Serializable_GetAttributes(duk_context* ctx)
 
         if (file)
         {
+
+            const VariantMap& defaultFieldValues = file->GetDefaultFieldValues();
             const HashMap<String, VariantType>& fields =  file->GetFields();
 
             if (fields.Size())
@@ -272,6 +325,22 @@ static int Serializable_GetAttributes(duk_context* ctx)
                     duk_push_number(ctx, (double) itr->second_);
                     duk_put_prop_string(ctx, -2, "type");
 
+                    if (itr->second_ == VAR_RESOURCEREF && defaultFieldValues.Contains(itr->first_))
+                    {
+                        if (defaultFieldValues[itr->first_]->GetType() == VAR_RESOURCEREF)
+                        {
+                            const ResourceRef& ref = defaultFieldValues[itr->first_]->GetResourceRef();
+                            const String& typeName = GetResourceRefClassName(serial->GetContext(), ref);
+
+                            if (typeName.Length())
+                            {
+                                duk_push_string(ctx, typeName.CString());
+                                duk_put_prop_string(ctx, -2, "resourceTypeName");
+
+                            }
+                        }
+                    }
+
                     duk_push_string(ctx, itr->first_.CString());
                     duk_put_prop_string(ctx, -2, "name");
 

+ 15 - 0
Source/ToolCore/Assets/AssetDatabase.cpp

@@ -478,5 +478,20 @@ void AssetDatabase::HandleFileChanged(StringHash eventType, VariantMap& eventDat
     }
 }
 
+String AssetDatabase::GetResourceImporterName(const String& resourceTypeName)
+{
+    // TODO: have resource type register themselves
+    if (resourceTypeToImporterType_.Empty())
+    {
+        resourceTypeToImporterType_["Sound"] = "AudioImporter";
+    }
+
+    if (!resourceTypeToImporterType_.Contains(resourceTypeName))
+        return String::EMPTY;
+
+    return resourceTypeToImporterType_[resourceTypeName];
+
+}
+
 
 }

+ 4 - 0
Source/ToolCore/Assets/AssetDatabase.h

@@ -36,6 +36,8 @@ public:
 
     void GetFolderAssets(String folder, PODVector<Asset*>& assets) const;
 
+    String GetResourceImporterName(const String& resourceTypeName);
+
     void GetAssetsByImporterType(StringHash type, PODVector<Asset*>& assets) const;
 
     void GetDirtyAssets(PODVector<Asset*>& assets);
@@ -60,6 +62,8 @@ private:
     SharedPtr<Project> project_;
     List<SharedPtr<Asset>> assets_;
 
+    HashMap<StringHash, String> resourceTypeToImporterType_;
+
     Vector<String> usedGUID_;
 
 };