*/}}
Browse Source

Lua run driver from root objects.

YimingWu 1 year ago
parent
commit
9fd429a43c
9 changed files with 162 additions and 132 deletions
  1. 0 6
      la_interface.h
  2. 3 3
      la_kernel.c
  3. 3 1
      la_tns.h
  4. 74 10
      la_tns_kernel.c
  5. 63 104
      la_util.c
  6. 10 2
      la_util.h
  7. 3 3
      resources/la_lualibs.cpp
  8. 1 0
      resources/la_properties.c
  9. 5 3
      resources/la_widgets_viewers.c

+ 0 - 6
la_interface.h

@@ -45,12 +45,6 @@
 //#include "llvm-c/Target.h"
 //#include <Windows.h>
 
-#define luajit_c
-#include "lua.h"
-#include "lauxlib.h"
-#include "lualib.h"
-#include "luajit.h"
-
 #define LA_VERSION_MAIN 5
 #define LA_VERSION_SUB 2
 

+ 3 - 3
la_kernel.c

@@ -1032,9 +1032,9 @@ int laGetReadyWith(laInitArguments* ia){
     // starting lua
     logPrint("Starting luajit...\n");
     MAIN.L = lua_open();
-    la_LuaJITLoadLibs();
-    
-    la_PrintLuaJITStatus();
+    la_luaLoadLibs(MAIN.L);
+    la_luaPrintStatus(MAIN.L);
+    tnsLuaInit(MAIN.L);
     
     //interactions:
     MAIN.TopFramerate = 60;

+ 3 - 1
la_tns.h

@@ -458,7 +458,7 @@ STRUCTURE(tnsEvaluatedNode){
     tnsMatrix44d Mat;
     tnsObject* Target;
     laListHandle Children;
-    int LuaID;
+    int LuaID, LuaLoaded;
 };
 STRUCTURE(tnsEvaluatedScene){
     int NextLuaID;
@@ -488,6 +488,7 @@ STRUCTURE(tnsEvaluateData){
     tnsEvaluatedInstance* Selections; int NextSelection, MaxSelection;
     tnsMatrix44d* MatArr; int NextMat, MaxMat;
     tnsEvaluatedScene* Scene;
+    lua_State* L;
 };
 
 NEED_STRUCTURE(laRackPageCollection);
@@ -1142,6 +1143,7 @@ tnsObject* tnsEnsurePlayDuplicate(tnsObject* o);
 void tnsFreePlayDuplicate(tnsObject* o);
 
 void tnsInvalidateEvaluation(tnsObject* o);
+void tnsInvalidatePlayEvaluation(tnsObject* o);
 void tnsFreeEvaluatedArray(tnsEvaluateData* ed);
 void tnsEvaluateThisObject(tnsObject *o, tnsEvaluateData* ed);
 void tnsSetObjectTreeEvaluationArgs(tnsObject* from, tnsObject* Active, int FillOutline, int FillSelectionID);

+ 74 - 10
la_tns_kernel.c

@@ -3441,6 +3441,9 @@ void tnsDestroyRootObject(tnsObject *root){
     memAssignRef(w,&w->ActiveRoot,root->Item.pPrev ? root->Item.pPrev : root->Item.pNext ? root->Item.pNext : 0);
 
     tnsFreeEvaluatedArray(&root->Evaluated);
+    tnsClearPlayDuplicate(o);
+    if(o->EvaluatedPlay.Commands) tnsFreeEvaluatedArray(&o->EvaluatedPlay);
+    tnsFreeEvaluatedScene(&o->EvaluatedPlay);
 
     //XXX: destroy rack page; and then release memory;
     root->Drivers=0; memLeave(root->Drivers); 
@@ -3454,6 +3457,9 @@ void tnsDestroyObject(tnsObject *o){
     if(o->Type==TNS_OBJECT_ROOT){ tnsDestroyRootObject(o); return; }
 
     tnsFreeEvaluatedArray(&o->Evaluated);
+    tnsClearPlayDuplicate(o);
+    if(o->EvaluatedPlay.Commands) tnsFreeEvaluatedArray(&o->EvaluatedPlay);
+    tnsFreeEvaluatedScene(&o->EvaluatedPlay);
 
     tnsUnparentObject(o,1);
     while(o->ChildObjects.pFirst){ tnsUnparentObject(((laListItemPointer*)o->ChildObjects.pFirst)->p, 1); }
@@ -3717,8 +3723,15 @@ void tnsEvaluateInstancerObject(tnsInstancer* o, tnsEvaluateData* ed){
     }
     ed->OverrideID=origid; ed->FillOutline=origoutline;
 }
+
+void tnsLuaRunNode(lua_State* L, tnsEvaluatedNode* en, char* func, tnsObject* root);
 void tnsEvaluateThisObject(tnsObject *o, tnsEvaluateData* ed){
-    if(ed->SceneEvaluateMode){ tnsEvaluateSyncNode(ed,o); }
+    if(ed->SceneEvaluateMode){ tnsEvaluateSyncNode(ed,o);
+        if(o->Type==TNS_OBJECT_ROOT){ tnsEvaluatedNode* en=ed->Scene->CurrentChild;
+            tnsLuaEnsureNode(ed->L,en->LuaID);
+            tnsLuaRunNode(ed->L,en,"run",o);
+        }
+    }
     if (!o->Show) return;
     switch (o->Type){
     case TNS_OBJECT_MESH: tnsEvaluateMeshObject(o, ed); break;
@@ -3742,8 +3755,10 @@ void tnsFreeEvaluatedNode(tnsEvaluatedNode* n){
     memFree(n);
 }
 void tnsFreeEvaluatedScene(tnsEvaluateData* ed){
-    if(!ed->Scene) ed->Scene=memAcquire(sizeof(tnsEvaluatedScene));
+    if(!ed->Scene) return;
     if(ed->Scene->Root) tnsFreeEvaluatedNode(ed->Scene->Root);
+    logPrintNew("Shutting down Lua for the evaluated scene.\n");
+    lua_close(ed->L);
     memFree(ed->Scene); ed->Scene=0;
 }
 void tnsClearPlayState(tnsObject* o){
@@ -3772,6 +3787,47 @@ void tnsFreePlayDuplicate(tnsObject* o){
     o->PlayDuplicate=0;
 }
 
+void tnsLuaInit(lua_State* L){ lua_settop(L,0);
+    lua_newtable(L); lua_setglobal(L,"scene");
+    lua_getglobal(L,"scene"); lua_pushstring(L,"nodes"); lua_newtable(L); lua_settable(L,1);
+    lua_pushnil(L); lua_setfield(L,1,"node"); lua_pop(L,1);
+}
+void tnsLuaEnsureNode(lua_State* L,int index){ lua_settop(L,0);
+    lua_getglobal(L,"scene"); lua_getfield(L,1,"nodes");
+    lua_pushinteger(L,index); lua_gettable(L,2); if(lua_isnil(L,3) || (!lua_istable(L,3))){
+        lua_pushinteger(L,index); lua_newtable(L); lua_settable(L,2); lua_pop(L,2);
+    }
+}
+void tnsLuaRemoveNode(lua_State* L,int index){ lua_settop(L,0);
+    lua_getglobal(L,"scene"); lua_pushstring(L,"nodes"); lua_gettable(L,1);
+    lua_pushinteger(L,index); lua_pushnil(L); lua_settable(L,2); lua_pop(L,2);
+}
+void tnsLuaRunNode(lua_State* L, tnsEvaluatedNode* en, char* func, tnsObject* root){ lua_settop(L,0); int index=en->LuaID;
+    lua_getglobal(L,"scene"); lua_getfield(L,1,"nodes");
+    lua_pushinteger(L,index); lua_gettable(L,2); lua_setfield(L,1,"node"); lua_pop(L,1);
+    lua_getfield(L,1,"node"); lua_getfield(L,2,"scripts");
+    if(lua_isnil(L,3) || (!lua_istable(L,3))){
+        lua_pop(L,1); lua_newtable(L); lua_setfield(L,2,"scripts"); lua_getfield(L,2,"scripts");
+    }
+    if(!en->LuaLoaded){
+        if(root->Drivers) for(laRackPage*rp=root->Drivers->Pages.pFirst;rp;rp=rp->Item.pNext){
+            if(!(rp->UseScript&&rp->Script&&rp->Script->Ptr)){ continue; }
+            char* debug_name=rp->Name&&rp->Name->Ptr?rp->Name->Ptr:"unamed_driver_page";
+            lua_pushinteger(L,lua_objlen(L,3)+1);
+            if(!luaL_loadbuffer(L,rp->Script->Ptr,strlen(rp->Script->Ptr),debug_name)){ lua_call(L,0,1); lua_settable(L,3); }
+            else{ lua_pop(L,2); /* pop error and index */ }
+        }
+        en->LuaLoaded=1;
+    }
+
+    lua_pushnil(L); while(lua_next(L,3)){
+        lua_getfield(L,-1,func); if((!lua_isnil(L,-1))&&lua_isfunction(L,-1)){
+            lua_pushvalue(L,-2); lua_call(L,1,0);
+        }
+        lua_pop(L,1);
+    }
+    lua_pop(L,3);
+}
 void tnsAddEvaluatedInstance(tnsEvaluateData* ed, tnsObject* ob, tnsDrawEvaluatedInstanceF Draw, int Layer, 
     int IsActive, int MeshSelectionType, int InstanceSelectionID){
     tnsEvaluatedInstance* ei;
@@ -3803,6 +3859,9 @@ void tnsFreeEvaluatedArray(tnsEvaluateData* ed){
 void tnsInvalidateEvaluation(tnsObject* o){
     if(o->InRoot) o->InRoot->Evaluated.Done=0; else o->Evaluated.Done=0;
 }
+void tnsInvalidatePlayEvaluation(tnsObject* o){
+    if(o->InRoot) o->InRoot->EvaluatedPlay.Done=0; else o->EvaluatedPlay.Done=0;
+}
 void tnsSetObjectTreeEvaluationArgs(tnsObject* from, tnsObject* Active, int FillOutline, int FillSelectionID){
     tnsEvaluateData* ed=&from->Evaluated; int set=0;
 #define SETARG(a)\
@@ -3811,11 +3870,8 @@ void tnsSetObjectTreeEvaluationArgs(tnsObject* from, tnsObject* Active, int Fill
     if(set) ed->Done=0;
 }
 tnsEvaluatedNode* tnsAcquireEvaluateNode(tnsEvaluateData* ed){
-    if(!ed->Scene->WastedEvaluateNodes.pFirst){ 
-        tnsEvaluatedNode* en=memAcquire(sizeof(tnsEvaluatedNode));
-        en->LuaID=ed->Scene->NextLuaID; ed->Scene->NextLuaID++;
-        return en;
-    }else{ return lstPopItem(&ed->Scene->WastedEvaluateNodes); }
+    if(!ed->Scene->WastedEvaluateNodes.pFirst){ return memAcquire(sizeof(tnsEvaluatedNode)); }
+    else{ return lstPopItem(&ed->Scene->WastedEvaluateNodes); }
 }
 void tnsEvaluateNewNode(tnsEvaluateData* ed, tnsObject* ob){
     tnsEvaluatedScene* s=ed->Scene;
@@ -3838,7 +3894,7 @@ void tnsEvaluateEndNode(tnsEvaluateData* ed, tnsEvaluatedNode* en){
     tnsEvaluatedNode* sen;
     while(sen=lstPopItem(&en->Children)){ tnsEvaluateEndNode(ed,sen); }
     int luaid=en->LuaID; memset(en,0,sizeof(tnsEvaluatedNode)); //memFree(sen);
-    en->LuaID=luaid;
+    en->LuaID=luaid; if(en->LuaID!=0) tnsLuaRemoveNode(ed->L,en->LuaID);
     lstAppendItem(&ed->Scene->WastedEvaluateNodes,en);
 }
 void tnsEvaluateSyncNode(tnsEvaluateData* ed, tnsObject* ob){
@@ -3853,8 +3909,16 @@ void tnsEvaluateSyncNode(tnsEvaluateData* ed, tnsObject* ob){
     }
 }
 void tnsEnsureEvaluatedScene(tnsEvaluateData* ed, tnsObject* root){
-    if(!ed->Scene) ed->Scene=memAcquire(sizeof(tnsEvaluatedScene));
-    if(!ed->Scene->Root) ed->Scene->Root=tnsAcquireEvaluateNode(ed);
+    if(!ed->Scene){
+        ed->Scene=memAcquire(sizeof(tnsEvaluatedScene));
+        logPrintNew("Starting Lua for evaluated scene.\n");
+        ed->L = lua_open(); la_luaLoadLibs(ed->L);
+        la_luaPrintStatus(ed->L); tnsLuaInit(ed->L);
+    } 
+    if(!ed->Scene->Root){ 
+        ed->Scene->Root=tnsAcquireEvaluateNode(ed);
+        ed->Scene->NextLuaID++; ed->Scene->Root->LuaID=ed->Scene->NextLuaID;
+    }
     ed->Scene->CurrentChild=ed->Scene->Root;
     ed->Scene->CurrentChild->Target=root; tnsLoadIdentity44d(ed->Scene->CurrentChild->Mat);
 }

+ 63 - 104
la_util.c

@@ -19,10 +19,6 @@
 #define _CRT_SEQURE_NO_WARNINGS
 #include "la_util.h"
 #include "la_interface.h"
-#include "lua.h"
-#include "lauxlib.h"
-#include "lualib.h"
-#include "luajit.h"
 #include <stdio.h>
 #include <stdlib.h>
 
@@ -2236,126 +2232,52 @@ void laSpinUnlock(SYSLOCK* lock) {
 //======================================= lua utils
 
 static const char *progname = LUA_PROGNAME;
-static int traceback(lua_State *L){
+
+static int la_luaTraceback(lua_State *L){
   if (!lua_isstring(L, 1)) { /* Non-string error object? Try metamethod. */
-    if (lua_isnoneornil(L, 1) ||
-	!luaL_callmeta(L, 1, "__tostring") ||
-	!lua_isstring(L, -1))
-      return 1;  /* Return non-string error object. */
-    lua_remove(L, 1);  /* Replace object by result of __tostring metamethod. */
+        if (lua_isnoneornil(L, 1) ||
+        !luaL_callmeta(L, 1, "__tostring") ||
+        !lua_isstring(L, -1))
+        return 1;  /* Return non-string error object. */
+        lua_remove(L, 1);  /* Replace object by result of __tostring metamethod. */
   }
   luaL_traceback(L, L, lua_tostring(L, 1), 1);
   return 1;
 }
-
-static void l_message(const char *msg){
-  if (progname) { fputs(progname, stderr); fputc(':', stderr); fputc(' ', stderr); }
-  fputs(msg, stderr); fputc('\n', stderr);
-  fflush(stderr);
+static void la_luaMessage(const char *msg){
+  if (progname) { logPrint("%s: ",progname); } logPrint("%s\n",msg);
 }
-static int report(lua_State *L, int status){
+static int la_luaReport(lua_State *L, int status){
   if (status && !lua_isnil(L, -1)) {
     const char *msg = lua_tostring(L, -1);
     if (msg == NULL) msg = "(error object is not a string)";
-    l_message(msg);
+    la_luaMessage(msg);
     lua_pop(L, 1);
   }
   return status;
 }
-static int docall(lua_State *L, int narg, int clear){
+static int la_luaDoCall(lua_State *L, int narg, int clear){
   int status;
   int base = lua_gettop(L) - narg;  /* function index */
-  lua_pushcfunction(L, traceback);  /* push traceback function */
+  lua_pushcfunction(L, la_luaTraceback);  /* push la_luaTraceback function */
   lua_insert(L, base);  /* put it under chunk and args */
 
   status = lua_pcall(L, narg, (clear ? 0 : LUA_MULTRET), base);
 
-  lua_remove(L, base);  /* remove traceback function */
+  lua_remove(L, base);  /* remove la_luaTraceback function */
   /* force a complete garbage collection in case of errors */
   if (status != LUA_OK) lua_gc(L, LUA_GCCOLLECT, 0);
   return status;
 }
-static void write_prompt(lua_State *L, int firstline){
-  const char *p;
-  lua_getfield(L, LUA_GLOBALSINDEX, firstline ? "_PROMPT" : "_PROMPT2");
-  p = lua_tostring(L, -1);
-  if (p == NULL) p = firstline ? LUA_PROMPT : LUA_PROMPT2;
-  fputs(p, stdout);
-  fflush(stdout);
-  lua_pop(L, 1);  /* remove global */
-}
-static int incomplete(lua_State *L, int status){
+static int la_luaIncomplete(lua_State *L, int status){
   if (status == LUA_ERRSYNTAX) {
     size_t lmsg;
     const char *msg = lua_tolstring(L, -1, &lmsg);
     const char *tp = msg + lmsg - (sizeof(LUA_QL("<eof>")) - 1);
-    if (strstr(msg, LUA_QL("<eof>")) == tp) {
-      lua_pop(L, 1);
-      return 1;
-    }
+    if (strstr(msg, LUA_QL("<eof>")) == tp) { lua_pop(L, 1); return 1; }
   }
   return 0;  /* else... */
 }
-static int pushline(lua_State *L, int firstline){
-  char buf[LUA_MAXINPUT];
-  write_prompt(L, firstline);
-  if (fgets(buf, LUA_MAXINPUT, stdin)) {
-    size_t len = strlen(buf);
-    if (len > 0 && buf[len-1] == '\n')
-      buf[len-1] = '\0';
-    if (firstline && buf[0] == '=')
-      lua_pushfstring(L, "return %s", buf+1);
-    else
-      lua_pushstring(L, buf);
-    return 1;
-  }
-  return 0;
-}
-static int loadline(lua_State *L){
-  int status;
-  lua_settop(L, 0);
-  if (!pushline(L, 1))
-    return -1;  /* no input */
-  for (;;) {  /* repeat until gets a complete line */
-    status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "=stdin");
-    if (!incomplete(L, status)) break;  /* cannot try to add lines? */
-    if (!pushline(L, 0))  /* no more input? */
-      return -1;
-    lua_pushliteral(L, "\n");  /* add a new line... */
-    lua_insert(L, -2);  /* ...between the two lines */
-    lua_concat(L, 3);  /* join them */
-  }
-  lua_remove(L, 1);  /* remove line */
-  return status;
-}
-
-static int lalua_Log(lua_State *L) {
-  int n = lua_gettop(L); int i;
-  lua_getglobal(L, "tostring");
-  for (i=1; i<=n; i++) {
-    const char *s;
-    lua_pushvalue(L, -1);  /* tostring function */
-    lua_pushvalue(L, i);   /* value to print */
-    lua_call(L, 1, 1);
-    s = lua_tostring(L, -1);  /* get result */
-    if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to "LUA_QL("use `log`"));
-    if (i>1) logPrint("    ");
-    logPrint(s); lua_pop(L, 1);  /* pop result */
-  }
-  logPrint("\n");
-  return 0;
-}
-
-void la_LuaJITLoadLibs(){
-    lua_State *L=MAIN.L;
-    lua_gc(MAIN.L, LUA_GCSTOP, 0);
-    
-    luaL_openlibs(MAIN.L);
-    lua_register(L,"log",lalua_Log);
-    if(luaL_loadstring(L, LA_LUA_LIB_COMMON) || lua_pcall(L, 0, 0, 0)){ logPrint("    Error loading lagui lua libs"); };
-
-    lua_gc(MAIN.L, LUA_GCRESTART, -1);
-}
 
 int terLoadLine(char* buf, int firstline){
     lua_State *L=MAIN.L;
@@ -2372,7 +2294,7 @@ int terLoadLine(char* buf, int firstline){
     }
 
     int status = luaL_loadbuffer(L, lua_tostring(L, 1), lua_strlen(L, 1), "terLoadLine");
-    if(incomplete(L,status)){
+    if(la_luaIncomplete(L,status)){
         MAIN.TerminalIncomplete=1;
     }else{
         MAIN.TerminalIncomplete=0;
@@ -2380,13 +2302,13 @@ int terLoadLine(char* buf, int firstline){
     }
 
     if(status==LUA_OK && (!MAIN.TerminalIncomplete)){
-        status = docall(L, 0, 0);
-        report(L, status);
+        status = la_luaDoCall(L, 0, 0);
+        la_luaReport(L, status);
         if (status == LUA_OK && lua_gettop(L) > 0) {  /* any result to print? */
         lua_getglobal(L, "log");
         lua_insert(L, 1);
         if (lua_pcall(L, lua_gettop(L)-1, 0, 0) != 0)
-        l_message(lua_pushfstring(L, "error calling " LUA_QL("log") " (%s)",
+        la_luaMessage(lua_pushfstring(L, "error calling " LUA_QL("log") " (%s)",
                     lua_tostring(L, -1)));
         }
     }
@@ -2394,8 +2316,34 @@ int terLoadLine(char* buf, int firstline){
     return status;
 }
 
-void la_PrintLuaJITStatus(){
-    lua_State *L=MAIN.L;
+static int lalua_Log(lua_State *L) {
+  int n = lua_gettop(L); int i;
+  lua_getglobal(L, "tostring");
+  for (i=1; i<=n; i++) {
+    const char *s;
+    lua_pushvalue(L, -1);  /* tostring function */
+    lua_pushvalue(L, i);   /* value to print */
+    lua_call(L, 1, 1);
+    s = lua_tostring(L, -1);  /* get result */
+    if (s == NULL) return luaL_error(L, LUA_QL("tostring") " must return a string to "LUA_QL("use `log`"));
+    if (i>1) logPrint("    ");
+    logPrint(s); lua_pop(L, 1);  /* pop result */
+  }
+  logPrint("\n");
+  return 0;
+}
+
+void la_luaLoadLibs(lua_State *L){
+    lua_gc(L, LUA_GCSTOP, 0);
+    
+    luaL_openlibs(L);
+    lua_register(L,"log",lalua_Log);
+    if(luaL_loadstring(L, LA_LUA_LIB_COMMON) || lua_pcall(L, 0, 0, 0)){ logPrint("    Error loading lagui lua libs"); };
+
+    lua_gc(L, LUA_GCRESTART, -1);
+}
+
+void la_luaPrintStatus(lua_State *L){
     logPrint(LUAJIT_VERSION " -- " LUAJIT_COPYRIGHT ". " LUAJIT_URL "\n");
     int n; const char *s;
     lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED");
@@ -2404,9 +2352,20 @@ void la_PrintLuaJITStatus(){
     n = lua_gettop(L);
     lua_call(L, 0, LUA_MULTRET);
     logPrint(lua_toboolean(L, n) ? "JIT: ON" : "JIT: OFF");
-    for (n++; (s = lua_tostring(L, n)); n++) {
-        logPrint("%s ",s);
-    }
-    putc('\n', stdout);
+    for (n++; (s = lua_tostring(L, n)); n++) { logPrint("%s ",s); }
+    logPrint("\n");
     lua_settop(L, 0);  /* clear stack */
 }
+
+void la_luaDumpStack(lua_State *L){
+    int top=lua_gettop(L); for (int i=1; i <= top; i++) {
+        printf("%d\t%s\t", i, luaL_typename(L,i));
+        switch (lua_type(L, i)) {
+        case LUA_TNUMBER: printf("%g\n",lua_tonumber(L,i)); break;
+        case LUA_TSTRING: printf("%s\n",lua_tostring(L,i)); break;
+        case LUA_TBOOLEAN: printf("%s\n", (lua_toboolean(L, i) ? "true" : "false")); break;
+        case LUA_TNIL: printf("%s\n", "nil"); break;
+        default: printf("%p\n",lua_topointer(L,i)); break;
+        }
+    }
+}

+ 10 - 2
la_util.h

@@ -56,8 +56,15 @@
 #define SYSLOCK CRITICAL_SECTION
 #endif
 
+#define luajit_c
+#include "lua.h"
+#include "lauxlib.h"
+#include "lualib.h"
+#include "luajit.h"
 
 
+void tnsLuaInit(lua_State* L);
+
 #define NEED_STRUCTURE(a) \
 typedef struct _##a a;
 
@@ -714,8 +721,9 @@ void laSpinDestroy(SYSLOCK * lock);
 void laSpinLock(SYSLOCK* lock);
 void laSpinUnlock(SYSLOCK* lock);
 
-void la_PrintLuaJITStatus();
-void la_LuaJITLoadLibs();
+void la_luaPrintStatus(lua_State *L);
+void la_luaLoadLibs(lua_State *L);
+void la_luaDumpStack(lua_State *L);
 
 int terLoadLine(char* buf, int firstline);
 

+ 3 - 3
resources/la_lualibs.cpp

@@ -23,9 +23,9 @@ function ddump(tbl, ...)
     if(type(tbl)~='table') then log("Not a table."); return end
     local levels=100
     local dtableonly=0
-    local arg={...}
-    if(select(1,...)) then levels=select(1,...) end
-    if(select(2,...)) then dtableonly=select(2,...) end
+    local l,d=...
+    if(l) then levels=l end
+    if(d) then dtableonly=d end
     local checklist = {}
     local function print_kv(indent,k,v,t)
         log(indent..t.." "..k,v,""..(t==' ' and type(v) or ""))

+ 1 - 0
resources/la_properties.c

@@ -1020,6 +1020,7 @@ void la_RegisterTNSProps(){
             laAddEnumItem(ep, "true", "IsTrue", "IsTrue", U'📷');
         }
         ep = laAddEnumProperty(p, "type", "Type", "Object Type Like Mesh,Camera And Lamp", 0,0,0,0,0,offsetof(tnsObject, Type), 0,0,0,0,0,0,0,0,0,LA_READ_ONLY);{
+            laAddEnumItemAs(ep, "ROOT", "Root", "Root object", TNS_OBJECT_ROOT, 0);
             laAddEnumItemAs(ep, "INSTANCER", "Instancer", "Instancer object", TNS_OBJECT_INSTANCER, 0);
             laAddEnumItemAs(ep, "CAMERA", "Camera", "Camera object, to render a scene", TNS_OBJECT_CAMERA, U'📷');
             laAddEnumItemAs(ep, "LIGHT", "Lamp", "Lamp object, to illuminate the scene", TNS_OBJECT_LIGHT, 0);

+ 5 - 3
resources/la_widgets_viewers.c

@@ -151,8 +151,10 @@ void la_RootObjectDraw(laBoxedTheme *bt, tnsObject *root, laUiItem* ui){
     laListHandle xrays={0};
 
     if(root){
-        tnsSetObjectTreeEvaluationArgs(root,root->Active,1,1);
-        tnsEvaluateObjectTree(root,0, e->AsPlayer?TNS_EVAL_MODE_ALWAYS:0);
+        if(!e->AsPlayer){
+            tnsSetObjectTreeEvaluationArgs(root,root->Active,1,1);
+            tnsEvaluateObjectTree(root,0,0);
+        }
 
         glEnable(GL_DEPTH_TEST); glDisable(GL_BLEND);
 
@@ -182,7 +184,7 @@ void la_RootObjectDraw(laBoxedTheme *bt, tnsObject *root, laUiItem* ui){
     if(e->SelectThrough){ glClear(GL_DEPTH_BUFFER_BIT); }
     tnsDrawObjectTree(root,TNS_EVAL_LAYER_OVERLAY,&de,e->AsPlayer);
 
-    if(root){
+    if(root && (!e->AsPlayer)){
         glDisable(GL_DEPTH_TEST);
         tnsDrawCursor(root);
         glPointSize(8);