From 2a8081b042b5fbb2925faa8fd80e418ae070bed0 Mon Sep 17 00:00:00 2001 From: Marco Cawthorne Date: Sun, 2 Oct 2022 17:10:54 -0700 Subject: [PATCH] Overhaul of the entire documentation. --- .gitignore | 1 + Documentation/Building.md | 58 ++++ Documentation/Contributing.md | 36 +++ Documentation/Filesystem.md | 69 ++++ doc/history => Documentation/History.md | 2 +- Documentation/Main.md | 68 ++++ Documentation/Materials/MatCommands.md | 4 + Documentation/Materials/MatGoldSrc.md | 258 +++++++++++++++ Documentation/Materials/MatOverview.md | 80 +++++ Documentation/Materials/MatShaders.md | 55 ++++ Documentation/Materials/MatVMap.md | 4 + Documentation/Materials/commands/alphafunc.md | 30 ++ Documentation/Materials/commands/alphagen.md | 23 ++ Documentation/Materials/commands/blendfunc.md | 142 +++++++++ Documentation/Materials/commands/cull.md | 45 +++ .../Materials/commands/deformvertexes.md | 140 +++++++++ Documentation/Materials/commands/depthfunc.md | 15 + .../Materials/commands/depthwrite.md | 13 + .../Materials/commands/diffusemap.md | 24 ++ Documentation/Materials/commands/fogparms.md | 42 +++ .../Materials/commands/fte_clutter.md | 13 + .../Materials/commands/fullbrightmap.md | 25 ++ .../Materials/commands/gl_blendmodes.jpg | Bin 0 -> 74730 bytes Documentation/Materials/commands/nomipmaps.md | 15 + Documentation/Materials/commands/nopicmip.md | 12 + Documentation/Materials/commands/normalmap.md | 27 ++ .../Materials/commands/polygonoffset.md | 15 + Documentation/Materials/commands/portal.md | 12 + Documentation/Materials/commands/program.md | 14 + .../Materials/commands/reflectcube.md | 20 ++ .../Materials/commands/reflectmask.md | 25 ++ Documentation/Materials/commands/rgbgen.md | 117 +++++++ Documentation/Materials/commands/skyparms.md | 52 +++ Documentation/Materials/commands/sort.md | 53 ++++ .../Materials/commands/specularmap.md | 22 ++ .../Materials/commands/surfaceparm.md | 296 ++++++++++++++++++ Documentation/Materials/commands/tcgen.md | 24 ++ Documentation/Materials/commands/tcmod.md | 132 ++++++++ .../Materials/commands/vmap_backmaterial.md | 21 ++ .../Materials/commands/vmap_backsplash.md | 16 + .../Materials/commands/vmap_basematerial.md | 14 + .../Materials/commands/vmap_bounce.md | 10 + .../Materials/commands/vmap_clonematerial.md | 12 + .../Materials/commands/vmap_invert.md | 15 + .../Materials/commands/vmap_lightimage.md | 16 + .../commands/vmap_lightmapfilterradius.md | 33 ++ .../commands/vmap_lightmapmergable.md | 14 + .../commands/vmap_lightmapsampleoffset.md | 13 + .../commands/vmap_lightmapsamplesize.md | 15 + .../Materials/commands/vmap_lightrgb.md | 12 + .../Materials/commands/vmap_lightsubdivide.md | 15 + .../Materials/commands/vmap_nodirty.md | 10 + .../Materials/commands/vmap_offset.md | 11 + .../Materials/commands/vmap_remapmaterial.md | 20 ++ .../Materials/commands/vmap_shadeangle.md | 11 + .../Materials/commands/vmap_surfacelight.md | 24 ++ .../commands/vmap_surfacelightdistance.md | 13 + .../Materials/commands/vmap_surfacemodel.md | 16 + .../Materials/commands/vmap_tesssize.md | 25 ++ Documentation/Models/VVM.md | 79 +++++ Documentation/Models/VVM_Tutorial.md | 150 +++++++++ doc/mods => Documentation/Mods.md | 23 +- Documentation/Sound/EAX.md | 12 + Documentation/Sound/EFX.md | 87 +++++ Documentation/Sound/SoundDefs.md | 86 +++++ Documentation/Sound/sounds_attenuation.png | Bin 0 -> 25516 bytes Documentation/Support.md | 18 ++ {doc => Documentation}/fte.svg | 0 {doc => Documentation}/idtech.svg | 0 Documentation/openal.svg | 85 +++++ Documentation/opengl.svg | 85 +++++ .../release-readme.txt | 2 +- Documentation/vulkan.svg | 116 +++++++ Doxyfile | 60 +++- README.md | 4 +- doc/about | 34 -- doc/building | 48 --- doc/contributing | 17 - doc/filesystem | 36 --- doc/hlmaterials | 57 ---- 80 files changed, 3097 insertions(+), 221 deletions(-) create mode 100644 Documentation/Building.md create mode 100644 Documentation/Contributing.md create mode 100644 Documentation/Filesystem.md rename doc/history => Documentation/History.md (99%) create mode 100644 Documentation/Main.md create mode 100644 Documentation/Materials/MatCommands.md create mode 100644 Documentation/Materials/MatGoldSrc.md create mode 100644 Documentation/Materials/MatOverview.md create mode 100644 Documentation/Materials/MatShaders.md create mode 100644 Documentation/Materials/MatVMap.md create mode 100644 Documentation/Materials/commands/alphafunc.md create mode 100644 Documentation/Materials/commands/alphagen.md create mode 100644 Documentation/Materials/commands/blendfunc.md create mode 100644 Documentation/Materials/commands/cull.md create mode 100644 Documentation/Materials/commands/deformvertexes.md create mode 100644 Documentation/Materials/commands/depthfunc.md create mode 100644 Documentation/Materials/commands/depthwrite.md create mode 100644 Documentation/Materials/commands/diffusemap.md create mode 100644 Documentation/Materials/commands/fogparms.md create mode 100644 Documentation/Materials/commands/fte_clutter.md create mode 100644 Documentation/Materials/commands/fullbrightmap.md create mode 100644 Documentation/Materials/commands/gl_blendmodes.jpg create mode 100644 Documentation/Materials/commands/nomipmaps.md create mode 100644 Documentation/Materials/commands/nopicmip.md create mode 100644 Documentation/Materials/commands/normalmap.md create mode 100644 Documentation/Materials/commands/polygonoffset.md create mode 100644 Documentation/Materials/commands/portal.md create mode 100644 Documentation/Materials/commands/program.md create mode 100644 Documentation/Materials/commands/reflectcube.md create mode 100644 Documentation/Materials/commands/reflectmask.md create mode 100644 Documentation/Materials/commands/rgbgen.md create mode 100644 Documentation/Materials/commands/skyparms.md create mode 100644 Documentation/Materials/commands/sort.md create mode 100644 Documentation/Materials/commands/specularmap.md create mode 100644 Documentation/Materials/commands/surfaceparm.md create mode 100644 Documentation/Materials/commands/tcgen.md create mode 100644 Documentation/Materials/commands/tcmod.md create mode 100644 Documentation/Materials/commands/vmap_backmaterial.md create mode 100644 Documentation/Materials/commands/vmap_backsplash.md create mode 100644 Documentation/Materials/commands/vmap_basematerial.md create mode 100644 Documentation/Materials/commands/vmap_bounce.md create mode 100644 Documentation/Materials/commands/vmap_clonematerial.md create mode 100644 Documentation/Materials/commands/vmap_invert.md create mode 100644 Documentation/Materials/commands/vmap_lightimage.md create mode 100644 Documentation/Materials/commands/vmap_lightmapfilterradius.md create mode 100644 Documentation/Materials/commands/vmap_lightmapmergable.md create mode 100644 Documentation/Materials/commands/vmap_lightmapsampleoffset.md create mode 100644 Documentation/Materials/commands/vmap_lightmapsamplesize.md create mode 100644 Documentation/Materials/commands/vmap_lightrgb.md create mode 100644 Documentation/Materials/commands/vmap_lightsubdivide.md create mode 100644 Documentation/Materials/commands/vmap_nodirty.md create mode 100644 Documentation/Materials/commands/vmap_offset.md create mode 100644 Documentation/Materials/commands/vmap_remapmaterial.md create mode 100644 Documentation/Materials/commands/vmap_shadeangle.md create mode 100644 Documentation/Materials/commands/vmap_surfacelight.md create mode 100644 Documentation/Materials/commands/vmap_surfacelightdistance.md create mode 100644 Documentation/Materials/commands/vmap_surfacemodel.md create mode 100644 Documentation/Materials/commands/vmap_tesssize.md create mode 100644 Documentation/Models/VVM.md create mode 100644 Documentation/Models/VVM_Tutorial.md rename doc/mods => Documentation/Mods.md (83%) create mode 100644 Documentation/Sound/EAX.md create mode 100644 Documentation/Sound/EFX.md create mode 100644 Documentation/Sound/SoundDefs.md create mode 100644 Documentation/Sound/sounds_attenuation.png create mode 100644 Documentation/Support.md rename {doc => Documentation}/fte.svg (100%) rename {doc => Documentation}/idtech.svg (100%) create mode 100644 Documentation/openal.svg create mode 100644 Documentation/opengl.svg rename doc/release-readme => Documentation/release-readme.txt (91%) create mode 100644 Documentation/vulkan.svg delete mode 100644 doc/about delete mode 100644 doc/building delete mode 100644 doc/contributing delete mode 100644 doc/filesystem delete mode 100644 doc/hlmaterials diff --git a/.gitignore b/.gitignore index eafb8909..fbb895bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ *~ +Documentation/html/ darkstar/ noffice/ pvk/ diff --git a/Documentation/Building.md b/Documentation/Building.md new file mode 100644 index 00000000..623e040f --- /dev/null +++ b/Documentation/Building.md @@ -0,0 +1,58 @@ +# Building + +## Building the Engine {#engine} +The **build_engine.sh** will do that for you. It will still ask you to have at least +a certain amount of dependencies installed (such as the **GCC**, **GNU make** and the **X11/SDL** +headers for your platform. + +### Dependencies +* subversion +* gmake +* gcc +* mesa-libGL-devel +* libgnutls-devel +* libopenal-devel +* libX11-devel +* libXcursor-devel +* libXrandr-devel +* libSDL2-devel (only if you pass BUILD_SDL2=1 in build.cfg) + +## Building the Level Editor {#editor} +Handled by **build_editor.sh**. + +### Dependencies +* gcc-c++ +* gtk2-devel +* gtkglext-devel +* libxml2-devel +* libjpeg8-devel +* minizip-devel + +## Building Game-Logic {#game} +You can build the game source tree with **build_game.sh**. + +The script also takes a parameter. If you specify: + +`./build_game.sh valve` + +then it will only build the game-logic for the `valve` directory. + +Otherwise, it will iterate through all of the game directories, look for a Makefile and build its default target. + +It'll try use the **fteqcc** binary that's in the *./bin/* directory. +So make sure to build run **build_engine.sh** first. +Some distributions may carry the **fteqcc** compiler, but it usually is a very ancient version +that's probably not going to build any of this. + +## Custom Configuration {#config} +There's a **build.cfg** file with which you can tweak build parameters of the various **build_** scripts. +For example, this is where you select between X11 and SDL2 builds. There you can specify which engine revision +you want to build and also which plugins you want to build along with it. +It's well commented, so I encourage you to check it out. However on some platforms, changing those settings +might introduce additional setup/dependency steps. + +## Additional Information {#notes} +The game-logic is written in QuakeC, it is thus platform and architecture independent. + +You do not need to rebuild the logic for each and every platform. +The results will be identical. diff --git a/Documentation/Contributing.md b/Documentation/Contributing.md new file mode 100644 index 00000000..e062c1b7 --- /dev/null +++ b/Documentation/Contributing.md @@ -0,0 +1,36 @@ +# Contributing + +If you'd like to contribute code: Awesome. +There's just 2 very short requirements. + +## Rule #1 + +The code must be yours. + +It's simple: +* Do not take code that is by anyone else that is not you. + + +## Rule #2 +No submission or decompilation of third-party code. This is important if you want to recreate behaviour from another game. + +* We do not decompile - we reverse-engineer by trial and error. + +This is generally referred to as 'clean-room engineering'. +Most of the behaviour is rather predictable, other is not. + +This is why some of the parts of Nuclide, which emulate entities/frameworks +from other games may feel a bit off. It's because it's all implemented by eye. + +# Other notes +Game specific features need to be put into game-specific directories. + +Unless other games benefit from this, keep it seperate to one of the game +sub-directories. This usually means it'll be part of another git repository too. + +That's about it! + +# Saying Thanks +If you want to solely say thanks, the main developer of Nuclide **loves** receiving E-Mail. You can contact him at and let him know your thoughts. + +If you'd like to donate to him via [PayPal](https://paypal.me/eukara) or [Patreon](https://www.patreon.com/eukara) you can do that too. That money pays for a lot of free services he provides that come with both [FTEQW](https://www.fteqw.org/) and **Nuclide**. \ No newline at end of file diff --git a/Documentation/Filesystem.md b/Documentation/Filesystem.md new file mode 100644 index 00000000..64e5b79e --- /dev/null +++ b/Documentation/Filesystem.md @@ -0,0 +1,69 @@ +# Filesystem + +## Launching + +The `nuclide` shell script is the launcher. + +It sets **$PATH** to include the directory `bin/` which contains the engine that +you've built with `build_engine.sh`. + +When nuclide is run and executes the engine, it'll first read `default.fmf` which +is a manifest file the engine reads. It is updated occasionally. + +It defines which directories to mount in the virtual filesystem of the engine +and has a document entirely dedicated to itself. For that please read: +``` + src/engine/specs/fte_manifests.txt +``` + +## Loading Games + +On its own, Nuclide launches the game directory `base/`, unless you tell it otherwise: +``` +./nuclide -game mygame +``` + +Will load `mygame/` instead of `base/`. + +You can also load multiple additional directories in a specific order by specifying +multiple `-game` command-line arguments. + +It will still load the other `BASEGAME` entries listed in the default.fmf. +You can even load your own manifest file over default.fmf, by passing + +``` + ./nuclide -manifest mymanifest.fmf +``` + +## Virtual-Filesystem + +When a game is mounted, we're either looking for **loose files** (loaded last), or +**archives** the engine supports. + +The Quake .pak archive format, or zip archives with the pk3 and pk4 extensions are supported. +Upon initializing the filesystem they are enumerated alphabetically and then loaded in order. + +Directories with the .pk3dir extensions are treated as if they were .pk3 archives. +The editor also supports .pk3dir directories. + +Once the game has loaded a game directory, it'll load the persistent menu.dat into our QuakeC +virtual machine. + +It' always running, you can make your own by modifying `src/menu-fn/`, `src/menu-vgui/` +or writing your own module. + +## Archives + +Protected archives always start have the prefix **pak**[...] and cannot be downloaded by connecting +to a server that has them. +**Use this for any copyrighted data.** + +When you spawn a map, you create a server which will distribute its own client-side game-logic. + +It's advised that you do not pack **csprogs.dat** and **progs.dat** into a protected archive. + + +## Nuclide specific formats +Nuclide contains many custom definition files that are not engine specific. +`.efx`, `.font`, `.sndshd` and `.way` to name a few. +The game-logic mostly handles them and can thus be, in theory, extended by you. \ No newline at end of file diff --git a/doc/history b/Documentation/History.md similarity index 99% rename from doc/history rename to Documentation/History.md index cb6558a2..76cac88f 100644 --- a/doc/history +++ b/Documentation/History.md @@ -1,4 +1,4 @@ -NUCLIDE PROJECT HISTORY +# History In November 2016 eukara started work on 'OpenCS', a project which focused solely on creating a clean-room implementation of Counter-Strike 1.5 in QuakeC diff --git a/Documentation/Main.md b/Documentation/Main.md new file mode 100644 index 00000000..7985f2ec --- /dev/null +++ b/Documentation/Main.md @@ -0,0 +1,68 @@ +# Overview + +Welcome to the documentation for Nuclide. + +This is a software development kit and development environment created by [Vera Visions, L.L.C.](https://www.vera-visions.com/) + +## What this project is {#what} + +The Nuclide project produces a freely available game-logic component and +development platform on top of FTEQW; which is the engine we happen to use. +The goal is to create a modern research base for new advancements, as well +as to have a stable base with a decent API for making games. + +- Client-side predicted movement and inputs for things like weapons-systems +- Lots of well documented objects to use in level formats supported by FTEQW +- Reference implementations for a lot of features exlusive to FTEQW compared + to other idTech engines +- Designed to be familar to developers who're used to GoldSrc and Source engine + projects + +### 1. Why might I want to use it? {#why} + +You want to develop a game using a lot of complex and well-tested objects +which might be tedious to implement on your own. + +You want to run or make modifications for a game using Nuclide and need full +control over what you can do. + +### 2. How free is Nuclide? {#license} + +Everything in Nuclide is **free software**. The copyright terms for the game-logic are +very permitting. Nuclide does not use the GPL as a point of reference, it +instead uses a ISC-like license. This means you can use, copy, modify and +distribute the code and work resulting from it for any purpose. + +Please read the very short 'LICENSE' document for details. + +### 3. What are the alternatives? {#alternatives} + +Implementing systems such as prediction, complex map objects and entities on +your own or licensing another engine such as [Source](https://partner.steamgames.com/doc/sdk/uploading/distributing_source_engine) that ships with its own **Source SDK Base**. + +## Getting started {#prerequisites} + +First of all, you want to be sure this is what you want to get into. +If you do posess a basic knowledge of the following: + +- The C programming language +- Debugging a native application +- Makefiles +- Compiling your own binaries +- Concept of public and private APIs + +Then you will have a good time. +We strive to keep the codebase portable and conform to open standards wherever possible. +This means that if you develop on Windows, you probably want to install something like [Cygwin](https://www.cygwin.com/) to make this bearable. + +Please don't ask us how to learn UNIX/Cygwin. + +**This is a development kit and a development environment. This is not a game.** + +## Actually getting started {#how} + +You clone the Nuclide git repository first. There's multiple places to get it, one such place may be GitHub: + +`git clone https://github.com/veravisions/nuclide` + +And then you can [get started on building the engine and the rest of the toolchain](Building.md). \ No newline at end of file diff --git a/Documentation/Materials/MatCommands.md b/Documentation/Materials/MatCommands.md new file mode 100644 index 00000000..67f49092 --- /dev/null +++ b/Documentation/Materials/MatCommands.md @@ -0,0 +1,4 @@ +# Materials: Commands {#mat_commands} + +These are all the important material commands that affect the renderer in some shape or form. +[For material commands that affect the compiling process, look over here.](MatVMap.md) \ No newline at end of file diff --git a/Documentation/Materials/MatGoldSrc.md b/Documentation/Materials/MatGoldSrc.md new file mode 100644 index 00000000..dda53ad3 --- /dev/null +++ b/Documentation/Materials/MatGoldSrc.md @@ -0,0 +1,258 @@ +# Materials: GoldSrc +Material definitions handle what footsteps and what impact effects are played against each texture in the game-world. +So when you walk on dirt, you can hear the difference; or when you shoot wood with a gun it will have splinters spawn instead of black bits and smoke. + +*Note:* While Nuclide supports this for legacy BSP files, you are encouraged to define the material of a texture via the [surfaceparm](MatCommands.md) command in a **Material** instead. + +## Details +Usually, a game in the GoldSrc engine would provide a `sound/materials.txt` file, where each line defines the properties for a single (or a series of) textures. + +For example: + +``` +C CONCRETEWALL +``` + +Will make any surface **concrete** that starts with the name `CONCRETEWALL`. In vanilla Half-Life, the second argument (name) can only be 12 characters long. + +It will also only compare the length of characters of the name above. So for example if you were to define: + +``` +C CONC +``` + +Then `CONCRETEWALL` would still be marked as concrete, and any other texture that starts with `CONC[...]`. + +## Community Changes and Additions + +In stock GoldSrc, the **materials.txt** can be overwritten by a modification, and users could customize the file on the client-side only for themselves. This means there was no map specific materials, and mods could not inherit Half-Life's materials, so mods would always have to manage a nearly duplicate file if they desired custom texture sounds. + +A few mods tried to remedy this problem, the following below is methods +documented so far: + +* maps/MAPNAME.mat +> Introduced in The Wastes (2003) + +* maps/MAPNAME_materials.txt +> Convention by Andrew Lucas, creator of Trinity SDK, modeled after +> MAPNAME_details.txt + +* materials_file `PATH/FILE.txt` via the `worldspawn` entity +> Introduced in Sven Co-op 5.0 + +All these methods are supported by Nuclide, as one goal is to implement +conventions by not only Valve but the community as well. + +In addition Nuclide has also implemented a way of giving modifications +their own inheritable materials file: + +``` +sounds/materials_UNIQUENAME.txt +``` + +The idea here is that any mod or even map pack can include ONLY the textures +used, and no longer will anyone have to manage a near-clone of materials.txt + +For repackaging or modding purposes, if you desire to give your map custom +sound definitions, we recommend **The Wastes** its method for individual maps, while +the Nuclide method should be used for Mods or Map Packs. We find these to be +the most clean and efficient way. + +**NOTE**: We recommend only using materials text files for GoldSrc related modding +purposes. It is inefficient for modern projects as there are much better +standards already supported in Nuclide. Keep in mind, it takes memory to load +big text files, and lots of text files adds up over play sessions. + +# Material List +In Nuclide, this is the currently supported list of material IDs: + +| Material ID | Material Name | +|-------------|---------------| +| B | Bloody Flesh | +| C | Concrete | +| D | Dirt | +| F | Flesh | +| G | Grate | +| H | Alien | +| K | Snow | +| M | Metal | +| N | Sand | +| O | Foliage | +| P | Computer | +| S | Slosh | +| T | Tile | +| V | Vent | +| W | Wood | +| Y | Glass | + + +## Game differences +Listed below are definitions for various games and mods. Only the changes and additions are listed since the rest are identical. + +The * indicates these definitions are not implemented in Nuclide + +## GoldSrc +### Arrangement +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| B | Blue Texture | +| N | Snow | +| R | Red Texture | +| U | Slime | +| X | Yellow Texture | +| Z | Black Texture | + +### Counter-Strike +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| N | Snow | +| X | Grass | +*Afraid of Monsters: DC, Natural Selection, and Snow War uses the same definitions.* + +### Cry of Fear +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| B | Random Twig Snap | +| H | Paper | +| I | Mud | +| O | Sand | +| P | Snow | +| R | Gravel | +| U | Grass | +| Y | Broken Glass | +| Z | Carpet | + +### Gunman Chronicles +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| G | Wood | +| T | Rock | +| V | Sand | +| W | Unknown/Unused? | + +### Firearms +| **Material ID** | **Material Name** | +|-------------------|-----------------------------------------| +| B | Sandbag | +| N | Snow | +| U | No impact or decals, just smoke effects | + +### Heart of Evil +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| U | Mud | + +### Hostile Intent +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| A | Sand | +| E | Foliage | +| N | Snow | +| R | Carpet | +| U | Mud | +| Z | Grass | + +### Household DEATH! +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| H | Wood (Creaky) | +| I | Grass | + +### Night at the Office +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| T | Carpet | + +### Opposing Force +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| O | Snow | +*Science and Industry uses the same definitions as Opposing Force.* + +### Poke646 +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| M | Metal/Grate | +| T | Wood/Tile | +| G | Carpet/Grass | + +### Wasteland Half-Life +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| B | Barrel | +| I | Sand | +| N | Tinroof | +| R | Rust | +| U | Drywall | +*The Wastes uses the same definitions.* + +## Source +While Source has materials describing a surface with its **$surfaceprop** command, the GoldSrc way of describing materials with a 1-character symbol is still used to define which impact effect to use. + +So be aware that a Source engine game may actually have more surface materials than this, these are solely the IDs associated with impact effects. + +### Alien Swarm +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| 11 | Steam Pipe | + +Alien Swarm: Reactive Drop uses the same definitions. + +### Counter-Strike: Global Offensive +| **Material ID** | **Material Name** | +|-------------------|---------------------------| +| 11 | Mud | +| 12 | Sand Barrel | +| 13 | Sand Barrel (Penetration) | +| 14 | Metal Shield | + +### Half-Life 2 +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| A | Antlion | +| B | Flesh (Bloody) | +| H | Flesh (Antlion) | +| K | Snow | +| L | Plastic | +| N | Sand | +| I | Clip | +| O | Foliage | +| X | Fake | +| - | No Decal | + +### Half-Life 2: Episode 2 +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| E | Antlion Egg Sacks | +| Z | Adviser Shield | + +### Insurgency +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| A | Fruit | + +*Day of Infamy uses the same definitions.* + +### Left 4 Dead +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| J | Grass | +| K | Mud | +| Q | Asphalt | +| R | Brick | +| U | Cardboard | +| 1 | Clay | +| 2 | Plaster | +| 3 | Rock | +| 4 | Rubber | +| 5 | Sheetrock | +| 6 | Cloth | +| 7 | Carpet | +| 8 | Paper | +| 9 | Upholstery | +| 10 | Puddle | + +### Portal 2 +| **Material ID** | **Material Name** | +|-------------------|---------------------| +| R | Reflective | diff --git a/Documentation/Materials/MatOverview.md b/Documentation/Materials/MatOverview.md new file mode 100644 index 00000000..bf8fd288 --- /dev/null +++ b/Documentation/Materials/MatOverview.md @@ -0,0 +1,80 @@ +# Materials + +**Materials**, also formerly known **Q3 shaders** or wrongly referred to as just **shaders** are scripts that define texture and surface properties. + +## History + +In the idTech series of engines, **id Tech 3/Quake III Arena** was the first to introduce such a system, wrongly referred to back then as **shaders**. This was before vertex and fragment shaders were commonplace in the video-game asset pipeline. + +They effectively merged the Quake texture-prefix hacks and Quake II .wal surface flags into one plain text format. + +Starting with **id Tech 4/Doom III** in 2004, the name of **shader** was changed to **material** to avoid confusion with proper GPU oriented vertex and fragment shaders. + +FTE has since coupled the old 'shader' syntax with proper GPU shaders using the [program](MatCommands.md) material command. + +## What is a Material? +Materials are short text scripts that define the properties of a surface as it appears and functions in a game world (or compatible editing tool). By convention, the documents that contain these scripts usually has the same name as the texture set which contains the textures being modified (e.g; base, hell, castle, etc,). Several specific materials have also been created to handle special cases, like liquids, sky and special effects. + +## Usage + +When the engine looks for a texture, it will look for a material file in the file-tree first, then attempt to 'make one up' if only an image file is present. A rendereable surface will **always** have a material. The question is whether you provide it, or if the engine has to automatically generate one internally. + +The file extension for materials in Nuclide is .mat. There are two ways of defining materials: + +* A large .shader script file containing multiple materials inside the **./scripts/** folder (not recommended) +* One small .mat file with the same path as the texture. E.g: **models/weapons/handcannon/w_handcannon/w_handcannon.mat** handles **models/weapons/handcannon/w_handcannon/w_handcannon.tga** + +A material file consists of a series of surface attributes (global scope) and rendering instructions formatted within braces ("{" and "}"). Below you can see a simple example of syntax and format for a single process, including the VMAP keywords or 'Surface Parameters', which follow the first bracket and a single bracketed 'stage': + +``` + // Vera Visions Material + { + diffusemap textures/common/lava.tga + vmap_tessSize 64 + { + program unlit + blendFunc add + } + } +``` + +The first line is a simple comment. All the official textures are marked that way. + +`diffusemap` defines a texture sampler to associate with the material, it'll also be used by the real-time lights to render an impacted surface, hence why it's defined at a global scope. Other materials may need to be aware of it. The compiler also uses it to compute radiosity. + +`vmap_tessSize` will tell the BSP compiler to tesselate the surface in a specific way. This affects the entire surface and not just a single rendering stage, hence why it's defined at a global scope. + +We then follow into the territory of defining specific rendering instructions within braces ("{" and "}"), most of the time that's not needed if we're defining a simple, opaque material. However in this case we want to make sure that we have 1 rendering stage that blends additively. Things such as real-time lights and the BSP compiler ignore these additional stages entirely. + +`program unlit` will tell it not to be affected by lighting and just use a simple fullbright shader. + +`blendFunc add` will now tell the renderer that this surface is no longer opaque and will be blended with whatever equation will result in an additive blend. + +[You can read more about those commands in detail right here.](MatCommands.md) + +## Engine generated materials +If no material definition for a surface is present, the engine will create an internal one. +It's generally a primitive material using the internal **defaultwall** shader if it's a world texture, the **defaultskin** texture if it's a model, or a **default2d** if it's a HUD element. + +## Legacy materials + +You can support old-style Q3A materials alongside modern shader oriented ones. +``` + { + if $programs + program vertexlit + diffusemap "models/weapons/handcannon/w_handcannon.dds" + normalmap "models/weapons/handcannon/w_handcannon_normal.dds" + else + { + map "models/weapons/handcannon/w_handcannon.tga" + rgbGen lightingDiffuse + } + endif + } +``` +Here everything inside the **if $programs** block will only ever load if it's capable of handling the GLSL. +So if you have a card only performing well under the GL 1.X pipeline, the **else** path will take priority. +This means less graphical fidelity, but generally better performance on those old chipsets. + +All of Quake III's shading language is supported in the else block. diff --git a/Documentation/Materials/MatShaders.md b/Documentation/Materials/MatShaders.md new file mode 100644 index 00000000..5105d1cb --- /dev/null +++ b/Documentation/Materials/MatShaders.md @@ -0,0 +1,55 @@ +# Materials: Shaders +Shaders are referring to GPU-oriented pieces of a program, performing shading and rendering related functions instead of letting the engine handle it. + +In **FTEQW** you can specify a custom GLSL or HLSL shader using the [program](MatCommands.md) command inside a [Material](MatOverview.md). + +## Example Shader +This is a primitive shader file. It includes the vertex and fragment program. + +It will respond to the [diffusemap](MatCommands.md) only, which is loaded +into the **d_f** variable. It can be modified from that point onwards. +The commented out line will turn all of the output red. + +Give it a try, or something! + +``` +//======= Copyright (c) 2015-2020 Vera Visions LLC. All rights reserved. ======= +// +// Purpose: +// +// Example surface +//============================================================================== + +!!ver 110 +!!samps diffuse + +#include "sys/defs.h" + +varying vec2 tex_c; + +#ifdef VERTEX_SHADER +void main () +{ + tex_c = v_texcoord; /* get our texture coordinates, which we'll use for the texture2D command */ + gl_Position = ftetransform(); /* place vertex into the place FTE wants us to put them at */ +} +#endif + +#ifdef FRAGMENT_SHADER +void main () +{ + vec4 d_f = texture2D(s_diffuse, tex_c); /* load the fragment from our diffusemap sample */ + // d_f.rgb = vec3(1.0, 0.0, 0.0); /* turns out fragment (aka pixel) red */ + gl_FragColor = d_f; /* final pixel output is that of d_f */ +} +#endif +``` + +## Dissecting GLSL shaders +When we pass `program ` in our [Material](MatOverview.md), the engine will load `glsl/.glsl` to handle the material for us. + +The shader in question needs to define a `main` function for both a vertex and a fragment shader. That's what the **ifdef**s are for in the above example. + +You can not have separate files handle vertex/fragment programs, unlike in **id Tech 4/Doom III**. + +At some point in the `main` function, we do have to set `gl_Position` and `gl_FragColor` respectively. Those can not be undefined. diff --git a/Documentation/Materials/MatVMap.md b/Documentation/Materials/MatVMap.md new file mode 100644 index 00000000..4a2f9237 --- /dev/null +++ b/Documentation/Materials/MatVMap.md @@ -0,0 +1,4 @@ +# Materials: VMap Commands {#mat_vmap} + +The following commands are alter specific behaviour during the **BSP** compilation process. +These are not respected by the engine, the renderer or the game-logic. \ No newline at end of file diff --git a/Documentation/Materials/commands/alphafunc.md b/Documentation/Materials/commands/alphafunc.md new file mode 100644 index 00000000..f276c9c6 --- /dev/null +++ b/Documentation/Materials/commands/alphafunc.md @@ -0,0 +1,30 @@ +# Materials: Commands {#mat_commands} +## alphafunc +### Syntax {#syntax} + +**alphaFunc ** + +### Overview {#overview} + +Determines the alpha test function used when rendering this surface. + +Valid values are **GT0**, **LT128**, and **GE128**. These correspond to +**"GREATER THAN 0"**, **"LESS THAN 128"**, and **"GREATER THAN OR EQUAL +TO 128"**. + +This function is used when determining if a pixel should be written to +the frame-buffer. For example, if **GT0** is specified, the only the +portions of the texture map with corresponding alpha values greater than +zero will be written to the framebuffer. **By default alpha testing is +disabled.** + +Both alpha testing and normal [alpha +blending](blendFunc) can be used to get +textures that have see-through parts. The difference is that +**alphaFunc** is an all-or-nothing test, while blending smoothly blends +between opaque and translucent at pixel edges. + +Alpha test can also be used with +[depthWrite](depthWrite), allowing other +effects to be conditionally layered on top of just the opaque pixels by +setting [depthFunc](depthFunc) to equal. \ No newline at end of file diff --git a/Documentation/Materials/commands/alphagen.md b/Documentation/Materials/commands/alphagen.md new file mode 100644 index 00000000..8f671a93 --- /dev/null +++ b/Documentation/Materials/commands/alphagen.md @@ -0,0 +1,23 @@ +# Materials: Commands {#mat_commands} +## alphagen +### Syntax {#syntax} + +**alphaGen ** + +### Overview {#overview} + +The alpha channel can be specified like the [rgb +channels](rgbGen). If not specified, it +defaults to 1.0. + +### Functions {#functions} + +#### portal {#portal} + +This rendering stage keyword is used in conjunction with the +[surfaceparm](surfaceparm) keyword +portal. The function accomplishes the "fade" that causes the scene in +the portal to fade from view. Specifically, it means "Generate alpha +values based on the distance from the viewer to the portal." + +Use `alphaGen portal` on the last rendering pass. \ No newline at end of file diff --git a/Documentation/Materials/commands/blendfunc.md b/Documentation/Materials/commands/blendfunc.md new file mode 100644 index 00000000..4cb317ad --- /dev/null +++ b/Documentation/Materials/commands/blendfunc.md @@ -0,0 +1,142 @@ +# Materials: Commands {#mat_commands} +## blendfunc +![OpenGL blending cheat-sheet](gl_blendmodes.jpg "from zanir.wz.cz/?p=60") + +### Syntax {#syntax} + +**blendFunc ** + +**blendFunc ** + +### Overview {#overview} + +Blend functions are the keyword commands that tell the renderer how +graphic layers are to be mixed together. + +### Usage {#usage} + +#### Simplified blend functions {#simplified_blend_functions} + +The most common blend functions are set up here as simple commands, and +should be used unless you really know what you are doing. + +##### add {#add} + +This is a shorthand command for `blendFunc GL_ONE GL_ONE`. Effects like +fire and energy are additive. + +##### filter {#filter} + +This is a shorthand command that can be substituted for either +`blendFunc GL_DST_COLOR GL_ZERO` or `blendFunc GL_ZERO GL_SRC_COLOR`. A +filter will always result in darker pixels than what is behind it, but +it can also remove color selectively. Lightmaps are filters. + +##### blend {#blend} + +Shorthand for `blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA`. This is +conventional transparency, where part of the background is mixed with +part of the texture. + +#### Explicit blend functions {#explicit_blend_functions} + +Getting a handle on this concept is absolutely key to understanding all +shader manipulation of graphics. + +BlendFunc or "Blend Function" is the equation at the core of processing +shader graphics. The formula reads as follows: + +` [Source *``] + [Destination * ``]` + +**Source** is usually the RGB color data in a texture file. + +**Destination** is the color data currently existing in the frame +buffer. + +Rather than think of the entire texture as a whole, it maybe easier to +think of the number values that correspond to a single pixel, because +that is essentially what the computer is processing: One pixel of the +bitmap at a time. + +The process for calculating the final look of a texture in place in the +game world begins with the pre-calculated lightmap for the area where +the texture will be located. This data is in the frame buffer. That is +to say, it is the initial data in the Destination. In an unmanipulated +texture (i.e. one without a special shader script), color information +from the texture is combined with the lightmap. In a shader-modified +texture, the $lightmap stage must be present for the lightmap to be +included in the calculation of the final texture appearance. + +Each pass or "stage" of blending is combined (in a cumulative manner) +with the color data passed onto it by the previous stage. How that data +combines together depends on the values chosen for the Source Blends and +Destination Blends at each stage. Remember it's numbers that are being +mathematically combined together that are ultimately interpreted as +colors. + +A general rule is that any Source Blend other than **GL_ONE** (or +**GL_SRC_ALPHA** where the alpha channel is entirely white) will cause +the Source to become darker. + +##### Source Blend {#source_blend} + +The following values are valid for the Source Blend part of the +equation. + +- **GL_ONE** This is the value 1. When multiplied by the Source, the + value stays the same the value of the color information does not + change. +- **GL_ZERO** This is the value 0. When multiplied by the Source, all + RGB data in the Source becomes Zero (essentially black). +- **GL_DST_COLOR** This is the value of color data currently in the + Destination (frame buffer). The value of that information depends on + the information supplied by previous stages. +- **GL_ONE_MINUS_DST_COLOR** This is nearly the same as + **GL_DST_COLOR** except that the value for each component color is + inverted by subtracting it from one. (,i.e. R = 1.0 - DST.R, G = + 1.0 - DST.G, B = 1.0 - DST.B, etc.) +- **GL_SRC_ALPHA** The TGA file being used for the Source data must + have an alpha channel in addition to its RGB channels (for a total + of four channels). The alpha channel is an 8-bit black and white + only channel. An entirely white alpha channel will not darken the + Source. +- **GL_ONE_MINUS_SRC_ALPHA** This is the same as **GL_SRC_ALPHA** + except that the value in the alpha channel is inverted by + subtracting it from one.(i.e. A=1.0 - SRC.A) + +##### Destination Blend {#destination_blend} + +The following values are valid for the Destination Blend part of the +equation. + +- **GL_ONE** This is the value 1. When multiplied by the Destination, + the value stays the same the value of the color information does not + change. +- **GL_ZERO** This is the value 0. When multiplied by the Destination, + all RGB data in the Destination becomes Zero (essentially black). +- **GL_SRC_COLOR** This is the value of color data currently in the + Source (which is the texture being manipulated here). +- **GL_ONE_MINUS_SRC_COLOR** This is the value of color data currently + in Source, but subtracted from one(i.e. inverted). +- **GL_SRC_ALPHA The TGA** file being used for the Source data must + have an alpha channel in addition to its RGB channels (four a total + of four channels). The alpha channel is an 8-bit black and white + only channel. An entirely white alpha channel will not darken the + Source. +- **GL_ONE_MINUS_SRC_ALPHA** This is the same as **GL_SRC_ALPHA** + except that the value in the alpha channel is inverted by + subtracting it from one. (i.e. A=1.0 - SRC.A). + +##### Doing the Math: The Final Result {#doing_the_math_the_final_result} + +The product of the Source side of the equation is added to the product +of the Destination side of the equation. The sum is then placed into the +frame buffer to become the Destination information for the next stage. +Ultimately, the equation creates a modified color value that is used by +other functions to define what happens in the texture when it is +displayed in the game world. + +#### Default Blend Function {#default_blend_function} + +If no blendFunc is specified then no blending will take place. That's +just a fact of life. \ No newline at end of file diff --git a/Documentation/Materials/commands/cull.md b/Documentation/Materials/commands/cull.md new file mode 100644 index 00000000..b5c33550 --- /dev/null +++ b/Documentation/Materials/commands/cull.md @@ -0,0 +1,45 @@ +# Materials: Commands {#mat_commands} +## cull +### Syntax {#syntax} + +**cull ** + +### Overview {#overview} + +Every surface of a polygon has two sides, a front and a back. Typically, +we only see the front or "out" side. For example, a solid block you only +show the front side. In many applications we see both. For example, in +water, you can see both front and a back. The same is true for things +like grates and screens. + +To "cull" means to remove. The value parameter determines the type of +face culling to apply. The default value is cull front if this keyword +is not specified. However for items that should be inverted then the +value back should be used. To disable culling, the value disable or none +should be used. Only one cull instruction can be set for the material. + +### Sides {#sides} + +#### front {#front} + +**This is the default value.** The front or "outside" of the polygon is +not drawn in the world. It is used if the keyword `"cull "` appears in +the content instructions without a value or if the keyword cull +does not appear at all in the shader. + +#### back {#back} + +Cull back removes the back or "inside" of a polygon from being drawn in +the world. + +#### none {#none} + +Neither side of the polygon is removed. Both sides are drawn in the +game. Very useful for making panels or barriers that have no depth, such +as grates, screens, metal wire fences and so on and for liquid volumes +that the player can see from within. Also used for energy fields, +sprites, and weapon effects (e.g. plasma). + +**Design Notes:** For things like grates and screens, put the texture +with the cull none property on one face only. On the other faces, use a +non-drawing texture. \ No newline at end of file diff --git a/Documentation/Materials/commands/deformvertexes.md b/Documentation/Materials/commands/deformvertexes.md new file mode 100644 index 00000000..e760b532 --- /dev/null +++ b/Documentation/Materials/commands/deformvertexes.md @@ -0,0 +1,140 @@ +# Materials: Commands {#mat_commands} +## deformvertexes +### Syntax {#syntax} + +**deformVertexes ** + +### Overview {#overview} + +This command performs a general deformation on the surface's vertexes, +changing the actual shape of the surface before drawing the shader +passes. You can stack multiple deformVertexes commands to modify +positions in more complex ways, making an object move in two dimensions, +for instance. + +### Functions {#functions} + +#### wave {#wave} + +Designed for water surfaces, modifying the values differently at each +point. + +It accepts the standard **wave** functions of the type: **sin**, +**triangle**, **square**, **sawtooth** or **inversesawtooth**. + +The "div" parameter is used to control the wave "spread" - a value equal +to the [tessSize](vmap_tessSize) of the +surface is a good default value. + +#### normal <amplitude ~0.1-~0.5> <frequency ~1.0-~4.0> {#normal_amplitude_0.1_0.5_frequency_1.0_4.0} + +This deformation affects the normals of a vertex without actually moving +it, which will effect later material options like lighting and +especially environment mapping. If the material stages don't use normals +in any of their calculations, there will be no visible effect. + +**Design Notes:** Putting values of 0.1 to 0.5 in Amplitude and 1.0 to +4.0 in the Frequency can produce some satisfying results. Some things +that have been done with it: A small fluttering bat, falling leaves, +rain, flags. + +#### bulge {#bulge} + +This forces a bulge to move along the given s and t directions. Designed +for use on curved pipes. + +#### move {#move} + +This keyword is used to make a brush, curve patch or model appear to +move together as a unit. The **** **** and **** values are the +distance and direction in game units the object appears to move relative +to it's point of origin in the map. + +The ** ** and **** values are the +same as found in other wave form manipulations. + +The product of the function modifies the values x, y, and z.Therefore, +if you have an amplitude of 5 and an x value of 2, the object will +travel 10 units from its point of origin along the x axis. This results +in a total of 20 units of motion along the x axis, since the amplitude +is the variation both above and below the base. + +It must be noted that an object made with this material does not +actually change position, it only appears to. + +**Design Note**: If an object is made up of surfaces with different +materials, all must have matching deformVertexes move values or **the +object will appear to tear itself apart!** + +#### autosprite {#autosprite} + +This function can be used to make any given triangle quad (pair of +triangles that form a square rectangle) automatically behave like a +sprite without having to make it a separate entity. + +This means that the "sprite" on which the texture is placed will rotate +to always appear at right angles to the player's view as a sprite would. +Any four-sided brush side, flat patch, or pair of triangles in a model +can have the autosprite effect on it. The brush face containing a +texture with this material keyword must be square. + +**Design Note**: This is best used on objects that would appear the same +regardless of viewing angle. An example might be a glowing light flare. + +#### autosprite2 {#autosprite2} + +Is a slightly modified "sprite" that only rotates around the middle of +its longest axis. + +This allows you to make a pillar of fire that you can walk around, or an +energy beam stretched across the room. + +### Notes {#notes} + +Specific parameter definitions for deform keywords: + +#### {#section} + +This is roughly defined as the size of the waves that occur. It is +measured in game units. Smaller values create agreater density of +smaller wave forms occurring in a given area. Larger values create a +lesser density of waves, or otherwise put, the appearance of larger +waves. To look correct this value should closely correspond to the value +(in pixels) set for +[tessSize](vmap_tessSize) of the texture. +A value of 100.0 is a good default value (which means your +[tessSize](vmap_tessSize) should be close +to that for things tolook "wavelike"). + +#### {#section_1} + +This is the type of wave form being created. **sin** stands for sine +wave, a regular smoothly flowing wave. **triangle** is a wave with a +sharp ascent and a sharp decay. It will make a choppy looking wave +forms. A **square** wave is simply on or off for the period of the +frequency with no in between. The **sawtooth** wave has the ascent of a +triangle wave, but has the decay cut off sharply like a square wave. An +**inversesawtooth** wave reverses this. + +#### {#section_2} + +This is the distance, in game units that the apparent surface of the +texture is displaced from the actual surface of the brush as placed in +the editor. A positive value appears above the brush surface. A negative +value appears below the brush surface. + +#### {#section_3} + +The distance that the deformation moves away from the base value. See +Wave Forms in the introduction for a description of amplitude. +SeeWave Forms in the introduction for a description of phase) + +#### {#section_4} + +See Wave Forms in the introduction for a description of frequency) + +Design Note: The siv and amplitude parameters, when used in conjunction +with liquid volumes like water should take into consideration how much +the water will be moving. A large ocean area would have have massive +swells (big siv values) that rose and fell dramatically (big amplitude +values). While a small, quiet pool may move very little. \ No newline at end of file diff --git a/Documentation/Materials/commands/depthfunc.md b/Documentation/Materials/commands/depthfunc.md new file mode 100644 index 00000000..3cda0765 --- /dev/null +++ b/Documentation/Materials/commands/depthfunc.md @@ -0,0 +1,15 @@ +# Materials: Commands {#mat_commands} +## depthfunc +### Syntax {#syntax} + +**depthFunc ** + +### Overview {#overview} + +This controls the depth comparison function used while rendering. + +The default is **lequal** (Less than or equal to) where any surface that +is at the same depth or closer of an existing surface is drawn. This is +used for textures with transparency or translucency. Under some +circumstances you may wish to use **equal**, e.g. for light-mapped +grates that are alpha tested (it is also used for mirrors). \ No newline at end of file diff --git a/Documentation/Materials/commands/depthwrite.md b/Documentation/Materials/commands/depthwrite.md new file mode 100644 index 00000000..afa1d6fb --- /dev/null +++ b/Documentation/Materials/commands/depthwrite.md @@ -0,0 +1,13 @@ +# Materials: Commands {#mat_commands} +## depthwrite +### Syntax {#syntax} + +**depthWrite** + +### Overview {#overview} + +By default, writes to the depth buffer when +[depthFunc](depthFunc) passes will happen +for opaque surfaces and not for translucent surfaces. + +Blended surfaces can have the depth writes forced with this function. \ No newline at end of file diff --git a/Documentation/Materials/commands/diffusemap.md b/Documentation/Materials/commands/diffusemap.md new file mode 100644 index 00000000..02b96a5e --- /dev/null +++ b/Documentation/Materials/commands/diffusemap.md @@ -0,0 +1,24 @@ +# Materials: Commands {#mat_commands} +## diffusemap +### Syntax {#syntax} + +**diffusemap ** + +### Overview {#overview} + +Specifies the default texture asset to use on the diffuse/albedo pass of +the material. This is the base texture in most cases. Some special +materials used for special effects and the like might not have one. +However surfaces such as floors, walls etc. certainly do. It will affect +which texture is used to get color information from for lighting passes, +etc. + +### See also {#see_also} + +- [normalmap](normalmap) +- [specularmap](specularmap) +- [fullbrightmap](fullbrightmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectmask](reflectmask) +- [reflectcube](reflectcube) \ No newline at end of file diff --git a/Documentation/Materials/commands/fogparms.md b/Documentation/Materials/commands/fogparms.md new file mode 100644 index 00000000..ec6fe4f6 --- /dev/null +++ b/Documentation/Materials/commands/fogparms.md @@ -0,0 +1,42 @@ +# Materials: Commands {#mat_commands} +## fogparms +### Syntax {#syntax} + +**fogParms ** + +### Overview {#overview} + +**Note**: you must also specify "surfaceparm fog" to cause +[vmap](vmap) to identify the surfaces inside the volume. +Fogparms only describes how to render the fog on the surfaces. + +** ** These are normalized values. +To obtain the values that define fog color divide the desired color's +Red, Green and Blue values by 255 to obtain three normalized numbers +within the 0.0 to 1.0 range. + +**** This is the distance, in game units, until the +fog becomes totally opaque, as measured from the point of view of the +observer. By making the height of the fog brush shorter than the +distance to opaque, the apparent density of the fog can be reduced +(because it never reaches the depth at which full opacity occurs). + +### Notes {#notes} + +- The fog volume can only have one surface visible (from outside the + fog). +- Fog must be made of one brush. It cannot be made of adjacent + brushes. +- Fog brushes must be axial. This means that only square or + rectangular brushes may contain fog, and those must have their edges + drawn along the axes of the map grid (all 90 degree angles). + +**Design Notes:** + +- If a water texture contains a fog parameter, it must be treated as + if it were a fog texture when in use. +- If a room is to be filled completely with a fog volume,it can only + be entered through one surface (and still have the fog function + correctly). +- Additional shader passes may be placed on a fog brush, as with other + brushes. \ No newline at end of file diff --git a/Documentation/Materials/commands/fte_clutter.md b/Documentation/Materials/commands/fte_clutter.md new file mode 100644 index 00000000..bcf0664b --- /dev/null +++ b/Documentation/Materials/commands/fte_clutter.md @@ -0,0 +1,13 @@ +# Materials: Commands {#mat_commands} +## fte_clutter +### Syntax {#syntax} + +**fte_clutter + ** + +### Overview {#overview} + +Similar to [vmap_surfaceModel (Material +Command)](vmap_surfaceModel), however +it'll place models at runtime. The density can be controlled via the +cvar `r_clutter_density`. \ No newline at end of file diff --git a/Documentation/Materials/commands/fullbrightmap.md b/Documentation/Materials/commands/fullbrightmap.md new file mode 100644 index 00000000..92a435e3 --- /dev/null +++ b/Documentation/Materials/commands/fullbrightmap.md @@ -0,0 +1,25 @@ +# Materials: Commands {#mat_commands} +## fullbrightmap +### Syntax {#syntax} + +**fullbrightmap ** + +### Overview {#overview} + +The texture is essentially a fullbright overlay on top of the +diffuse/albedomap. + +Not all [Shaders](Shaders) support them. In some, like the +[unlit](unlit_(Shader)) shader, the +[diffusemap](diffusemap) is always +fullbright. + +### See also {#see_also} + +- [diffusemap](diffusemap) +- [normalmap](normalmap) +- [specularmap](specularmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectmask](reflectmask) +- [reflectcube](reflectcube) \ No newline at end of file diff --git a/Documentation/Materials/commands/gl_blendmodes.jpg b/Documentation/Materials/commands/gl_blendmodes.jpg new file mode 100644 index 0000000000000000000000000000000000000000..b2e4c39482a1e1f043f3c0b94f07b1b49d517eac GIT binary patch literal 74730 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

