BlankeLab / ExVR

EPFL-LNCO: ExVR - Open Virtual Psychology
Other
10 stars 1 forks source link

Update Model component transform from C# script #5

Closed FlorianLance closed 1 year ago

FlorianLance commented 1 year ago

I would like to modify the CS function that moves a VR object to directly modify the config so that the changes remains in next routines. I saw this documentation for the cube component:

Content:

// unity
using UnityEngine;

namespace Ex{
    public class CubeComponent : ExComponent{
        public override void update_from_current_config();
    }
}

### Samples:

// retrieve component 
var modelComponent = (CubeComponent)get("cube_component_name");
// or 
var modelComponent = get<CubeComponent>("cube_component_name");
// or if only one available
var modelComponent = get_first<CubeComponent>();

// change model current condition parameters
// # retrieve config
var modelConfig = modelComponent.current_config();    

// # change material color
modelConfig.set_color("color", Color.blue);
// # change material metallic value
modelConfig.set<float>("metallic", 0.5f);
// # change material smoothness value
modelConfig.set<float>("smoothness", 0.2f);   

// you can move/resize the model with:
// # a list of 3 Vector3 (postion,rotation,scale)
List<Vector3> listV = new List<Vector3>();
listV.Add(new Vector3(0,2f,0));  // position
listV.Add(new Vector3(30f,0,0)); // rotation
listV.Add(Vector3.one);          // scale
modelConfig.set_transform("transform", listV);

// # a TransformValue
TransformValue trV = new TransformValue();
trV.position = new Vector3(0,2f,0);
trV.rotation = Quaternion.Euler(new Vector3(30f,0,0));
trV.scale    = Vector3.one;
modelConfig.set_transform("transform", trV);

// # a Transform
Transform tr = someGameObject.transform;
modelConfig.set_transform("transform", tr);

// apply changes
modelComponent.update_from_current_config();

### Configs parameters:

// initialization config
var initC = init_config();
TransformValue trV = initC.get_transform("init_transform");
initC.get<float>("size");
initC.get<bool>("tranparent");

// condition config
var currentC = current_config();
TransformValue trV = currentC.get_transform("transform");
currentC.get_color("color");
currentC.get<float>("metallic");
currentC.get<float>("smoothness");

#####

How to us this syntax inside the CS function ?

If I just use:

var modelComponent = (CubeComponent)get("cube_component_name");

'get' is not recognized

if I add something like this in the extra section

using Ex.ExComponent
using CubeComponent

'using' is not recognized

I also tried the CS script component, but I don't understand how it works (adding elements, no scripting at all ?)

Thanks in advance.

Originally posted by @makselro in https://github.com/BlankeLab/ExVR/issues/3#issuecomment-1742783134

FlorianLance commented 1 year ago

You are doing a mix between C# function and C# script components. C# function is mostly for creating small utility function for doing the glue between nodes and others scripts. C# script have may more functionalities:

For using them you have to click on the "Display resources manager" button or from the menu ("Resources/Display resources manager") image

Click on the C# script panel, and from this place you can either add a previously created C# script or to create a new one using the "Generate script" button.

