piotr.karkocha Posted October 12, 2018 Share Posted October 12, 2018 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 Link to comment
fox Posted October 17, 2018 Share Posted October 17, 2018 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! Link to comment
piotr.karkocha Posted October 17, 2018 Author Share Posted October 17, 2018 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 Link to comment
fox Posted October 18, 2018 Share Posted October 18, 2018 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): Select the cone NodeReference (with a chain icon) in the World hierarchy Click Edit in the Parameters window 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 Then select the cone NodeReference(with a chain icon) in the hierarchy again and click Apply in the Parameters window 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
piotr.karkocha Posted October 18, 2018 Author Share Posted October 18, 2018 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
piotr.karkocha Posted October 22, 2018 Author Share Posted October 22, 2018 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
fox Posted October 23, 2018 Share Posted October 23, 2018 Hi, Piotr, Could you send or attach a small test project, to make identification of the cause easier. Thanks! Link to comment
piotr.karkocha Posted October 23, 2018 Author Share Posted October 23, 2018 Hi Fox, I have attached the project to a message. Thanks Link to comment
fox Posted October 24, 2018 Share Posted October 24, 2018 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
piotr.karkocha Posted October 24, 2018 Author Share Posted October 24, 2018 Hi Fox! Thanks for your time and help. I'll implement the changes and will let you know if it works. Thanks once again! Link to comment
piotr.karkocha Posted October 27, 2018 Author Share Posted October 27, 2018 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
piotr.karkocha Posted October 30, 2018 Author Share Posted October 30, 2018 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
Recommended Posts