Skip to content

A cross platform 3D graphics meta engine for Java used for building several styles of maze games, most easy to be run in a browser.

License

Notifications You must be signed in to change notification settings

thomass171/tcp-22

Repository files navigation

tcp-22

A cross platform 3D graphics meta engine for Java, but still WiP!

See also

Building And Deploying

This shows the build and installation to a web browser running locally serving from directory $HOME/Sites. Set shell variable HOSTDIR, eg.:

export HOSTDIR=$HOME/Sites/tcp-22

and create that base directory. If this is the first build or for making a getting started easier, the modules 'sceneserver' and 'servermanager' should be commented in the top level pom.xml:

   <!--<module>sceneserver</module>
       <module>servermanager</module>-->

These are adanced modules that are not needed for starting. They need artifacts from other modules for testing, so tend to fail in fresh environments.

In platform-jme/pom.xml you should set the property 'lwjgl.natives' with a fitting value for your platform.

Run the following steps either manually or use the script bin/buildAndDeploy.sh for running all.

The module platform-jme requires customized JMonkeyEngine build artifact files, which were built from JMonkeyEngine 3.8.1-stable with all datatypes float replaced by double. These use the lwjgl3 version 3.3.6 (https://www.lwjgl.org).

The files reside in subfolder lib and should be installed in the local maven repository.

for l in jme3-core jme3-effects jme3-lwjgl3 jme3-desktop
do
  mvn install:install-file -Dfile=./platform-jme/lib/$l-3.8.1-dbl.jar -DgroupId=org.jmonkeyengine -DartifactId=$l -Dversion=3.8.1-dbl -Dpackaging=jar
done  

Maven is needed for building. Run testless first to be ready to create test data.

mvn clean install -DskipTests=true

Deploy data bundles needed for testing to $HOSTDIR:

sh bin/deployBundle.sh data
sh bin/deployBundle.sh corrupted
sh bin/deployBundle.sh -m maze
sh bin/deployBundle.sh -m engine
sh bin/deployBundle.sh -m traffic

Run again with testing

mvn clean install

The build should succeed without error. If it is the first build, it might fail in module 'sceneserver'. In this case it might help to just build again.

After that deploy the software:

sh bin/deploy.sh

Deploy the remaining bundles:

sh bin/deployBundle.sh -m engine

Unity

NOTE:(3/2025): This feature has not been maintained for a while and is broken very likely.

Convert the needed files to CS (be sure to use a zsh compatible shell like bash):

for m in core engine maze outofbrowser-common
do
        zsh bin/java2cs.sh $m
done

The converted files are copied to platform-unity/PlatformUnity/Assets/scripts-generated

Open the project platform-unity/PlatformUnity in Unity. It should compile all the files with some errors related to using delegates. Unfortunately the converter does not remove the FunctionalInterface (which become delegates in CS) method names, so you need to remove these manually. In detail these are (list is not up to date):

  • ImageHelper:handler.handlePixel
  • AbstractSceneRunner:handler.handle
  • An @Override annotation needs to be removed in GraphVisualizer.cs manually
  • DefaultMenuProvider:menuBuilder.buildMenu
  • EcsEntity:entityFilter.matches
  • ReferenceScene:GeneralHandler.handle
  • PointVisitor.visit
  • PointValidator.isValid
  • LinePrinter.println
  • EntityFilter.matches
  • ModelBuilder.buildModel

Furthermore manual fixes are needed

  • in AbstractSceneRunner for futures list

The code pieces are prepared and marked with "C#".

Unity needs to know your HOSTDIR for finding bundle data. Go to file Main.js and adjust the SetEnvironmentVariable() call accordingly.

And it might be necessary to reassign the script file Main.cs to game object "MyScriptContainer" to trigger it.

Now the scene "SampleScene" is ready to be started.

JMonkeyEngine

Nothing special to do any more (after setting the correct 'lwjgl.natives' value in pom.xml). All is prepared to launch a scene in JMonkeyEngine.

Running

Browser

Enter the URL

http://localhost/<youruserdir>/tcp-22/tcp-22.html?host=http://localhost/<youruserdir>/tcp-22

in your browser. Be sure to add the "host" parameter for really accessing your local installation. Check the developer console for possible errors. You should see the ReferenceScene

Unity

Just start the scene.

JMonkeyEngine

Start a scene by the wrapper script launchScene.sh, eg.:

sh bin/launchScene.sh de.yard.threed.engine.apps.reference.ReferenceScene

HomeBrew

Start a scene by the wrapper script launchScene.sh, eg.:

sh bin/launchScene.sh -p homebrew de.yard.threed.engine.apps.reference.ReferenceScene

Settings

The following properties are evaluated from the environment or system properties.

ADDITIONALBUNDLE

This is a colon separated list of bundle locations, either filesystem paths or web URLs. The default bundle location is $HOSTDIR/bundles for filesystems and 'origin'/bundles from a browser. For the bundles listed in ADDITIONALBUNDLE a bundle resolver is added after the default bundle resolver during platform setup. See also property 'bundleResolver', which has a similar semantic.

Properties

(what is the difference to 'Settings'?)

initialVehicle: Just a name as reference to a defined vehicle (in future maybe also or a full qualified path to a config).

initialRoute:

bundleResolver(in future?;or httpBundleResolver?): Similar to ADDITIONALBUNDLE, but not base64 encoded and adding the resolver before the default resolver. This allows overriding default bundles by custom bundles.

initialLocation: In traffic a (smart)location used in association with initialVehicle. Sometimes resulting/superseeded in FlightLocation. Competes with 'initialRoute'.

initialHeading: Contributing to initialLocation(FlightLocation)

Generic

Use Case Maze

Use Case Traffic

Property Purpose
enableAutomove Start vehicle immediately after scene start without the need for a start request

Development

The most convenient way is to develop for a Java platform like JME (or homebrew) initially and later test it on other platforms like ThreeJs and Unity. Thats because the other platforms need converting which reduces roundtrip time.

In your IDE you might create a launch configuration like the following.

LWJGL

For running Java 3D software native libraries like 'lwjgl' are required. These are typically located in the current working directory or via LD_LIBRARY_PATH. As of 8/2025 the native libs are loaded via maven and LWJGL3s bom.

Unfortunately LWJGL3 interferes with AWT on MacOS, so e.g. BufferedImage (for implementing NativeCanvas) cannot be used any more.

JMonkeyEngine has an ImageWriter(https://github.com/jMonkeyEngine-Contributions/ImagePainter/blob/master/ImagePainter/src/com/zero_separation/plugins/imagepainter/ImagePainter.java) for adding text and lines to a JME Image.

An option in pure LWJGL (platform-homebrew) is org.lwjgl.stb.STBImage (demo in https://github.com/LWJGL/lwjgl3/blob/master/modules/samples/src/test/java/org/lwjgl/demo/stb/Image.java)

For now we set NativeCanvas to deprecated until we have a real use case that justifies the efford for implementation. Maybe we should remove it at all, because other platforms (Unity) also have a lack of utils. Or have a complete homebrew implementation like JMEs ImagePainter.

Build your own scene

The best starting point is to use class ReferenceScene and modify it for your needs.

GWT

Platform platform-webgl uses GWT to compile Java code to JS. GWT Dev mode is started as usual by

cd platform-webgl
mvn gwt:run

This starts a local Jetty and makes the main entry point "available at http://127.0.0.1:8888/webgl.html". However, the page shown is empty, because parameter are missing. Open the dev console of the browser for additional information.

For ReferenceScene use

HOSTDIR=http://localhost:80/~thomas/tcp-22

which leads to URL

http://localhost:8888/webgl.html?scene=ReferenceScene&devmode=true&HOSTDIR=http%3A%2F%2Flocalhost%3A80%2F~thomas%2Ftcp-22

and for maze with the default sokoban grid it will be

http://localhost:8888/webgl.html?scene=MazeScene&devmode=true&HOSTDIR=http%3A%2F%2Flocalhost%3A80%2F~thomas%2Ftcp-22

For avoiding URL de/encoding issues, ADDITIONALBUNDLE parts which are URLs need to be base64 encoded.

Don't forget to allow access in your web server by setting corresponding CORS header.

Measures

MA46: Defer processing of newly created events in a frame cycle. Otherwise a server system might keep processing without giving the client the chance to catch up. Occured in the sequence LOGGEDIN->JOINED->ASSEMBLED.

Credits

See https://thomass171.github.io/tcp-22/tcp-22.html for credits.

Releases

This is the release list In terms of installations to the examples hosting server used from https://thomass171.github.io/tcp-22/tcp-22.html. Every release contains lots of bug fixes. Only major changes are listed here.

2021-06

Initial release

2022-04

Major changes:

  • Additional 'P' style maze games

2022-12

Major changes:

  • VR moving in mazes by controller stick instead of using teleport location marker.

2023-03

  • fix for parameter "offsetVR" passing in browser
  • preparation for multi player scene server.

2023-05

  • multi player server completed

2023-07-18

  • fix for NPE when firing

2023-08-25

  • upgrade GWT 2.8.1 -> 2.10
  • Ready for Java 17 (still language level 8/9 for C# parts)
  • Sound added

2024-01-04

  • Bundle load refactoring including remote bundle
  • More traffic configuration including aircraft
  • InitChain for init

  • upgrade of maven-compiler-plugin to 3.12 for setting java release 11

2024-09-24

  • PortableModel(List) refactored
  • transparency refactored
  • findNodeByName() in Platform
  • reactivated delayed bundle
  • GLTFs more according spec
  • normal building refactored(SmoothingMap->VertexMap)

2024-10-16

  • Shader handling fixed for WebGL

2025-02-03

  • Shader refactored for material specific uniforms. Class 'Effect' replaced with 'ShaderPool'.
  • Reactivated 'Universalshader' as multi purpose shader (including textureMatrix).
  • Property ADDITIONALBUNDLE no longer for gwt dev mode but with http query param HOSTDIR

2025-03-26

  • ObserverSystem uses Shift-CUR* keys instead of CUR* for having CUR* available for movement control
  • Speedup/Down with PGUP/DOWN keys instead of '+'/'-' because those keys might be inconvenient depending on keyboard layout
  • FreeFlyingSystem added
  • 'Wayland' added to home page

2025-04-02

  • Define FG vehicle orientation the default in 'traffic'. This helps to avoid confusion with eg. method buildZUpRotation(). Previously there was no standard. Every use case had it's own.

2025-08-01

  • Migration from JME 3.2.4 to 3.8.1 (and thus LWJGL2 to LWJGL 3.3.6) to be prepared for Apple Silicon, which is supported as of LWJGL 3.3. Requires VM option '-XstartOnFirstThread' on MacOS.
  • NativeCanvas deprecated roughly because of LWJGL/AWT conflicts on MacOS
  • Migration of platform-homebrew only started but not complete

Technical Details

Architecture

Conventions

This is a definition of phrases used througout the project.

position

Consider a 3D coordinate (Vector3) like Payload and XML config (traffic.xsd) does.

location

Consider an abstract position, in traffic a (Smart/FlightLocation)

Design

Spheres

A sphere is a limited area of gaming like

  • an airport in flight simulation
  • the solar system in a space simulation
  • a grid in a maze game
  • a lobby for entering a game

The transition from one sphere to the other might be animated, or just showing a screen 'Loading...' or whatever. Spheres are controlled by the SphereSystem.

ECS

An Entity Component System (ECS) is a software architectural pattern that helps organizing software.

Events and Requests are the main elements for inter system communication. But where to register these?

For example in client/server mode a "UserSystem" runs on the server not the client. But the client needs to send LOGIN requests and might process LoggedIn events. This suggests to decouple event/request registration from specific systems.

Traffic request/event flow

sequenceDiagram
  actor App
  participant SphereSystem
  participant FlatTerrainSystem
  participant GraphTerrainSystem
  participant TrafficSystem
  App->>SphereSystem: USER_REQUEST_SPHERE
  SphereSystem->>FlatTerrainSystem: TRAFFIC_EVENT_SPHERE_LOADED
  SphereSystem->>GraphTerrainSystem: TRAFFIC_EVENT_SPHERE_LOADED
  SphereSystem->>TrafficSystem: TRAFFIC_EVENT_SPHERE_LOADED

Loading

Startup of a TravelScene(Bluebird) in tcp-flightgear (parts currently hard coded):

sequenceDiagram
  participant SphereSystem
  participant GroundServicesSystem
  participant TrafficSystem
  SphereSystem->>GroundServicesSystem: TRAFFIC_REQUEST_LOADGROUNDNET (hard coded)


Loading

Load a vehicle (eg. by pressing key 'l' or setting property 'initialVehicle'):

sequenceDiagram
  actor App
  participant TrafficSystem
  App->>TrafficSystem: TRAFFIC_REQUEST_LOADVEHICLE


Loading

Data Flow

Data available in one system might also be needed in other systems. The options are

  • other systems read data again
  • data is transferred via events
  • data is available by a DataProvider

Anyway, data units should be fine granular and specific for avoiding coupling and dependencies. The event option with small packets is currently the preferred option.

Authentication

Authentication is useful when data is shared across several user.

The hoster of a maze or sceneserver is the adminstrator and defines the 'admin' password by properties

  • servermanager.admin.password
  • services.admin.password

Modules

Scene Server

A simple Java network server, no GWT, no C#. Uses a headless platform. Might also be a Web server (Jetty, Tomcat, SpringBoot), but for now its just using plain (web)socket communication. This might be replaced later by a more sophisticated network layer (MQTT?).

Because there is no need for GWT and C#, there is no limitation in using Java. So logging outside the platform (SL4J) and reflection might be used..

services

A SpringBoot applications that provides maze grid CRUD services via HTTP.

servermanager

A SpringBoot application that provides scene server control via HTTP, eg.:

  • launch a scene server for a specific scene

Start the server manager (and enable DEBUG logging) with

cd servermanager
mvn spring-boot:run -Dspring-boot.run.arguments="--logging.level.de.yard.threed=DEBUG"

The UI is then available on 'http://localhost:8080/servermanager.html'.

traffic-services

A SpringBoot applications that provides traffic services like eg. airport information via HTTP.

Tools

ac3d

From AC3D manual:

If a surface is flat-shaded, the color across the surface will be constant. If it is shaded then the color will be 'graded' depending on lighting conditions.

Vertices can be shared between surfaces (i.e. one or more surfaces use the same vertex). If these surface types are set to ‘smooth’, the effect is of one continuous 'smooth' surface. The vertices MUST be shared for two surfaces to be smoothed together. This method of shading allows smooth- looking objects to be created from relatively simple shapes.

and about 'crease':

A crease angle of zero degrees forces all surfaces to be flat. A crease angle of 180 degrees forces all surfaces to be drawn smooth. Setting a surface type to ‘flat’ forces the surfaces to remain flat shaded. Setting a surface type to smooth allows it to be under control of the crease angle.

Traffic

The default/expected orientation of a vehicle is front at +x and +y up, like eg. 'loc'. (See ReferenceScene which uses the default orientation).

For vehicles moving on a graph GraphMovingSystem calculates the correct 3D orientation, which depends on the orientation of the base model. The default orientation of a graph differs by a -90 degree y-rotation from the default vehicle orientation.

Currently, a graph needs to know how models are oriented via
GraphMovingComponent.customModelRotation for providing the visually correct rotation.

The alternative to decouple the knowledge from a graph seems promising, but requires to use a common model orientation. That in turn leads to complexity with projected graphs (groundnet) which by nature have a different rotation strategy.

Bundles

Bundles are a well defined set of resources (files) that reside somewhere. They are identified by a name. Lookup is done by bundle resolver which check whether they know how to load a bundle (there is no probing). The first resolver wins. Every platform has a default low prio bundle resolver that points to HOSTDIR/HOSTDIRFG on desktop and 'origin' in a browser. The browser location can be overwritten by HTTP query param HOSTDIR.

Alternatively, a bundle can be loaded with a full qualified name ('bundle@location'), which doesn't need a resolver.

Additionally a bundle can be loaded by a HTTP URL. The path must point to a directory where a file directory.txt must exist, eg.:

https://yard.de/bundlepool/fgdatabasicmodel

The bundle name will be derived from the last segment of the URL.

Async Operations

These occur when blocking operations (network IO) use a callback. Multithreading related asyncs are not considered as MT isn't used at all. Game engines do not like MT.

Some of the async operations are capsuled inside the platform, eg. loading a texture. These are not of further interest to the app.

Other async operations that are of interest to the app (like bundle loading) are capsuled in AbstractSceneRunner, that cares for a consistent program flow by executing all pending callbacks while preparing a frame.

Java Language Level

Changed from Java 8 to Java 11 in January 24 due to ClassCastExceptions in java.nio.(Byte)Buffer flip() and clear(). There was a kind of breaking change in the API after Java 8. Still needs care about Java 9 limitation for C# conversion.

Material

This is in most parts just a recap of well known issues in 3D graphics.

As long as no (or just the default) shader are used, the appearance of material is defined by a few properties.

Unshaded (basic) material is the most simple. A pixel has just the color of the object. Light has no effect.

For shading the are two options, flat and smooth shading. Unfortunately model formats like AC and GLTF have no explicit property for the shading type. AC just has a 'shaded' flag, GLTF the extension "KHR_materials_unlit" (ie. no light effects).

And a model doesn't necessarily contain normals. So what we do during import of a model is to analyse the geometry (eg. by ACs 'cease'), possibly calculate normals and than have the simple decision (in DefaultMaterialFactory) for shaded objects:

  • hasNormals -> smooth shaded
  • !hasNormals -> flat shaded (the shader might calculate the surface normal from vertices)

flat shading

a.k.a. (per) fragment shading. The same color is set for all pixel of a fragment. The effective normal value is the average of the normals of the fragment vertices.

smooth shading

Smooth shading typically has the best visual ... The effective normal is interpolated between the vertex normals.

visual edges

Depending on the geometry edges should be visible (eg. a cube) or not visible (a sphere). Visual edges highly depend on the geometry and whether split shared vertices with different normals are used.

Visible edges can be gained with flat shading, even though the resulting fragment might be too dark or bright. This depends on the normal values and whether shared vertices are used. If shared vertices are split and the normals are fitting the fragment will be good looking. But in that case also smooth shading is an option.

Non visible edges is the typical result of smooth shading where the vertex normals are correct and no shared vertices are used.

Flat or smooth shading should be specified as part of the model, because only the model designer knowns how the normals were built.

Shader

Each platform seems to have its own special idea how to handle shader (at least ThreeJS and JME have) and which GLSL versions are supported. To avoid the need to have different shader for each platform we use an abstraction layer where the shader code contains keywords in capital letters, which are string replaced for each platform. JME has the special feature of naming custom uniform variables with a "m_" prefix.

Due to its binding to an old OpenGL version and some internal shader using GLSL 1.0, JME sticks to GLSL 1.0(1.2), while ThreeJS uses OpenGL ES 3.0.

So as a consequence, for now we can only have shader that consider these constraints. The following rules apply to shader:

  • IN/OUT instead of varying
  • no version macros
  • No integer literals for float operations (eg. use 1.0 instead of 1)
  • Uniform names should have prefix "u_" (helpful for string replacement in JME)
  • Initialize uniforms in the app, not the shader. Apparently JME has a problem with default values when uniforms are not set in all materials. And WebGL might report "'uniform' : cannot initialize this type of qualifier"
  • Don't use identifier that might be predefined, like 'uv'

In JME the orientation of normals visually appears not correct when transformed with g_NormalMatrix, which is intended to be the normalMatrix(transpose inverse of modelView matrix). Even with debugging the reason couldn't be revealed. The 'mirror' in JmeCamera seems not to be the reason. It's all quite strange. BTW: The 'normalMatrix' provided by ThreeJS appears correct.

Lighting

Custom shader need to be updated about light changes, eg. light might be moving in a scene. This could be done by the platform. ThreeJs for example requires multiple changes (additional uniforms) in the shader and probably using THREE.UniformsLib['lights'] for generating all the additional uniforms. But documentation is an issue. Eg. it's not clear whether light vectors are already transformed to view space when they are injected to the shader.

So the question is, should we use the platform or create a generic helper. The main challenge for a helper (RegisteredShaderMaterial.java) is to know when a light was moved.

Transparency

Transparency can be done with platform shader or with custom shader. Important is to put the object at the end of the rendering list in the platform to have background objects ready to shine through.

Transparency for textures might be related to transparent pixel in the texture only (like SokobanTarget?) or an alpha blending to all pixel. Currently there is no parameter to define this.

About

A cross platform 3D graphics meta engine for Java used for building several styles of maze games, most easy to be run in a browser.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published