OpenRacing Universal Track Interface
Background and goals
Most people think that the base of moderns simulators are about the vehicle they represents. But actually the heart of the simulation is how the track made of. It defines the visual context, the physical parameters of the whole application. One problem might arise during the development of a simulator: the physical part of track modeling is done the way that it is so deeply integrated to the application that it sticks the whole simulator to one physical engine.
The effort between OpenRacing Universal Track Interface (UTI) is to address the above mentioned issue. By creating a well defined universal interface between the simulator and the physical engine those parts can be separated as well. So one is able to change the Physical engine under OpenRacing or create and apply a new track without knowing anything about the structure of Openracing code. Simply knowing interface makes possible to create new tracks as long as the necessary functions are implemented.
The UTI addresses three aspect of track creation: visual - that is how a track looks like for the player physical - describes the physical attribute of the track (invisible, that is how the physical engine calculates what would happen) gameplay - defines the tracks and goals for the player to achieve (what route the car should be driven and so on)
The UTI has also helper functions for managing the tracks.
UTI supports different level of completeness of the track as defined below: Stage 1 - complete visual and physical track, free-ride available Stage 2 - as Stage 1 and robots launching is possible (waypoints and lanes exist) Stage 3 - as Stage 2 and Track Rules are implemented Stage 4 - as Stage 3 and Track AI is implemented
As far as I know, the current concept of defining a track is to calculate the center line and then the body of the track is calculated knowing the width. It is sufficient as long as you have only two lanes. It also calculated for the physical part (height, collision,etc)
What I would suggest is separate the track model (TM) as physical surface from the track path (TP) as logical. The TM describes the physical attributes of the track: height,friction, surface, obstacles and so on. The TP defines what to do on that track. Imagine a big track for rally cross. It has a small village, a road serpentine, some beach part and so on. There is need to create the TM once, and you can make different types of races on it by providing different type of TP over it, like: (1) race from beach to serpentines through the forest. (2) race from the serpentines to the city through the beach. You can revert the direction on the track too.
- Y+ : up
- Y- : down
- Z+ : front
- Z- : rear
- X- : right
- X+ : left
(DISCUSSION NEEDED): did I define it right? It should be Ogre's default coordinate system, but I might remember that wrong.
Jeko, jump in here.
Terms & Definitions
(other part of this page is not yet updated with here mentioned terms and conditions, please take care! )
- Track: the whole package with UTI interface
- TVM: Track Visual Model - includes all meshes, textures to build up visuals
of a given track
- TPM: Track Physical Model - includes all meshes, properties to build up
physical entity of a track
- RS: Road segment: larges build segment of a road. It defines the center
lines(s), ports and ports' width of a part of a road to be built.
- Port: "socket" in which Road segments are plugged together.
- Lane: One Road Segment can have one or more lanes. Lane has direction.
- WP: WayPoint (point, where lanes are split or joined : see picture) WP
might trigger event.
Weather and environment
The weather and environment are handled isolated from the track itself. It provides flexibility (there is no need to create rainy, rainy-evening, snowy-evening and so on versions of a track). The weather system of Open Racing interacts with the track. If a Road Segment's given attribute is set to "Weather Interacts" then the parameters of that segment can change. Like if there is snow in the simulation, the friction of the surface can decrease. Later on, it would be possible to build up snow on the road or small puddles to appear.
In order to UTI to be a successful open structure for defining tracks, it should provide an easy way to generate new tracks within minutes, but it also should maintain ways to fine tune it. The easiest way for it is to supply some kind of World Generator (UTI Genesis), that generates most of the track physical parameters, so only some visual make up and attribute setting is required from the part of the track maker.
Since a Road Segment is a well defined element of the track, it is advised for artists to generate at least one from each types of segments. Using Road Segments as building blocks.
The workflow of building a track:
- create the road itself
- setting parameters of the road
- create plane consisting the road
- elevating plane as needed
- setting parameters of the plane
- putting non-interactive visual elements on the track
- connecting triggers and Track AI devices
There are different methods to build up a new track: (for now, the support of grid mode is the goal. As UTI development progress, Morph and Wire mode will also be available)
- Grid mode (simplest): in this mode the area of track divided into rectangles with same size. All Road segments take up exactly one (or multiple of) rectangle. Their port aligned on the edge of the rectangle. The trackmaker simply picks up a Road Segment from inventory, drops it into the the grid. The Road segment aligned to the grid. Rotation might be necessary to connect two Road Segments' ports. Ports are connected automatically if they matched on grid line. No parameters of Road segments can be changed.
- Pros: The simplest way to create track. Artists can make up the inventory, they might create "pack" that contain all types of Road Segments. Trackmaker just select this pack and drops all needed Road Segment into the track. Expected to build a track in 10-20 minutes without previuos knowledge.
- Cons: Although it is the easiest way to create a track, the road itself can only fixed length segments. Like, if the grid-distance is 10 meters, you can have 10, 20, 30 meters of straight road, but you cannot have 17.6 meters.
- Morph mode: in this mode we also use previously pre-crafted Road Segments, but those can be put and rotated on the plane freely. Ports should be selected and connected by the trackmaker manually. Segment attributes can be changed (like: straight-length, curve - radius)
- Pros: More mature and exotic tracks can be made.
- Cons: It needs a lot of development and math from the side or Track Generator's developer. (matching up ports and so on)
- Wire mode: in this mode the trackmaker defines the WayPoints and Lanes in 3D. Defines the attributes of all WPs and Lanes. After it, select the "skin (artist package)" and attributes of the track (forest density, building density) and Track Generator generating the whole track.
- Pros: Least effort from trackmaker, elevating and visual building of track can be left out. The same wire can be used for different tracks.
- Cons: Even more development required from Track Generator's developers.
- Genesis mode: In this mode, only the intervalum parameters are defined (how hard the track is, is it speedy or not, how many intersections should be there, height distances and so on). The wireframe and the track itself are generated by genetic algorithms. The visual of the track is based on the "artist pack" used.
- Pros: only the base values should be supported by trackmaker. Hundreds of tracks and their screenshots can be generated in hours.
- Cons: even more development and testing :)
(DISCUSSION NEEDED) : Is it possible to implement these in Blender or should we create a Track Editoror use Track Editor for creating only road, then use Blender to create everything else?
Excellent analysis! Jeko, please jump in here.
The main goal of UTI is to provide track for simulation without knowing what that simulation is about. In order to provide maximum flexibility, a developer ("loaderMaker") who uses UTI must inherit from all the following classes forming the UTI interface into a module called "Loader". That loader is capable of loading that kind of tracks. It is the sole responsibility of "LoaderMaker" to dispatch all informations not defined here, so other tracks can be build successfully for his loader.
For example someone wants to make a track that uses ODE as physic engine. He should write the loader that imports/converts track informations so physical track informations can be picked up the ODE engine.
(DISCUSSION NEEDED: should everyone use the TrackGenerator's format, or one can introduce new track format as long as they comply with UTI?)
Keith's advice: Don't over-engineer the higher-level classes. In fact, worry mostly about the details of the low-level ones, and leveraging and understanding the existing data structures. Then, write the minimum glue logic to hook the low-level classes up. Over time, we can figure out how to download new tracks on the Internet and other cool ideas you suggest. But that code will all be in C# / Python.
Torcs already has a Track module interface. Leverage it and make the minimal necessary changes. That is easiest and fastest.
It handles the inventory of available tracks on the host. (Might be able to download track from another host or FTP, HTTP server, it is up to the developer, who implements it)
- loadTrackInventory() - scans directory for available tracks
- listTracks() - returns the names of the tracks available
- checkTrack() - check if all files and resources are available for a track (no missing or misversioned file)
- queryTrackInfo - returns a detailed info of the track (screenshot, track outline, width, length, difficulty and so on)
- loadTrack() - loads selected track
- releaseTrack() - release loaded track (freeing up resources)
It is the main class that contain all information (visual, physical, logical) of a track.
For now, just make this be mostly what Torcs already has.
- createTrack() - loading up the track. It creates all Ogre and physical entities and meshes, link them together. The array of the created bodies should be stored. (Discussion: later TrackAI and WeatherInterface (desc. later) might use that)
- trackName() - returns the name of the track.
- trackVersion() - returns the version of the track.
- trackFlags() - returns the flags of the track (can be overriden by RoadSegment flag, if)
- getContactPoint(Vec3 from, Vec3 to) - it is basically a raycast function that casted between "from" and "to". From the first hit a ContactInfo should be generated and returned. If there was no hit, NULL should be returned.
Road Segment (RoadSegment.cpp)
Point where lanes split or joined, ot other trigger events occures.
- id() - returns the ID of this WayPoint
- name() - returns the name of this WayPoint
- allLanes() - returns array of reference of Lanes connected to this WayPoint
- inLanes() - returns array of references of Lanes that arrive into this WayPoint
- outLanes() - returns array of references of Lanes that departures from this WayPoint
The section between two WayPoints. Lane has direction.
- fromWP() - returns reference of the WP this Lane origins from.
- toWP() - returns reference of the WP this Lane goes to.
Intelligence for the track. It can help the robots to get their goal or obstruct them. (Like trees can fall in front of the robot if there is a "bad" track :) ) For general use, it can instuct the elements and rules of a track, can organize the traffic, etc.
Don't worry about this yet.
Developement phases (from TORCS to UTI)
UTI is not just about to provide an interface, but an effort to port already existing tracks of TORCS and extend their possibilities. There are a lot of well designed tracks, no one should be abadoned.
In order to get OpenRacing and TORCS to use the already existing set of tracks, the following steps should be taken:
The UTI compatible Loader should implement