Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamically set object textures from existing UE material or texture PNG #3992

Merged
merged 7 commits into from
Nov 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions AirLib/include/api/RpcLibClientBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,8 @@ namespace airlib
msr::airlib::Kinematics::State simGetGroundTruthKinematics(const std::string& vehicle_name = "") const;
msr::airlib::Environment::State simGetGroundTruthEnvironment(const std::string& vehicle_name = "") const;
std::vector<std::string> simSwapTextures(const std::string& tags, int tex_id = 0, int component_id = 0, int material_id = 0);
bool simSetObjectMaterial(const std::string& object_name, const std::string& material_name);
bool simSetObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path);

// Recording APIs
void startRecording();
Expand Down
2 changes: 2 additions & 0 deletions AirLib/include/api/WorldSimApiBase.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,8 @@ namespace airlib
virtual bool runConsoleCommand(const std::string& command) = 0;
virtual bool setObjectScale(const std::string& object_name, const Vector3r& scale) = 0;
virtual std::unique_ptr<std::vector<std::string>> swapTextures(const std::string& tag, int tex_id = 0, int component_id = 0, int material_id = 0) = 0;
virtual bool setObjectMaterial(const std::string& object_name, const std::string& material_name) = 0;
virtual bool setObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path) = 0;
virtual vector<MeshPositionVertexBuffersResponse> getMeshPositionVertexBuffers() const = 0;

virtual bool createVoxelGrid(const Vector3r& position, const int& x_size, const int& y_size, const int& z_size, const float& res, const std::string& output_file) = 0;
Expand Down
10 changes: 10 additions & 0 deletions AirLib/src/api/RpcLibClientBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -411,6 +411,16 @@ __pragma(warning(disable : 4239))
return pimpl_->client.call("simSwapTextures", tags, tex_id, component_id, material_id).as<vector<string>>();
}

bool RpcLibClientBase::simSetObjectMaterial(const std::string& object_name, const std::string& material_name)
{
return pimpl_->client.call("simSetObjectMaterial", object_name, material_name).as<bool>();
}

bool RpcLibClientBase::simSetObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path)
{
return pimpl_->client.call("simSetObjectMaterialFromTexture", object_name, texture_path).as<bool>();
}

bool RpcLibClientBase::simLoadLevel(const string& level_name)
{
return pimpl_->client.call("simLoadLevel", level_name).as<bool>();
Expand Down
8 changes: 8 additions & 0 deletions AirLib/src/api/RpcLibServerBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -397,6 +397,14 @@ namespace airlib
return *getWorldSimApi()->swapTextures(tag, tex_id, component_id, material_id);
});

pimpl_->server.bind("simSetObjectMaterial", [&](const std::string& object_name, const std::string& material_name) -> bool {
return getWorldSimApi()->setObjectMaterial(object_name, material_name);
});

pimpl_->server.bind("simSetObjectMaterialFromTexture", [&](const std::string& object_name, const std::string& texture_path) -> bool {
return getWorldSimApi()->setObjectMaterialFromTexture(object_name, texture_path);
});

pimpl_->server.bind("startRecording", [&]() -> void {
getWorldSimApi()->startRecording();
});
Expand Down
27 changes: 27 additions & 0 deletions PythonClient/airsim/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,33 @@ def simSwapTextures(self, tags, tex_id = 0, component_id = 0, material_id = 0):
"""
return self.client.call("simSwapTextures", tags, tex_id, component_id, material_id)

def simSetObjectMaterial(self, object_name, material_name):
"""
Runtime Swap Texture API
See https://microsoft.github.io/AirSim/retexturing/ for details
Args:
object_name (str): name of object to set material for
material_name (str): name of material to set for object

Returns:
bool: True if material was set
"""
return self.client.call("simSetObjectMaterial", object_name, material_name)

def simSetObjectMaterialFromTexture(self, object_name, texture_path):
"""
Runtime Swap Texture API
See https://microsoft.github.io/AirSim/retexturing/ for details
Args:
object_name (str): name of object to set material for
texture_path (str): path to texture to set for object

Returns:
bool: True if material was set
"""
return self.client.call("simSetObjectMaterialFromTexture", object_name, texture_path)


# time-of-day control
def simSetTimeOfDay(self, is_enabled, start_datetime = "", is_start_datetime_dst = False, celestial_clock_speed = 1, update_interval_secs = 60, move_sun = True):
"""
Expand Down
7 changes: 7 additions & 0 deletions PythonClient/environment/change_texture_example.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import airsim

c = airsim.MultirotorClient()
c.confirmConnection()

c.simSetObjectMaterialFromTexture("OrangeBall", "sample_texture.jpg")

Binary file added PythonClient/environment/sample_texture.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
16 changes: 16 additions & 0 deletions Unity/AirLibWrapper/AirsimWrapper/Source/WorldSimApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,22 @@ std::unique_ptr<std::vector<std::string>> WorldSimApi::swapTextures(const std::s
return result;
}

bool WorldSimApi::setObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path)
{
throw std::invalid_argument(common_utils::Utils::stringf(
"setObjectMaterialFromTexture is not supported on unity")
.c_str());
return false;
}

bool WorldSimApi::setObjectMaterial(const std::string& object_name, const std::string& material_name)
{
throw std::invalid_argument(common_utils::Utils::stringf(
"setObjectMaterial is not supported on unity")
.c_str());
return false;
}

std::vector<std::string> WorldSimApi::listSceneObjects(const std::string& name_regex) const
{
std::vector<std::string> result;
Expand Down
2 changes: 2 additions & 0 deletions Unity/AirLibWrapper/AirsimWrapper/Source/WorldSimApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ class WorldSimApi : public msr::airlib::WorldSimApiBase
const std::string& message_param = "", unsigned char severity = 0) override;

