Browse Source

Submesh editing support, fixing array edit widget, display name of mesh in mesh inspector

Josh Engebretson 10 years ago
parent
commit
dda5a07557

+ 9 - 27
Script/AtomicEditor/ui/frames/inspector/ArrayEditWidget.ts

@@ -9,7 +9,7 @@ import InspectorUtils = require("./InspectorUtils");
 
 class ArrayEditWidget extends Atomic.UILayout {
 
-    constructor(title:string) {
+    constructor(title: string) {
 
         super();
 
@@ -29,48 +29,30 @@ class ArrayEditWidget extends Atomic.UILayout {
 
         InspectorUtils.createSeparator(this);
 
-        this.countEditField = <Atomic.UIEditField> countEdit.getWidget("editfield");
+        this.countEditField = <Atomic.UIEditField>countEdit.getWidget("editfield");
 
         this.subscribeToEvent(this.countEditField, "UIWidgetEditComplete", (ev) => this.handleUIWidgetEditCompleteEvent(ev));
-        this.subscribeToEvent(this.countEditField, "WidgetFocusChanged", (data) => this.handleCountWidgetFocusChanged(data));
 
     }
 
-    handleCountWidgetFocusChanged(ev) {
+    handleUIWidgetEditCompleteEvent(ev):boolean {
 
-        if (ev.focused) {
+        var count = Number(this.countEditField.text);
 
-            this.countRestore = this.countEditField.text;
+        if (this.onCountChanged) {
 
-        } else {
-
-            this.countEditField.text = this.countRestore;
+            this.onCountChanged(count);
 
         }
 
-    }
-
-    handleUIWidgetEditCompleteEvent(ev) {
-
-      if (this.countRestore != this.countEditField.text) {
-
-          this.countRestore = this.countEditField.text;
-
-          if (this.onCountChanged) {
-
-            this.onCountChanged(Number(this.countRestore));
-
-          }
-
-      }
+        return true;
 
     }
 
-    countEdit:Atomic.UIEditField;
+    countEdit: Atomic.UIEditField;
 
-    onCountChanged: (count:number) => void;
+    onCountChanged: (count: number) => void;
 
-    countRestore: string;
     countEditField: Atomic.UIEditField;
 
 }

+ 22 - 17
Script/AtomicEditor/ui/frames/inspector/AttributeInfoEdit.ts

@@ -17,6 +17,7 @@ class AttributeInfoEdit extends Atomic.UILayout {
     editWidget: Atomic.UIWidget;
 
     nameOverride: string;
+    hideName: boolean = false;
 
     constructor() {
 
@@ -52,11 +53,6 @@ class AttributeInfoEdit extends Atomic.UILayout {
 
         this.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
 
-        var name = new Atomic.UITextField();
-        name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
-        name.skinBg = "InspectorTextAttrName";
-        name.layoutParams = attrNameLP;
-
         if (attr.type == Atomic.VAR_VECTOR3 || attr.type == Atomic.VAR_COLOR ||
             attr.type == Atomic.VAR_QUATERNION) {
             this.axis = Atomic.UI_AXIS_Y;
@@ -64,19 +60,28 @@ class AttributeInfoEdit extends Atomic.UILayout {
             this.skinBg = "InspectorVectorAttrLayout";
         }
 
-        var bname = attr.name;
+        if (!this.hideName) {
+
+            var name = new Atomic.UITextField();
+            name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+            name.skinBg = "InspectorTextAttrName";
+            name.layoutParams = attrNameLP;
+            var bname = attr.name;
+
+            if (bname == "Is Enabled")
+                bname = "Enabled";
 
-        if (bname == "Is Enabled")
-            bname = "Enabled";
+            if (this.nameOverride)
+                name.text = this.nameOverride;
+            else
+                name.text = bname;
 
-        if (this.nameOverride)
-            name.text = this.nameOverride;
-        else
-            name.text = bname;
+            name.fontDescription = AttributeInfoEdit.fontDesc;
 
-        name.fontDescription = AttributeInfoEdit.fontDesc;
+            this.addChild(name);
 
-        this.addChild(name);
+
+        }
 
         this.addChild(this.editWidget);
 
@@ -139,7 +144,7 @@ class AttributeInfoEdit extends Atomic.UILayout {
     private static Ctor = (() => {
 
         var attrNameLP = AttributeInfoEdit.attrNameLP = new Atomic.UILayoutParams();
-        attrNameLP.width = 100;
+        attrNameLP.width = 120;
 
         var fd = AttributeInfoEdit.fontDesc = new Atomic.UIFontDescription();
         fd.id = "Vera";
@@ -681,7 +686,7 @@ class ResourceRefAttributeEdit extends AttributeInfoEdit {
                 if (resource) {
                     if (resource instanceof Atomic.Animation) {
 
-                      text = (<Atomic.Animation>resource).animationName;
+                        text = (<Atomic.Animation>resource).animationName;
 
                     } else {
 
@@ -826,7 +831,7 @@ class ResourceRefListAttributeEdit extends AttributeInfoEdit {
 
         var name = this.attrInfo.name + " Size";
         if (name == "AnimationResources")
-          name = "Animations";
+            name = "Animations";
 
         var sizeEdit = this.sizeEdit = InspectorUtils.createAttrEditField(name, layout);
 

+ 315 - 0
Script/AtomicEditor/ui/frames/inspector/ComponentAttributeUI.ts

@@ -5,8 +5,10 @@
 // license information: https://github.com/AtomicGameEngine/AtomicGameEngine
 //
 
+import EditorUI = require("ui/EditorUI");
 import InspectorUtils = require("./InspectorUtils");
 import AttributeInfoEdit = require("./AttributeInfoEdit");
+import SerializableEditType = require("./SerializableEditType");
 
 class LightCascadeAttributeEdit extends AttributeInfoEdit {
 
@@ -101,4 +103,317 @@ class LightCascadeAttributeEdit extends AttributeInfoEdit {
 
 }
 
+interface MaterialEdit {
+
+    index: number;
+    editField: Atomic.UIEditField;
+    selectButton: Atomic.UIButton;
+
+}
+
+class SubmeshAttributeEdit extends AttributeInfoEdit {
+
+    materialIndexes: number[] = [];
+
+    materialEdits: { [index: number]: MaterialEdit } = {};
+
+    mainLayout: Atomic.UILayout;
+    enabledCheckBox: Atomic.UICheckBox;
+    nameField: Atomic.UITextField;
+    name: string;
+
+    constructor(name: string) {
+
+        super();
+        this.name = name;
+        this.hideName = true;
+    }
+
+    createMaterialEdit(materialIndex: number) {
+
+        var o = InspectorUtils.createAttrEditFieldWithSelectButton("", this.mainLayout);
+
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 250;
+        o.editField.layoutParams = lp;
+        o.editField.readOnly = true;
+
+        var selectButton = o.selectButton;
+
+        var materialEdit: MaterialEdit = { index: materialIndex, editField: o.editField, selectButton: selectButton };
+        this.materialEdits[materialIndex] = materialEdit;
+
+        var resourceTypeName = "Material";
+        var importerName = ToolCore.assetDatabase.getResourceImporterName(resourceTypeName);
+
+        selectButton.onClick = () => {
+
+            EditorUI.getModelOps().showResourceSelection("Select " + resourceTypeName + " Resource", importerName, resourceTypeName, function(retObject: any) {
+
+                var resource: Atomic.Resource = null;
+
+                if (retObject instanceof ToolCore.Asset) {
+
+                    resource = (<ToolCore.Asset>retObject).getResource(resourceTypeName);
+
+                } else if (retObject instanceof Atomic.Resource) {
+
+                    resource = <Atomic.Resource>retObject;
+
+                }
+
+                this.editType.onAttributeInfoEdited(this.attrInfo, resource, materialIndex);
+                this.refresh();
+
+            }.bind(this));
+        }
+
+    }
+
+    createEditWidget() {
+
+        var mainLayout = this.mainLayout = new Atomic.UILayout();
+        mainLayout.axis = Atomic.UI_AXIS_Y;
+        mainLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+        mainLayout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        mainLayout.gravity = Atomic.UI_GRAVITY_LEFT_RIGHT;
+        mainLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+
+        var cb = InspectorUtils.createAttrCheckBox(this.name, mainLayout);
+        this.enabledCheckBox = cb.checkBox;
+
+        cb.checkBox.subscribeToEvent(cb.checkBox, "WidgetEvent", (ev: Atomic.UIWidgetEvent) => {
+
+            if (ev.type == Atomic.UI_EVENT_TYPE_CHANGED) {
+
+                var scene: Atomic.Scene;
+
+                for (var i in this.editType.objects) {
+
+                    var staticModel = <Atomic.StaticModel>this.editType.objects[i];
+
+                    scene = staticModel.scene;
+
+                    if (cb.checkBox.value) {
+
+                        staticModel.showGeometry(this.name);
+
+                    } else {
+
+                        staticModel.hideGeometry(this.name);
+
+                    }
+
+                }
+
+                scene.sendEvent("SceneEditEnd");
+
+                return true;
+
+            }
+
+            return false;
+
+
+        });
+
+
+        this.nameField = cb.textField;
+
+        this.editWidget = mainLayout;
+    }
+
+    refresh() {
+
+        var editType = this.editType;
+
+        var object = this.editType.getFirstObject();
+
+        if (!object) {
+            this.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
+            return;
+        }
+
+        this.visibility = Atomic.UI_WIDGET_VISIBILITY_VISIBLE;
+
+        var enabled = (<Atomic.StaticModel>object).getGeometryVisible(this.name);
+        var mixed = false;
+        for (var i in editType.objects) {
+
+            object = editType.objects[i];
+            var _enabled = (<Atomic.StaticModel>object).getGeometryVisible(this.name);
+            if (_enabled != enabled) {
+                mixed = true;
+                break;
+            }
+        }
+
+        if (mixed) {
+
+            this.enabledCheckBox.skinBg = "TBGreyCheckBoxNonUniform";
+            this.enabledCheckBox.value = 1;
+
+        } else {
+
+            this.enabledCheckBox.skinBg = "TBGreyCheckBox";
+            this.enabledCheckBox.value = enabled ? 1 : 0;
+
+        }
+
+        for (var i in this.materialIndexes) {
+
+            var idx = this.materialIndexes[i];
+
+            if (!this.materialEdits[idx]) {
+                this.createMaterialEdit(idx);
+            }
+
+            var matEdit = this.materialEdits[idx];
+
+            var uniform = editType.getUniformValue(this.attrInfo, matEdit.index);
+
+            if (uniform) {
+
+                var object = editType.getFirstObject();
+
+                if (object) {
+
+                    // for cached resources, use the asset name, otherwise use the resource path name
+                    var resource: Atomic.Resource;
+                    if (matEdit.index != -1) {
+                        resource = object.getAttribute(this.attrInfo.name).resources[matEdit.index];
+                    } else {
+                        resource = <Atomic.Resource>object.getAttribute(this.attrInfo.name);
+                    }
+
+                    var text = "";
+
+                    if (resource) {
+                        if (resource instanceof Atomic.Animation) {
+
+                            text = (<Atomic.Animation>resource).animationName;
+
+                        } else {
+
+                            text = resource.name;
+                            var asset = ToolCore.assetDatabase.getAssetByCachePath(resource.name);
+                            if (asset)
+                                text = asset.name;
+                        }
+                    }
+                    var pathinfo = Atomic.splitPath(text);
+
+                    matEdit.editField.text = pathinfo.fileName;
+                }
+
+
+            } else {
+                matEdit.editField.text = "--";
+            }
+
+        }
+
+    }
+
+}
+
+class SubmeshListAttributeEdit extends AttributeInfoEdit {
+
+    layout: Atomic.UILayout;
+
+    submeshEdits: { [name: string]: SubmeshAttributeEdit } = {};
+
+    constructor() {
+
+        super();
+
+        this.hideName = true;
+
+    }
+
+    createEditWidget() {
+
+        this.spacing = 0;
+
+        var layout = this.layout = new Atomic.UILayout();
+
+        layout.axis = Atomic.UI_AXIS_Y;
+        layout.spacing = 2;
+        layout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+        layout.layoutPosition = Atomic.UI_LAYOUT_POSITION_LEFT_TOP;
+        layout.gravity = Atomic.UI_GRAVITY_LEFT_RIGHT;
+        layout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+
+        var lp = new Atomic.UILayoutParams();
+        lp.width = 304;
+        layout.layoutParams = lp;
+
+        var name = new Atomic.UITextField();
+        name.textAlign = Atomic.UI_TEXT_ALIGN_LEFT;
+        name.skinBg = "InspectorTextAttrName";
+        name.layoutParams = AttributeInfoEdit.attrNameLP;
+        name.text = "Submeshes";
+
+        layout.addChild(name);
+
+        InspectorUtils.createSeparator(layout);
+
+        this.editWidget = layout;
+
+    }
+
+    refresh() {
+
+        var editType = this.editType;
+
+        var object = this.editType.getFirstObject();
+
+        if (!object) {
+            this.visibility = Atomic.UI_WIDGET_VISIBILITY_GONE;
+            return;
+        }
+
+        this.visibility = Atomic.UI_WIDGET_VISIBILITY_VISIBLE;
+
+        var name: string;
+        for (var i in editType.objects) {
+
+            // TODO: check for multiple selection with different models and display multiple selection info box
+
+            var staticModel = <Atomic.StaticModel>editType.objects[i];
+            var model = staticModel.model;
+
+            if (!model)
+                continue;
+
+            for (var j = 0; j < model.numGeometries; j++) {
+
+                name = model.getGeometryName(j);
+
+                if (!name)
+                    name = "Mesh";
+
+                if (!this.submeshEdits.hasOwnProperty(name)) {
+
+                    var submeshEdit = new SubmeshAttributeEdit(name);
+                    submeshEdit.initialize(this.editType, this.attrInfo);
+                    this.layout.addChild(submeshEdit);
+                    this.submeshEdits[name] = submeshEdit;
+                }
+
+                this.submeshEdits[name].materialIndexes.push(j);
+
+            }
+
+        }
+
+        for (name in this.submeshEdits)
+            this.submeshEdits[name].refresh();
+
+    }
+
+}
+
+AttributeInfoEdit.registerCustomAttr("AnimatedModel", "Material", SubmeshListAttributeEdit);
+AttributeInfoEdit.registerCustomAttr("StaticModel", "Material", SubmeshListAttributeEdit);
 AttributeInfoEdit.registerCustomAttr("Light", "CSM Splits", LightCascadeAttributeEdit);

+ 24 - 3
Script/AtomicEditor/ui/frames/inspector/InspectorUtils.ts

@@ -47,7 +47,7 @@ class InspectorUtils {
 
     // atttribute name layout param
     var atlp = new Atomic.UILayoutParams();
-    atlp.width = 100;
+    atlp.width = 120;
     nameField.layoutParams = atlp;
 
     return nameField;
@@ -61,7 +61,7 @@ class InspectorUtils {
     edit.skinBg = "TBAttrEditorField";
     edit.fontDescription = InspectorUtils.attrFontDesc;
     var lp = new Atomic.UILayoutParams();
-    lp.width = 140;
+    lp.width = 160;
     edit.layoutParams = lp;
 
     return edit;
@@ -75,7 +75,7 @@ class InspectorUtils {
     attrLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
     attrLayout.gravity = Atomic.UI_GRAVITY_LEFT_RIGHT;
     attrLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
-    
+
     var _name = InspectorUtils.createAttrName(name);
     attrLayout.addChild(_name);
 
@@ -88,6 +88,27 @@ class InspectorUtils {
 
   }
 
+  static createAttrCheckBox(name:string, parent:Atomic.UIWidget):{ textField:Atomic.UITextField, checkBox: Atomic.UICheckBox} {
+
+    var attrLayout = new Atomic.UILayout();
+
+    attrLayout.layoutSize = Atomic.UI_LAYOUT_SIZE_AVAILABLE;
+    attrLayout.gravity = Atomic.UI_GRAVITY_LEFT_RIGHT;
+    attrLayout.layoutDistribution = Atomic.UI_LAYOUT_DISTRIBUTION_GRAVITY;
+
+    var _name = InspectorUtils.createAttrName(name);
+    attrLayout.addChild(_name);
+
+    var checkBox = new Atomic.UICheckBox();
+
+    attrLayout.addChild(checkBox);
+    parent.addChild(attrLayout);
+
+    return {textField: _name, checkBox : checkBox};
+
+  }
+
+
   static createAttrEditFieldWithSelectButton(name:string, parent:Atomic.UIWidget):{editField:Atomic.UIEditField, selectButton:Atomic.UIButton} {
 
     var attrLayout = new Atomic.UILayout();

+ 7 - 1
Script/AtomicEditor/ui/frames/inspector/ModelInspector.ts

@@ -69,7 +69,13 @@ class ModelInspector extends InspectorWidget {
         // Model Section
         var modelLayout = this.createSection(rootLayout, "Model", 1);
 
-        this.scaleEdit = this.createAttrEditField("Scale", modelLayout);
+        var editField = InspectorUtils.createAttrEditField("Name", modelLayout);
+
+        var lp = new Atomic.UILayoutParams();
+        editField.readOnly = true;
+        editField.text = asset.name;
+
+        this.scaleEdit = InspectorUtils.createAttrEditField("Scale", modelLayout);
         this.scaleEdit.text = this.importer.scale.toString();
 
         // Animations Section

+ 34 - 0
Source/Atomic/Atomic3D/AnimatedModel.cpp

@@ -113,6 +113,14 @@ void AnimatedModel::RegisterObject(Context* context)
         Variant::emptyVariantVector, AM_FILE);
     ACCESSOR_ATTRIBUTE("Morphs", GetMorphsAttr, SetMorphsAttr, PODVector<unsigned char>, Variant::emptyBuffer,
         AM_DEFAULT | AM_NOEDIT);
+
+    // ATOMIC BEGIN
+
+    ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
+        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
+
+    // ATOMIC END
+
 }
 
 bool AnimatedModel::Load(Deserializer& source, bool setInstanceDefault)
@@ -273,6 +281,30 @@ void AnimatedModel::UpdateBatches(const FrameInfo& frame)
         lodDistance_ = newLodDistance;
         CalculateLodLevels();
     }
+
+    // Handle mesh hiding
+    if (true)//geometryEnabled_.Size() == batches_.Size())
+    {
+        for (unsigned i = 0; i < batches_.Size(); ++i)
+        {
+            SourceBatch* batch = &batches_[i];
+            StaticModelGeometryData* data = &geometryData_[i];
+
+            if (batch->geometry_)
+                data->batchGeometry_ = batch->geometry_;
+
+            if (data->enabled_ && !batch->geometry_)
+            {
+                batch->geometry_ = data->batchGeometry_;
+            }
+            else if (!data->enabled_ && batch->geometry_)
+            {
+                data->batchGeometry_ = batch->geometry_;
+                batch->geometry_ = 0;
+            }
+        }
+    }
+
 }
 
 void AnimatedModel::UpdateGeometry(const FrameInfo& frame)
@@ -333,6 +365,8 @@ void AnimatedModel::SetModel(Model* model, bool createBones)
         {
             geometries_[i] = geometries[i];
             geometryData_[i].center_ = geometryCenters[i];
+            geometryData_[i].enabled_ = true;
+            geometryData_[i].batchGeometry_ = 0;
         }
 
         // Copy geometry bone mappings

+ 88 - 0
Source/Atomic/Atomic3D/StaticModel.cpp

@@ -70,6 +70,13 @@ void StaticModel::RegisterObject(Context* context)
     ACCESSOR_ATTRIBUTE("LOD Bias", GetLodBias, SetLodBias, float, 1.0f, AM_DEFAULT);
     COPY_BASE_ATTRIBUTES(Drawable);
     ATTRIBUTE("Occlusion LOD Level", int, occlusionLodLevel_, M_MAX_UNSIGNED, AM_DEFAULT);
+
+    // ATOMIC BEGIN
+
+    ACCESSOR_ATTRIBUTE("Geometry Enabled", GetGeometryEnabledAttr, SetGeometryEnabledAttr, VariantVector,
+        Variant::emptyVariantVector, AM_FILE | AM_NOEDIT);
+
+    // ATOMIC END
 }
 
 void StaticModel::ProcessRayQuery(const RayOctreeQuery& query, PODVector<RayQueryResult>& results)
@@ -262,6 +269,8 @@ void StaticModel::SetModel(Model* model)
             batches_[i].worldTransform_ = worldTransform;
             geometries_[i] = geometries[i];
             geometryData_[i].center_ = geometryCenters[i];
+            geometryData_[i].enabled_ = true;
+            geometryData_[i].batchGeometry_ = 0;
         }
 
         SetBoundingBox(model->GetBoundingBox());
@@ -454,4 +463,83 @@ void StaticModel::HandleModelReloadFinished(StringHash eventType, VariantMap& ev
     SetModel(currentModel);
 }
 
+// ATOMIC BEGIN
+
+bool StaticModel::GetGeometryVisible(const String& name)
+{
+    if (!model_)
+        return false;
+
+    const Vector<String>& names = model_->GetGeometryNames();
+
+    for (unsigned i = 0; i < names.Size(); i++)
+    {
+        if (name == names[i])
+            return geometryData_[i].enabled_;
+    }
+
+    return false;
+
+}
+
+void StaticModel::ShowGeometry(const String& name)
+{
+    if (!model_)
+        return;
+
+    const Vector<String>& names = model_->GetGeometryNames();
+
+    for (unsigned i = 0; i < names.Size(); i++)
+    {
+        if (name == names[i])
+            geometryData_[i].enabled_ = true;
+    }
+}
+
+void StaticModel::HideGeometry(const String& name)
+{
+    if (!model_)
+        return;
+
+    const Vector<String>& names = model_->GetGeometryNames();
+
+    for (unsigned i = 0; i < names.Size(); i++)
+    {
+        if (name == names[i])
+            geometryData_[i].enabled_ = false;
+    }
+}
+
+void StaticModel::SetGeometryEnabledAttr(const VariantVector& value)
+{
+    if (!value.Size() || value.Size() != geometryData_.Size())
+    {
+        geometryEnabled_.Clear();
+        return;
+    }
+
+    bool init = !geometryEnabled_.Size();
+
+    geometryEnabled_ = value;
+
+    for (unsigned i = 0; i < geometryData_.Size(); i++)
+    {
+        geometryData_[i].enabled_ = geometryEnabled_[i].GetBool();
+        if (init)
+            geometryData_[i].batchGeometry_ = 0;
+    }
+
+}
+const VariantVector& StaticModel::GetGeometryEnabledAttr() const
+{
+    geometryEnabled_.Resize(geometryData_.Size());
+
+    for (unsigned i = 0; i < geometryData_.Size(); i++)
+        geometryEnabled_[i] = geometryData_[i].enabled_;
+
+    return geometryEnabled_;
+}
+
+// ATOMIC END
+
 }

+ 22 - 0
Source/Atomic/Atomic3D/StaticModel.h

@@ -36,6 +36,13 @@ struct StaticModelGeometryData
     Vector3 center_;
     /// Current LOD level.
     unsigned lodLevel_;
+
+    // ATOMIC BEGIN
+
+    bool enabled_;
+    Geometry* batchGeometry_;
+
+    // ATOMIC END
 };
 
 /// Static model component.
@@ -99,6 +106,17 @@ public:
     /// Return materials attribute.
     const ResourceRefList& GetMaterialsAttr() const;
 
+    // ATOMIC BEGIN
+
+    bool GetGeometryVisible(const String& name);
+    void ShowGeometry(const String& name);
+    void HideGeometry(const String& name);
+
+    void SetGeometryEnabledAttr(const VariantVector& value);
+    const VariantVector& GetGeometryEnabledAttr() const;
+
+    // ATOMIC END
+
 protected:
     /// Recalculate the world-space bounding box.
     virtual void OnWorldBoundingBoxUpdate();
@@ -122,6 +140,10 @@ protected:
     /// Material list attribute.
     mutable ResourceRefList materialsAttr_;
 
+    // ATOMIC BEGIN
+    mutable VariantVector geometryEnabled_;
+    // ATOMIC END
+
 private:
     /// Handle model reload finished.
     void HandleModelReloadFinished(StringHash eventType, VariantMap& eventData);