By doing the later, it open a popup window: image You have to choose the component name (you will have to refer by this name from the C# script component later)

Once the destination validated, you can open it by clicking on "Open file", (it will use the default editor for .cs files, so if you have installed Visual studio it will be opened with it)

Once open you will be able to seen a bunch of functions you can comment or uncomment, every component in ExVR is using theses functions: image

Below you can see the documentation of the main classes inherited by the script.

You now have to reference your script component by setting its name: image

So if you use the code you wrote inside the initialize or the start_routine functions for example and if you have a Cube component in your scene named "cube_component_name", it will now work.

public override bool initialize() {

      var modelComponent = (CubeComponent)get("cube_component_name");
      log_message(modelComponent.name);

      return true; 
 }

It's because the TemplateComponent we just created is inheriting from BaseCompiledCSharpComponent and contains a lot of quality of life functionnalites.

FlorianLance commented 1 year ago

One important thing regarding transforms with model components: image

From the initialization config panel, the transform will be only applied once (except if you check Do not apply) when you load the experiment by clicking on one of theses buttons:

image

But from the Conditions configs panel:

image

Each time you start a routine with a condition referencing this component config, it will apply the Config transform, except if you check Do not apply.

So if you wish to control the transform of this component only from scripting or visual scripting, I encourage you to check the Do not apply button at least for the Conditions config panel.

After from you script you can use either of theses methods to update the component position:

// you can move/resize the model with:
// # a list of 3 Vector3 (postion,rotation,scale)
List<Vector3> listV = new List<Vector3>();
listV.Add(new Vector3(0,2f,0));  // position
listV.Add(new Vector3(30f,0,0)); // rotation
listV.Add(Vector3.one);          // scale
modelConfig.set_transform("transform", listV);

// # a TransformValue
TransformValue trV = new TransformValue();
trV.position = new Vector3(0,2f,0);
trV.rotation = Quaternion.Euler(new Vector3(30f,0,0));
trV.scale    = Vector3.one;
modelConfig.set_transform("transform", trV);

// # a Transform
Transform tr = someGameObject.transform;
modelConfig.set_transform("transform", tr);

// apply changes
modelComponent.update_from_current_config();`

So your final script may be like this:


// system
using System;
using System.Reflection;
using System.Collections.Generic;

// unity
using UnityEngine;

namespace Ex{

    public class TemplateComponent : BaseCompiledCSharpComponent{

        CubeComponent cube = null;

        // Ex component flow functions, uncomment to use
        // # once per loading
        public override bool initialize() {
            cube = (CubeComponent)get("cube_component_name");
            return true; 
        }

        public override void update() {

            // # a TransformValue
            TransformValue trV = new TransformValue();
            trV.position = new Vector3(..., ..., ...);
            trV.rotation = Quaternion.Euler(new Vector3(..., ..., ...));
            trV.scale = Vector3.one;

            cube.current_config().set_transform("transform", trV);

        }
    }
}
FlorianLance commented 1 year ago

The template file generated by the component is a bit out of date, I'll update it.

makselro commented 1 year ago

Thank you for the amazing support.

Below is my attempt to read the keyboard input from slot 5:

#################### // system using System; using System.Reflection; using System.Collections.Generic;

// unity using UnityEngine;

namespace Ex{

public class CS_move_cube : BaseCompiledCSharpComponent{

    CubeComponent Cube = null;
    // Ex component flow functions, uncomment to use
    // # once per loading
    public override bool initialize() {
        Cube = (CubeComponent)get("Cube");
        return true;
    }

    public override void update() {

        var CubeConfig = Cube.current_config();
        Vector3 CubePos = CubeConfig.get<Vector3>("position");
        float CubeHeight = CubePos.y;

        var idV = (IdAny)slot5;

        if(idV.id == 0){
            CubeHeight += 0.1f;
        }else if(idV.id == 1){
            CubeHeight -= 0.1f; 
        }

        // # a TransformValue
        TransformValue trV = new TransformValue();
        trV.position = new Vector3(CubePos.x, CubeHeight, CubePos.z);
        trV.rotation = Quaternion.Euler(new Vector3(0, 0, 0));
        trV.scale = Vector3.one;

        Cube.current_config().set_transform("transform", trV);

    }       
}

}

################

I get the following error:

[COMPILER] Compilation error CS0428 : "Cannot convert method group slot5' to non-delegate typeEx.IdAny'. Consider using parentheses to invoke the method" from file D:/Michel/ExVR/PPS_reaching/assets/CS_move_cube.cs at line 27 and column 25

################ The connections:

image

###############

An alternative would be the read directly the keyboard within the script. If this would be the preferred way, what is the correct way to "catch" the latest pressed key ? last_event() or send_infos_and_signals()