mlA#IC87@nBik~lY`TTOBvFRRbhCM#FYxs^?>4| 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 o_-~jhEOJ>jtM1Uv#?d&CL?I4XkHpGOp;o%** + +### Overview {#overview} + +Specifies the default texture to use for any normalmap operations. This +depends heavily on which [GLSL program](Shaders) is used +inside the later stages. The dynamic lights will use this to determine +height information for light and shadows. So sometimes you want to skip +setting this. + +### Creating normal maps to use with this command {#creating_normal_maps_to_use_with_this_command} + +Check out our [Normal mapping guide](Normal_mapping_guide). + +### See also {#see_also} + +- [diffusemap](diffusemap) +- [specularmap](specularmap) +- [fullbrightmap](fullbrightmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectmask](reflectmask) +- [reflectcube](reflectcube) \ No newline at end of file diff --git a/Documentation/Materials/commands/polygonoffset.md b/Documentation/Materials/commands/polygonoffset.md new file mode 100644 index 00000000..028879de --- /dev/null +++ b/Documentation/Materials/commands/polygonoffset.md @@ -0,0 +1,15 @@ +# Materials: Commands {#mat_commands} +## polygonoffset +### Syntax {#syntax} + +**polygonOffset ** + +### Overview {#overview} + +Surfaces rendered with the polygonOffset keyword are rendered slightly +off the polygon's surface. This is typically used for wall markings and +"decals." The distance between the offset and the polygon is variable. +If no value is supplied a distance of 1 is assumed, however this is +meant for backwards compatibility. Being explicit will help grepping +values later in case you need to find all surfaces with just a +polygonOffset of 1. \ No newline at end of file diff --git a/Documentation/Materials/commands/portal.md b/Documentation/Materials/commands/portal.md new file mode 100644 index 00000000..ca449a64 --- /dev/null +++ b/Documentation/Materials/commands/portal.md @@ -0,0 +1,12 @@ +# Materials: Commands {#mat_commands} +## portal +### Syntax {#syntax} + +**portal** + +### Overview {#overview} + +Specifies that this texture is the surface for a portal or mirror. In +the game map, a portal entity must be placed directly in front of the +texture (within 64 game units). All this does is set "sort portal", so +it isn't needed if you specify that explicitly. \ No newline at end of file diff --git a/Documentation/Materials/commands/program.md b/Documentation/Materials/commands/program.md new file mode 100644 index 00000000..352273a5 --- /dev/null +++ b/Documentation/Materials/commands/program.md @@ -0,0 +1,14 @@ +# Materials: Commands {#mat_commands} +## program +**program** defines which GLSL/HLSL shader to load for a defined +material. It also accepts arguments that will recompile a shader with +certain permutations. This is kinda ugly, + +Starting in [The Wastes](The_Wastes) 1.2, there are the +following shader programs available to you: + +- program [unlit](unlit_(Shader)) +- program [lightmapped](lightmapped_(Shader)) +- program [vertexlit](vertexlit_(Shader)) +- program [water](water_(Shader)) +- program [refract](refract_(Shader)) \ No newline at end of file diff --git a/Documentation/Materials/commands/reflectcube.md b/Documentation/Materials/commands/reflectcube.md new file mode 100644 index 00000000..78c6a36d --- /dev/null +++ b/Documentation/Materials/commands/reflectcube.md @@ -0,0 +1,20 @@ +# Materials: Commands {#mat_commands} +## reflectcube +### Syntax {#syntax} + +**reflectcube ** + +### Overview {#overview} + +Specifies which cubemap to use on this material. By default, the engine +will pass the nearest in-world cubemap instead. + +### See also {#see_also} + +- [diffusemap](diffusemap) +- [normalmap](normalmap) +- [specularmap](specularmap) +- [fullbrightmap](fullbrightmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectmask](reflectmask) \ No newline at end of file diff --git a/Documentation/Materials/commands/reflectmask.md b/Documentation/Materials/commands/reflectmask.md new file mode 100644 index 00000000..e987a5d6 --- /dev/null +++ b/Documentation/Materials/commands/reflectmask.md @@ -0,0 +1,25 @@ +# Materials: Commands {#mat_commands} +## reflectmask +### Syntax {#syntax} + +**reflectmask ** + +### Overview {#overview} + +Defines a texture that specifies which parts of a material will reveal a +reflective material, such as a +[cubemap](reflectcube). This applies to +standard FTEQW. In Nuclide the reflectmask is currently unused with the +included shaders. If you want to apply reflectivity to your materials, +use the alpha channel of your +[normalmap](normalmap) instead. + +### See also {#see_also} + +- [diffusemap](diffusemap) +- [normalmap](normalmap) +- [specularmap](specularmap) +- [fullbrightmap](fullbrightmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectcube](reflectcube) \ No newline at end of file diff --git a/Documentation/Materials/commands/rgbgen.md b/Documentation/Materials/commands/rgbgen.md new file mode 100644 index 00000000..b6788a91 --- /dev/null +++ b/Documentation/Materials/commands/rgbgen.md @@ -0,0 +1,117 @@ +# Materials: Commands {#mat_commands} +## rgbgen +### Syntax {#syntax} + +**rgbGen ** + +**rgbGen wave ** + +### Overview {#overview} + +Defines what vertex colors are set to for any given surface. + +If no rgbGen is specified, either "identityLighting" or"identity" will +be selected, depending on which blend modes are used. + +Valid parameters are const, wave, identity, identityLighting, +entity, oneMinusEntity, fromVertex, and lightingDiffuse. + +### Functions {#functions} + +#### const {#const} + +Follow this up with a vector of the color that you'd like the vertex +colors to be set as. An example for green would be: + +` rgbGen const 0.0 1.0 0.0` + +#### identityLighting {#identitylighting} + +Colors will be (1.0,1.0,1.0) if running without overbright bits (NT, +linux, windowed modes), or (0.5, 0.5, 0.5) if running with overbright. +Overbright allows a greater color range at the expense of a loss of +precision. Additive and blended stages will get this by default. + +#### identity {#identity} + +Colors are assumed to be all white (1.0,1.0,1.0). All filters stages +(lightmaps, etc) will get this by default. + +#### entity {#entity} + +Colors are grabbed from the entity's .colormod field. + +#### oneMinusEntity {#oneminusentity} + +Colors are grabbed from 1.0 minus the entity's .colormod field. + +#### entityLighting {#entitylighting} + +Introduced by [FTE](FTE), same as entity, but will receive +lighting similar to identityLighting on top of it. + +#### vertex {#vertex} + +Colors are filled in directly by the data from the map or model files. +This is used for blending brushes and patches. It was used at one point +to store primitive lighting, in case the lightmapped rendering path was +to expensive (this is no longer available). + +#### oneMinusVertex {#oneminusvertex} + +As rgbGen vertex, but inverted. + +Design Note: This keyword would probably not be used by a level designer + +#### lightingDiffuse {#lightingdiffuse} + +Colors are computed using a standard diffuse lighting equation. It uses +the vertex normals to illuminate the object correctly. + +Design Note: -rgbGen lightingDiffuse is used when you want the RGB +values to be computed for a dynamic model (i.e. non-map object) in the +world using regular in-game lighting. For example, you would specify on +materials for items, characters, weapons, etc. + +#### wave {#wave} + +Colors are generated using the specified waveform. An affected texture +with become darker and lighter, but will not change hue. Hue stays +constant. Note that the rgb values for color will not go below 0 (black) +or above 1 (white). Valid waveforms are **sin**, **triangle**, +**square**, **sawtooth** and **inversesawtooth**. + +##### {#section} + +- **Sin**: color flows smoothly through changes. +- **Triangle**: color changes at a constant rate and spends no + appreciable time at peaks and valleys. +- **Square**: color alternates instantly between its peak and valley + values. +- **Sawtooth**: With a positive frequency value, the color changes at + a constant rate to the peak then instantly drops to its valley + value. +- **Inversesawtooth**: An inverse sawtooth wave will reverse this, + making the ascent immediate (like a square wave) and the decay fall + off like a triangle wave. + +##### {#section_1} + +Baseline value. The initial RGB formula of a color (normalized). + +##### {#section_2} + +Amplitude. This is the degree of change from the baseline value. In some +cases you will want values outside the 0.0 to 1.0 range, but it will +induce clamping (holding at the maximum or minimum value for a time +period) instead of continuous change. + +##### {#section_3} + +See the explanation for phase under the waveforms heading of Key +Concepts. + +##### {#section_4} + +Frequency. This is a value (NOT normalized) that indicates peaks per +second. \ No newline at end of file diff --git a/Documentation/Materials/commands/skyparms.md b/Documentation/Materials/commands/skyparms.md new file mode 100644 index 00000000..d84893b8 --- /dev/null +++ b/Documentation/Materials/commands/skyparms.md @@ -0,0 +1,52 @@ +# Materials: Commands {#mat_commands} +## skyparms +### Overview {#overview} + +**skyParms ** + +Specifies how to use the surface as a sky, including an optional far box +(stars, moon, etc), optional cloud layers with any shader attributes, +and an optional near box (mountains in front of the clouds, etc). Some +of the VMAP specific commands use this as a reference as to what skybox +to use for color, intensity etc. + +The renderer will take it into account only if you do not supply any +Stages in the material. + +**** Specifies a set of files to use as an environment box +behind all cloudlayers. Specify "-" for no farbox, or a file base name. +A base name of "env/test" would look for files "env/test_rt.tga", +"env/test_lf.tga", "env/test_ft.tga", "env/test_bk.tga", +"env/test_up.tga", "env/test_dn.tga" to use as the right / left / front +/ back / up / down sides. + +**** controls apparent curvature of the cloud layers - +lower numbers mean more curvature (and thus more distortion at the +horizons). Higher height values create "flatter" skies with less horizon +distortion. Think of height as the radius of a sphere on which the +clouds are mapped. Good ranges are 64 to 256. The default value is 128. + +**** Specified as farbox, to be alpha blended ontop of the +clouds. This has not be tested in a long time, so it probably doesn't +actually work. Set to "-" to ignore. + +### Example Sky Material {#example_sky_material} + +`   // Vera Visions Material` +`   {` +`       qer_editorImage textures/skies/dune.tga` +`       skyParms textures/skies/dune/bg 256 -` +`       noPicMip` +`       noMipmaps` +`       ` +`       surfaceParm sky` +`       surfaceParm noimpact` +`       surfaceParm nolightmap` +`       surfaceParm nodlight` +`       {` +`           program skybox` +`           map $cube:textures/skies/dune/bg` +`           map textures/skies/clouds/dunecloud.tga` +`           map textures/skies/clouds/dunecloud_layer.tga` +`       }` +`   }` \ No newline at end of file diff --git a/Documentation/Materials/commands/sort.md b/Documentation/Materials/commands/sort.md new file mode 100644 index 00000000..283d13af --- /dev/null +++ b/Documentation/Materials/commands/sort.md @@ -0,0 +1,53 @@ +# Materials: Commands {#mat_commands} +## sort +### Syntax {#syntax} + +**sort ** + +### Overview {#overview} + +Use this keyword to fine-tune the depth sorting of materials as they are +compared against other materials in the game world. The basic concept is +that if there is a question or a problem with materials drawing in the +wrong order against each other, this allows the designer to create a +hierarchy of which materials draws in what order. + +The default behavior is to put all blended materials in sort "additive" +and all other materials in sort "opaque", so you only need to specify +this when you are trying to work around a sorting problem with multiple +transparent surfaces in a scene. + +### Values {#values} + +The value here can be either a numerical value or one of the keywords in +the following list (listed in order of mostly ascending priority): + +- **ripple**: Meant for surfaces blending below water surfaces I + guess. +- **eferredlight**: Blend at the same order as deferred lighting. So + before diffuse mapping usually takes place. +- **portal**: This surface is a portal, it draws over every other + shader seen inside the portal, but before anything in the main view. +- **sky**: Typically, the sky is the farthest surface in the game + world. Drawing this after other opaque surfaces can be an + optimization on some cards. This currently has the wrong value for + this purpose, so it doesn't do much of anything. +- **opaque**: This surface is opaque (rarely needed since this is the + default with noblendfunc) +- **decal**: Blend it like a decal. Ones affected by light, or + something. +- **seethrough**: Not sure what to call this, beyond repeating its + name and it being between decal and unlitdecal. +- **unlitdecal**: Blend it like an unlit decal, this most commonly is + bullet impacts. +- **banner**: Transparent, but very close to walls. +- **underwater**: Draw behind normal transparent surfaces. +- **blend**: Draw like a blendFunc blend transparent surface. +- **additive**: normal transparent surface (default for shaders with + blendfuncs) +- **nearest**: this shader should always sort closest to the viewer, + e.g. muzzle flashes and blend blobs + +It is generally recommended you stick to the keywords. The engine may +introduce new ones and you will benefit from internal sorting parameters +not clashing with yours whenever the engine may update them. \ No newline at end of file diff --git a/Documentation/Materials/commands/specularmap.md b/Documentation/Materials/commands/specularmap.md new file mode 100644 index 00000000..9502654f --- /dev/null +++ b/Documentation/Materials/commands/specularmap.md @@ -0,0 +1,22 @@ +# Materials: Commands {#mat_commands} +## specularmap +### Syntax {#syntax} + +**specularmap ** + +### Overview {#overview} + +Can set the specularity of the surface in relation to dlights. +Specularity is the intensity of the light reflecting off the surface. In +special cases some [GLSL programs](Shaders) might use the +texture it for other purposes, too. + +### See also {#see_also} + +- [diffusemap](diffusemap) +- [normalmap](normalmap) +- [fullbrightmap](fullbrightmap) +- [uppermap](uppermap) +- [lowermap](lowermap) +- [reflectmask](reflectmask) +- [reflectcube](reflectcube) \ No newline at end of file diff --git a/Documentation/Materials/commands/surfaceparm.md b/Documentation/Materials/commands/surfaceparm.md new file mode 100644 index 00000000..e2c3bb60 --- /dev/null +++ b/Documentation/Materials/commands/surfaceparm.md @@ -0,0 +1,296 @@ +# Materials: Commands {#mat_commands} +## surfaceparm +### Overview {#overview} + +The surfaceparm keyword is not only read by the VMAP compiler, but also +by the renderer. A few keywords will only apply to any one of them. + +All surfaceparm keywords are preceded by the word surfaceparm as +follows: + +: + + : surfaceparm **fog** + +### Behaviour Keywords {#behaviour_keywords} + +Commands that affect core functionality of a surface, or could impact +the entire room or the gameplay surrounding it. + +#### areaportal {#areaportal} + +A brush marked with this keyword functions as an area portal, a break in +the VMAP tree. It is typically placed on a very thin brush placed inside +a door entity (but is not a part of that entity). The intent is to block +the game from processing surface triangles located behind it when the +door is closed. It is also used by the BSPC (bot area file creation +compiler) in the same manner as a clusterportal. The brush must touch +all the structural brushes surrounding the areaportal. + +#### clusterportal {#clusterportal} + +A brush marked with this keyword function creates a subdivision of the +area file (.aas) used by the bots for navigation. It is typically placed +in locations that are natural breaks in a map, such a sentrances to +halls, doors, tunnels, etc. The intent is keep the bot from having to +process the entire map at once. As with the the areaportal parameter, +the affected brush must touch all the structural brushes surrounding the +areaportal. + +#### donotenter {#donotenter} + +Read as "do not enter." Like clusterportal, this is a bot-only property. +A brush marked with donotenter will not affect non-bot players, but bots +will not enter it. It should be used only when bots appear to have +difficulty navigating around some map features. + +#### lava {#lava} + +Assigns to the texture the game properties set for lava. This affects +both the surface and the content of a brush. + +#### nodamage {#nodamage} + +The player takes no damage if he falls onto a texture with this +surfaceparm + +#### nosteps {#nosteps} + +The player makes no sound when walking on this texture. + +#### nonsolid {#nonsolid} + +This attribute indicates a brush, which does not block the movement of +entities in the game world. It applied to triggers, hint brushes and +similar brushes. This affects the content of a brush. + +#### origin {#origin} + +Used on the "origin" texture. Rotating entities need to contain an +origin brush in their construction. The brush must be rectangular (or +square). The origin point is the exact center of the origin brush. + +#### playerclip {#playerclip} + +Blocks player movement through a nonsolid texture. Other game world +entities can pass through a brush marked playerclip. The intended use +for this is to block the player but not block projectiles like rockets. + +#### slick {#slick} + +This surfaceparm included in a texture should give it significantly +reduced friction. + +#### slime {#slime} + +Assigns to the texture the game properties for slime. This affects both +the surface and the content of a brush. + +#### structural {#structural} + +This surface attribute causes a brush to be seen by the VMAP process as +a possible break-point in a BSP tree. It is used as a part of the +material for the "hint" texture. Generally speaking, any opaque texture +not marked as "detail" is, by default, structural, so you shouldn't need +to specify this. + +#### water {#water} + +Assigns to the texture the game properties for water. + +#### climb {#climb} + +Marks the desired surface as a climbable surface. This currently affects +the entire volume. + +#### vehicleclip {#vehicleclip} + +Blocks all movement of vehicle entities through this surface. + +#### leakssteam {#leakssteam} + +When this surface is impacted, steam will leak out temporarily. Specific +to The Wastes 1.3. + +#### leakswater {#leakswater} + +When this surface is impacted, water will leak out temporarily. Specific +to The Wastes 1.3. + +#### fl_r1 {#fl_r1} + +Reserved for custom games. This can be anything. + +#### fl_r2 {#fl_r2} + +Reserved for custom games. This can be anything. + +#### fl_r3 {#fl_r3} + +Reserved for custom games. This can be anything. + +#### fl_r4 {#fl_r4} + +Reserved for custom games. This can be anything. + +#### fl_r5 {#fl_r5} + +Reserved for custom games. This can be anything. + +#### fl_r6 {#fl_r6} + +Reserved for custom games. This can be anything. + +#### fl_r7 {#fl_r7} + +Reserved for custom games. This can be anything. + +### Rendering Keywords {#rendering_keywords} + +Commands that affect rendering of a surface, or the how surfaces are +made to look by the compiler. These do not affect gameplay function. + +#### alphashadow {#alphashadow} + +This keyword applied to a texture on a brush, patch or model will cause +the lighting phase of the VMAP process to use the texture's alpha +channel as a mask for casting static shadows in the game world. + +Design Note: Alphashadow does not work well with fine line detail on a +texture. Fine lines may not cast acceptable shadows. It appears to work +best with well-defined silhouettes and wider lines within the texture. +Most of our tattered banners use this to cast tattered shadows. + +#### fog {#fog} + +Fog defines the brush as being a "fog" brush. This is a VMAP function +that chops and identifies all geometry inside the brush. The General +material keyword fogparms must also be specified to tell how to draw the +fog. + +#### lightfilter {#lightfilter} + +Causes the VMAP light stage to use the texture's RGB and alpha channels +to generate colored alpha shadows in the lightmap. For example, this can +be used to create the colored light effect cast by stained glass +windows. This can be used with surfaceparm alphashadow if an alpha is to +be respected. + +#### nodlight {#nodlight} + +Read as "No DeeLight". A texture containing this parameter will not be +affected or lit by dynamic lights, such as weapon effects. The VMAP +compiler doesn't really care about this, but the renderer does. + +#### nodraw {#nodraw} + +A texture marked with nodraw will not visually appear in the game world. +Most often used for triggers, clip brushes, origin brushes, and so on. +Light will pass through it, therefore beware of bleeding issues when +using nodraw/caulk textures with this. + +#### nodraw2 {#nodraw2} + +Same as nodraw, but the engine won't draw it, whereas the VMAP compiler +will react to the surface. So unlike nodraw, light will not pass through +these surfaces. + +#### noimpact {#noimpact} + +World entities will not impact on this texture. No explosions occur when +projectiles strike this surface and no marks will be left on it. Sky +textures are usually marked with this texture so those projectiles will +not hit the sky and leave marks. + +#### nomarks {#nomarks} + +Projectiles will explode upon contact with this surface, but will not +leave marks. Blood will also not mark this surface. This is useful to +keep lights from being temporarily obscured by battle damage. + +Blob shadows (aka **r_shadows 1**) also counts as a mark. So any surface +that you don't want to see affected by shadows should receive this +surfaceparm. + +#### nolightmap {#nolightmap} + +This texture does not have a lightmap phase. It is not affected by the +ambient lighting of the world around it. It does not require the +addition of an rgbGen identity keyword in that stage. + +#### trans {#trans} + +Light will pass through this surface, but only if either alphashadow or +lightfilter are applied. Tells VMAP that pre-computed visibility should +not be blocked by this surface. Generally, any materials that have +blendfuncs should be marked as surfaceparm trans. + +### Material Related Keywords {#material_related_keywords} + +Specifies which impact effects and footstep sounds are played when +interacting with a given surface. Only one of them can be given at once: + +#### alien {#alien} + +Defines that the surface is of an 'alien' material. Affects impact sound +and effects. + +#### flesh {#flesh} + +Defines that the surface is of flesh. Affects impact sound and effects. + +#### foliage {#foliage} + +Defines that the surface is foliage. Affects impact sound and effects. + +#### computer {#computer} + +Defines that the surface is of computer parts. Affects impact sound and +effects. + +#### dirt {#dirt} + +Defines that the surface is of dirt. Affects impact sound and effects. + +#### vent {#vent} + +Defines that the surface is a vent. Affects impact sound and effects. + +#### grate {#grate} + +Defines that the surface is a grate. Affects impact sound and effects. + +#### metal {#metal} + +Defines that the surface is of metal. Affects impact sound and effects. + +#### glass {#glass} + +Defines that the surface is of glass. Affects impact sound and effects. + +#### sand {#sand} + +Defines that the surface is of sand. Affects impact sound and effects. + +#### slosh {#slosh} + +Defines that the surface is of a liquid. Affects impact sound and +effects. + +#### snow {#snow} + +Defines that the surface is of snow. Affects impact sound and effects. + +#### tile {#tile} + +Defines that the surface is of kitchen/bathroom tiles. Affects impact +sound and effects. + +#### wood {#wood} + +Defines that the surface is of wood. Affects impact sound and effects. + +#### concrete {#concrete} + +Defines that the surface is of concrete. Affects impact sound and +effects. \ No newline at end of file diff --git a/Documentation/Materials/commands/tcgen.md b/Documentation/Materials/commands/tcgen.md new file mode 100644 index 00000000..fe656f75 --- /dev/null +++ b/Documentation/Materials/commands/tcgen.md @@ -0,0 +1,24 @@ +# Materials: Commands {#mat_commands} +## tcgen +### Syntax {#syntax} + +**tcGen ** + +### Overview {#overview} + +Specifies how texture coordinates are generated and where they come +from. Valid functions are **base**, **lightmap** and **environment**. + +### Sources {#sources} + +#### base {#base} + +Base texture coordinates from the original art. + +#### lightmap {#lightmap} + +Lightmap texture coordinates. + +#### environment {#environment} + +Make this object environment mapped. \ No newline at end of file diff --git a/Documentation/Materials/commands/tcmod.md b/Documentation/Materials/commands/tcmod.md new file mode 100644 index 00000000..51761908 --- /dev/null +++ b/Documentation/Materials/commands/tcmod.md @@ -0,0 +1,132 @@ +# Materials: Commands {#mat_commands} +## tcmod +### Syntax {#syntax} + +**tcMod <…>** + +### Overview {#overview} + +Specifies how texture coordinates are modified after they are generated. + +The valid functions for tcMod are **rotate**, **scale**, **scroll**, +**stretch** and **transform**. + +**Transform** is a function generally reserved for use by programmers +who suggest that designers leave it alone. + +When using multiple **tcMod** functions during a stage, place the scroll +command last in order, because it performs a mod operation to save +precision, and that can disturb other operations. + +Texture coordinates are modified in the order in which **tcMods** are +specified. In otherwords, if you see: + +` tcMod scale 0.5 0.5` +` tcMod scroll 1 1` + +Then the texture coordinates will be **scaled then**scrolled'''. + +### Functions {#functions} + +#### rotate {#rotate} + +This keyword causes the texture coordinates to rotate. The value is +expressed in degrees rotated each second. A positive value means +clockwise rotation. A negative value means counterclockwise rotation. +For example "tcMod rotate 5" would rotate texture coordinates 5 degrees +each second in a clockwise direction. The texture rotates around the +center point of the texture map, so you are rotating a texture with a +single repetition, be careful to center it on the brush (unless +off-center rotation is desired). + +#### scale {#scale} + +Resizes (enlarges or shrinks) the texture coordinates by multiplying +them against the given factors of and <tScale). The values +"s" and "t" conform to the "x" and "y" values (respectively) as they are +found in the original texture. The values for sScale and tScale are +**NOT** normalized. This means that a value greater than 1.0 will +increase the size of the texture. A positive value less than one will +reduce the texture to a fraction of its size and cause it to repeat +within the same area as the original texture (Note: see +[clampmap](clampmap) for ways to control +this). + +Example: `tcMod scale 0.5 2` would cause the texture to repeat twice +along its width, but expand to twice its height (in which case half of +the texture would be seen in the same area as the original) + +#### scroll {#scroll} + +Scrolls the texture coordinates with the given speeds. The values "s" +and "t" conform to the "x" and "y" values (respectively) as they are +found in the original texture. The scroll speed is measured in +"textures" per second. A "texture" is the dimension of the texture being +modified and includes any previous material modifications to the +original texture). A negative s value would scroll the texture to the +left. A negative t value would scroll the texture down. + +Example: tcMod scroll 0.5 -0.5 moves the texture down and right +(relative to the texture's original coordinates) at the rate of a half +texture each second of travel. + +**This should be the LAST tcMod in a stage.** Otherwise there maybe +popping or snapping visual effects in some materials. + +#### stretch {#stretch} + +Stretches the texture coordinates with the given function. Stretching is +defined as stretching the texture coordinate away from the center of the +polygon and then compressing it towards the center of the polygon. + +****: A base value of one is the original dimension of the texture +when it reaches the stretch stage. Inserting other '''values positive or +negative in this variable will produce unknown effects. + +****: This is the measurement of distance the texture will +stretch from the base size. It is measured, like scroll, in textures. A +value of 1 here will double the size of the texture at its peak. + +****: See the explanation for phase under the deform vertexes +keyword. + +****: this is wave peaks per second. + +****: + +- **Sin**: the texture expands smoothly to its peak dimension and then + shrinks smoothly to its valley dimension in a flowing manner. +- **Triangle**: The textures stretch at a constant rate and spend no + appreciable time at the peak or valley points. +- **Square**: The texture is shown at its peak for the duration of the + frequency and then at its valley for the duration of the frequency. +- **Sawtooth**: the texture stretches like a triangle wave until it + reaches a peak, then instantly drops to the valley, as in a square + wave. +- **Inversesawtooth**: this is the reverse of the sawtooth wave. + +#### transform {#transform} + +Transforms each texture coordinate as follows: + +S' = s \* m00 + t \* m10 + t0 T' = s \* m01 + t \* m11 + t1 + +This is for use by programmers. + +#### turb {#turb} + +Applies turbulence to the texture coordinate. Turbulence is a back and +forth churning and swirling effect on the texture. + +The parameters for this are defined as follows: + +- ****: Currently undefined. +- ****: This is essentially the intensity of the + disturbance or twisting and squiggling of the texture. +- ****: See the explanation for phase under the + [deformvertexes](DeformVertexes) + keyword. +- ****: Frequency. This value is expressed as repetitions or + cycles of the wave per second. A value of one would cycle once per + second. A value of 10 would cycle 10 times per second. A value of + 0.1 would cycle once every 10 seconds. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_backmaterial.md b/Documentation/Materials/commands/vmap_backmaterial.md new file mode 100644 index 00000000..ec3f5f1d --- /dev/null +++ b/Documentation/Materials/commands/vmap_backmaterial.md @@ -0,0 +1,21 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_backmaterial +### Syntax {#syntax} + +**vmap_backMaterial ** + +### Overview {#overview} + +This allows a brush surface to use a different material when you are +inside it looking out. + +By way of example, this would allow a water brush (or other) surfaces to +have a different sort order or appearance when seen from the inside. + +**vmap_backMaterial** only works on brush faces. For this reason, it is +often deprecated in favor of using +[vmap_cloneMaterial](vmap_cloneMaterial) +where the target material contains +[vmap_invert](vmap_invert). + +It can still be useful as a kind of shorthand. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_backsplash.md b/Documentation/Materials/commands/vmap_backsplash.md new file mode 100644 index 00000000..890bcb53 --- /dev/null +++ b/Documentation/Materials/commands/vmap_backsplash.md @@ -0,0 +1,16 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_backsplash +### Syntax {#syntax} + +**vmap_backsplash ** + +### Overview {#overview} + +Controls the percentage of [surface +light](vmap_surfaceLight) backsplash (how +much light the emitting surface hits), as well as the distant away from +the surface of which it is cast. This is really tricky to get right in +tight areas, so put time aside to experiment. + +**The default backsplash amount is 5%, and the default distance is about +16 units.** \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_basematerial.md b/Documentation/Materials/commands/vmap_basematerial.md new file mode 100644 index 00000000..de11f350 --- /dev/null +++ b/Documentation/Materials/commands/vmap_basematerial.md @@ -0,0 +1,14 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_basematerial +### Syntax {#syntax} + +**vmap_baseMaterial ** + +### Overview {#overview} + +Copies the vmap_/surfaceparm commands from the specified material into +this one. + +However, if you want to copy the appearance and only specify +compiler-specific modifications, use +[vmap_remapMaterial](vmap_remapMaterial). \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_bounce.md b/Documentation/Materials/commands/vmap_bounce.md new file mode 100644 index 00000000..87eafd7c --- /dev/null +++ b/Documentation/Materials/commands/vmap_bounce.md @@ -0,0 +1,10 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_bounce +### Syntax {#syntax} + +**vmap_bounce ** + +### Overview {#overview} + +Specifies the fraction of light to re-emit during a bounce pass on this +surface. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_clonematerial.md b/Documentation/Materials/commands/vmap_clonematerial.md new file mode 100644 index 00000000..ef0a1fcf --- /dev/null +++ b/Documentation/Materials/commands/vmap_clonematerial.md @@ -0,0 +1,12 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_clonematerial +### Syntax {#syntax} + +**vmap_cloneMaterial ** + +### Overview {#overview} + +A material with this directive will inherit the target material's +properties and appearance. **Be careful, this can lead to an infinite +loop if a cloning material references another cloning material or +itself.** \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_invert.md b/Documentation/Materials/commands/vmap_invert.md new file mode 100644 index 00000000..dddef4f3 --- /dev/null +++ b/Documentation/Materials/commands/vmap_invert.md @@ -0,0 +1,15 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_invert +### Syntax {#syntax} + +**vmap_invert** + +### Overview {#overview} + +Inverts a surface normal. Works on brush faces, models and patches. Used +in celshading to achieve the inverted backfacing hull. + +Can be used with a material that is targeted by +[vmap_cloneMaterial](vmap_cloneMaterial) +for something similar to +[vmap_backMaterial](vmap_backMaterial). \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightimage.md b/Documentation/Materials/commands/vmap_lightimage.md new file mode 100644 index 00000000..7f028a04 --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightimage.md @@ -0,0 +1,16 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightimage +### Syntax {#syntax} + +**vmap_lightImage ** + +### Overview {#overview} + +By default, surface lights use the average color of the source image to +generate the color of the light. vmap_lightImage specifies an alternate +image to be used for light color emission, radiosity color emission, +light filtering and alpha shadows. You can even use a light image with a +different alpha channel for blurrier alpha shadows. The light color is +averaged from the referenced texture. The texture must be the same size +as the base image map. vmap_lightImage should appear before +qer_editorImage. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightmapfilterradius.md b/Documentation/Materials/commands/vmap_lightmapfilterradius.md new file mode 100644 index 00000000..ab15280d --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightmapfilterradius.md @@ -0,0 +1,33 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightmapfilterradius +### Syntax {#syntax} + +**vmap_lightmapFilterRadius +** + +### Overview {#overview} + +This is usually used on [light emitting +materials](vmap_surfaceLight) to +approximate finer subdivided lighting. It adds a gaussian blur effect to +the lightmaps of either the material itself, or the surfaces affected by +the material, or both. The values for **** and +**** are measured in world units of filtering +(blurring) of lightmap data cast by any light sources. + +**** Amount of blur set for the material itself +to approximate for the [surface +light's](vmap_surfaceLight) finer +subdivided lighting. It should be set to 0 for sky materials since they +don't have lightmaps. + +****: Amount of blur set for other surfaces +affected by this material's emitted light. It should be set just high +enough to eliminate the "stadium shadow" effect sometimes produced by +[light_environment](light_environment) or to smooth out the +lighting on [surface +lights](vmap_surfaceLight). + +[vmap_lightmapFilterRadius](vmap_lightmapFilterRadius) +should be placed before any light related material directives that you +want it to affect. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightmapmergable.md b/Documentation/Materials/commands/vmap_lightmapmergable.md new file mode 100644 index 00000000..1c20d573 --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightmapmergable.md @@ -0,0 +1,14 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightmapmergable +### Syntax {#syntax} + +**vmap_lightmapMergable** + +### Overview {#overview} + +When specified, the compiler will attempt to merge all surfaces using +this material together onto lightmap sheets. This can drastically reduce +the artifacts occuring on surfaces like terrain where precision issues +might get you black spots along the lightmap seams. Definitely use this +across large collections of surfaces on which you expect smooth +transitions. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightmapsampleoffset.md b/Documentation/Materials/commands/vmap_lightmapsampleoffset.md new file mode 100644 index 00000000..2635537d --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightmapsampleoffset.md @@ -0,0 +1,13 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightmapsampleoffset +### Syntax {#syntax} + +**vmap_lightmapSampleOffset ** + +### Overview {#overview} + +Takes a single parameter, defaulting to 1.0, which specifies how many +units off a surface should the compiler sample lighting from. Use larger +values (2.0-8.0) if you're getting ugly splotches on lightmapped +terrain. Try to use filtering to solve splotches if possible, leaving +vmap_lightmapSampleOffset as a last resort. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightmapsamplesize.md b/Documentation/Materials/commands/vmap_lightmapsamplesize.md new file mode 100644 index 00000000..df2691f4 --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightmapsamplesize.md @@ -0,0 +1,15 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightmapsamplesize +### Syntax {#syntax} + +**vmap_lightmapSampleSize ** + +### Overview {#overview} + +Surfaces using a material with this option will have the pixel size of +the lightmaps set to N world grid units. This option can be used to +produce high-resolution shadows on certain surfaces. In addition, it can +be used to reduce the size of lightmap data, where high-resolution +shadows are not required, gaining memory and performance benefits. The +default sample size is 8, smaller numbers increases lightmap resolution. +In general, you should stick with power of 2 values. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightrgb.md b/Documentation/Materials/commands/vmap_lightrgb.md new file mode 100644 index 00000000..6b230d8b --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightrgb.md @@ -0,0 +1,12 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightrgb +### Syntax {#syntax} + +**vmap_lightRGB ** + +### Overview {#overview} + +Alternative to [vmap_lightImage (Material +Command)](vmap_lightImage) and the +automatic way of figuring out which light color a specified surface +emits. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_lightsubdivide.md b/Documentation/Materials/commands/vmap_lightsubdivide.md new file mode 100644 index 00000000..f876b4d7 --- /dev/null +++ b/Documentation/Materials/commands/vmap_lightsubdivide.md @@ -0,0 +1,15 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_lightsubdivide +### Syntax {#syntax} + +**q3map_lightSubdivide ** + +### Overview {#overview} + +Used on surface lights (see [vmap_surfaceLight (Material +Command)](vmap_surfaceLight)). Controls +the distance between surface generated light sources for uniform +lighting. It defaults to 120 game units, but can be made larger or +smaller as needed (for light surfaces at the bottom of cracks, for +example). This can be a dominant factor in processing time for lightmap +generation. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_nodirty.md b/Documentation/Materials/commands/vmap_nodirty.md new file mode 100644 index 00000000..34989660 --- /dev/null +++ b/Documentation/Materials/commands/vmap_nodirty.md @@ -0,0 +1,10 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_nodirty +### Syntax {#syntax} + +**vmap_nodirty** + +### Overview {#overview} + +This surface is not affected by dirt-mapping. The compiler baked variant +of ambient occlusion. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_offset.md b/Documentation/Materials/commands/vmap_offset.md new file mode 100644 index 00000000..d8dc2fc8 --- /dev/null +++ b/Documentation/Materials/commands/vmap_offset.md @@ -0,0 +1,11 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_offset +### Syntax {#syntax} + +**vmap_offset ** + +### Overview {#overview} + +Offsets a surface along the vertex normals by N.N units. Used in +celshading for those black edges. This is the sort-of the compiler +version of [polygonOffset](polygonOffset) \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_remapmaterial.md b/Documentation/Materials/commands/vmap_remapmaterial.md new file mode 100644 index 00000000..7095e21f --- /dev/null +++ b/Documentation/Materials/commands/vmap_remapmaterial.md @@ -0,0 +1,20 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_remapmaterial +### Syntax {#syntax} + +**vmap_remapMaterial ** + +### Overview {#overview} + +Allows the material to later become known as the specified material. + +These materials should not contain anything but compiler-specific +keywords. + +For example, if you want a material that is **exactly like'' the +specified**vmap_remapMaterial**in appearance, but with a +specific**vmap_''' or surfaceparm characteristics, use this command. + +However, if you want want just a material's vmap_/surfaceparm +properties, use +[vmap_baseMaterial](vmap_baseMaterial). \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_shadeangle.md b/Documentation/Materials/commands/vmap_shadeangle.md new file mode 100644 index 00000000..9c853eed --- /dev/null +++ b/Documentation/Materials/commands/vmap_shadeangle.md @@ -0,0 +1,11 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_shadeangle +### Syntax {#syntax} + +**vmap_shadeAngle ** + +### Overview {#overview} + +In short: At which angle the phong shading on a material breaks. If you +set it to 0, basically no phong shading will ever be performed. The +default shade angle in [VMAP](VMAP) is 40 degrees. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_surfacelight.md b/Documentation/Materials/commands/vmap_surfacelight.md new file mode 100644 index 00000000..a56dad75 --- /dev/null +++ b/Documentation/Materials/commands/vmap_surfacelight.md @@ -0,0 +1,24 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_surfacelight +### Syntax {#syntax} + +**vmap_surfacelight ** + +### Overview {#overview} + +The texture gives off light equal to the value set for it. The relative +surface area of the texture in the world affects the actual amount of +light that appears to be radiated. + +To give off what appears to be the same amount of light, a smaller +texture must be significantly brighter than a larger texture. + +Unless the +[vmap_lightImage](vmap_lightImage) or +[vmap_lightRGB](vmap_lightRGB) directive +is used to select a different source for the texture's light color +information, the color of the light will be the averaged color of the +texture. + +If you want to change the distance/range of the surfacelight, use +[vmap_surfacelightDistance](vmap_surfaceLightDistance). \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_surfacelightdistance.md b/Documentation/Materials/commands/vmap_surfacelightdistance.md new file mode 100644 index 00000000..eda1c8ca --- /dev/null +++ b/Documentation/Materials/commands/vmap_surfacelightdistance.md @@ -0,0 +1,13 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_surfacelightdistance +### Syntax {#syntax} + +**vmap_surfaceLightDistance ** + +### Overview {#overview} + +Increases the range of a surfacelight. This is different from increasing +the [vmap_surfaceLight (Material +Command)](vmap_surfaceLight) value, as +it'll travel much wider without affecting the total intensity of the +light. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_surfacemodel.md b/Documentation/Materials/commands/vmap_surfacemodel.md new file mode 100644 index 00000000..c1212aa4 --- /dev/null +++ b/Documentation/Materials/commands/vmap_surfacemodel.md @@ -0,0 +1,16 @@ +# Materials: VMap Commands {#mat_vmap} +## vmap_surfacemodel +### Syntax {#syntax} + +**vmap_surfaceModel + ** + +### Overview {#overview} + +A surface with vmap_surfaceModel in its shader will randomly place a +specified model across it's face. This is designed to place grass or +tree models over terrain. + +This will bake the models into the .bsp itself, creating an +unnecessarily inflated map. Use [fte_clutter (Material +Command)](fte_clutter) instead. \ No newline at end of file diff --git a/Documentation/Materials/commands/vmap_tesssize.md b/Documentation/Materials/commands/vmap_tesssize.md new file mode 100644 index 00000000..a64afd15 --- /dev/null +++ b/Documentation/Materials/commands/vmap_tesssize.md @@ -0,0 +1,25 @@ +# Materials: Compiler Commands {#mat_vmap} +## vmap_tesssize +### Syntax {#syntax} + +**vmap_tessSize ** + +### Overview {#overview} + +Formely known as just **tessSize**. + +The command controls the tessellation size (how finely a surface is +chopped up in to triangles), in game units, of the surface. + +This is only applicable to solid brushes, not curves, and is generally +only used on surfaces that are flagged with the +[deformVertexes](deformVertexes) keyword. + +Abuse of this can create a huge number of triangles. This happens during +BSP processing, so maps must be reprocessed for changes to take effect. + +### Note {#note} + +It can also be used on tesselating surfaces to make sure that +tesselations are large, and thus, less costly in terms of triangles +created. \ No newline at end of file diff --git a/Documentation/Models/VVM.md b/Documentation/Models/VVM.md new file mode 100644 index 00000000..dcb56184 --- /dev/null +++ b/Documentation/Models/VVM.md @@ -0,0 +1,79 @@ +# Models: VVM +**Vera Visions Model**, also known as **VVM** or **IQM-FTE** is the main +skeletal model format. + +## Features {#features} + +Features that the original IQM exporter (by Lee Salzman) did not offer: + +- Support for external configuration files for IQM generation (ala + studiomdl) +- Pre-processor for rotating single or all input files +- Pre-processor for translating/repositioning single or all input + files +- Pre-processor for renaming bones inside a single or all input files +- Pre-processor for material prefixes inside a single or all input + files + +Some features that our extended specification (VVM) has to offer: + +- Support for hitmeshes for faster, content-aware collision detection +- Automatic hitmesh generation +- Submodels that allow showing/hiding specific groups of the model via + the game-logic +- Support for frame triggered events that can be read by the + game-logic (model-events) +- Level-of-detail range flag for references/sub-models +- User-defined surface and contentflags per reference/sub-model + +**NOTE: Using any of the VVM features is not supported in any engine +other than FTEQW** + +The tool's output will let you know if you're outputting an IQM +compatible file or not. + +It also has a re-engineered exporter that takes control files as input, +rather than a giant command-line string. This was done because some +models have hundreds of model parameters in The Wastes. + +## History {#history} + +In 2016 when we had our first prototype of The Wastes, we started out +using DPM for tool-chain related reasons and quickly migrated to IQM. +However we needed a better system for handling reproducible output +files. The original tool only handled compilation via plain command-line +parameters which was not good enough. + +The input command file (.qc) syntax is obviously inspired by qdata and +other tools such as studiomdl. So if you're familiar with those tools +you know exactly what to expect. + +We then went on and designed extensions that we needed to make the game +work, for example a generic model-events system that'd call events in +the game-logic when a certain key-frame in the model is displayed. + +More complicated things were hit-meshes, for more accurate hit detection +of body parts as well as sub-models that made the action of shooting +body-parts off possible. + +We developed this together with David of FTEQW, since this had to be +developed in conjunction with the FTEQW built-ins that'd later be +exposed to the game-logic. + +However, the initial extensions to the IQM format in FTE were designed +by us; and this is the exporter that was used to make our game. + +## Viewing VVM Models {#viewing_vvm_models} + +You can grab a binary build of FTE [1](https://www.fteqw.org/) +(basically a generic, non The Wastes specific version) and use the +'modelviewer' command in the console. The console can be opened via +Shift+ESC. + +If you want to see for example the Winston viewmodel, type *modelviewer +models/weapons/v_winchester.vvm* + +## External links {#external_links} + +[ vvmtool on +GitHub](https://github.com/VeraVisions/vvmtool) \ No newline at end of file diff --git a/Documentation/Models/VVM_Tutorial.md b/Documentation/Models/VVM_Tutorial.md new file mode 100644 index 00000000..2dcc7580 --- /dev/null +++ b/Documentation/Models/VVM_Tutorial.md @@ -0,0 +1,150 @@ +# Models: VVM Tutorial +## Introduction {#introduction} + +As you already know, the VVM exporter (vvmtool) takes inspiration from +Valve's **studiomdl** in terms of usage. There are *slight* syntax +differences, primarily because the way some features are implemented +differ **drastically**. + +For example, **model events** can be stacked and applied to many +animation sequences at once. *There is no way to do so with studiomdl.* + +## Input files {#input_files} + +Any .vvm consists of multiple input files. These can come in the +following formats: + +- SMD (GoldSrc & Source) +- MD5MESH and MD5ANIM (idTech 4) +- FBX +- IQE +- OBJ (no animations) + +Make sure that when you use animated files, that the bone naming and +bone order is consistent among them. + +## Control File {#control_file} + +This file is the head of the format. It specifies how the input files +are loaded in, with additional useful commands on how to manipulate the +input data. Many of these features would **not be possible** with an +internal, modeling program specific exporter for VVM. + +Here's an EXCERPT of a control file from **The Wastes*: + + output tommygun.vvm + materialprefix models/weapons/tommygun/ + + bone "Bone55" rename "Muzzleflash" + + scene ref/tommygun.smd + rotate 0 -90 0 + + scene seq/idle.smd fps 20.0 + scene seq/idle_empty.smd fps 20.0 + + event 0 1004 "weapons/tommygun/draw.wav 1" + scene seq/draw.smd fps 25.0 + + event reset + event 0 1004 "weapons/tommygun/draw.wav 1" + scene seq/draw_empty.smd fps 25.0 + + event reset + event 0 1004 "weapons/tommygun/holster.wav 1" + scene seq/holster.smd fps 30.0 + + event reset + event 0 1004 "weapons/tommygun/holster.wav 1" + scene seq/holster_empty.smd fps 30.0 + + origin 0 -32 0 + + event reset + event 14 1004 "weapons/tommygun/clipout.wav 1" + event 48 1004 "weapons/tommygun/clipin.wav 1" + scene seq/reload_not_empty.smd fps 22.0 + event 64 1004 "weapons/tommygun/bolt.wav 1" + scene seq/reload.smd fps 22.0 + + origin 0 0 0 + + event reset + event 0 1001 "33 models/effects/muzzleflash/muzzleflash2.vvm" + event 0 1002 "3 40 0 70" + event 0 1005 "33 1" + scene seq/shoot.smd fps 30.0 + scene seq/shoot_last.smd fps 30.0 + ... + +That's a really complicated control file, but it highlights a few +things: + +- You can specify as many events as you like... +- and apply them to as many scenes as you want. +- Creating an interface for a dedicated, in-modeling-program exporter + would be unfeasible. +- You can run commands to fix existing model input files after the + fact, like bone renaming... +- ... and even rotation, scaling and origin re-adjusting (on a + per-input-file basis!) +- The **origin** command is absolute, so set it to '0 0 0' to reset + +However, a model control file doesn't need to be this complicated: + + output terminal01.vvm + materialprefix models/props/computers/ + rotate 0 -90 0 + scene ref/terminal01.smd + +This is `models/props/computers/terminal01.qc` from The Wastes. + +What each line means: + +`output` specifies the resulting final output file name and location +relative to the control file. + +`materialprefix` just appends this string to any referenced material. +Ideally it's the location where the .vvm itself is stored in your game +filesystem. + +`rotate` Simply rotates all input files by 90 degrees on the Y/Yaw axis +upon importing. + +`scene` Tells it to load one input file, which is a reference. If it was +an animation it'd specify a framerate via the 'fps X' parameter. + +## Compiling {#compiling} + +You grab your copy of **vvmtool** and you either drag +and drop your control file onto it, or run it via command-line: + +`vvmtool.exe foobar.qc` + +It should be as simple as that! + +## Notes {#notes} + +Some helpful tips for your content creation journey. + +### vid_reload {#vid_reload} + +I recommend getting a single reference, plus the animations into your +game first, then adding events and other necessary commands on top. + +You can use the console command `vid_reload` to force the engine to +flush the model/texture cache and to reload an updated model from disk. +Really useful when iterating over model exports. + +### Blender users {#blender_users} + +If you're using the Blender Source Model tools, as of 2021, it still +doesn't down-mix certain animation features into the .smd format. + +**This is a problem with the Blender exporter, not vvmtool (as it also +won't work in studiomdl).** + +So if your bones seem completely messed up, try .fbx instead. + +If you absolutely need to use .smd, export it as .fbx in Blender, import +said output back into Blender and then export as .fbx. \ No newline at end of file diff --git a/doc/mods b/Documentation/Mods.md similarity index 83% rename from doc/mods rename to Documentation/Mods.md index edabc6cd..be755bc1 100644 --- a/doc/mods +++ b/Documentation/Mods.md @@ -1,5 +1,4 @@ -Mod loading and setting up games -================================ +# Mod/Game Setup For mods to show up in the "Custom Game" menu, we have to either find a manifest file, or a liblist.gam, or a gameinfo.txt inside the respective mod directory. @@ -11,10 +10,12 @@ restricting that. A liblist.gam file can look something like this: -game "My Cool Mod" -version "1.0" -startmap "e1m1" -trainingmap "traininglevel" +``` + game "My Cool Mod" + version "1.0" + startmap "e1m1" + trainingmap "traininglevel" +``` But more definitions are available. Check src/menu-fn/m_customgame.qc's customgame_liblist_parse() function to stay @@ -31,10 +32,12 @@ If you need more control, you can use manifest files. Similar to the default.fmf that's in the root Nuclide source tree. You can set liblist entries like this inside of them: --set gameinfo_game "My Cool Mod" --set gameinfo_version "1.0" --set gameinfo_startmap "e1m1" --set gameinfo_trainingmap "traininglevel" +``` + -set gameinfo_game "My Cool Mod" + -set gameinfo_version "1.0" + -set gameinfo_startmap "e1m1" + -set gameinfo_trainingmap "traininglevel" +``` Please name the manifest the same as the mod/game dir. For example if your game its directory is named "foobar" name your manifest "foobar.fmf". diff --git a/Documentation/Sound/EAX.md b/Documentation/Sound/EAX.md new file mode 100644 index 00000000..20447bb3 --- /dev/null +++ b/Documentation/Sound/EAX.md @@ -0,0 +1,12 @@ +# Sound: EAX +Creative Technology's **Environmental Audio Extensions**, also known as **EAX**, attempted to create more ambiance within video games by more accurately simulating a real-world audio environment. + + +Due to the release of Windows Vista which deprecated the DirectSound3D API EAX was based on in 2007, Creative discouraged EAX implementation in favor of its [OpenAL](OpenAL.md)-based EFX equivalent. + + +EFX is fully supported in **FTEQW** and exposed via user-friendly entities and scripting language in **Nuclide**. + +## See also +* [EFX](EFX.md) +* [OpenAL](OpenAL.md) diff --git a/Documentation/Sound/EFX.md b/Documentation/Sound/EFX.md new file mode 100644 index 00000000..3bcf66d6 --- /dev/null +++ b/Documentation/Sound/EFX.md @@ -0,0 +1,87 @@ +# Sound: EFX + +EFX is a system in [OpenAL](OpenAL.md) that delivers high quality sound reverberation. It is the successor to Creative's [Environmental Audio Extensions](EAX.md). + +**Nuclide** offers abstraction for new and old entities relying on **digital signal processing**. + +## Entities + +**env_sound** is most commonly used to change the environmental sound processing of a room/area. + +In GoldSrc, it'll specify which enumeration of DSP preset to use for audio playback, in Nuclide however we just map it to a file in the filesystem. + +## EFX files + +Entities that specify a EFX type, usually want to load one from a file. + +Here's an example one, `efx/city.efx`: + +``` + density "1.000000" + diffusion "0.500000" + gain "0.316200" + gain_hf "0.398100" + gain_lf "1.000000" + decay_time "1.490000" + decay_hf_ratio "0.670000" + decay_lf_ratio "1.000000" + reflections_gain "0.073000" + reflections_delay "0.007000" + reflections_pan "0 0 0" + late_reverb_gain "0.142700" + late_reverb_delay "0.011000" + late_reverb_pan "0 0 0" + echo_time "0.250000" + echo_depth "0.000000" + modulation_time "0.250000" + modulation_depth "0.000000" + air_absorbtion_hf "0.994300" + hf_reference "5000.000000" + lf_reference "250.000000" + room_rolloff_factor "0.000000" + decay_limit "1" +``` + +Most of the parameters are self explanatory. + +## Debug cvars + +With the cvar `>s_al_debug` you'll get an overlay of information about which EFX file is currently being used, as well as what every single parameter is set to. + +You can refresh EFX definitions with a simple map restart. + + +## Legacy translation table +Here you can see which **.efx file** is responsible for handling a legacy **env_sound** room-type. + +**roomtype ID**|**EFX file** +:-----:|:-----: +0|efx/default.efx +1|efx/gs\_generic.efx +2|efx/gs\_metal\_s.efx +3|efx/gs\_metal\_m.efx +4|efx/gs\_metal\_l.efx +5|efx/gs\_tunnel\_s.efx +6|efx/gs\_tunnel\_m.efx +7|efx/gs\_tunnel\_l.efx +8|efx/gs\_chamber\_s.efx +9|efx/gs\_chamber\_m.efx +10|efx/gs\_chamber\_l.efx +11|efx/gs\_bright\_s.efx +12|efx/gs\_bright\_m.efx +13|efx/gs\_bright\_l.efx +14|efx/gs\_water1.efx +15|efx/gs\_water2.efx +16|efx/gs\_water3.efx +17|efx/gs\_concrete\_s.efx +18|efx/gs\_concrete\_m.efx +19|efx/gs\_concrete\_l.efx +20|efx/gs\_big1.efx +21|efx/gs\_big2.efx +22|efx/gs\_big3.efx +23|efx/gs\_cavern\_s.efx +24|efx/gs\_cavern\_m.efx +25|efx/gs\_cavern\_l.efx +26|efx/gs\_weirdo1.efx +27|efx/gs\_weirdo2.efx +28|efx/gs\_weirdo3.efx \ No newline at end of file diff --git a/Documentation/Sound/SoundDefs.md b/Documentation/Sound/SoundDefs.md new file mode 100644 index 00000000..e09d4988 --- /dev/null +++ b/Documentation/Sound/SoundDefs.md @@ -0,0 +1,86 @@ +# Sound: soundDef +Nuclide mimics the sound defintion spec from **idTech 4** somewhat, albeit +with some changes/enhancements. We call them **soundDefs**. + +**The elevator pitch**: To allow for more control over the sounds than what was previously +allowed in idTech engines we also allow designers to drop sounds into +the game without having to set some common parameters every time. + +Instead of directly calling which .wav or .ogg file to play, we tell it +to play the single name of a sound def. For example: +*c1_sentry_loader_in* which can be located in **any** text file ending +with the **.sndshd** file extension inside the `sound/` directory. + +``` + c1_sentry_loader_in + { + dist_min 10 + dist_max 25 + + no_occlusion + volume 0.25 + + sample sound/movers/comm1/sentry_loader_in.wav + } +``` + +![Attenuation Visualisation](sounds_attenuation.png "Attenuation") +- dist_min / dist_max sets the radius where the sound fades out. The + sound is at maximum volume inside 'dist_min' radius, and it + completely silent after 'dist_max' radius. +- The no_occlusion key tells the engine not to take level geometry in + to account when calculating volume. +- 'volume' is the volume inside the inner radius of the sound. +- The last line sets the actual sound file to play. + +Let's take a look at another one: + +``` + emetal_impacts + { + dist_min 5 + dist_max 45 + volume 0.5 + + sample sound/impact/ambient_impacts/emetal_01.wav + sample sound/impact/ambient_impacts/emetal_02.wav + sample sound/impact/ambient_impacts/emetal_03.wav + sample sound/impact/ambient_impacts/emetal_04.wav + sample sound/impact/ambient_impacts/emetal_05.wav + } +``` + +This one has multiple sound files specified, which means that **Nuclide** will randomly choose one to play. + +## Commands {#commands} + +| | | | +|-----------------|-------------------------|-------------------------------------------------------------------------------------------------------------| +| **Key** | **Value** | **Description** | +| attenuation | idle/static/none/normal | Changes the sound's attenuation, aka playback radius/distance. This essentially just changes **dist_max**. | +| dist_min | | Sets the minimum playback distance in quake units. | +| dist_max | | Sets the maximum playback distance in quake units. | +| volume | | Sets the playback volume. 0.0 - 1.0 range. | +| shakes | | Will shake the screen with an intensity specified. Play around with this, 256 is a good starting value. | +| pitch | | Will set a specific pitch change. 0.0 - 2.0 range most commonly, but there's wiggle room. | +| pitch_min | | Will set a minimum pitch instead of an exact one. This means it'll play a random pitch between min and max. | +| pitch_max | | Will set a maximum pitch instead of an exact one. This means it'll play a random pitch between min and max. | +| offset | | Sound sample offset in seconds. | +| looping | none | Sound set to force loop, regardless of sound markers in file. | +| nodups | none | Don't play duplicate samples in sequence. | +| global | none | Play samples everywhere. | +| private | none | Play samples privately onto the entity that it gets played on. | +| no_reverb | none | Disable any [EAX](/EAX "wikilink") on samples in this def. | +| omnidirectional | none | Samples won't play from any particular direction. | +| follow | none | Samples will move alongside the entity it's being played on | +| footstep | none | Determines sample volume based upon the speed of the entity. | +| distshader | | Which sound def to play to everyone who is out of playback reach of this one. | +| sample | | Adds a sample to play to the list. Will only play one at a time. | + +## Power to the developer {#power_to_the_developer} + +Unlike the implementation in **idTech 4**, all of the sound defs handling +is done in the game-logic and is therefore exposed to all developers. +Some mods may want to hook AI callbacks into the system, or create +visual effects when commands are called by a sound def. The +possibilities are endless! \ No newline at end of file diff --git a/Documentation/Sound/sounds_attenuation.png b/Documentation/Sound/sounds_attenuation.png new file mode 100644 index 0000000000000000000000000000000000000000..cb8a92354f7b0e3cc7924f0f5155d4b4236fc2a0 GIT binary patch literal 25516 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

