Repository navigation
CARLA geometry
Eight modules in extensions/carla/ port CARLA's engine-independent geom/, image/ and pointcloud/ code. They give CARLA's math helpers, bounding boxes, map projections, R-trees, meshes, mesh simplification, point cloud files and image conversions. The numbers follow CARLA, except for the corrections listed on this page.
The port follows the LibCarla source at commit 1360bb9. See CARLA for the roads, sensors and frames.
| Module | CARLA source | What it gives |
|---|---|---|
math |
geom/Math.cpp, the vector headers, Quaternion.h
|
Distances to segments and arcs, 2D helpers, Vector3DInt, unit vectors, quaternion conversions. |
bounding_box |
geom/BoundingBox.h |
BoundingBox: contains, the local and world vertices. |
geo |
geom/GeoProjection*, GeoReferenceParser.cpp
|
GeoLocation, four projections, the OpenDRIVE geoReference parser. |
rtree |
geom/Rtree.h |
PointCloudRtree and SegmentCloudRtree: nearest k, with a filter, and box queries. |
mesh |
geom/Mesh.cpp |
CarlaMesh: strips, fans, materials, OBJ, PLY, a BufferGeometry. |
simplification |
geom/Simplification.cpp, third-party/simplify
|
Quadric edge-collapse simplification. |
pointcloud |
pointcloud/PointCloudIO.h |
The PLY file of a LiDAR measurement. |
image_convert |
image/ColorConverter.h, ImageConverter.h
|
Depth, logarithmic depth and CityScapes conversions, instance, normals and optical flow decoders. |
The modules use ThreeMojo's types where ThreeMojo has one. They add only what CARLA needs and ThreeMojo lacks.
- A CARLA
Vector3DorLocationis amath.vector3.Vector3in meters. AVector2Dis amath.vector2.Vector2. - A CARLA
Quaternionis amath.quaternion.Quaternion. The product, the conjugate andRotatedVectorare its own. -
Math::ClampandMath::LinearLerparemath.utils.clampandmath.utils.lerp. - The forward, right and up vectors are
CarlaRotation.forward_vector,right_vectorandup_vector. -
BoundingBoxis a thin layer overmath.obb.OBB.BoundingBox.to_obbgives the oriented box. - The R-tree boxes are
math.bounds.Box3. -
CarlaMesh.to_buffer_geometrygives acore.buffer_geometry.BufferGeometry.CarlaMesh.generate_plywrites it withexporters.ply.export_ply. -
image_convertapplies the pixel steps ofextensions.carla.sensorto arender.framebuffer.Framebuffer.
distance_segment_to_point and distance_arc_to_point project a point on a lane segment in the x-y plane. Each returns two lengths: the distance along the segment and the distance to it.
from extensions.carla.math import distance_segment_to_point
from math.vector3 import Vector3
var along_and_off = distance_segment_to_point(
Vector3(1, 2, 0), Vector3(0, 0, 0), Vector3(4, 0, 0)
)
# along_and_off[0] is 1 meter, along_and_off[1] is 2 meters.An arc has a length, a heading and a curvature. The heading is an Angle and the curvature is an InverseLength.
Vector3DInt.squared_length returns an exact UInt64. The former return type was Int64, which could overflow for valid components. Each signed 32-bit component is squared in Int64. Each square converts to UInt64 before addition. The largest sum is 13835058055282163712, for three components of -2147483648.
Vector3DInt.length returns a finite, nonnegative Float64. It converts the exact sum to Float64, then takes the square root. The relative error is below 2^-51. Zero gives an exact zero. The other integer vector operations keep their existing contracts.
CarlaRotation.normalized reduces each finite Float32 angle into [-180, 180) degrees. Both endpoints become -180. Whole turns reduce to zero with the input sign. The remainder is stable at large angles: 1e10 degrees becomes -80 degrees. A negative angle near zero keeps its low bits.
rotations_equal uses the same reduction for each angle. Signed zeros compare equal. A nonfinite angle becomes NaN during normalization. A rotation with a nonfinite angle compares unequal, including to itself. This rule also applies to transforms_equal and bounding box equality.
A map's projection turns a CARLA location into a latitude, a longitude and an altitude. The GNSS sensor and Map::TransformToGeolocation use this math.
from extensions.carla.geo import parse_geo_reference
from loaders.xml import parse_xml
from math.vector3 import Vector3
var document = parse_xml(xodr_text)
var projection_and_reference = parse_geo_reference(document)
var projection = projection_and_reference[0].copy()
var geo = projection.transform_to_geo_location(Vector3(100, -200, 3))parse_geo_reference reads OpenDRIVE/header/geoReference and the attributes of OpenDRIVE/header/offset. The PROJ string picks the projection:
+proj |
Projection | Parameters |
|---|---|---|
tmerc |
TRANSVERSE_MERCATOR |
lat_0, lon_0, k, x_0, y_0
|
utm |
UNIVERSAL_TRANSVERSE_MERCATOR |
zone, south, and the header offset |
merc |
WEB_MERCATOR |
none |
lcc |
LAMBERT_CONFORMAL_CONIC |
lat_0, lat_1, lat_2, lon_0, x_0, y_0
|
A supplied UTM zone is read once with stod, including decimal exponents and ignored text after the numeric prefix. The value must be finite, at least 1 and less than 61 before integer conversion. Fractional values still truncate toward zero: 1.5 selects zone 1, and 60.5 selects zone 60. A value below 1 or at least 61 raises an error. Large and nonfinite values cannot wrap into a valid zone.
The returned reference longitude uses the selected zone's central meridian, 6 times the zone minus 183 degrees. This fixes the former fractional reference: 31.5 now gives zone 31 and 3 degrees, rather than 6 degrees. Exponents now select the same numeric value for the zone and reference: 3.1e1 selects zone 31. With no zone, the existing default stays zone 31 with a zero reference.
Another value, or no +proj, gives the default transverse Mercator. The ellipsoid comes from ellps or datum, then a, b, f and rf. Without any of them it is WGS 84.
A latitude, a longitude and an altitude are Float64 numbers. An Angle holds a Float32, which keeps a latitude to about a meter only. The field names say the units: latitude_degrees, altitude_meters.
The projections call the C library's log, exp and pow. Mojo's own functions are off by up to 1e-11, which is tens of micrometers on the Earth.
SegmentCloudRtree holds segments with two whole numbers each. The numbers are the caller's own, such as indices into a list of waypoints.
from extensions.carla.rtree import SegmentCloudRtree
from math.vector3 import Vector3
var tree = SegmentCloudRtree()
tree.insert_element(Vector3(0, 0, 0), Vector3(10, 0, 0), 0, 1)
var nearest = tree.get_nearest_neighbours(Vector3(5, 2, 0), 1)A filter is a struct with an accepts method, as PointFilter and SegmentFilter define. get_intersections returns the segments that meet a Box3.
The results come in computed squared-distance order. Equal keys use insertion order. Boost leaves this order open. Code that depends on a tied result must use the port's insertion rule.
For finite Float32 coordinates, segment queries retain small endpoint gaps. Reversing a segment does not change its distance key. The nearest point can be inside a segment even when its parameter rounds to an endpoint in Float64. The distance kernel uses endpoint signs and cross products to retain that gap. It uses expanded products when ordinary wide arithmetic cannot resolve them.
Distances are Float64 estimates. They are not exact rational results. The numerical design gives the error bounds and the supported ordering contract.
A node box gives a lower bound for each segment below it. Each segment key is at least its own box key, so rounding cannot put it below a parent bound. This preserves query pruning and insertion order for equal computed keys.
Box queries widen coordinates before subtraction. They compare uncertain slab parameters before division. Thus a segment from negative to positive Float32 limits can meet a small box without an infinite direction. Distinct small slabs remain distinct even when their Float64 parameters round alike. Faces still count as inside. Intersection results keep insertion order.
These correct ThreeMojo's former finite-extreme behavior, tracked in
issue #589.
CARLA's pinned reference is
geom/Rtree.h at 1360bb9.
The correction does not change lane construction. Extreme-coordinate queries
can select a different nearest lane or junction segment than earlier releases.
There is no mode that restores the old incorrect results.
Packing first compares volume growth, then volume. Ties use area growth, then area, then length growth, then length. Area is the sum of the three pairwise products of side lengths. Length is the sum of side lengths. The comparisons keep these units separate.
This gives planar and linear data a spatial cost when every volume is zero. Final ties keep the existing deterministic rules. Side lengths widen both endpoints to Float64 before subtraction, so finite opposite-sign Float32 limits do not overflow during packing. See #583.
For example, insert the 17 points (0, 0, 0) through (16, 0, 0) in order. The former volume-only split made leaf bounds [0, 15] and [2, 16]. The new bounds are [0, 12] and [13, 16].
The change affects tree packing and query cost. The query and tie policies, payload storage, and public insertion order stay the same. Existing distance and slab arithmetic defects at extreme finite coordinates can affect query results; see #589. Exact result preservation for every finite input is not established.
CarlaMesh keeps CARLA's lists: vertices, normals, UVs, indices counted from one, and named materials. add_triangle_strip and add_triangle_fan turn the same way as CARLA's.
-
generate_objandgenerate_obj_for_recastwrite CARLA's text with its comments, itsusemtlnames and six digits after the point. -
generate_plywrites an ASCII PLY file through ThreeMojo's exporter. -
to_buffer_geometrygives aBufferGeometryin the three.js frame, with one group for each material. -
concat_meshjoins two meshes and stitches the seam, as CARLA'sConcatMeshdoes.
Simplification(0.5).simplificate(mesh) keeps about half of the triangles. It is Forstmann's quadric simplification, which CARLA keeps in its tree. Border edges do not collapse.
dump writes detections as CARLA's ASCII PLY text, with four digits after the point. save_to_disk writes the file and changes the extension to .ply.
from extensions.carla.pointcloud import LidarDetection, save_to_disk
var points = List[LidarDetection]()
for point in scan.points:
points.append(LidarDetection.from_lidar_point(point))
_ = save_to_disk("out/scan.ply", points)SemanticLidarDetection writes CosAngle, ObjIdx and ObjTag. Another detection type conforms to PlyDetection.
convert_in_place applies a ColorConverter to every pixel: RAW, DEPTH, LOGARITHMIC_DEPTH or CITY_SCAPES_PALETTE. A gray level g becomes the byte of g times 255 plus 0.5, as Boost.GIL writes it.
-
encode_instanceand the twodecode_instance_functions read the instance segmentation camera. Red is the tag, and green and blue hold the 16-bitInstanceId. -
decode_normalreads the normals camera. Each channel c is c / 255 * 2 - 1. -
encode_flow_pixelandencode_flow_imagecolor an optical flow image with CARLA's color wheel.
Each kind is a type with is_valid, and the functions that read one refuse a value that is not valid.
| Type | Values |
|---|---|
ProjectionType |
TRANSVERSE_MERCATOR, UNIVERSAL_TRANSVERSE_MERCATOR, WEB_MERCATOR, LAMBERT_CONFORMAL_CONIC
|
UtmZone |
1 to 60 |
ColorConverter |
RAW, DEPTH, LOGARITHMIC_DEPTH, CITY_SCAPES_PALETTE
|
InstanceId |
0 to 65535 |
The port refuses input where CARLA reads out of bounds, divides by zero or wraps around. It also fixes some behavior that has no use.
- R-tree packing uses area and length to resolve volume ties. Its linear seed selection and node limits stay the same. This is a packing correction to this port; it does not change the query contract.
- A bounding box half size must be finite and zero or more.
- A UTM zone must be from 1 to 60 after the checked fractional conversion above. CARLA takes any zone.
-
stoddoes not read a hexadecimal float. It reads the zero before thex. -
CarlaMesh.generate_plywrites a PLY file. CARLA'sGeneratePLYreturns an empty string. -
CarlaMesh.concat_meshrefuses a link count larger than either mesh. -
Simplificationrefuses a negative rate, and a mesh with fewer than two indices. - A red channel of 30 or more is not a semantic tag. The CityScapes conversion refuses it, and CARLA wraps it.
- An empty point cloud writes its header from the type.
BoundingBox.contains does not apply the box's own rotation. CARLA's Contains does not either, so the port keeps this.
Mojo fuses a multiply and an add into one step, which rounds once. The simplifier rounds each product on its own, as the C++ does, so the same edges collapse.
-
ImageIOandImageViewread and write image files with Boost.GIL, libpng and libjpeg. ThreeMojo's own image loaders and writers do this. -
Vector3D'soperator/(float, Vector3D)divides the vector by the number, which reads as the opposite. -
CubicPolynomial.his inextensions/carla/polynomial.mojo.Rotation.handTransform.hare inextensions/carla/transform.mojo.
Tutorials
How-to
Reference
- Scene graph
- Rotations
- Cameras
- Geometry
- Geometry addons
- Lofts and clipping groups
- Sculptor
- Procedural generators
- Meshes and assets
- Lines
- Points and sprites
- Helpers
- Scene objects
- Animated objects
- Raycasting
- Curves and paths
- Animation
- Skinning
- Materials
- Node materials
- TSL functions
- Lights
- Lighting addons
- Fog
- Textures
- Renderer
- Renderer hooks and material flags
- Rasterization
- GPU backend
- Render target
- Post-processing
- GPU computation
- Compute nodes
- Progressive light map
- Gaussian splats
- Voxel global illumination
- SVG renderer
- Image files
- Image decode queue
- Model files
- More model files
- Exporters
- Scene JSON
- Windowing and controls
- Math
- Norm consumers
- Math addons
- Units
- Coverage tool
- Commands
- Examples
- Benchmarks
- Extensions
- Shared physics
- Physics query snapshots
- Continuous collision
- Tick force restoration
- Water
- Animals
- Buildings
- CARLA
- Femur
- Tibia
- Fibula
- Patella
- Knee
- Muscles
- Vessels
- Lymph
- Nerves
- Integument
- Leg
- Foot
- Pelvis
- Torso
- Arm
- Hand
- Head
- Genome
- Game humanoid
- Humanoid fidelity
- Audio-aligned game faces
- Converted assets
- Mesh quality
- Segment inertia
Explanation
- Software rasterizer
- Scene graph as an array
- Linear color
- Normals are not colors
- Perspective correction
- Fixed-point coverage
- Per-fragment shading
- Mipmaps
- Sorted transparency
- Types and checks
- Render hooks as a trait
- Shared CPU and GPU code
- Bands and threads
- PNG structure
- Extensions beside the port
- CUDA teardown hang
- Mojo compiler hang