Jump to content

[SOLVED] Adding controller functionality/Changing a material


photo

Recommended Posts

Hello,

i am trying to change the way the laser pointer in the VR template works. 
By default, when the laser ray hits an interactive object (grab), a text with the object´s name will be set visible. 

I would like to change it, so that when above is true AND, in addition,  a button on the controller is pressed (oculus rift), a material on the obejct will be changed, lets say with every pressing of the button the user is switching to another material of an array of few defined materials. 

As far as I understand, it should be coded here (see attachment) but i dont really know how to map the controller´s button´s state (pressed) to it and how to let it cycle thorugh a defined array with every click. Help will be very appreciated!

Best,
Pio 

Laser_hit.jpg

Link to comment

Hi Piotr,

You're right, that's the place (one of the places, to be precise). Ok, let's switch the material of the object pointed by the laser, when a face button (XA) is pressed.

First, we should add a new interaction.

We add the following line to the VRInteractable.h file (which declares the base class for our ObjLaserPointer) to enable new interaction method (we insert it right after the useIt() method):

virtual void altuseIt(VRPlayer* player, int hand_num) {}

In the VRPlayer.cpp we also add some code to the VRPlayer::render() method to enable "alternative use" interaction when XA button is pressed:

void VRPlayer::render()
{
	for (int i = 0; i < getNumHands(); i++)
	{
		int hand_state = getHandState(i);
		if (hand_state != HAND_FREE)
		{
			auto &components = getGrabComponents(i);
			
            // ...
            //-------------CODE TO BE ADDED--------------------------
			// alternative use of the grabbed object
			if (getControllerButtonDown(i, BUTTON::XA))
			{
				for (int j = 0; j < components.size(); j++)
					components[j]->altuseIt(this, i);
				// add callback processing if necessary
			}
            //--------------------------------------------------------
		}
	}
	update_button_states();
}

Then, we should add processing for new interaction in the ObjLaserPointer class and add a set of materials that we're going to use when switching.

So, we add the following lines to the ObjLaserPointer.h file:

#pragma once
#include <UnigineWorld.h>
#include "../VRInteractable.h"

class ObjLaserPointer : public VRInteractable
{
public:
	// ...

	// interact methods
	// ...
	// alternative use method override
	void altuseIt(VRPlayer* player, int hand_num) override;

protected:
	// ...
	// shutdown to clear the vector of materials
	void shutdown() override;

private:
	// ...
	Unigine::Vector<MaterialPtr> my_materials;	// vector of materials to be used
	int mat_index = 0;				// current material index
	int alt_use;					// "alternative use" state

	// ...
};

And modify the ObjLaserPointer.cpp file as follows:

void ObjLaserPointer::init()
{
    // clearing the vector
	my_materials.clear();
    // getting all materials in the project inherited from the mesh_base and adding them to our vector
	MaterialPtr mesh_base = Materials::get()->findMaterial("mesh_base");
	int num = mesh_base->getNumChildren();
	for (int i = 0; i < num; i++)
	{
		my_materials.append(mesh_base->getChild(i));
	}
  	// set "alternative use" state to 0
	alt_use = 0;

    // ...
}

void ObjLaserPointer::update()
{
	if (laser->isEnabled())
	{
      	// ...
      		// show text
		
		if (hit_obj && hit_obj->getProperty() && grabbed)
		{
			//---------CODE TO BE ADDED TO PERFORM MATERIAL SWITCHING--------------------
			if (alt_use)// if "alternative use" button was pressed
			{
				alt_use = 0;
				hit_obj->setMaterial(my_materials[mat_index], "*");
				
				// changing current material index, and resetting it to 0, when we reach the last element
				if ((mat_index + 1) < my_materials.size())
					mat_index++;
				else
					mat_index = 0;
			}
			//---------------------------------------------------------------------------
			// ...
		}
		else
			obj_text->setEnabled(0);
	}
}

// clearing our materials vector on shutdown
void ObjLaserPointer::shutdown()
{
	my_materials.clear();
}

// change "alternative use" state for the laser pointer
void ObjLaserPointer::altuseIt(VRPlayer* player, int hand_num)
{
	// switch alt_use state
	alt_use = 1 - alt_use;
}