virtual std::unique_ptr<std::vector<std::string>> swapTextures(const std::string& tag, int tex_id = 0, int component_id = 0, int material_id = 0) override;
virtual bool setObjectMaterial(const std::string& object_name, const std::string& material_name) override;
virtual bool setObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path) override;
virtual std::vector<std::string> listSceneObjects(const std::string& name_regex) const override;
virtual Pose getObjectPose(const std::string& object_name) const override;

Expand Down
Binary file not shown.
4 changes: 4 additions & 0 deletions Unreal/Plugins/AirSim/Source/SimMode/SimModeBase.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ ASimModeBase::ASimModeBase()
}
else
loading_screen_widget_ = nullptr;
static ConstructorHelpers::FObjectFinder<UMaterial> domain_rand_mat_finder(TEXT("Material'/AirSim/HUDAssets/DomainRandomizationMaterial.DomainRandomizationMaterial'"));
if (domain_rand_mat_finder.Succeeded()) {
domain_rand_material_ = domain_rand_mat_finder.Object;
}
}

void ASimModeBase::toggleLoadingScreen(bool is_visible)
Expand Down
1 change: 1 addition & 0 deletions Unreal/Plugins/AirSim/Source/SimMode/SimModeBase.h
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ class AIRSIM_API ASimModeBase : public AActor

TMap<FString, FAssetData> asset_map;
TMap<FString, AActor*> scene_object_map;
UMaterial* domain_rand_material_;

protected: //must overrides
typedef msr::airlib::AirSimSettings AirSimSettings;
Expand Down
69 changes: 69 additions & 0 deletions Unreal/Plugins/AirSim/Source/WorldSimApi.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include "DrawDebugHelpers.h"
#include "Runtime/Engine/Classes/Components/LineBatchComponent.h"
#include "Runtime/Engine/Classes/Engine/Engine.h"
#include "ImageUtils.h"
#include <cstdlib>
#include <ctime>

Expand Down Expand Up @@ -431,6 +432,74 @@ std::unique_ptr<std::vector<std::string>> WorldSimApi::swapTextures(const std::s
return swappedObjectNames;
}

bool WorldSimApi::setObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path)
{
bool success = false;
UAirBlueprintLib::RunCommandOnGameThread([this, &object_name, &texture_path, &success]() {
if (!IsValid(simmode_->domain_rand_material_)) {
UAirBlueprintLib::LogMessageString("Cannot find material for domain randomization",
"",
LogDebugLevel::Failure);
}
else {
UTexture2D* texture_desired = FImageUtils::ImportFileAsTexture2D(FString(texture_path.c_str()));
AActor* actor = UAirBlueprintLib::FindActor<AActor>(simmode_, FString(object_name.c_str()));

if (IsValid(actor)) {
TArray<UStaticMeshComponent*> components;
actor->GetComponents<UStaticMeshComponent>(components);
for (UStaticMeshComponent* staticMeshComponent : components) {
UMaterialInstanceDynamic* dynamic_material = UMaterialInstanceDynamic::Create(simmode_->domain_rand_material_, staticMeshComponent);
dynamic_material->SetTextureParameterValue("TextureParameter", texture_desired);
staticMeshComponent->SetMaterial(0, dynamic_material);
}
success = true;
}
else {
UAirBlueprintLib::LogMessageString("Cannot find specified actor for domain randomization",
"",
LogDebugLevel::Failure);
}
}
},
true);

return success;
}

bool WorldSimApi::setObjectMaterial(const std::string& object_name, const std::string& material_name)
{
bool success = false;
UAirBlueprintLib::RunCommandOnGameThread([this, &object_name, &material_name, &success]() {
AActor* actor = UAirBlueprintLib::FindActor<AActor>(simmode_, FString(object_name.c_str()));
UMaterial* material = static_cast<UMaterial*>(StaticLoadObject(UMaterial::StaticClass(), nullptr, *FString(material_name.c_str())));

if (!IsValid(material)) {
UAirBlueprintLib::LogMessageString("Cannot find specified material for domain randomization",
"",
LogDebugLevel::Failure);
}
else {
if (IsValid(actor)) {
TArray<UStaticMeshComponent*> components;
actor->GetComponents<UStaticMeshComponent>(components);
for (UStaticMeshComponent* staticMeshComponent : components) {
staticMeshComponent->SetMaterial(0, material);
}
success = true;
}
else {
UAirBlueprintLib::LogMessageString("Cannot find specified actor for domain randomization",
"",
LogDebugLevel::Failure);
}
}
},
true);

return success;
}

//----------- Plotting APIs ----------/
void WorldSimApi::simFlushPersistentMarkers()
{
Expand Down
2 changes: 2 additions & 0 deletions Unreal/Plugins/AirSim/Source/WorldSimApi.h
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class WorldSimApi : public msr::airlib::WorldSimApiBase
const std::string& message_param = "", unsigned char severity = 0) override;

virtual std::unique_ptr<std::vector<std::string>> swapTextures(const std::string& tag, int tex_id = 0, int component_id = 0, int material_id = 0) override;
virtual bool setObjectMaterial(const std::string& object_name, const std::string& material_name) override;
virtual bool setObjectMaterialFromTexture(const std::string& object_name, const std::string& texture_path) override;
virtual std::vector<std::string> listSceneObjects(const std::string& name_regex) const override;
virtual Pose getObjectPose(const std::string& object_name) const override;
virtual bool setObjectPose(const std::string& object_name, const Pose& pose, bool teleport) override;
Expand Down