Skip to content

Commit 51232a3

Browse files
committed
Nicer materials/colours
1 parent b4a5b49 commit 51232a3

1 file changed

Lines changed: 257 additions & 10 deletions

File tree

src/gui/guihelperVSG.cpp

Lines changed: 257 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,223 @@
11
#include "guihelper_impl.h"
2+
3+
#include <algorithm>
4+
5+
#include <chrono/physics/ChBodyEasy.h>
6+
27
using namespace hydroc::gui;
38

49
using namespace chrono::vsg3d;
510

611
// -----------------------------------------------------------------------------
712

13+
namespace {
14+
// -----------------------------------------------------------------------------
15+
// Appearance tuning (Painted metal base — industrial yellow)
16+
// -----------------------------------------------------------------------------
17+
constexpr float kPaintedMetalR = 0.92f;
18+
constexpr float kPaintedMetalG = 0.72f;
19+
constexpr float kPaintedMetalB = 0.12f;
20+
21+
// Moderate, subtle highlight (painted steel: not mirror, not plastic).
22+
constexpr float kSpecular = 0.25f;
23+
constexpr float kSpecularExponent = 64.0f;
24+
25+
// Metallic/roughness parameters (painted metal: mostly dielectric coating).
26+
constexpr float kRoughness = 0.45f;
27+
constexpr float kMetallic = 0.05f;
28+
29+
// Per-body color variation range [0.92, 1.08] for visual distinction.
30+
constexpr float kColorVariationMin = 0.92f;
31+
constexpr float kColorVariationMax = 1.08f;
32+
constexpr int kColorVariationCycle = 8; // cycle length for deterministic variation
33+
34+
// -----------------------------------------------------------------------------
35+
// Appearance tuning (Water surface)
36+
// -----------------------------------------------------------------------------
37+
// Dark ocean blue-green — darker than sky for clear horizon.
38+
constexpr float kWaterR = 0.03f;
39+
constexpr float kWaterG = 0.10f;
40+
constexpr float kWaterB = 0.14f;
41+
42+
// Semi-transparent surface (60% opacity for visible waterline).
43+
constexpr float kWaterOpacity = 0.60f;
44+
45+
// Subtle specular highlight for calm water.
46+
constexpr float kWaterSpecular = 0.4f;
47+
constexpr float kWaterSpecularExp = 96.0f;
48+
49+
// Slightly glossy, mostly dielectric.
50+
constexpr float kWaterRoughness = 0.15f;
51+
constexpr float kWaterMetallic = 0.0f;
52+
53+
// Water plane geometry.
54+
constexpr double kWaterPlaneSize = 200.0; // meters (large enough to fill most views)
55+
constexpr double kWaterPlaneThickness = 0.02; // meters (thin slab)
56+
constexpr double kWaterPlaneZ = -0.05; // below z=0 to avoid z-fighting with craft
57+
58+
// -----------------------------------------------------------------------------
59+
// Camera tuning (deterministic side-on view)
60+
// -----------------------------------------------------------------------------
61+
// Eye on negative Y axis, slightly above, looking at origin. Z-up enforced.
62+
constexpr double kCameraDistance = 60.0; // meters along -Y axis
63+
constexpr double kCameraHeight = 40.0; // meters above z=0
64+
65+
// -----------------------------------------------------------------------------
66+
// Lighting tuning (3-light studio setup: key + fill + ambient)
67+
// -----------------------------------------------------------------------------
68+
// Key light (main sun) — primary illumination from upper-front.
69+
constexpr float kKeyIntensity = 1.0f;
70+
constexpr double kKeyAzimuth = 0.4 * chrono::CH_PI; // ~72° from front
71+
constexpr double kKeyElevation = chrono::CH_PI / 5.0; // ~36° above horizon
72+
73+
// Fill light (soft opposite) — reduces harsh shadows, lifts dark side.
74+
constexpr float kFillIntensity = 0.35f;
75+
constexpr double kFillAzimuth = kKeyAzimuth + chrono::CH_PI; // opposite side
76+
constexpr double kFillElevation = chrono::CH_PI / 8.0; // ~22.5° above horizon
77+
78+
// Note: Chrono-VSG includes a built-in ambient at 10% of key intensity.
79+
// Shadows remain OFF (water plane lacks per-object shadow control).
80+
81+
// -----------------------------------------------------------------------------
82+
// Wireframe overlay tuning
83+
// -----------------------------------------------------------------------------
84+
// Enable wireframe rendering for improved shape readability.
85+
// Default OFF: solid shaded rendering displays materials/colors properly.
86+
constexpr bool kEnableWireframe = false;
87+
88+
/// Creates the base painted-metal material (industrial yellow).
89+
chrono::ChVisualMaterial MakePaintedMetalBase() {
90+
chrono::ChVisualMaterial mat;
91+
92+
mat.SetDiffuseColor(chrono::ChColor(kPaintedMetalR, kPaintedMetalG, kPaintedMetalB));
93+
mat.SetSpecularColor(chrono::ChColor(kSpecular, kSpecular, kSpecular));
94+
mat.SetSpecularExponent(kSpecularExponent);
95+
mat.SetRoughness(kRoughness);
96+
mat.SetMetallic(kMetallic);
97+
mat.SetOpacity(1.0f);
98+
99+
return mat;
100+
}
101+
102+
/// Creates a painted-metal variant with subtle brightness variation for body distinction.
103+
/// The variation is deterministic based on the body index, cycling through kColorVariationCycle.
104+
chrono::ChVisualMaterial MakePaintedMetalVariant(int index) {
105+
// Compute variation factor in [kColorVariationMin, kColorVariationMax].
106+
// Cycle through values to keep variation subtle and deterministic.
107+
const float t = static_cast<float>(index % kColorVariationCycle) /
108+
static_cast<float>(kColorVariationCycle - 1);
109+
const float factor = kColorVariationMin + t * (kColorVariationMax - kColorVariationMin);
110+
111+
// Apply factor to base color, clamping to valid range.
112+
const float r = std::min(1.0f, kPaintedMetalR * factor);
113+
const float g = std::min(1.0f, kPaintedMetalG * factor);
114+
const float b = std::min(1.0f, kPaintedMetalB * factor);
115+
116+
chrono::ChVisualMaterial mat;
117+
mat.SetDiffuseColor(chrono::ChColor(r, g, b));
118+
mat.SetSpecularColor(chrono::ChColor(kSpecular, kSpecular, kSpecular));
119+
mat.SetSpecularExponent(kSpecularExponent);
120+
mat.SetRoughness(kRoughness);
121+
mat.SetMetallic(kMetallic);
122+
mat.SetOpacity(1.0f);
123+
124+
return mat;
125+
}
126+
127+
chrono::ChVisualMaterial MakeWaterSurfaceMaterial() {
128+
chrono::ChVisualMaterial mat;
129+
130+
mat.SetDiffuseColor(chrono::ChColor(kWaterR, kWaterG, kWaterB));
131+
mat.SetSpecularColor(chrono::ChColor(kWaterSpecular, kWaterSpecular, kWaterSpecular));
132+
mat.SetSpecularExponent(kWaterSpecularExp);
133+
mat.SetRoughness(kWaterRoughness);
134+
mat.SetMetallic(kWaterMetallic);
135+
mat.SetOpacity(kWaterOpacity);
136+
137+
return mat;
138+
}
139+
140+
void ApplyMaterialToAllVisualShapes(chrono::ChBody& body, const chrono::ChVisualMaterial& mat) {
141+
auto model = body.GetVisualModel();
142+
if (!model) {
143+
return;
144+
}
145+
146+
auto mat_ptr = chrono_types::make_shared<chrono::ChVisualMaterial>(mat);
147+
148+
const unsigned int num_shapes = model->GetNumShapes();
149+
for (unsigned int i = 0; i < num_shapes; ++i) {
150+
auto shape = model->GetShape(i);
151+
if (!shape) {
152+
continue;
153+
}
154+
155+
const unsigned int num_materials = shape->GetNumMaterials();
156+
if (num_materials == 0) {
157+
shape->AddMaterial(mat_ptr);
158+
continue;
159+
}
160+
161+
for (unsigned int m = 0; m < num_materials; ++m) {
162+
shape->SetMaterial(m, mat_ptr);
163+
}
164+
}
165+
}
166+
167+
/// Adds a fill light to the VSG scene for improved shape readability.
168+
/// Must be called AFTER pVis->Initialize() since the scene is created during init.
169+
void AddFillLight(chrono::vsg3d::ChVisualSystemVSG* vis) {
170+
auto scene = vis->GetVSGScene();
171+
if (!scene) {
172+
return;
173+
}
174+
175+
// Compute fill light direction (Z-up coordinate system).
176+
const double se = std::sin(kFillElevation);
177+
const double ce = std::cos(kFillElevation);
178+
const double sa = std::sin(kFillAzimuth);
179+
const double ca = std::cos(kFillAzimuth);
180+
181+
auto fillLight = vsg::DirectionalLight::create();
182+
fillLight->name = "fill light";
183+
fillLight->color.set(1.0f, 1.0f, 1.0f);
184+
fillLight->intensity = kFillIntensity;
185+
fillLight->direction.set(
186+
static_cast<float>(-ce * ca),
187+
static_cast<float>(-ce * sa),
188+
static_cast<float>(-se));
189+
190+
scene->addChild(fillLight);
191+
}
192+
193+
/// Creates a visual-only water plane (fixed, non-colliding) at z ~ 0.
194+
std::shared_ptr<chrono::ChBody> CreateWaterPlane() {
195+
// Create a thin box as the water surface (visual only: no collision, fixed).
196+
auto water = chrono_types::make_shared<chrono::ChBodyEasyBox>(
197+
kWaterPlaneSize, // x dimension
198+
kWaterPlaneSize, // y dimension
199+
kWaterPlaneThickness, // z dimension (thin slab)
200+
1.0, // density (irrelevant for fixed body)
201+
true, // enable visualization
202+
false // no collision geometry
203+
);
204+
205+
water->SetName("water_surface");
206+
water->SetPos(chrono::ChVector3d(0.0, 0.0, kWaterPlaneZ));
207+
water->SetFixed(true);
208+
water->EnableCollision(false);
209+
210+
// Apply water material to all visual shapes.
211+
const auto water_mat = MakeWaterSurfaceMaterial();
212+
ApplyMaterialToAllVisualShapes(*water, water_mat);
213+
214+
return water;
215+
}
216+
217+
} // namespace
218+
219+
// -----------------------------------------------------------------------------
220+
8221
class MyComponentVSG : public chrono::vsg3d::ChGuiComponentVSG {
9222
public:
10223
MyComponentVSG(chrono::vsg3d::ChVisualSystemVSG* vsys, bool& buttonPressed)
@@ -48,24 +261,58 @@ void GUIImplVSG::Init(UI& ui, chrono::ChSystem* system, const char* title) {
48261
pVis->SetWindowPosition(100, 100);
49262
pVis->SetWindowTitle(title);
50263

51-
pVis->AddCamera(chrono::ChVector3d(10, -50, 10), chrono::ChVector3d(0, 0, 0));
52-
pVis->SetCameraVertical(chrono::CameraVerticalDir::Z);
264+
// Deterministic side-on camera: eye on -Y axis, slightly above, looking at origin.
265+
const chrono::ChVector3d eye(0.0, -kCameraDistance, kCameraHeight);
266+
const chrono::ChVector3d target(0.0, 0.0, -10.0);
267+
pVis->AddCamera(eye, target);
268+
pVis->SetCameraVertical(chrono::CameraVerticalDir::Z); // enforce Z-up (no roll)
53269
pVis->SetCameraAngleDeg(40.0);
54270

55-
pVis->SetLightIntensity(1.0f);
56-
pVis->SetLightDirection(1.5 * chrono::CH_PI_2, chrono::CH_PI_4);
57-
pVis->EnableShadows();
271+
// Key light (main directional) — set before Initialize().
272+
pVis->SetLightIntensity(kKeyIntensity);
273+
pVis->SetLightDirection(kKeyAzimuth, kKeyElevation);
274+
275+
// Shadows OFF: Chrono/VSG lacks per-object shadow control. The water plane
276+
// would cast/receive shadows that flatten craft lighting.
277+
// pVis->EnableShadows();
278+
279+
// Skybox provides sky/horizon context, contrasting with dark water plane.
280+
pVis->EnableSkyBox();
281+
282+
// Grid disabled: water plane provides the marine context instead.
283+
// (Keeping this comment for easy re-enable if needed later.)
58284

59-
bool enable_grid = true; // TODO: Make this configurable
60-
if (enable_grid) {
61-
pVis->AddGrid(1, 1, 30, 30,
62-
chrono::ChCoordsys<>(chrono::ChVector3d(0, 0.0, 0), chrono::QuatFromAngleZ(chrono::CH_PI_2)),
63-
chrono::ChColor(.1f, .1f, .1f));
285+
if (system) {
286+
// Add water surface plane (visual-only, fixed, non-colliding).
287+
auto water_plane = CreateWaterPlane();
288+
system->AddBody(water_plane);
289+
290+
// Apply painted-metal material variants to all existing bodies.
291+
// Each body gets a subtle color variation for visual distinction.
292+
int body_index = 0;
293+
for (auto& body : system->GetBodies()) {
294+
if (body && body->GetName() != "water_surface") {
295+
const auto material = MakePaintedMetalVariant(body_index);
296+
ApplyMaterialToAllVisualShapes(*body, material);
297+
++body_index;
298+
299+
// Enable wireframe overlay for improved shape readability (CAD-style).
300+
if (kEnableWireframe) {
301+
auto model = body->GetVisualModel();
302+
if (model) {
303+
model->EnableWireframe(true);
304+
}
305+
}
306+
}
307+
}
64308
}
65309

66310
pVis->AddGuiComponent(std::make_shared<MyComponentVSG>(pVis.get(), ui.simulationStarted));
67311

68312
pVis->Initialize();
313+
314+
// Add fill light after initialization (scene must exist first).
315+
AddFillLight(pVis.get());
69316
}
70317

71318
void GUIImplVSG::SetCamera(double x, double y, double z, double dirx, double diry, double dirz) {

0 commit comments

Comments
 (0)