^;jq%F(P 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. + +While we are a tiny team, and need to be compensated, keep your expectations reasonable as to the level of support we can provide - especially with only one programmer. \ No newline at end of file diff --git a/doc/fte.svg b/Documentation/fte.svg similarity index 100% rename from doc/fte.svg rename to Documentation/fte.svg diff --git a/doc/idtech.svg b/Documentation/idtech.svg similarity index 100% rename from doc/idtech.svg rename to Documentation/idtech.svg diff --git a/Documentation/openal.svg b/Documentation/openal.svg new file mode 100644 index 00000000..8553eb03 --- /dev/null +++ b/Documentation/openal.svg @@ -0,0 +1,85 @@ + + + image/svg+xml diff --git a/Documentation/opengl.svg b/Documentation/opengl.svg new file mode 100644 index 00000000..43b80a76 --- /dev/null +++ b/Documentation/opengl.svg @@ -0,0 +1,85 @@ + + + image/svg+xml diff --git a/doc/release-readme b/Documentation/release-readme.txt similarity index 91% rename from doc/release-readme rename to Documentation/release-readme.txt index 8ed527ec..1239595b 100644 --- a/doc/release-readme +++ b/Documentation/release-readme.txt @@ -19,7 +19,7 @@ Make sure you're running the latest version of FTEQW. If your entire engine/binary crashes to desktop or whatever, it's an FTE bug. Contact #fte at irc.quakenet.org -If you crash to console, contact #freecs at irc.freenode.net +If you crash to console, contact #freecs at irc.libera.chat Notes ======================================= diff --git a/Documentation/vulkan.svg b/Documentation/vulkan.svg new file mode 100644 index 00000000..21aed0d7 --- /dev/null +++ b/Documentation/vulkan.svg @@ -0,0 +1,116 @@ + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff --git a/Doxyfile b/Doxyfile index 5a6fcd99..cbdac6d6 100644 --- a/Doxyfile +++ b/Doxyfile @@ -32,7 +32,7 @@ DOXYFILE_ENCODING = UTF-8 # title of most generated pages and in a few other places. # The default value is: My Project. -PROJECT_NAME = "Nuclide" +PROJECT_NAME = Nuclide # The PROJECT_NUMBER tag can be used to enter a project or revision number. This # could be handy for archiving the generated documentation or if some version @@ -44,7 +44,7 @@ PROJECT_NUMBER = # for a project that appears at the top of each page and should give viewer a # quick idea about the purpose of the project. Keep the description short. -PROJECT_BRIEF = +PROJECT_BRIEF = "Software Development Kit for id Tech" # With the PROJECT_LOGO tag one can specify a logo or an icon that is included # in the documentation. The maximum height of the logo should not exceed 55 @@ -58,7 +58,7 @@ PROJECT_LOGO = # entered, it will be relative to the location where doxygen was started. If # left blank the current directory will be used. -OUTPUT_DIRECTORY = ./doc +OUTPUT_DIRECTORY = ./Documentation # If the CREATE_SUBDIRS tag is set to YES then doxygen will create 4096 sub- # directories (in 2 levels) under the output directory of each output format and @@ -68,7 +68,7 @@ OUTPUT_DIRECTORY = ./doc # performance problems for the file system. # The default value is: NO. -CREATE_SUBDIRS = NO +CREATE_SUBDIRS = YES # If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII # characters to appear in the names of generated files. If set to NO, non-ASCII @@ -244,7 +244,7 @@ SEPARATE_MEMBER_PAGES = NO # uses this value to replace tabs by spaces in code fragments. # Minimum value: 1, maximum value: 16, default value: 4. -TAB_SIZE = 4 +TAB_SIZE = 8 # This tag can be used to specify a number of aliases that act as commands in # the documentation. An alias has the form: @@ -463,7 +463,7 @@ LOOKUP_CACHE_SIZE = 0 # DOT_NUM_THREADS setting. # Minimum value: 0, maximum value: 32, default value: 1. -NUM_PROC_THREADS = 1 +NUM_PROC_THREADS = 4 #--------------------------------------------------------------------------- # Build related configuration options @@ -489,7 +489,7 @@ EXTRACT_PRIVATE = YES # methods of a class will be included in the documentation. # The default value is: NO. -EXTRACT_PRIV_VIRTUAL = NO +EXTRACT_PRIV_VIRTUAL = YES # If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal # scope will be included in the documentation. @@ -874,7 +874,22 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = src/ +INPUT = src/ \ + Documentation/Main.md \ + Documentation/Building.md \ + Documentation/Filesystem.md \ + Documentation/Mods.md \ + Documentation/Materials/mat_overview.md \ + Documentation/Materials/mat_commands.md \ + Documentation/Materials/mat_vmap.md \ + Documentation/Materials/commands/ \ + Documentation/Materials/mat_shaders.md \ + Documentation/Materials/mat_legacy.md \ + Documentation/Sound/ \ + Documentation/Models/ \ + Documentation/History.md \ + Documentation/Contributing.md \ + Documentation/Support.md # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses @@ -903,7 +918,9 @@ INPUT_ENCODING = UTF-8 # comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, *.f18, *.f, *.for, *.vhd, # *.vhdl, *.ucf, *.qsf and *.ice. -FILE_PATTERNS = *.qc *.h +FILE_PATTERNS = *.qc \ + *.h \ + *.md # The RECURSIVE tag can be used to specify whether or not subdirectories should # be searched for input files as well. @@ -918,7 +935,12 @@ RECURSIVE = YES # Note that relative paths are relative to the directory from which doxygen is # run. -EXCLUDE = src/engine src/worldspawn src/vvmtool +EXCLUDE = src/engine \ + src/worldspawn \ + src/vvmtool \ + src/menu-vgui \ + src/menu-fn \ + src/plugins # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded @@ -934,7 +956,12 @@ EXCLUDE_SYMLINKS = NO # Note that the wildcards are matched against the file with absolute path, so to # exclude all test directories for example use the pattern */test/* -EXCLUDE_PATTERNS = */src/engine/* */src/worldspawn/* */src/vvmtool/* +EXCLUDE_PATTERNS = */src/engine/* \ + */src/worldspawn/* \ + */src/vvmtool/* \ + */src/menu-fn/* \ + */src/menu-vgui/* \ + */src/plugins/* # The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names # (namespaces, classes, functions, etc.) that should be excluded from the @@ -971,7 +998,8 @@ EXAMPLE_RECURSIVE = NO # that contain images that are to be included in the documentation (see the # \image command). -IMAGE_PATH = +IMAGE_PATH = Documentation/Materials/ \ + Documentation/Sound/ \ # The INPUT_FILTER tag can be used to specify a program that doxygen should # invoke to filter for each input file. Doxygen will invoke the filter program @@ -1027,7 +1055,7 @@ FILTER_SOURCE_PATTERNS = # (index.html). This can be useful if you have a project on for instance GitHub # and want to reuse the introduction page also for the doxygen output. -USE_MDFILE_AS_MAINPAGE = +USE_MDFILE_AS_MAINPAGE = Main.md #--------------------------------------------------------------------------- # Configuration options related to source browsing @@ -1521,7 +1549,7 @@ DISABLE_INDEX = NO # The default value is: NO. # This tag requires that the tag GENERATE_HTML is set to YES. -GENERATE_TREEVIEW = NO +GENERATE_TREEVIEW = YES # When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the # FULL_SIDEBAR option determines if the side bar is limited to only the treeview @@ -2240,7 +2268,9 @@ INCLUDE_FILE_PATTERNS = # recursively expanded use the := operator instead of the = operator. # This tag requires that the tag ENABLE_PREPROCESSING is set to YES. -PREDEFINED = SERVER CLIENT MENU +PREDEFINED = SERVER \ + CLIENT \ + MENU # If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then this # tag can be used to specify a list of macro names that should be expanded. The diff --git a/README.md b/README.md index f7b89dcc..43e20b04 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ![FTE Logo](doc/fte.svg) Nuclide - Software Development Kit +# ![FTE Logo](Documentation/fte.svg) Nuclide - Software Development Kit Software Development Kit, built around idTech with focus on advanced features and clean-room implementations of true-and-tested game-logic frameworks. @@ -101,7 +101,7 @@ David Walton for **FTEQW** and the **FTEQCC** compiler, which is the brain of th Timothee Besset and the **GtkRadiant** contributors, as well as the NetRadiant team for giving us a base for our editor **WorldSpawn**. -![idTech Logo](doc/idtech.svg) +![idTech Logo](Documentation/idtech.svg) ## License Copyright (c) 2016-2022 Vera Visions L.L.C. diff --git a/doc/about b/doc/about deleted file mode 100644 index 46044562..00000000 --- a/doc/about +++ /dev/null @@ -1,34 +0,0 @@ -About Nuclide - -The Nuclide project produces a freely available game-logic component and -development platform on top of FTEQW; which is the engine we use. -Our goals is to create a modern research base for new advancements, as well -as to have a stable base with a good API for games. - -- Client-side predicted movement and inputs for things like weapons-systems -- Lots of well documented objects to use in level formats supported by FTEQW -- Reference implementations for a lot of features exlusive to FTEQW compared - to other idTech engines -- Designed to be familar to developers who're used to GoldSrc and Source engine - projects - -1. Why might I want to use it? - -You want to develop a game using a lot of complex and well-tested objects -which might be tedious to implement on your own. -You want to run or make modifications for a game using Nuclide and need full -control over what you can do. - -2. How free is Nuclide? - -Everything in Nuclide is free. The copyright terms for the game-logic are -very permitting. Nuclide does not use the GPL as a point of reference, it -instead uses a ISC-like license. This means you can use, copy, modify and -distribute the code and work resulting from it for any purpose. -Please read the very short 'license' document for details. - -3. What are the alternatives? - -Implementing systems such as prediction, complex map objects and entities on -your own or licensing another engine such as Source that ships with its own -Source SDK Base. diff --git a/doc/building b/doc/building deleted file mode 100644 index c8bab155..00000000 --- a/doc/building +++ /dev/null @@ -1,48 +0,0 @@ -Building the engine and toolchain: -The build_engine.sh will do that for you. It will still ask you to have at least -a certain amount of dependencies installed (such as the GCC, make and the X/SDL -headers for your operating system. - -Building the editor: -Handled by build_editor.sh. -GTK2, Pango, GTKGLEXT and MiniZip are one of a few other main dependencies. - -Building the game-logic: -Once build_engine.sh has been executed, you can now build the source tree with -build_game.sh. It'll use the fteqcc binary that's in the ./bin/ directory. - -Old, manual build instructions: -================================================================================ - -1. Building all of the game-logic - -To build the game-logic for all games, issue 'make' inside the src/ -directory of the repository. This requires you to have the latest -fteqcc installed. - -2. Building a specific game or mod - -For that you'll have to navigate into the client and server directory -of the game you want to compile. For example if you're only wanting to -build the Half-Life game-logic, navigate into src/client/valve, issue -'make' there, then do the same inside src/server/valve. - -3. Building the engine - -The engine is not part of this project. -However, as of this writing, building the engine is simple. -Checkout/Clone the FTEQW engine repository, then navigate into the -engine/ sub-directory and issue 'make m-rel' there. -This will generate a 'fteqw' binary for your platform in the sub-directory -titled 'release'. - -3. Building fteqcc - -Issue 'make qcc-rel' in the FTEQW repository folder 'engine', like in 3. -It will generate an fteqcc binary inside the sub-directory 'release'. - -4. Misc notes - -To those not in the know, the game-logic is written in QuakeC, it is thus -platform and architecture independent. You do not need to rebuild the logic -for each and every platform. The result will be identical. diff --git a/doc/contributing b/doc/contributing deleted file mode 100644 index b65930f1..00000000 --- a/doc/contributing +++ /dev/null @@ -1,17 +0,0 @@ -NUCLIDE CODEBASE CONTRIBUTING RULES - -1. The code must be yours. - -It's simple: Do not take GPL code. Do not take Half-Life SDK code. -We do not decompile, we reverse-engineer by trial and error. -Also referred to as 'clean-room engineering'. -Most of the behaviour is rather predictable, other is not. - -2. Game specific features need to be put into game-specific directories. - -Unless other games benefit from this, keep it seperate to one of the game -sub-directories. - -That's about it. - -Join us on #nuclide at irc.libera.chat diff --git a/doc/filesystem b/doc/filesystem deleted file mode 100644 index 8c946c3c..00000000 --- a/doc/filesystem +++ /dev/null @@ -1,36 +0,0 @@ -NUCLIDE FILESYSTEM DOCUMENTATION - -The 'nuclide' shell script is the launcher. -It sets PATH to include the directory 'bin' which contains the engine that -you've built with build_engine.sh. - -When nuclide is run and executes the engine, it'll first read default.fmf which -is a manifest file the engine reads. It is updated occasionally. -It defines which folders to mount in the virtual filesystem of the engine -and has a document entirely dedicated to itself. For that please read: - src/engine/specs/fte_manifests.txt - -On its own, Nuclide launches the game 'base', unless you tell it otherwise: -./nuclide -game mygame -You can also load multiple additional folders in a specific order by specifying -multiple '-game' arguments. - -Will load 'mygame' instead of base. -It will still load the other 'BASEGAME' entries listed in the default.fmf. -You can even load your own manifest file over default.fmf, by passing - ./nuclide -manifest mymanifest.fmf - -Once the game has loaded, it'll load the persistent menu.dat into our QuakeC -progs VM. -It' always running, you can make your own by forking src/menu-fn, src/menu-vgui -or write something from scratch. - -When a game is mounted, we're either looking for loose files (loaded last), or -pak archives the engine supports. Plain .pak, or zip archives with the pk3 or -pk4 extensions are supported. -Folders with the .pk3dir extensions are treated as if they were .pk3 files. -The editor also supports .pk3dir folders. - -Nuclide contains many custom definition files that are not engine specific. -.efx, .font, .sndshd and .way to name a few. -The engine doesn't really read them, the game-logic mostly handles them. diff --git a/doc/hlmaterials b/doc/hlmaterials deleted file mode 100644 index 2a720be7..00000000 --- a/doc/hlmaterials +++ /dev/null @@ -1,57 +0,0 @@ -hlmaterials - Nuclide Documentation -Written by Gethyn ThomasQuail, 6th April 2021 - -This document gives a general overview of what the materials.txt files is, -and how it's used in Nuclide, and why the decisions were chosen for the -current implementation. It is not an in-depth explanation of the format -itself. - -The materials.txt is how the GoldSrc engine defined sounds for various -textures throughout the game. For example, crates make "wood" sounds, -vents produce metallic feedback, etc. It is an analogue to surfaceparms -for those familiar with idTech engines, this was just Valve Software's -way of doing it before adopting standards. - -In stock Half-Life, this file is located at: - -sounds/materials.txt - -It is allowed to be overwritten by a modification, and users could customize -the file on the client-side only for themselves. This means there was no -map specific materials, and mods could not inherit HL's materials, so -mods would always have to manage a nearly duplicate file if they desired -custom texture sounds. - -A few mods tried to remedy this problem, the following below is methods -documented so far: - -- maps/MAPNAME.mat -Introduced in The Wastes. - -- maps/MAPNAME_materials.txt -Convention by Andrew Lucas, creator of Trinity SDK, modeled after -MAPNAME_details.txt - -- materials_file "PATH/FILE.txt" via "worldspawn" entity -Introduced in Sven Co-op 5.0 - -All these methods are supported by Nuclide, as one goal is to implement -conventions by not only Valve but the community as well. - -In addition Nuclide has also implemented a way of giving modifications -their own inheritable materials file: - -sounds/materials_UNIQUENAME.txt - -The idea here is that any mod or even map pack can include ONLY the textures -used, and no longer will anyone have to manage a near-clone of materials.txt - -For repackaging or modding purposes, if you desire to give your map custom -sound definitions, we recommend The Wastes method for individual maps, while -the Nuclide method should be used for Mods or Map Packs. We find these to be -the most clean and efficient way. - -NOTE: We recommend only using materials text files for GoldSrc related modding -purposes. It is inefficient for modern projects as there are much better -standards already supported in Nuclide. Keep in mind, it takes memory to load -big text files, and lots of text files adds up over play sessions.