create3000 / x_ite

X_ITE X3D Browser, view and manipulate X3D and VRML scenes in HTML.
https://create3000.github.io/x_ite/
Other
67 stars 15 forks source link

Problems using createVrmlFromString #114

Closed olafgithub closed 1 year ago

olafgithub commented 2 years ago

When rewriting VRML written for BS Contact to X_ITE I noticed different behavior when creating objects in a script. I used a for-loop to create a number of objects in string s. Inside the for-loop I called Browser.createVrmlFromString( s ) This worked fine with BS Contact.

With X_ITE only the last object is created.

minimum example code

#VRML V2.0 utf8

PROTO Add
[
]
{
  DEF TR Transform
  {
    children
    [
      DEF SCR Script
      {
        eventOut      MFNode  O
        directOutput  TRUE
        mustEvaluate  TRUE

        url "javascript:

          function initialize()
          {
            O = Browser.createVrmlFromString( 'Transform{ translation -2 0 0 children[Shape{appearance Appearance{material Material{diffuseColor 1 0 0}}geometry Sphere{}}]}' );
            O = Browser.createVrmlFromString( 'Transform{                    children[Shape{appearance Appearance{material Material{diffuseColor 0 1 0}}geometry Sphere{}}]}' );
          }
        "
      } 
    ]

    ROUTE SCR.O TO TR.addChildren
  }
}

Add {}

With BS Contact I see 2 spheres With X_ITE I only the last (green) sphere

I tried every combination of mustEvaluate and directOutput. No luck

Is this a bug in X_ITE or a feature in BS Contact? Of course, I already used a workaround...

Windows 10 Google Chrome X_ITE v5.0.3

Best regards,

Olaf

olafgithub commented 2 years ago

Confirmed... but I am not so sure anymore. I was reading about this in some forums right now.

I tried the following code with (I hope) correct use of directOutput:

#VRML V2.0 utf8

PROTO Add
[
]
{
  Group
  {
    children
    [
      DEF TR Transform
      {
        children
        [
          DEF SCR Script
          {
            field         SFNode  tr    USE TR

            directOutput  TRUE
            mustEvaluate  FALSE

            url "javascript:

              function initialize()
              {
                tr.addChildren = Browser.createVrmlFromString( 'Transform{ translation -2 0 0 children[Shape{appearance Appearance{material Material{diffuseColor 1 0 0}}geometry Sphere{}}]}' );
                tr.addChildren = Browser.createVrmlFromString( 'Transform{                    children[Shape{appearance Appearance{material Material{diffuseColor 0 1 0}}geometry Sphere{}}]}' );
              }
            "
          } 
        ]
      }
    ]
  }
}

Add {}

Same result: 2 spheres in BS Contact. Only the last sphere in X_ITE. I assume that this should work. In the original code example, I set directOutput to TRUE but still used ROUTE to add the children.

I feel that this (latest) example should work.

Olaf

create3000 commented 2 years ago

If directOutput TRUE, then you can access node fields in a script like so:

function foo (node)
{
  tr.addChildren = node // If 'tr' is of type SFNode
}

The access to .addChildren is now permitted.

If mustEvaluate is TRUE then the node 'tr' will process the assignment immediately.

In X_ITE only directOutput TRUE is implemented, and mustEvaluate FALSE.

This means for your first example, that O get assigned two values and after leaving the function O sends the the last value assigned. This is that you can optimize some algorithm like:

function foo (nodes)
{
  O .length = 0 // O is of type outputOnly (eventOut) MFNode

  for (let i = 0; i < nodes .length; i += 2)
     O [O .length] = nodes [i] // Push every second node to O  
}

In this example you will probably have a lot of access to O, but nothing special happens during the assignment, only after leaving the function the event O is send with every second node of 'nodes'.

If mustEvaluate is TRUE every assignment would send the value every time an assignment is made. As I mentioned before this is not implemented yet.

As workaround you could do this:

      url "javascript:

        function initialize()
        {
          const nodes = new MFNode ();

          nodes [nodes .length] = Browser.createVrmlFromString( 'Transform{ translation -2 0 0 children[Shape{appearance Appearance{material Material{diffuseColor 1 0 0}}geometry Sphere{}}]}' ) [0];
          nodes [nodes .length] = Browser.createVrmlFromString( 'Transform{                    children[Shape{appearance Appearance{material Material{diffuseColor 0 1 0}}geometry Sphere{}}]}' ) [0];

          O = nodes;
        }
      "
olafgithub commented 2 years ago

Thanks,

Mostly clear now! I use createVrmlFromString() mostly to create shapes I need at startup. So almost always it is called from function initialize(). At that time there is no scene yet, so responsiveness is not a problem.

Olaf