As you finish you should have something like this (see the attached gif)

Hope this helps!

Thanks!

VRTemplate.gif

Link to comment

Wow!

Thats really a massive help to me! Great, excellent support!

But I still have two questions:
- i added the code you have written, but have problems compiling the ObjLaserPointer.cpp, i cant get the if statement to work, it says that (alt_use) is not declared

(See attached image - all the errors are because of:

if (hit_obj && hit_obj->getProperty() && grabbed)
		{
			//---------CODE TO BE ADDED TO PERFORM MATERIAL SWITCHING--------------------
			if (alt_use)// if "alternative use" button was pressed
			{
				alt_use = 0;
				hit_obj->setMaterial(my_materials[mat_index], "*");
				
				// changing current material index, and resetting it to 0, when we reach the last element
				if ((mat_index + 1) < my_materials.size())
					mat_index++;
				else
					mat_index = 0;
			}

)

- would it be possible to rather than use one group of materials (mesh_base inherited) for all the selectable objects to have tha laser read the materials iherited from a material named after the selected object? this way the available materials would be object-dependent and not "global". so if i had a chair, i would create a material named chair and inherit from it (chair_wood, chair_plastic, chair_blue) and the toggle between them. how should the code be modified?

Thanks once again!
Best,
Pio

Problem_Laser.jpg

Link to comment

Hi Piotr,

Looks like you did not add the alt_use member to the ObjLaserPointer.h file (see code above)

Quote

- would it be possible to rather than use one group of materials (mesh_base inherited) for all the selectable objects to have tha laser read the materials iherited from a material named after the selected object? this way the available materials would be object-dependent and not "global". so if i had a chair, i would create a material named chair and inherit from it (chair_wood, chair_plastic, chair_blue) and the toggle between them. how should the code be modified?

Sure it is possible: just make a parent material for each object inherited from the mesh_base, and then inherit other object's materials from this parent (repeat that for all your objects).

Let's implement material changing for movable objects, for example. For this purpose, you'll have to modify the ObjMovable.h file by adding the following lines to the public section of the ObjMovable class (and a shutdown method declaration to the private section):

class ObjMovable: public VRInteractable
{
public:
	// ...
	PROPERTY_PARAMETER(String, mat_name, "mesh_base");	//<--- name of the parent material to use its children for materials switching
	Unigine::Vector<MaterialPtr> my_materials;		// vector of materials to be used for the object <---
	int mat_index = 0;					// current material index for the object <---
	// ...
private:
	// ...
	void shutdown() override;
	// ...
};

Then you should add materials vector initialization code to the ObjMovable.cpp file:

void ObjMovable::init()
{
      // clearing the vector
      my_materials.clear();
      // getting the name of the parent material for the group of object's materials (mat_name parameter of the property)
      MaterialPtr parent_mat = Materials::get()->findMaterial(mat_name.get());
      int num = parent_mat->getNumChildren();
      // adding all its children to the vector
      for (int i = 0; i < num; i++)
      {
            my_materials.append(parent_mat->getChild(i));
      }
      
      //...
}
// method performing vector cleanup on shutdown
void ObjMovable::shutdown()
{
	my_materials.clear();
}

Now you can remove or comment previously added materials vector initialization code from the ObjLaserPointer::init() method (in the ObjLaserPointer.cpp file). And modify the code we added earlier for the case when the "alternative use" button is pressed as follows:

if (alt_use)// if "alternative use" button was pressed
{
	alt_use = 0;
	
	// using the Component System to get the ObjMovable component (i.e., checking if an object hit by the ray is a movable object)
	ObjMovable *movable_object = ComponentSystem::get()->getComponent<ObjMovable>(hit_obj->getNode());
	if (movable_object != nullptr){
                // setting a material from the vector of materials for this particular object
		hit_obj->setMaterial(movable_object->my_materials[movable_object->mat_index], "*");
		// changing current material index for this particular object, and resetting it to 0, as we reach the last element
		if ((movable_object->mat_index + 1) < movable_object->my_materials.size())
			movable_object->mat_index++;
		else
			movable_object->mat_index = 0;
	}
}

Now, the programming part is over, and you can open your project in the UnigineEditor and perform the following actions to specify parent materials for objects (we're going to use buildings_preset_0 materials for the cone object on the table, and default mesh_base for other movable objects):

  1. Select the cone NodeReference (with a chain icon) in the World hierarchy
  2. Click Edit in the Parameters window
  3. Among the parameters of the movable.prop property assigned to the node find the one named Mat Name and change its value to buildings_preset_0
  4. Then select the cone NodeReference(with a chain icon) in the hierarchy again and click Apply in the Parameters window
  5. Save your world.

Now, you can compile and launch you application: the laser pointer will work the same way, but it'll switch only materials inherited from buildings_preset_0 for the cone, while switching all mesh_base-inherited materials for other objects. You can adjust materials for other objects the same way and have the desired functionality.

Thank you!

 

 

Link to comment

Hi,

thanks again for your great help.My code was ok - and I don't understand it really, but after just rewriting some parts of the code manually instead of copy-pasting them I managed to compile and start the application.

Additionally, I included Movable.h in the Laserpointer.h. I also added the new code from your last post, compiled it and updated the movable.prop file manually - and I'm not sure if I did it right. Here's the mat_name feature I added to it:

<?xml version="1.0" encoding="utf-8"?>
<property version="2.7.2.2" name="movable" parent_name="vr_object" manual="1">
    <parameter name="mat_name" title="Material Name" type="string">"mesh_base"</parameter>

So I see the Material Name in my editor under movable.property, i select a name of the material from which I inherit - but in VR, when pointing to an object and pressing the controller's buttons, allthough I still get the object's name printed, materials don't switch.

The only thing I did differently : in VRPlayer.cpp I have the YB button mapped, as XA activates teleportation. But even with XA mapped to altuseit, it still does not work. 

Thanks,
Pio
 

 

Link to comment

Hi,

I have rewritten the code in a new project, it compiles.
I was trying to change the way the mat_name parameter is defined in the .prop file, as in the example in the docs:

<parameter name="Param3" title="test_title" type="string">Hello, World!!!</parameter>

<parameter name="mat_name" title="Material Name" type="string">mesh_base</parameter>

but it didn't help too.

I think it must be a problem with the way the parameter is defined in the prop. file. 

Thanks 

Link to comment

Hi Piotr,

Seems like there was an Oculus-related bug in VR Template - right touch controller's buttons were ignored... In the VRPlayeOculus.cpp file find the implementation of the VRPlayerOculus::getControllerButton() method and replace the following lines for XA and YB:

case XA: return oculus.getControllerButtonPressed(OCULUS_BUTTON_X);
case YB: return oculus.getControllerButtonPressed(OCULUS_BUTTON_Y);

with these ones:

case XA: return itof(device == OCULUS_DEVICE_CONTROLLER_LEFT ? oculus.getControllerButtonPressed(OCULUS_BUTTON_X) : oculus.getControllerButtonPressed(OCULUS_BUTTON_A));
case YB: return itof(device == OCULUS_DEVICE_CONTROLLER_LEFT ? oculus.getControllerButtonPressed(OCULUS_BUTTON_Y) : oculus.getControllerButtonPressed(OCULUS_BUTTON_B));

I've compiled your code, and now it works fine.

The bug is fixed now and updated version will be available with the next SDK release! Sorry for the inconvenience caused!

By the way, in order to refresh your *.prop file for ObjectMovable component (instead of doing it manually) you can add the following line to the AppSystemLogic.cpp file after registering the component:

ComponentSystem::get()->refreshProperty<ObjMovable>();

Thank you!

Link to comment

Hi,

I have tried it many times, made a new project, implemented all changes in the code once again.
The code compiles, but in VR there still is no material changing. 
If it works in your project - could you please send me a small working scene?

Thanks
 

Link to comment
  • silent changed the title to [SOLVED] Adding controller functionality/Changing a material

Hi Fox,

I got it to work, both on pc and in vr.
All because I did not have the "debug" option turned on (customize run settings in the browser) in my project, the changes in the code were not being applied to the project.
So actually it was fine from the very beginning, and only because of my lack of knowledge it all lasted so long.
Sorry for that!
But once again, I really want to thank you for all your time, patience and supporting me till the things get done. I really appreciate it, as I have just started with this great engine.   

Thanks!

Link to comment
×
×
  • Create New...