diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e2363a0 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +# Stuff +_example/ \ No newline at end of file diff --git a/controllers/sheep/sheep.py b/controllers/sheep/sheep.py new file mode 100644 index 0000000..3cb3326 --- /dev/null +++ b/controllers/sheep/sheep.py @@ -0,0 +1,213 @@ +""" +Sheep flocking controller (Webots, Reynolds boids variant). + +Each sheep broadcasts its GPS position every 3 steps on channel 1 and +listens for the dog and peer sheep positions. Peers are keyed by robot +name so each neighbour has exactly one current entry in the dict. + +Force stack each step (summed then converted to a heading + speed): + flee — away from dog, quadratic ramp, dominant when close + cohesion — toward flock centre, halved while fleeing + separation — inverse-distance push, prevents physical overlap + walls — linear repulsion from field boundary + wander — small persistent drift for natural idle motion + +Pen behaviour: on first entry into the quarantine pen the sheep latches +permanently — it turns pink (via the exposed woolColor PROTO field) and +the normal force stack is replaced by pen-confinement forces only. +""" + +import random +import math +from controller import Supervisor + +# --------------------------------------------------------------------------- +# Tuning constants +# --------------------------------------------------------------------------- + +MAX_SPEED = 22.0 # rad/s hard clamp on both motors +FLEE_SPEED = 20.0 # rad/s upper bound while panicking +WANDER_SPEED = 3.0 # rad/s lower bound during calm wandering + +X_MIN, X_MAX = -14.5, 14.5 # stone wall inner edges (metres) +Y_MIN, Y_MAX = -14.5, 14.5 +WALL_MARGIN = 3.5 # avoidance starts this far from the wall + +FLEE_DIST = 7.0 # dog within this radius triggers flee (metres) +SEPARATION_DIST = 2.5 # inverse-distance push active inside this radius +COHESION_DIST = 8.0 # pull toward flock centre active inside this radius + +PEN_X_MIN, PEN_X_MAX = 10.0, 13.0 # quarantine pen extents (metres) +PEN_Y_MIN, PEN_Y_MAX = -15.0, -8.0 # open entrance at y=-8, gate at y=-15 +PEN_MARGIN = 0.8 # confinement force starts this far from pen wall + +# --------------------------------------------------------------------------- +# Device setup +# --------------------------------------------------------------------------- + +robot = Supervisor() +timestep = int(robot.getBasicTimeStep()) +name = robot.getName() +self_node = robot.getSelf() + +left_motor = robot.getDevice("left wheel motor") +right_motor = robot.getDevice("right wheel motor") +left_motor.setPosition(float("inf")) +right_motor.setPosition(float("inf")) +left_motor.setVelocity(0.0) +right_motor.setVelocity(0.0) + +gps = robot.getDevice("gps"); gps.enable(timestep) +compass = robot.getDevice("compass"); compass.enable(timestep) +receiver = robot.getDevice("receiver"); receiver.enable(timestep) +emitter = robot.getDevice("emitter") + +# --------------------------------------------------------------------------- +# Helpers +# --------------------------------------------------------------------------- + +def norm_angle(a): + return math.atan2(math.sin(a), math.cos(a)) + + +def bearing(): + # Compass returns north direction in sensor frame; for this Z-up world + # with north = +Y, atan2(n[0], n[1]) gives the standard math angle + # (0 = east, π/2 = north) matching atan2(fy, fx) used for heading. + n = compass.getValues() + return math.atan2(n[0], n[1]) + + +def drive(heading, speed): + err = norm_angle(heading - bearing()) + # Scale forward component by cos(err): at 90° error fwd→0 so the robot + # spins in place to realign rather than driving sideways at full speed. + fwd = speed * max(0.0, math.cos(err)) + k = 4.0 + left_motor.setVelocity( max(-MAX_SPEED, min(MAX_SPEED, fwd - k * err))) + right_motor.setVelocity(max(-MAX_SPEED, min(MAX_SPEED, fwd + k * err))) + + +def paint_pink(): + # woolColor is declared as a PROTO field with IS binding to the DEF WOOL + # PBRAppearance baseColor. Changing it here propagates to every USE WOOL + # shape on the body. Direct field access avoids PROTO-internal opacity. + self_node.getField("woolColor").setSFColor([1.0, 0.55, 0.72]) + +# --------------------------------------------------------------------------- +# State +# --------------------------------------------------------------------------- + +wander_angle = random.uniform(-math.pi, math.pi) +step = 0 +dog_x = None +dog_y = None +peers = {} # name → (x, y), one entry per neighbour, cleared every 30 steps +penned = False + +# --------------------------------------------------------------------------- +# Main loop +# --------------------------------------------------------------------------- + +while robot.step(timestep) != -1: + step += 1 + pos = gps.getValues() + x, y = pos[0], pos[1] + + # Pen entry: one-way latch, never unset + if not penned and PEN_X_MIN < x < PEN_X_MAX and PEN_Y_MIN < y < PEN_Y_MAX: + penned = True + paint_pink() + + # Refresh peer table (clear before receiving so fresh data is never lost) + if step % 30 == 0: + peers.clear() + while receiver.getQueueLength() > 0: + msg = receiver.getString() + receiver.nextPacket() + p = msg.split(":") + if p[0] == "dog" and len(p) >= 3: + dog_x, dog_y = float(p[1]), float(p[2]) + elif p[0] == "sheep" and len(p) >= 4 and p[1] != name: + peers[p[1]] = (float(p[2]), float(p[3])) + + fx, fy = 0.0, 0.0 + + if penned: + # Inside pen: wander freely, strong boundary forces prevent exit, + # separation still active to avoid collisions with other penned sheep. + + pm = PEN_MARGIN + if x < PEN_X_MIN + pm: fx += ((PEN_X_MIN + pm - x) / pm) * 15.0 + if x > PEN_X_MAX - pm: fx -= ((x - (PEN_X_MAX - pm)) / pm) * 15.0 + if y < PEN_Y_MIN + pm: fy += ((PEN_Y_MIN + pm - y) / pm) * 15.0 + if y > PEN_Y_MAX - pm: fy -= ((y - (PEN_Y_MAX - pm)) / pm) * 15.0 + + for px, py in peers.values(): + dx, dy = px - x, py - y + d = math.hypot(dx, dy) + if 0.05 < d < SEPARATION_DIST: + push = (SEPARATION_DIST - d) / d + fx -= (dx / d) * push * 2.5 + fy -= (dy / d) * push * 2.5 + + if random.random() < 0.02: + wander_angle += random.uniform(-0.6, 0.6) + fx += math.cos(wander_angle) * 0.5 + fy += math.sin(wander_angle) * 0.5 + + else: + fleeing = False + + # Flee — quadratic ramp so force grows rapidly as the dog closes in + if dog_x is not None: + dx = dog_x - x + dy = dog_y - y + dist = math.hypot(dx, dy) + if 0.01 < dist < FLEE_DIST: + fleeing = True + t = 1.0 - dist / FLEE_DIST + s = t * t * 20.0 + fx -= (dx / dist) * s + fy -= (dy / dist) * s + + # Cohesion — halved while fleeing to reduce mid-panic collisions + cx, cy, cn = 0.0, 0.0, 0 + for px, py in peers.values(): + d = math.hypot(px - x, py - y) + if 0.3 < d < COHESION_DIST: + cx += px; cy += py; cn += 1 + if cn > 0: + w = 0.08 if fleeing else 0.15 + fx += (cx / cn - x) * w + fy += (cy / cn - y) * w + + # Separation — inverse-distance: huge when nearly overlapping, fades quickly + for px, py in peers.values(): + dx, dy = px - x, py - y + d = math.hypot(dx, dy) + if 0.05 < d < SEPARATION_DIST: + push = (SEPARATION_DIST - d) / d + fx -= (dx / d) * push * 2.5 + fy -= (dy / d) * push * 2.5 + + # Walls + if x < X_MIN + WALL_MARGIN: fx += ((X_MIN + WALL_MARGIN - x) / WALL_MARGIN) * 6.0 + if x > X_MAX - WALL_MARGIN: fx -= ((x - (X_MAX - WALL_MARGIN)) / WALL_MARGIN) * 6.0 + if y < Y_MIN + WALL_MARGIN: fy += ((Y_MIN + WALL_MARGIN - y) / WALL_MARGIN) * 6.0 + if y > Y_MAX - WALL_MARGIN: fy -= ((y - (Y_MAX - WALL_MARGIN)) / WALL_MARGIN) * 6.0 + + # Wander — suppressed while fleeing so drift cannot deflect the flee heading + if not fleeing: + if random.random() < 0.02: + wander_angle += random.uniform(-0.6, 0.6) + fx += math.cos(wander_angle) * 0.5 + fy += math.sin(wander_angle) * 0.5 + + heading = math.atan2(fy, fx) + mag = math.hypot(fx, fy) + speed = max(WANDER_SPEED, min(FLEE_SPEED, mag * 3.0)) + drive(heading, speed) + + if step % 3 == 0: + emitter.send(f"sheep:{name}:{x:.4f}:{y:.4f}") diff --git a/controllers/shepherd_dog/shepherd_dog.py b/controllers/shepherd_dog/shepherd_dog.py new file mode 100644 index 0000000..54d87a1 --- /dev/null +++ b/controllers/shepherd_dog/shepherd_dog.py @@ -0,0 +1,88 @@ +""" +Shepherd Dog controller (Webots, manual keyboard control). + +WASD / arrow keys drive the robot. +/- adjust speed in 10 % increments. +GPS position is broadcast every step on channel 1 so sheep controllers +can compute flee forces. Ears wag continuously via sinusoidal position +targets — purely cosmetic. +""" + +import math +from controller import Robot, Keyboard + +robot = Robot() +timestep = int(robot.getBasicTimeStep()) + +left_motor = robot.getDevice("left wheel motor") +right_motor = robot.getDevice("right wheel motor") +left_motor.setPosition(float("inf")) +right_motor.setPosition(float("inf")) +left_motor.setVelocity(0.0) +right_motor.setVelocity(0.0) + +lidar = robot.getDevice("lidar") +lidar.enable(timestep) +lidar.enablePointCloud() + +gps = robot.getDevice("gps"); gps.enable(timestep) +compass = robot.getDevice("compass"); compass.enable(timestep) +emitter = robot.getDevice("emitter") +receiver = robot.getDevice("receiver"); receiver.enable(timestep) + +left_ear = robot.getDevice("left ear motor") +right_ear = robot.getDevice("right ear motor") +left_ear.setPosition(float("inf")) +right_ear.setPosition(float("inf")) +left_ear.setVelocity(0.0) +right_ear.setVelocity(0.0) + +keyboard = robot.getKeyboard() +keyboard.enable(timestep) + +MOTOR_MAX = left_motor.getMaxVelocity() +speed_level = 0.5 # fraction of MOTOR_MAX; adjusted by +/- + +EAR_AMPLITUDE = 0.35 # rad, peak ear deflection +EAR_RATE = 8.0 # rad/s, how fast the ears are driven +ear_phase = 0.0 + +while robot.step(timestep) != -1: + speed = MOTOR_MAX * speed_level + turn = speed * 0.6 # differential turn radius + + left_vel = 0.0 + right_vel = 0.0 + key = keyboard.getKey() + while key > 0: + if key in (ord('W'), Keyboard.UP): + left_vel = speed + right_vel = speed + elif key in (ord('S'), Keyboard.DOWN): + left_vel = -speed + right_vel = -speed + elif key in (ord('A'), Keyboard.LEFT): + left_vel = -turn + right_vel = turn + elif key in (ord('D'), Keyboard.RIGHT): + left_vel = turn + right_vel = -turn + elif key in (ord('+'), ord('=')): + speed_level = min(1.0, speed_level + 0.1) + print(f"Speed: {speed_level:.0%} ({MOTOR_MAX * speed_level:.1f} rad/s)") + elif key in (ord('-'), ord('_')): + speed_level = max(0.1, speed_level - 0.1) + print(f"Speed: {speed_level:.0%} ({MOTOR_MAX * speed_level:.1f} rad/s)") + key = keyboard.getKey() + + left_motor.setVelocity(left_vel) + right_motor.setVelocity(right_vel) + + pos = gps.getValues() + emitter.send(f"dog:{pos[0]}:{pos[1]}") + + ear_phase += 0.12 + ear_pos = EAR_AMPLITUDE * math.sin(ear_phase) + left_ear.setVelocity(EAR_RATE) + right_ear.setVelocity(EAR_RATE) + left_ear.setPosition( ear_pos) + right_ear.setPosition(-ear_pos) diff --git a/docs/project.md b/docs/project.md new file mode 100644 index 0000000..eea5a9a --- /dev/null +++ b/docs/project.md @@ -0,0 +1,56 @@ +# Group G25 - Formal & Title & Goals + +## Team members +- Diogo Costa +- Johnny Fernandes +- Nelson Neto + +## (i) Title and General objectives +**RL-Based Autonomous Shepherd Robot for Livestock Herding** + +- Implement effective herding behaviors through proximity and movement strategies +- Build a 3D environment with realistic robot dynamics and LIDAR-based perception +- Develop a mobile robot capable of autonomously guiding a flock of sheep into a designated target area using Reinforcement Learning + + +# Group G25 - (ii) Intermediate Goals + +## Intermediate goals +- Set up the Webots simulation environment with an open field and target zone +- Implement lightweight Gymnasium-based 2D herding environment +- Design a Sheep and Dog robot +- Implement a sheep flocking model for fast RL iteration +- Validate LiDAR sensor feedback for sheep detection and distance estimation + + +# Group G25 - Course Project (Final) Goals + +## (iii) Main goals +- State-of-the-art survey on shepherding algorithms and multi-agent RL herding +- Train the robot using PPO to successfully herd a single sheep into the goal +- Achieve fully autonomous herding of multiple sheep and a full flock into the target area +- Optimize robot trajectory to minimize the time required to group the flock +- Ensure zero collisions between the robot and the sheep during the task +- Quantitative evaluation: success rate, time-to-pen, flock dispersion metrics +- Article, demo video, and final presentation + +## (iv) Extra Merit +- Curriculum Learning (scaling from 1 sheep to a flock) +- Comparison of performance between Differential Drive and Mecanum wheels +- Robustness testing under sensor noise or varying sheep speeds, configurations and parameters +- Multi-shepherd cooperative mode: 2 dogs learn role specialization (collector vs. driver) +- Obstacles and terrain (walls, narrow gates, corridors) the flock must be funneled through + + +# Group G25 - Tools & Limitations + +## (v) Tools +- Webots for 3D physics simulation with ROS2 integration via `webots_ros2` package +- Stable-Baselines3 for the PPO algorithm implementation +- Gymnasium (OpenAI) for the RL environment wrapper (lightweight 2D herding env for fast RL training) +- Python as the primary programming language (sheep flocking model, reward shaping, evaluation) + +## (vi) Limitations +- Computational Power: Training time might be high for complex flock behaviors +- Sim-to-Real Gap: No real-world validation of the herding controller; project is simulation-only (2D + Webots 3D) +- Model Complexity: Simplified sheep behavior (scripted) may not account for all biological livestock nuances \ No newline at end of file diff --git a/protos/Sheep.proto b/protos/Sheep.proto new file mode 100644 index 0000000..d53bc41 --- /dev/null +++ b/protos/Sheep.proto @@ -0,0 +1,531 @@ +#VRML_SIM R2025a utf8 +# Sheep - wheeled base with sheep character on top + +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/appearances/protos/TireRubber.proto" + +PROTO Sheep [ + field SFVec3f translation 0 0 0 + field SFRotation rotation 0 1 0 0 + field SFString name "sheep" + field SFString controller "sheep" + field MFString controllerArgs [] + field SFString customData "" + field SFBool supervisor TRUE + field SFBool synchronization TRUE + field SFColor woolColor 0.92 0.90 0.85 +] +{ + Robot { + translation IS translation + rotation IS rotation + name IS name + controller IS controller + controllerArgs IS controllerArgs + customData IS customData + supervisor IS supervisor + synchronization IS synchronization + children [ + # ========== CHASSIS / BASE ========== + DEF SHEEP_CHASSIS Transform { + translation 0 0 0.04 + children [ + Shape { + appearance DEF SHEEP_CHASSIS_APP PBRAppearance { + baseColor 0.25 0.25 0.25 + roughness 0.6 + metalness 0.3 + } + geometry Box { + size 0.24 0.14 0.05 + } + } + ] + } + + # ========== FLUFFY WOOL BODY ========== + DEF SHEEP_BODY Transform { + translation 0 0 0.12 + children [ + Shape { + appearance DEF WOOL PBRAppearance { + baseColor IS woolColor + roughness 0.95 + metalness 0.0 + } + geometry Box { + size 0.26 0.16 0.12 + } + } + # Fluffy wool bumps — 6 spheres clustered on top of the body box + Transform { + translation -0.04 0 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.055 + subdivision 3 + } + } + ] + } + Transform { + translation 0.04 0.035 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.05 + subdivision 3 + } + } + ] + } + Transform { + translation 0.04 -0.035 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.05 + subdivision 3 + } + } + ] + } + Transform { + translation -0.04 0.035 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.05 + subdivision 3 + } + } + ] + } + Transform { + translation -0.04 -0.035 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.05 + subdivision 3 + } + } + ] + } + Transform { + translation 0 0 0.07 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.06 + subdivision 3 + } + } + ] + } + ] + } + + # ========== HEAD ========== + DEF SHEEP_HEAD Transform { + translation 0.16 0 0.12 + children [ + Shape { + appearance DEF SHEEP_DARK PBRAppearance { + baseColor 0.25 0.22 0.18 + roughness 0.8 + metalness 0.0 + } + geometry Box { + size 0.08 0.10 0.08 + } + } + # Left eye (white sphere + dark pupil) + Transform { + translation 0.04 0.03 0.02 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.95 0.95 0.95 + roughness 0.3 + } + geometry Sphere { + radius 0.013 + subdivision 2 + } + } + Transform { + translation 0.008 0 0.003 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.1 0.1 0.1 + roughness 0.2 + } + geometry Sphere { + radius 0.007 + subdivision 2 + } + } + ] + } + ] + } + # Right eye + Transform { + translation 0.04 -0.03 0.02 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.95 0.95 0.95 + roughness 0.3 + } + geometry Sphere { + radius 0.013 + subdivision 2 + } + } + Transform { + translation 0.008 0 0.003 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.1 0.1 0.1 + roughness 0.2 + } + geometry Sphere { + radius 0.007 + subdivision 2 + } + } + ] + } + ] + } + # Left ear + Transform { + translation 0 0.055 0.03 + rotation 0 0 -1 0.3 + children [ + Shape { + appearance USE SHEEP_DARK + geometry Box { + size 0.05 0.018 0.03 + } + } + ] + } + # Right ear + Transform { + translation 0 -0.055 0.03 + rotation 0 0 1 0.3 + children [ + Shape { + appearance USE SHEEP_DARK + geometry Box { + size 0.05 0.018 0.03 + } + } + ] + } + ] + } + + # ========== SHORT TAIL ========== + Transform { + translation -0.15 0 0.14 + children [ + Shape { + appearance USE WOOL + geometry Sphere { + radius 0.035 + subdivision 2 + } + } + ] + } + + # ========== RIGHT AXLE ARM ========== + DEF SHEEP_RIGHT_AXLE Transform { + translation 0 -0.085 0.031 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.5 0.5 0.5 + roughness 0.3 + metalness 0.8 + } + geometry Box { + size 0.02 0.05 0.02 + } + } + ] + } + + # ========== LEFT AXLE ARM ========== + DEF SHEEP_LEFT_AXLE Transform { + translation 0 0.085 0.031 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.5 0.5 0.5 + roughness 0.3 + metalness 0.8 + } + geometry Box { + size 0.02 0.05 0.02 + } + } + ] + } + + # ========== RIGHT WHEEL ========== + DEF SHEEP_RIGHT_WHEEL HingeJoint { + jointParameters HingeJointParameters { + axis 0 1 0 + anchor 0 -0.10 0.031 + } + device [ + RotationalMotor { + name "right wheel motor" + maxVelocity 25.0 + maxTorque 3.0 + } + PositionSensor { + name "right wheel sensor" + resolution 0.00628 + } + ] + endPoint Solid { + translation 0 -0.10 0.031 + rotation 0 -1 0 1.570796 + children [ + DEF SHEEP_WHEEL_VIS Pose { + rotation 1 0 0 -1.5708 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.15 0.15 0.15 + roughness 0.4 + metalness 0.5 + } + geometry Cylinder { + height 0.014 + radius 0.028 + subdivision 24 + } + } + Shape { + appearance TireRubber { + textureTransform TextureTransform { + scale 1.2 0.5 + } + type "bike" + } + geometry Cylinder { + height 0.018 + radius 0.031 + subdivision 24 + } + } + Shape { + appearance PBRAppearance { + baseColor 0.5 0.5 0.5 + metalness 0.6 + } + geometry Cylinder { + height 0.016 + radius 0.012 + subdivision 12 + } + } + ] + } + ] + name "right wheel" + boundingObject Pose { + rotation 1 0 0 -1.5708 + children [ + Cylinder { + height 0.018 + radius 0.031 + } + ] + } + physics Physics { + density -1 + mass 0.04 + centerOfMass [ + 0 0 0 + ] + } + } + } + + # ========== LEFT WHEEL ========== + DEF SHEEP_LEFT_WHEEL HingeJoint { + jointParameters HingeJointParameters { + axis 0 1 0 + anchor 0 0.10 0.031 + } + device [ + RotationalMotor { + name "left wheel motor" + maxVelocity 25.0 + maxTorque 3.0 + } + PositionSensor { + name "left wheel sensor" + resolution 0.00628 + } + ] + endPoint Solid { + translation 0 0.10 0.031 + rotation 0.707105 0 0.707109 -3.14159 + children [ + USE SHEEP_WHEEL_VIS + ] + name "left wheel" + boundingObject Pose { + rotation 1 0 0 -1.5708 + children [ + Cylinder { + height 0.018 + radius 0.031 + } + ] + } + physics Physics { + density -1 + mass 0.04 + centerOfMass [ + 0 0 0 + ] + } + } + } + + # ========== FRONT CASTER ========== + BallJoint { + jointParameters BallJointParameters { + anchor 0.10 0 0.016 + } + endPoint Solid { + translation 0.10 0 0.016 + name "front caster" + children [ + Shape { + appearance PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.3 + } + geometry Sphere { + radius 0.016 + subdivision 2 + } + } + ] + boundingObject Sphere { + radius 0.016 + } + physics Physics { + density -1 + mass 0.02 + } + } + } + + # ========== REAR CASTER ========== + BallJoint { + jointParameters BallJointParameters { + anchor -0.10 0 0.016 + } + endPoint Solid { + translation -0.10 0 0.016 + name "rear caster" + children [ + Shape { + appearance PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.3 + } + geometry Sphere { + radius 0.016 + subdivision 2 + } + } + ] + boundingObject Sphere { + radius 0.016 + } + physics Physics { + density -1 + mass 0.02 + } + } + } + + # ========== RECEIVER ========== + Receiver { + name "receiver" + channel 1 + } + + # ========== EMITTER ========== + Emitter { + name "emitter" + channel 1 + range 30.0 + } + + # ========== GPS ========== + GPS { + translation 0 0 0.15 + name "gps" + } + + # ========== COMPASS ========== + Compass { + translation 0 0 0.10 + name "compass" + } + ] + + boundingObject Group { + children [ + # Chassis box + Transform { + translation 0 0 0.04 + children [ + Box { + size 0.24 0.14 0.05 + } + ] + } + # Body box + Transform { + translation 0 0 0.12 + children [ + Box { + size 0.26 0.16 0.12 + } + ] + } + ] + } + + physics Physics { + density -1 + mass 5.0 + centerOfMass [ + 0 0 0.02 + ] + } + } +} diff --git a/protos/ShepherdDog.proto b/protos/ShepherdDog.proto new file mode 100644 index 0000000..7bea476 --- /dev/null +++ b/protos/ShepherdDog.proto @@ -0,0 +1,689 @@ +#VRML_SIM R2025a utf8 +# Shepherd Dog Robot - wheeled base with dog character on top, tail-mounted 360 lidar + +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/appearances/protos/TireRubber.proto" + +PROTO ShepherdDog [ + field SFVec3f translation 0 0 0 + field SFRotation rotation 0 1 0 0 + field SFString name "ShepherdDog" + field SFString controller "shepherd_dog" + field MFString controllerArgs [] + field SFString customData "" + field SFBool supervisor FALSE + field SFBool synchronization TRUE +] +{ + Robot { + translation IS translation + rotation IS rotation + name IS name + controller IS controller + controllerArgs IS controllerArgs + customData IS customData + supervisor IS supervisor + synchronization IS synchronization + children [ + # ========== CHASSIS / BASE ========== + DEF CHASSIS Transform { + translation 0 0 0.05 + children [ + Shape { + appearance DEF CHASSIS_APP PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.6 + metalness 0.3 + } + geometry Box { + size 0.32 0.16 0.06 + } + } + ] + } + # Front slope + DEF CHASSIS_FRONT Transform { + translation 0.14 0 0.07 + children [ + Shape { + appearance USE CHASSIS_APP + geometry Box { + size 0.06 0.14 0.04 + } + } + ] + } + # Rear slope + DEF CHASSIS_REAR Transform { + translation -0.14 0 0.07 + children [ + Shape { + appearance USE CHASSIS_APP + geometry Box { + size 0.06 0.14 0.04 + } + } + ] + } + + # ========== DOG BODY on top of chassis ========== + DEF BODY Transform { + translation 0 0 0.11 + children [ + Shape { + appearance DEF FUR_BROWN PBRAppearance { + baseColor 0.55 0.35 0.17 + roughness 0.85 + metalness 0.0 + } + geometry Box { + size 0.30 0.16 0.08 + } + } + ] + } + + # ========== CHEST ========== + DEF CHEST Transform { + translation 0.12 0 0.11 + children [ + Shape { + appearance DEF FUR_CREAM PBRAppearance { + baseColor 0.85 0.72 0.55 + roughness 0.85 + metalness 0.0 + } + geometry Box { + size 0.08 0.18 0.08 + } + } + ] + } + + # ========== HEAD ========== + DEF HEAD Transform { + translation 0.20 0 0.17 + children [ + Shape { + appearance USE FUR_BROWN + geometry Box { + size 0.10 0.12 0.09 + } + } + ] + } + + # ========== SNOUT + LIDAR ========== + DEF SNOUT Transform { + translation 0.28 0 0.155 + children [ + Shape { + appearance USE FUR_CREAM + geometry Box { + size 0.08 0.07 0.05 + } + } + # Nose + Transform { + translation 0.04 0 0.01 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.1 0.1 0.1 + roughness 0.4 + } + geometry Sphere { + radius 0.013 + subdivision 2 + } + } + ] + } + # Lidar — front-facing 140° FOV, mounted at snout tip + Lidar { + translation 0.05 0 0.01 + name "lidar" + horizontalResolution 180 + fieldOfView 2.44 + numberOfLayers 1 + minRange 0.10 + maxRange 12.0 + noise 0.005 + } + ] + } + + # ========== LEFT EAR ========== + DEF LEFT_EAR HingeJoint { + jointParameters HingeJointParameters { + axis 0 0 1 + anchor 0.19 0.055 0.21 + } + device [ + RotationalMotor { + name "left ear motor" + maxVelocity 10.0 + minPosition -0.5 + maxPosition 0.5 + } + ] + endPoint Solid { + translation 0.19 0.055 0.21 + rotation 0 0 1 0.2 + name "left ear" + children [ + Shape { + appearance DEF FUR_DARK PBRAppearance { + baseColor 0.35 0.20 0.10 + roughness 0.85 + metalness 0.0 + } + geometry Box { + size 0.035 0.025 0.06 + } + } + ] + boundingObject Box { + size 0.035 0.025 0.06 + } + physics Physics { + density -1 + mass 0.005 + } + } + } + + # ========== RIGHT EAR ========== + DEF RIGHT_EAR HingeJoint { + jointParameters HingeJointParameters { + axis 0 0 1 + anchor 0.19 -0.055 0.21 + } + device [ + RotationalMotor { + name "right ear motor" + maxVelocity 10.0 + minPosition -0.5 + maxPosition 0.5 + } + ] + endPoint Solid { + translation 0.19 -0.055 0.21 + rotation 0 0 -1 0.2 + name "right ear" + children [ + Shape { + appearance USE FUR_DARK + geometry Box { + size 0.035 0.025 0.06 + } + } + ] + boundingObject Box { + size 0.035 0.025 0.06 + } + physics Physics { + density -1 + mass 0.005 + } + } + } + + # ========== EYES ========== + DEF LEFT_EYE Transform { + translation 0.25 0.05 0.19 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.95 0.95 0.95 + roughness 0.3 + } + geometry Sphere { + radius 0.016 + subdivision 2 + } + } + # Pupil + Transform { + translation 0.012 0 0.004 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.1 0.1 0.1 + roughness 0.2 + } + geometry Sphere { + radius 0.009 + subdivision 2 + } + } + ] + } + ] + } + DEF RIGHT_EYE Transform { + translation 0.25 -0.05 0.19 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.95 0.95 0.95 + roughness 0.3 + } + geometry Sphere { + radius 0.016 + subdivision 2 + } + } + # Pupil + Transform { + translation 0.012 0 0.004 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.1 0.1 0.1 + roughness 0.2 + } + geometry Sphere { + radius 0.009 + subdivision 2 + } + } + ] + } + ] + } + + # ========== COLLAR ========== + DEF COLLAR Transform { + translation 0.16 0 0.125 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.8 0.1 0.1 + roughness 0.5 + } + geometry Cylinder { + height 0.02 + radius 0.095 + subdivision 16 + } + } + # ID tag + Transform { + translation 0 0.10 0 + rotation 1 0 0 1.5708 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.75 0.75 0.0 + metalness 0.8 + roughness 0.2 + } + geometry Cylinder { + height 0.003 + radius 0.018 + subdivision 8 + } + } + ] + } + ] + } + + # ========== TAIL (lidar inside tail tip ball) ========== + DEF TAIL HingeJoint { + jointParameters HingeJointParameters { + axis 0 1 0 + anchor -0.15 0 0.11 + } + device [ + RotationalMotor { + name "tail motor" + maxVelocity 5.0 + minPosition -1.0 + maxPosition 1.0 + } + ] + endPoint Solid { + translation -0.17 0 0.13 + name "tail solid" + children [ + Shape { + appearance USE FUR_BROWN + geometry Capsule { + height 0.12 + radius 0.013 + top FALSE + } + } + # Tail tip ball + Transform { + translation 0 0 0.08 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.3 + metalness 0.6 + } + geometry Sphere { + radius 0.028 + subdivision 4 + } + } + ] + } + ] + boundingObject Group { + children [ + Capsule { + height 0.12 + radius 0.013 + } + Transform { + translation 0 0 0.08 + children [ + Sphere { + radius 0.028 + } + ] + } + ] + } + physics Physics { + density -1 + mass 0.08 + } + } + } + + # ========== RIGHT AXLE ARM (horizontal bar from chassis to wheel) ========== + DEF RIGHT_AXLE Transform { + translation 0 -0.115 0.038 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.5 0.5 0.5 + roughness 0.3 + metalness 0.8 + } + geometry Box { + size 0.02 0.08 0.02 + } + } + ] + } + + # ========== LEFT AXLE ARM ========== + DEF LEFT_AXLE Transform { + translation 0 0.115 0.038 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.5 0.5 0.5 + roughness 0.3 + metalness 0.8 + } + geometry Box { + size 0.02 0.08 0.02 + } + } + ] + } + + # ========== RIGHT WHEEL ========== + DEF RIGHT_WHEEL_JOINT HingeJoint { + jointParameters HingeJointParameters { + axis 0 1 0 + anchor 0 -0.14 0.038 + } + device [ + RotationalMotor { + name "right wheel motor" + maxVelocity 70.0 + maxTorque 20.0 + } + PositionSensor { + name "right wheel sensor" + resolution 0.00628 + } + ] + endPoint Solid { + translation 0 -0.14 0.038 + rotation 0 -1 0 1.570796 + children [ + DEF WHEEL_VIS Pose { + rotation 1 0 0 -1.5708 + children [ + Shape { + appearance PBRAppearance { + baseColor 0.15 0.15 0.15 + roughness 0.4 + metalness 0.5 + } + geometry Cylinder { + height 0.016 + radius 0.035 + subdivision 24 + } + } + Shape { + appearance PBRAppearance { + baseColor 0.6 0.6 0.6 + roughness 0.3 + metalness 0.7 + } + geometry Cylinder { + height 0.018 + radius 0.014 + subdivision 12 + } + } + Shape { + appearance TireRubber { + textureTransform TextureTransform { + scale 1.5 0.6 + } + type "bike" + } + geometry Cylinder { + height 0.022 + radius 0.038 + subdivision 24 + } + } + ] + } + ] + name "right wheel" + boundingObject Pose { + rotation 1 0 0 -1.5708 + children [ + Cylinder { + height 0.022 + radius 0.038 + } + ] + } + physics Physics { + density -1 + mass 0.06 + centerOfMass [ + 0 0 0 + ] + } + } + } + + # ========== LEFT WHEEL ========== + DEF LEFT_WHEEL_JOINT HingeJoint { + jointParameters HingeJointParameters { + axis 0 1 0 + anchor 0 0.14 0.038 + } + device [ + RotationalMotor { + name "left wheel motor" + maxVelocity 70.0 + maxTorque 20.0 + } + PositionSensor { + name "left wheel sensor" + resolution 0.00628 + } + ] + endPoint Solid { + translation 0 0.14 0.038 + rotation 0.707105 0 0.707109 -3.14159 + children [ + USE WHEEL_VIS + ] + name "left wheel" + boundingObject Pose { + rotation 1 0 0 -1.5708 + children [ + Cylinder { + height 0.022 + radius 0.038 + } + ] + } + physics Physics { + density -1 + mass 0.12 + centerOfMass [ + 0 0 0 + ] + } + } + } + + # ========== FRONT CASTER ========== + DEF FRONT_CASTER BallJoint { + jointParameters BallJointParameters { + anchor 0.14 0 0.02 + } + endPoint Solid { + translation 0.14 0 0.02 + name "front caster" + children [ + Shape { + appearance PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.3 + metalness 0.5 + } + geometry Sphere { + radius 0.02 + subdivision 2 + } + } + ] + boundingObject Sphere { + radius 0.02 + } + physics Physics { + density -1 + mass 0.03 + } + } + } + + # ========== REAR CASTER ========== + DEF REAR_CASTER BallJoint { + jointParameters BallJointParameters { + anchor -0.14 0 0.02 + } + endPoint Solid { + translation -0.14 0 0.02 + name "rear caster" + children [ + Shape { + appearance PBRAppearance { + baseColor 0.2 0.2 0.2 + roughness 0.3 + metalness 0.5 + } + geometry Sphere { + radius 0.02 + subdivision 2 + } + } + ] + boundingObject Sphere { + radius 0.02 + } + physics Physics { + density -1 + mass 0.03 + } + } + } + + # ========== IMU SENSORS ========== + Accelerometer { + translation 0 0 0.10 + name "accelerometer" + } + Gyro { + translation 0 0 0.10 + name "gyro" + } + Compass { + translation 0 0 0.10 + name "compass" + } + + # ========== GPS ========== + GPS { + translation 0 0 0.17 + name "gps" + } + + # ========== RECEIVER ========== + Receiver { + name "receiver" + channel 1 + } + + # ========== EMITTER ========== + Emitter { + name "emitter" + channel 1 + range 50.0 + } + ] + + # ========== BOUNDING OBJECT ========== + boundingObject Group { + children [ + # Chassis box + Transform { + translation 0 0 0.05 + children [ + Box { + size 0.32 0.16 0.06 + } + ] + } + # Body box + Transform { + translation 0 0 0.11 + children [ + Box { + size 0.30 0.16 0.08 + } + ] + } + ] + } + + # ========== PHYSICS ========== + physics Physics { + density -1 + mass 5.0 + centerOfMass [ + 0 0 0.03 + ] + } + } +} diff --git a/tools/camera_debug.py b/tools/camera_debug.py new file mode 100644 index 0000000..e937ed2 --- /dev/null +++ b/tools/camera_debug.py @@ -0,0 +1,22 @@ +""" +Viewpoint inspector — prints position, orientation and FOV to the console +once per second. Attach as the controller of a dummy supervisor robot to +copy-paste exact camera values into field.wbt. +""" + +from controller import Supervisor + +robot = Supervisor() +timestep = int(robot.getBasicTimeStep()) +vp = robot.getFromDef("VIEWPOINT") + +step = 0 +while robot.step(timestep) != -1: + if step % 60 == 0: + pos = vp.getField("position").getSFVec3f() + ori = vp.getField("orientation").getSFRotation() + fov = vp.getField("fieldOfView").getSFFloat() + print(f"position: {pos[0]:.3f} {pos[1]:.3f} {pos[2]:.3f}") + print(f"orientation: {ori[0]:.3f} {ori[1]:.3f} {ori[2]:.3f} {ori[3]:.3f}") + print(f"fieldOfView: {fov:.3f}\n") + step += 1 diff --git a/worlds/field.wbt b/worlds/field.wbt index 7396db7..6e94161 100644 --- a/worlds/field.wbt +++ b/worlds/field.wbt @@ -1,13 +1,529 @@ #VRML_SIM R2025a utf8 + EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/objects/backgrounds/protos/TexturedBackground.proto" EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/objects/backgrounds/protos/TexturedBackgroundLight.proto" +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/objects/floors/protos/UnevenTerrain.proto" +EXTERNPROTO "https://raw.githubusercontent.com/cyberbotics/webots/R2025a/projects/appearances/protos/Grass.proto" +EXTERNPROTO "../protos/ShepherdDog.proto" +EXTERNPROTO "../protos/Sheep.proto" + +# World WorldInfo { + info [ + "RL-Based Autonomous Shepherd Robot" + "Group G25" + ] + title "Shepherd Herding" + ERP 0.62 + basicTimeStep 16 + contactProperties [ + ContactProperties { + coulombFriction [ + 12 + ] + softCFM 1e-05 + } + ] } -Viewpoint { - orientation -0.5773 0.5773 0.5773 2.0944 - position 0 0 10 + +# Viewpoint +DEF VIEWPOINT Viewpoint { + position 4.34 -100.99 41.73 + orientation 0.199 -0.190 -0.961 4.624 + fieldOfView 0.785 } -TexturedBackground { + +# Background +Background { + skyColor [0.55 0.75 0.95] } -TexturedBackgroundLight { +# Single sun (diagonal from SW) +DirectionalLight { + ambientIntensity 1 + direction -0.3 0.5 -0.85 + color 1 0.98 0.92 + intensity 2.5 + castShadows TRUE +} + +# Grass terrain +UnevenTerrain { + rotation 0 0 1 -1.5708 + size 100 100 0.3 + xDimension 50 + yDimension 50 + appearance Grass { + colorOverride 0.78 0.88 0.68 + textureTransform TextureTransform { + scale 100 100 + } + } + perlinNOctaves 4 +} + +# ==================== APPEARANCES ==================== +Transform { + children [ + Shape { appearance DEF STONE_A PBRAppearance { baseColor 0.48 0.45 0.40 roughness 0.95 metalness 0 } } + Shape { appearance DEF STONE_B PBRAppearance { baseColor 0.36 0.33 0.29 roughness 0.95 metalness 0 } } + Shape { appearance DEF STONE_C PBRAppearance { baseColor 0.58 0.55 0.50 roughness 0.90 metalness 0 } } + Shape { appearance DEF CAP PBRAppearance { baseColor 0.54 0.51 0.46 roughness 0.80 metalness 0 } } + Shape { appearance DEF BARN_RED PBRAppearance { baseColor 0.62 0.18 0.12 roughness 0.80 metalness 0 } } + Shape { appearance DEF BARN_ROOF PBRAppearance { baseColor 0.28 0.20 0.13 roughness 0.72 metalness 0 } } + Shape { appearance DEF WOOD PBRAppearance { baseColor 0.48 0.32 0.16 roughness 0.90 metalness 0 } } + Shape { appearance DEF TRUNK PBRAppearance { baseColor 0.38 0.24 0.11 roughness 0.90 metalness 0 } } + Shape { appearance DEF LEAF_A PBRAppearance { baseColor 0.22 0.52 0.16 roughness 0.85 metalness 0 } } + Shape { appearance DEF LEAF_B PBRAppearance { baseColor 0.16 0.42 0.10 roughness 0.85 metalness 0 } } + Shape { appearance DEF LEAF_C PBRAppearance { baseColor 0.30 0.60 0.20 roughness 0.80 metalness 0 } } + Shape { appearance DEF STRAW PBRAppearance { baseColor 0.85 0.75 0.35 roughness 0.95 metalness 0 } } + Shape { appearance DEF HAT PBRAppearance { baseColor 0.50 0.35 0.18 roughness 0.85 metalness 0 } } + Shape { appearance DEF SHIRT PBRAppearance { baseColor 0.60 0.30 0.30 roughness 0.80 metalness 0 } } + Shape { appearance DEF PANTS PBRAppearance { baseColor 0.25 0.25 0.30 roughness 0.80 metalness 0 } } + Shape { appearance DEF DOOR_MAT PBRAppearance { baseColor 0.55 0.38 0.20 roughness 0.72 metalness 0 } } + Shape { appearance DEF GLASS PBRAppearance { baseColor 0.60 0.80 0.95 roughness 0.20 metalness 0.05 } } + Shape { appearance DEF HAY PBRAppearance { baseColor 0.82 0.72 0.32 roughness 0.95 metalness 0 } } + ] +} +DEF TRIM PBRAppearance { baseColor 0.90 0.88 0.82 roughness 0.70 metalness 0 } + +# ==================== STONE WALL ENCLOSURE (±15 m) ==================== + +# East wall + cap +Solid { translation 15 0 0.40 children [ Shape { appearance USE STONE_A geometry Box { size 0.16 30.5 0.80 } } ] boundingObject Box { size 0.16 30.5 0.80 } } +Solid { translation 15 0 0.84 children [ Shape { appearance USE CAP geometry Box { size 0.26 30.6 0.07 } } ] boundingObject Box { size 0.26 30.6 0.07 } } + +# West wall + cap +Solid { translation -15 0 0.40 children [ Shape { appearance USE STONE_B geometry Box { size 0.16 30.5 0.80 } } ] boundingObject Box { size 0.16 30.5 0.80 } } +Solid { translation -15 0 0.84 children [ Shape { appearance USE CAP geometry Box { size 0.26 30.6 0.07 } } ] boundingObject Box { size 0.26 30.6 0.07 } } + +# North wall + cap +Solid { translation 0 15 0.40 children [ Shape { appearance USE STONE_C geometry Box { size 30.0 0.16 0.80 } } ] boundingObject Box { size 30.0 0.16 0.80 } } +Solid { translation 0 15 0.84 children [ Shape { appearance USE CAP geometry Box { size 30.1 0.26 0.07 } } ] boundingObject Box { size 30.1 0.26 0.07 } } + +# South wall + cap (split for gate, near SE corner) +Solid { translation -2.5 -15 0.40 children [ Shape { appearance USE STONE_A geometry Box { size 25.0 0.16 0.80 } } ] boundingObject Box { size 25.0 0.16 0.80 } } +Solid { translation -2.5 -15 0.84 children [ Shape { appearance USE CAP geometry Box { size 25.1 0.26 0.07 } } ] boundingObject Box { size 25.1 0.26 0.07 } } +Solid { translation 14 -15 0.40 children [ Shape { appearance USE STONE_A geometry Box { size 2.0 0.16 0.80 } } ] boundingObject Box { size 2.0 0.16 0.80 } } +Solid { translation 14 -15 0.84 children [ Shape { appearance USE CAP geometry Box { size 2.1 0.26 0.07 } } ] boundingObject Box { size 2.1 0.26 0.07 } } +# Gate posts +Solid { translation 10 -15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } +Solid { translation 13 -15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } +# Outer gate (wooden, slightly ajar, Z-brace) +Solid { translation 11.5 -15.08 0.55 rotation 0 0 1 0.25 children [ + Shape { appearance USE WOOD geometry Box { size 2.80 0.05 1.00 } } + Transform { translation 0 0.02 0 rotation 0 1 0 0.34 children [ Shape { appearance DEF FPOST PBRAppearance { baseColor 0.35 0.22 0.10 roughness 0.90 } geometry Box { size 2.97 0.04 0.06 } } ] } +] boundingObject Box { size 2.80 0.08 1.00 } } + +# ==================== QUARANTINE PEN (wooden post-and-rail fence, inside field) ==================== +# Flow: main field → inner gate → quarantine area → outer gate → outside + +# West wall (x=10, ~7m along Y) +Solid { translation 10 -11.46 0.55 children [ + Transform { translation 0 -3.46 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 -1.73 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 0 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 1.73 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 3.46 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 0 -0.38 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 -0.05 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 0.30 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 0.53 children [ Shape { appearance USE FPOST geometry Box { size 0.14 6.92 0.04 } } ] } +] boundingObject Box { size 0.14 6.92 1.10 } } + +# East wall (x=13) +Solid { translation 13 -11.46 0.55 children [ + Transform { translation 0 -3.46 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 -1.73 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 0 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 1.73 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 3.46 0 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] } + Transform { translation 0 0 -0.38 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 -0.05 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 0.30 children [ Shape { appearance USE WOOD geometry Box { size 0.06 6.92 0.08 } } ] } + Transform { translation 0 0 0.53 children [ Shape { appearance USE FPOST geometry Box { size 0.14 6.92 0.04 } } ] } +] boundingObject Box { size 0.14 6.92 1.10 } } + +# North wall - open entrance (no wall, just corner posts) +Solid { translation 10 -8 0.55 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] boundingObject Box { size 0.12 0.12 1.10 } } +Solid { translation 13 -8 0.55 children [ Shape { appearance USE FPOST geometry Box { size 0.12 0.12 1.10 } } ] boundingObject Box { size 0.12 0.12 1.10 } } + +# Corner pillars +Solid { translation 15 15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } +Solid { translation 15 -15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } +Solid { translation -15 15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } +Solid { translation -15 -15 0.56 children [ Shape { appearance USE STONE_B geometry Box { size 0.44 0.44 1.12 } } Shape { appearance USE CAP geometry Box { size 0.54 0.54 0.08 } } ] boundingObject Box { size 0.44 0.44 1.12 } } + +# Mid-pillars every 5 m — East +Solid { translation 15 10 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 15 5 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 15 0 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 15 -5 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 15 -10 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +# West +Solid { translation -15 10 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -15 5 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -15 0 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -15 -5 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -15 -10 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +# North +Solid { translation 10 15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 5 15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 0 15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -5 15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -10 15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +# South +Solid { translation 5 -15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation 0 -15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -5 -15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } +Solid { translation -10 -15 0.53 children [ Shape { appearance USE STONE_B geometry Box { size 0.34 0.34 1.06 } } Shape { appearance USE CAP geometry Box { size 0.44 0.44 0.07 } } ] boundingObject Box { size 0.34 0.34 1.06 } } + +# ==================== BARN 1 — Gambrel/Dutch style (NE, outside fence) ==================== +# Body 10×7×4, weathered gray-brown wood, gambrel roof, large double doors +Solid { + translation 18.5 25.49 2 + children [ + Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 10 7 4 } } + # Gambrel roof + Transform { translation -3.5 0 3.05 rotation 0 1 0 -0.611 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.18 0.16 roughness 0.82 metalness 0.02 } geometry Box { size 3.9 7.2 0.18 } } ] } + Transform { translation 3.5 0 3.05 rotation 0 1 0 0.611 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.18 0.16 roughness 0.82 metalness 0.02 } geometry Box { size 3.9 7.2 0.18 } } ] } + Transform { translation -1.0 0 4.55 rotation 0 1 0 -0.422 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.18 0.16 roughness 0.82 metalness 0.02 } geometry Box { size 2.5 7.2 0.18 } } ] } + Transform { translation 1.0 0 4.55 rotation 0 1 0 0.422 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.18 0.16 roughness 0.82 metalness 0.02 } geometry Box { size 2.5 7.2 0.18 } } ] } + Transform { translation 0 0 5.04 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.18 0.16 roughness 0.82 metalness 0.02 } geometry Box { size 1.6 7.2 0.22 } } ] } + # South gable fill + Transform { translation 0 -3.57 2.40 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 8.8 0.16 0.80 } } ] } + Transform { translation 0 -3.57 3.10 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 6.8 0.16 0.70 } } ] } + Transform { translation 0 -3.57 3.70 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 5.1 0.16 0.60 } } ] } + Transform { translation 0 -3.57 4.10 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 4.0 0.16 0.40 } } ] } + Transform { translation 0 -3.57 4.42 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 2.7 0.16 0.60 } } ] } + Transform { translation 0 -3.57 4.84 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 0.9 0.16 0.36 } } ] } + # North gable fill + Transform { translation 0 3.57 2.40 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 8.8 0.16 0.80 } } ] } + Transform { translation 0 3.57 3.10 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 6.8 0.16 0.70 } } ] } + Transform { translation 0 3.57 3.70 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 5.1 0.16 0.60 } } ] } + Transform { translation 0 3.57 4.10 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 4.0 0.16 0.40 } } ] } + Transform { translation 0 3.57 4.42 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 2.7 0.16 0.60 } } ] } + Transform { translation 0 3.57 4.84 children [ Shape { appearance PBRAppearance { baseColor 0.52 0.42 0.30 roughness 0.92 metalness 0 } geometry Box { size 0.9 0.16 0.36 } } ] } + # Double barn doors (south face) + Transform { + translation 0 -3.51 -0.50 + children [ + Shape { appearance PBRAppearance { baseColor 0.44 0.30 0.14 roughness 0.88 metalness 0 } geometry Box { size 2.8 0.10 3.0 } } + Transform { rotation 0 0 1 0.83 children [ Shape { appearance PBRAppearance { baseColor 0.34 0.22 0.10 roughness 0.90 metalness 0 } geometry Box { size 0.10 0.12 3.75 } } ] } + Transform { rotation 0 0 1 -0.83 children [ Shape { appearance PBRAppearance { baseColor 0.34 0.22 0.10 roughness 0.90 metalness 0 } geometry Box { size 0.10 0.12 3.75 } } ] } + Transform { translation -1.45 0 0 children [ Shape { appearance PBRAppearance { baseColor 0.34 0.22 0.10 roughness 0.90 metalness 0 } geometry Box { size 0.12 0.14 3.24 } } ] } + Transform { translation 1.45 0 0 children [ Shape { appearance PBRAppearance { baseColor 0.34 0.22 0.10 roughness 0.90 metalness 0 } geometry Box { size 0.12 0.14 3.24 } } ] } + Transform { translation 0 0 1.62 children [ Shape { appearance PBRAppearance { baseColor 0.34 0.22 0.10 roughness 0.90 metalness 0 } geometry Box { size 3.04 0.14 0.14 } } ] } + ] + } + # Windows + Transform { translation -3.6 -3.52 0.55 children [ Shape { appearance PBRAppearance { baseColor 0.60 0.80 0.95 roughness 0.20 metalness 0.05 } geometry Box { size 1.40 0.12 1.10 } } ] } + Transform { translation 3.6 -3.52 0.55 children [ Shape { appearance PBRAppearance { baseColor 0.60 0.80 0.95 roughness 0.20 metalness 0.05 } geometry Box { size 1.40 0.12 1.10 } } ] } + Transform { translation 5.06 2.0 0.55 children [ Shape { appearance PBRAppearance { baseColor 0.60 0.80 0.95 roughness 0.20 metalness 0.05 } geometry Box { size 0.12 1.20 1.0 } } ] } + Transform { translation 5.06 -2.0 0.55 children [ Shape { appearance PBRAppearance { baseColor 0.60 0.80 0.95 roughness 0.20 metalness 0.05 } geometry Box { size 0.12 1.20 1.0 } } ] } + Transform { translation 0 -3.52 3.90 children [ Shape { appearance PBRAppearance { baseColor 0.44 0.30 0.14 roughness 0.88 metalness 0 } geometry Box { size 1.30 0.12 1.00 } } ] } + ] + boundingObject Box { size 10 7 7 } +} + +# ==================== BARN 3 — Red barn (NE, outside fence, gate facing fence) ==================== +# Body 7×9×3.5, red walls, steep dark roof +Solid { + translation 29.76 9.52 1.75 + rotation 0 0 1 -1.5708 + children [ + Shape { appearance USE BARN_RED geometry Box { size 7 9 3.5 } } + # Roof + Transform { translation -2.0 0 3.0 rotation 0 1 0 -0.70 children [ Shape { appearance USE BARN_ROOF geometry Box { size 4.2 9.2 0.20 } } ] } + Transform { translation 2.0 0 3.0 rotation 0 1 0 0.70 children [ Shape { appearance USE BARN_ROOF geometry Box { size 4.2 9.2 0.20 } } ] } + Transform { translation 0 0 4.28 children [ Shape { appearance USE BARN_ROOF geometry Box { size 2.0 9.2 0.24 } } ] } + # South gable fill + Transform { translation 0 -4.52 2.05 children [ Shape { appearance USE BARN_RED geometry Box { size 6.2 0.16 0.60 } } ] } + Transform { translation 0 -4.52 2.65 children [ Shape { appearance USE BARN_RED geometry Box { size 4.5 0.16 0.60 } } ] } + Transform { translation 0 -4.52 3.25 children [ Shape { appearance USE BARN_RED geometry Box { size 2.9 0.16 0.60 } } ] } + Transform { translation 0 -4.52 3.85 children [ Shape { appearance USE BARN_RED geometry Box { size 1.2 0.16 0.60 } } ] } + # North gable fill + Transform { translation 0 4.52 2.05 children [ Shape { appearance USE BARN_RED geometry Box { size 6.2 0.16 0.60 } } ] } + Transform { translation 0 4.52 2.65 children [ Shape { appearance USE BARN_RED geometry Box { size 4.5 0.16 0.60 } } ] } + Transform { translation 0 4.52 3.25 children [ Shape { appearance USE BARN_RED geometry Box { size 2.9 0.16 0.60 } } ] } + Transform { translation 0 4.52 3.85 children [ Shape { appearance USE BARN_RED geometry Box { size 1.2 0.16 0.60 } } ] } + # Door + Transform { + translation 0 -4.52 -0.62 + children [ + Shape { appearance USE DOOR_MAT geometry Box { size 1.70 0.14 2.26 } } + Transform { translation 0 0 1.22 children [ Shape { appearance USE WOOD geometry Box { size 2.10 0.18 0.26 } } ] } + Transform { translation -0.90 0 0 children [ Shape { appearance USE WOOD geometry Box { size 0.24 0.18 2.52 } } ] } + Transform { translation 0.90 0 0 children [ Shape { appearance USE WOOD geometry Box { size 0.24 0.18 2.52 } } ] } + Transform { translation 0 0 -0.68 children [ Shape { appearance USE WOOD geometry Box { size 1.60 0.12 0.12 } } ] } + Transform { translation 0 0 0.30 children [ Shape { appearance USE WOOD geometry Box { size 1.60 0.12 0.12 } } ] } + ] + } + # Windows — south face + Transform { translation -2.2 -4.53 0.30 children [ Shape { appearance USE GLASS geometry Box { size 0.80 0.14 0.70 } } ] } + Transform { translation 2.2 -4.53 0.30 children [ Shape { appearance USE GLASS geometry Box { size 0.80 0.14 0.70 } } ] } + # East-face windows + Transform { translation 3.52 3.0 0.30 children [ Shape { appearance USE GLASS geometry Box { size 0.14 0.80 0.70 } } ] } + Transform { translation 3.52 0.0 0.30 children [ Shape { appearance USE GLASS geometry Box { size 0.14 0.80 0.70 } } ] } + Transform { translation 3.52 -3.0 0.30 children [ Shape { appearance USE GLASS geometry Box { size 0.14 0.80 0.70 } } ] } + ] + boundingObject Box { size 7 9 6 } +} + +# ==================== TREES (outside fence) ==================== + +# Tree A — large oak, SE +Solid { + translation 20 -18 0 + children [ + Transform { translation 0 0 2.0 children [ Shape { appearance USE TRUNK geometry Cylinder { height 4.0 radius 0.30 subdivision 10 } } ] } + Transform { translation 0.0 0.0 4.6 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 2.6 subdivision 4 } } ] } + Transform { translation 1.2 0.6 5.6 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.9 subdivision 4 } } ] } + Transform { translation -1.0 0.9 5.3 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 1.7 subdivision 4 } } ] } + Transform { translation 0.4 -1.1 5.1 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.5 subdivision 4 } } ] } + Transform { translation -0.5 -0.4 6.2 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.0 subdivision 4 } } ] } + ] +} + +# Tree B — medium, NE near barn +Solid { + translation -8 26 0 + children [ + Transform { translation 0 0 1.7 children [ Shape { appearance USE TRUNK geometry Cylinder { height 3.4 radius 0.25 subdivision 10 } } ] } + Transform { translation 0.0 0.0 3.8 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 2.2 subdivision 4 } } ] } + Transform { translation 0.9 -0.7 4.7 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.6 subdivision 4 } } ] } + Transform { translation -0.6 0.8 4.4 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.4 subdivision 4 } } ] } + ] +} + +# Tree C — large, NW +Solid { + translation -23 20 0 + children [ + Transform { translation 0 0 2.3 children [ Shape { appearance USE TRUNK geometry Cylinder { height 4.6 radius 0.36 subdivision 10 } } ] } + Transform { translation 0.0 0.0 5.2 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 2.9 subdivision 4 } } ] } + Transform { translation 1.3 0.9 6.3 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 2.1 subdivision 4 } } ] } + Transform { translation -1.1 1.1 6.0 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 1.9 subdivision 4 } } ] } + Transform { translation 0.6 -1.3 5.8 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.6 subdivision 4 } } ] } + ] +} + +# Tree D — small, SW +Solid { + translation -20 -23 0 + children [ + Transform { translation 0 0 1.4 children [ Shape { appearance USE TRUNK geometry Cylinder { height 2.8 radius 0.20 subdivision 10 } } ] } + Transform { translation 0.0 0.0 3.2 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 1.9 subdivision 4 } } ] } + Transform { translation -0.7 0.6 4.0 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.4 subdivision 4 } } ] } + Transform { translation 0.6 -0.5 3.8 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.2 subdivision 4 } } ] } + ] +} + +# Tree E — north cluster +Solid { + translation 7 23 0 + children [ + Transform { translation 0 0 1.9 children [ Shape { appearance USE TRUNK geometry Cylinder { height 3.8 radius 0.27 subdivision 10 } } ] } + Transform { translation 0.0 0.0 4.1 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 2.3 subdivision 4 } } ] } + Transform { translation 1.0 0.5 5.0 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 1.7 subdivision 4 } } ] } + Transform { translation -0.6 -0.9 4.8 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.4 subdivision 4 } } ] } + ] +} + +# Tree F — SW +Solid { + translation -2.98 -22.8 0 + children [ + Transform { translation 0 0 1.3 children [ Shape { appearance USE TRUNK geometry Cylinder { height 2.6 radius 0.19 subdivision 10 } } ] } + Transform { translation 0.0 0.0 2.9 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.7 subdivision 4 } } ] } + Transform { translation 0.6 0.4 3.7 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.2 subdivision 4 } } ] } + ] +} + +# Tree G — west side +Solid { + translation -23 -5 0 + children [ + Transform { translation 0 0 2.0 children [ Shape { appearance USE TRUNK geometry Cylinder { height 4.0 radius 0.29 subdivision 10 } } ] } + Transform { translation 0.0 0.0 4.4 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 2.4 subdivision 4 } } ] } + Transform { translation -1.0 0.8 5.3 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 1.8 subdivision 4 } } ] } + Transform { translation 0.9 -0.7 5.0 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.6 subdivision 4 } } ] } + ] +} + +# Tree H — east side +Solid { + translation 21.35 -1.05 0 + children [ + Transform { translation 0 0 1.5 children [ Shape { appearance USE TRUNK geometry Cylinder { height 3.0 radius 0.22 subdivision 10 } } ] } + Transform { translation 0.0 0.0 3.4 children [ Shape { appearance USE LEAF_A geometry Sphere { radius 2.0 subdivision 4 } } ] } + Transform { translation 0.7 0.6 4.2 children [ Shape { appearance USE LEAF_C geometry Sphere { radius 1.4 subdivision 4 } } ] } + Transform { translation -0.5 -0.8 4.0 children [ Shape { appearance USE LEAF_B geometry Sphere { radius 1.2 subdivision 4 } } ] } + ] +} + +# ==================== SCARECROW (east side, outside fence) ==================== +Solid { + translation 20 -10 0 + children [ + Transform { translation 0 0 1.22 children [ Shape { appearance USE TRUNK geometry Cylinder { height 2.44 radius 0.045 subdivision 8 } } ] } + Transform { translation 0 0 2.02 rotation 1 0 0 1.5708 children [ Shape { appearance USE TRUNK geometry Cylinder { height 1.60 radius 0.032 subdivision 8 } } ] } + Transform { + translation 0 0 2.44 + children [ + Shape { appearance USE STRAW geometry Sphere { radius 0.17 subdivision 3 } } + Transform { translation 0.13 0.05 0.06 children [ Shape { appearance PBRAppearance { baseColor 0.06 0.06 0.06 } geometry Sphere { radius 0.028 subdivision 2 } } ] } + Transform { translation 0.13 -0.05 0.06 children [ Shape { appearance PBRAppearance { baseColor 0.06 0.06 0.06 } geometry Sphere { radius 0.028 subdivision 2 } } ] } + Transform { translation 0.16 0 -0.02 rotation 0 1 0 1.5708 children [ Shape { appearance PBRAppearance { baseColor 0.75 0.50 0.30 } geometry Cone { height 0.07 bottomRadius 0.032 subdivision 6 } } ] } + Transform { translation 0.14 0.04 -0.06 children [ Shape { appearance PBRAppearance { baseColor 0.18 0.08 0.08 } geometry Box { size 0.01 0.04 0.01 } } ] } + Transform { translation 0.14 -0.04 -0.06 children [ Shape { appearance PBRAppearance { baseColor 0.18 0.08 0.08 } geometry Box { size 0.01 0.04 0.01 } } ] } + ] + } + Transform { translation 0 0 2.62 children [ Shape { appearance USE HAT geometry Cylinder { height 0.04 radius 0.28 subdivision 12 } } ] } + Transform { translation 0 0 2.80 children [ Shape { appearance USE HAT geometry Cylinder { height 0.30 radius 0.17 subdivision 10 } } ] } + Transform { translation 0 0 1.60 children [ Shape { appearance USE SHIRT geometry Box { size 0.20 0.40 0.46 } } ] } + Transform { translation 0 0 1.14 children [ Shape { appearance USE PANTS geometry Box { size 0.17 0.32 0.34 } } ] } + Transform { translation 0 0.68 2.03 rotation 0 0 1 0.25 children [ Shape { appearance USE STRAW geometry Box { size 0.03 0.24 0.03 } } ] } + Transform { translation 0 -0.68 2.03 rotation 0 0 -1 0.25 children [ Shape { appearance USE STRAW geometry Box { size 0.03 0.24 0.03 } } ] } + Transform { translation 0.10 0.08 1.82 children [ Shape { appearance USE STRAW geometry Box { size 0.03 0.03 0.14 } } ] } + Transform { translation 0.10 -0.08 1.82 children [ Shape { appearance USE STRAW geometry Box { size 0.03 0.03 0.14 } } ] } + ] +} + +# ==================== HAY BALES (near barn) ==================== +Solid { translation 25.75 13.76 0.62 children [ Transform { rotation 1 0 0 1.5708 children [ Shape { appearance USE HAY geometry Cylinder { height 1.30 radius 0.62 subdivision 14 } } ] } ] boundingObject Box { size 1.30 1.24 1.24 } } +Solid { translation 24.34 12.32 0.62 children [ Transform { rotation 1 0 0 1.5708 children [ Shape { appearance USE HAY geometry Cylinder { height 1.30 radius 0.62 subdivision 14 } } ] } ] boundingObject Box { size 1.30 1.24 1.24 } } +Solid { translation 24.28 13.79 0.62 children [ Transform { rotation 1 0 0 1.5708 children [ Shape { appearance USE HAY geometry Cylinder { height 1.30 radius 0.62 subdivision 14 } } ] } ] boundingObject Box { size 1.30 1.24 1.24 } } + +# ==================== TRACTOR (near barn) ==================== +Solid { + translation 17 19 0 + rotation 0 0 1 1.9 + children [ + # Chassis + Transform { translation 0 0 0.35 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.20 0.20 roughness 0.6 metalness 0.3 } geometry Box { size 2.0 0.90 0.12 } } ] } + # Engine hood + Transform { translation 0.60 0 0.60 children [ Shape { appearance PBRAppearance { baseColor 0.15 0.50 0.12 roughness 0.7 metalness 0.1 } geometry Box { size 0.65 0.80 0.45 } } ] } + # Main body + Transform { translation -0.15 0 0.60 children [ Shape { appearance PBRAppearance { baseColor 0.15 0.50 0.12 roughness 0.7 metalness 0.1 } geometry Box { size 0.80 0.85 0.45 } } ] } + # Cabin + Transform { translation -0.20 0 0.95 children [ Shape { appearance PBRAppearance { baseColor 0.15 0.50 0.12 roughness 0.7 metalness 0.1 } geometry Box { size 0.75 0.80 0.45 } } ] } + # Cabin roof + Transform { translation -0.20 0 1.22 children [ Shape { appearance PBRAppearance { baseColor 0.12 0.40 0.10 roughness 0.75 metalness 0.1 } geometry Box { size 0.85 0.90 0.06 } } ] } + # Windshield + Transform { translation 0.12 0 0.95 children [ Shape { appearance USE GLASS geometry Box { size 0.02 0.55 0.35 } } ] } + # Rear window + Transform { translation -0.58 0 0.95 children [ Shape { appearance USE GLASS geometry Box { size 0.02 0.55 0.35 } } ] } + # Side windows + Transform { translation -0.20 0.40 0.95 children [ Shape { appearance USE GLASS geometry Box { size 0.55 0.02 0.30 } } ] } + Transform { translation -0.20 -0.40 0.95 children [ Shape { appearance USE GLASS geometry Box { size 0.55 0.02 0.30 } } ] } + # Seat + Transform { translation -0.25 0 0.55 children [ Shape { appearance PBRAppearance { baseColor 0.12 0.12 0.12 roughness 0.9 } geometry Box { size 0.30 0.35 0.06 } } ] } + # Exhaust stack + Transform { translation 0.50 0.30 0.60 children [ + Shape { appearance PBRAppearance { baseColor 0.25 0.25 0.25 roughness 0.4 metalness 0.6 } geometry Cylinder { height 0.90 radius 0.03 subdivision 6 } } + Transform { translation 0 0 0.50 children [ Shape { appearance PBRAppearance { baseColor 0.20 0.20 0.20 roughness 0.4 metalness 0.6 } geometry Cylinder { height 0.04 radius 0.045 subdivision 6 } } ] } + ] } + # Rear axle + Transform { translation -0.45 0 0.40 children [ Shape { appearance PBRAppearance { baseColor 0.25 0.25 0.25 roughness 0.5 metalness 0.5 } geometry Box { size 0.08 1.15 0.08 } } ] } + # Front axle + Transform { translation 0.60 0 0.25 children [ Shape { appearance PBRAppearance { baseColor 0.25 0.25 0.25 roughness 0.5 metalness 0.5 } geometry Box { size 0.08 0.90 0.08 } } ] } + # Rear left wheel + Transform { translation -0.45 0.60 0.40 rotation 1 0 0 1.5708 children [ + Shape { appearance PBRAppearance { baseColor 0.08 0.08 0.08 roughness 0.95 } geometry Cylinder { height 0.22 radius 0.40 subdivision 20 } } + Shape { appearance PBRAppearance { baseColor 0.35 0.35 0.35 metalness 0.5 } geometry Cylinder { height 0.24 radius 0.14 subdivision 10 } } + ] } + # Rear right wheel + Transform { translation -0.45 -0.60 0.40 rotation 1 0 0 1.5708 children [ + Shape { appearance PBRAppearance { baseColor 0.08 0.08 0.08 roughness 0.95 } geometry Cylinder { height 0.22 radius 0.40 subdivision 20 } } + Shape { appearance PBRAppearance { baseColor 0.35 0.35 0.35 metalness 0.5 } geometry Cylinder { height 0.24 radius 0.14 subdivision 10 } } + ] } + # Front left wheel + Transform { translation 0.60 0.45 0.25 rotation 1 0 0 1.5708 children [ + Shape { appearance PBRAppearance { baseColor 0.08 0.08 0.08 roughness 0.95 } geometry Cylinder { height 0.16 radius 0.25 subdivision 16 } } + Shape { appearance PBRAppearance { baseColor 0.35 0.35 0.35 metalness 0.5 } geometry Cylinder { height 0.18 radius 0.09 subdivision 8 } } + ] } + # Front right wheel + Transform { translation 0.60 -0.45 0.25 rotation 1 0 0 1.5708 children [ + Shape { appearance PBRAppearance { baseColor 0.08 0.08 0.08 roughness 0.95 } geometry Cylinder { height 0.16 radius 0.25 subdivision 16 } } + Shape { appearance PBRAppearance { baseColor 0.35 0.35 0.35 metalness 0.5 } geometry Cylinder { height 0.18 radius 0.09 subdivision 8 } } + ] } + # Rear fenders + Transform { translation -0.45 0.50 0.72 children [ Shape { appearance PBRAppearance { baseColor 0.12 0.40 0.10 roughness 0.75 metalness 0.1 } geometry Box { size 0.50 0.12 0.20 } } ] } + Transform { translation -0.45 -0.50 0.72 children [ Shape { appearance PBRAppearance { baseColor 0.12 0.40 0.10 roughness 0.75 metalness 0.1 } geometry Box { size 0.50 0.12 0.20 } } ] } + # Front bumper + Transform { translation 0.95 0 0.35 children [ Shape { appearance PBRAppearance { baseColor 0.35 0.35 0.35 roughness 0.7 metalness 0.3 } geometry Box { size 0.12 0.75 0.30 } } ] } + # Headlights + Transform { translation 0.97 0.25 0.45 children [ Shape { appearance PBRAppearance { baseColor 0.95 0.92 0.70 roughness 0.3 } geometry Sphere { radius 0.05 subdivision 3 } } ] } + Transform { translation 0.97 -0.25 0.45 children [ Shape { appearance PBRAppearance { baseColor 0.95 0.92 0.70 roughness 0.3 } geometry Sphere { radius 0.05 subdivision 3 } } ] } + # Taillights + Transform { translation -0.58 0.25 0.45 children [ Shape { appearance PBRAppearance { baseColor 0.80 0.10 0.10 roughness 0.4 } geometry Box { size 0.04 0.08 0.06 } } ] } + Transform { translation -0.58 -0.25 0.45 children [ Shape { appearance PBRAppearance { baseColor 0.80 0.10 0.10 roughness 0.4 } geometry Box { size 0.04 0.08 0.06 } } ] } + # Drawbar hitch + Transform { translation -0.95 0 0.20 children [ Shape { appearance PBRAppearance { baseColor 0.25 0.25 0.25 roughness 0.5 metalness 0.5 } geometry Box { size 0.12 0.06 0.06 } } ] } + ] + boundingObject Box { size 2.2 1.4 1.3 } +} + +# ==================== GRASS PATCHES (inside field, decorative) ==================== +Solid { translation -8 6 0.15 children [ + Transform { translation 0.10 0.00 0 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.30 } } ] } + Transform { translation -0.05 0.12 0 rotation 0 0 1 0.4 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.26 } } ] } + Transform { translation 0.08 -0.10 0 rotation 0 0 1 -0.3 children [ Shape { appearance USE LEAF_C geometry Box { size 0.04 0.02 0.28 } } ] } + Transform { translation -0.12 0.04 0 rotation 0 0 1 0.2 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.24 } } ] } +] } +Solid { translation 6 -9 0.15 children [ + Transform { translation 0.08 0.06 0 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.28 } } ] } + Transform { translation -0.10 0.00 0 rotation 0 0 1 -0.3 children [ Shape { appearance USE LEAF_C geometry Box { size 0.04 0.02 0.32 } } ] } + Transform { translation 0.02 -0.12 0 rotation 0 0 1 0.35 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.26 } } ] } + Transform { translation -0.06 0.10 0 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.22 } } ] } +] } +Solid { translation -3 11 0.15 children [ + Transform { translation 0.06 -0.06 0 children [ Shape { appearance USE LEAF_C geometry Box { size 0.04 0.02 0.26 } } ] } + Transform { translation -0.08 0.08 0 rotation 0 0 1 0.3 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.30 } } ] } + Transform { translation 0.12 0.02 0 rotation 0 0 1 -0.25 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.28 } } ] } +] } +Solid { translation 10 8 0.15 children [ + Transform { translation -0.07 0.05 0 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.24 } } ] } + Transform { translation 0.09 -0.07 0 rotation 0 0 1 0.4 children [ Shape { appearance USE LEAF_C geometry Box { size 0.04 0.02 0.28 } } ] } + Transform { translation 0.00 0.11 0 rotation 0 0 1 -0.2 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.26 } } ] } +] } +Solid { translation -11 -7 0.15 children [ + Transform { translation 0.05 0.08 0 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.30 } } ] } + Transform { translation -0.09 -0.04 0 rotation 0 0 1 0.35 children [ Shape { appearance USE LEAF_B geometry Box { size 0.04 0.02 0.28 } } ] } + Transform { translation 0.10 -0.09 0 rotation 0 0 1 -0.3 children [ Shape { appearance USE LEAF_C geometry Box { size 0.04 0.02 0.24 } } ] } + Transform { translation -0.03 0.12 0 children [ Shape { appearance USE LEAF_A geometry Box { size 0.04 0.02 0.26 } } ] } +] } + +# ==================== SHEPHERD DOG ==================== +ShepherdDog { + translation 0 0 0.5 + rotation 0 0 1 0 + controller "shepherd_dog" +} + +# ==================== SHEEP ==================== +Sheep { + translation 3 2 0.5 + name "sheep1" + controller "sheep" +} +Sheep { + translation 3 -2 0.5 + name "sheep2" + controller "sheep" +} +Sheep { + translation 4 0 0.5 + name "sheep3" + controller "sheep" +} +Sheep { + translation 3.5 1 0.5 + name "sheep4" + controller "sheep" +} +Sheep { + translation 3.5 -1 0.5 + name "sheep5" + controller "sheep" }