Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
Loading...
To import vehicles models from Wolvenkit into Blender, use THIS GUIDE .
This update method allows for import all components and rigs together as one model. I recommend following it before continuing with this guide, as it has more valuable information.
To add our models for the different parts, we’ll be looking mostly in the .app
file, under our main appearance.
Open the “AppearanceVisualController
” in the appearance’s item list. It should be the last item in the list. Then select the “meshProxy
” value.
Go ahead and add this mesh to the project by clicking the yellow arrow.
This mesh is used at distance, before your vehicle is loaded fully. We’ll be using it as a reference for our modeling in blender, as it is a low poly, complete model with wheels and all.
Open the Export Tool, under the Tools menu on the top of wkit.
Hit refresh to load all items, and select your proxy mesh you just added, then hit “Export Selected”. This should have created a .glb
and .json
file under the new “raw
” folder above the resources in the Project Explorer.
Example:
Open a new project in blender, delete the base cube. Hit File > Import > Cyberpunk GLTF
and load the proxymesh.glb
Your proxy mesh should now load in blender.
Example:
You can now import your source 3d models for your vehicle.
The proxy mesh will initially be positioned offset from other vanilla meshes. This is due to the vehicle’s .rig
file settings(will be discussed later).
Example showing the offset:
To get the exact offset, open the .rig
file located at this path in the .ent
file:
RDTDataViewModel > components > deformation_rig > rig
Open the boneNames
list and look for the index (number to the left) of “Base
”, in this case 2:
Then close boneNames
and open boneTransforms
. Open the same index value as “Base
” and look at the translation values:
(Values that are set at “_.___E-17
” are in scientific notation, and if the E
number is negative as they are here, the number is close to 0)
Look at the Z
value and noticee how it is 0.4399999
in the example. (~0.44) This is the number we will translate the proxy mesh by. Return to blender and select the proxymesh object. Translate it by the same value just found in the .rig
file by using the Transform Location
values in the Object Properties
menu: (positive to negative)
Repeat the translation for all objects in the proxymesh collection:
The proxy mesh can be used as a reference in blender for scale. Note the porsche is especially convenient, as it has real-world dimensions available online. If you don’t know your source car’s size, (length, wheelbase, width) You can compare in-game between it and the Porsche, then convert those numbers as appropriate.
Import the source 3D model for your mod into the same project.
Your model will likely be scaled incorrectly.
Find the correct scale of your vehicle in reference to the proxy mesh.
Example: A mini cooper has a wheelbase of 97.1”, and a porsche 911 has a wheelbase of 89.45”. So, Mini cooper wheelbase should be 8.55% longer in Blender once scaled correctly.
Fix positioning with the move, rotate, and scale tools. “Numpad 5” can be used for orthographic perspective, which is useful for comparing wheelbase accurately. I recommend matching the wheel bottoms at the same height, and matching the driver seat position as close as you can. This will save some steps later for poses.
Example with both rendered:
Apply all transformations to all objects in blender. This is required after any blender changes, if you skip this before exporting from Blender into WolvenKit, any positioning changes will not be saved.
Always Remember to apply all after modifying something in Blender.
Disable visibility on the proxy mesh for now, but leave it in the project as we’ll need it later.
Now we can setup the model so it can be converted into meshes for Cyberpunk.
Depending on your source model, you may need to either split or join your model into groups for different meshes in game. These “groups” are the components list/array in the .app
file.
Here’s a quick reference for the major groupings to look for:
2 tire models, one for font and back (left and right mirror the model)
2 wheel/rim models, see ↑
Body
Engine
Window front & window back
Chassis
Various lights, backleft, backright, body, reverse, break,
Wipers
Door left & door right, front/back
Window left & right, front/back
Hood
Trunk
Interior
Mirror
Steering wheel
License plate
Bumper front / back
May be more or less of different components. Depends on source model.
Make a backup of your blender project often while editing. I suggest keeping multiple copies as you work with your model. While splitting/joining objects you might not realize certain components need to be different until later on in the mod project, and an older project file will be very helpful.
I recommend creating “collections” or folders in blender, one for each component. Split and join the files as necessary to organize them together.
Example of Collection/Folder/Object structure in Blender:
For each component in the vehicle, you’ll want a separate collection to organize meshes together. Each mesh will need a separate object in Blender for each material layer.
Here are the major mesh groupings you will want in your project:
Split and join objects as needed to organize the Blender project structure.
Join objects by selecting multiple, and using Object > Join:
Now we’ll import our first mesh into Cyberpunk.
In wkit, open the .app
file and navigate to the default appearance. Open the components and find “body_01
” and expand it. Look for the “mesh
” value, and add it to your project.
Example:
Use the export tool as we did with the proxy mesh to convert it to a .glb
file.
Import this body.glb
file into the blender project.
Prepare your custom body mesh and ensure the replacement is one object. Join relevant objects together if needed.
Make sure your body mesh is less than 65,635 vertices
. If any .glb
file has more than this, the import into the game will fail.
You can check the face count in the top left of your blender window:
This number counts all visible objects in blender. Turning off an object’s visibility removes it from the count
As a general rule, keep the count low if you can to preserve performance in-game. Higher polygon count will increase quality, but decrease game performance.
Use the decimate modifier (or other methods) to reduce the face count if needed.
Example adding decimate modifier:
For simplicity, we’ll join all the parts of the body into one object for exporting. We can split them as submeshes later, when we want to indicate separate materials within a mesh.
With the body object selected, go to File > Export > Export Selection to GLB for Cyberpunk:
Save it to a new folder for your project’s .glb
files.
Copy the .glb
file into the folder with the original exported body mesh.
Copy the original file’s name, delete it, and rename your new part with the old file’s name.
Open the import tool in wkit, find the correct body .glb
and import it into the game.
Select the updated .mesh
file in the Project Explorer, and it will open in the File Information window. It should show your new mesh.
Example:
Rename your new .mesh
file to a project-specific name, and move it to a custom folder.
Example:
“boe6\mini_cooper\meshes\boe6_mini_cooper_body.mesh
”
Update your .app file with the path to the new body file. You’ll need to update it in 2 places:
RDTDataViewModel > appearances > 0 > components > body_01 > mesh
RDTDataViewModel > appearances > 0 > components > AppearanceVisualController > appearanceDependancy > body_01 > mesh
Test the mod in-game. It should look very wrong since we only replaced one mesh. Example:
Next, make all other visible components invisible while we’re modeling.
Easiest way to mark components as invisible is to break the entVisualControllerComponent
appearances path.
“RDTDataViewModel > appearances > 0 > components > entVisualControllerComponent > appearanceDependency > partname > mesh
”
I do this by renaming the file with an added “-disabled
” in the name. This breaks the file path, and notes that it is disabled for reference later. example:
TheSpliffz said : " Not sure if this is due to 2.0 but only adding '-disabled
' to the meshes name in the EntVisualControllerComponent
doesn't seem to work.
Adding '-disabled
' to "RDTDataViewModel > appearances > 0 > components > entMeshComponent/entPhysicalMeshComponent > meshname > mesh
" does seem to work. "
You will receive warning in the log when saving the .mesh if you use this method, be aware:
Save and test in-game. The renamed component should now not render in-game. Note that this only removes the visual mesh. The interactions still exist. Example:
Continue to change components until all except the modded body and wheels remain. Start with the major parts, bumpers, doors, etc. Test in-game frequently. Only disable parts that render in-game.
Once you have disabled all the other components, you can see your model clearly and notice any initial problems. Expect the textures to be broken. Opposite sides may not render as we still need to add an interior. Example:
Some model issues may show themselves at this point. Check out the “Fixing Body Glitches” section of this document below.
You can use the body_01
instructions for creating most non-moving parts. (engine, trim, chassis, etc)
For the non-moving windows, we’ll start by enabling the mesh in the entVisualController
, removing the “-disabled
”. Then we can add the mesh to our project. Move/rename is appropriate.
Example:
“boe6\mini_cooper\meshes\boe6_mini_cooper_window_f.mesh
”
Update the .app
file with both .mesh
paths for the component.
Export it to .glb
, replace it with your model’s window, and import it back.
Save and Test.
Repeat for each non-moving window. (door windows are done after doors)
Similar materials can be copy/pasted as meshes, and then import/exported, and entered into the .app
file. This is instead of importing from the original car that is being mirrored. This saves time on moving files.
Save and test.
To add a new component instead of replacing a default one, you can duplicate the entVisualControllerDependency
of the most similar component by right-clicking and selecting “Duplicate Item in Array/Buffer
”.
Example:
Edit the new component’s “componentName
”, here I use “body_02
”, as it is for exterior trim bodywork.
Edit the “mesh
” value to the correct new .mesh
file.
Example:
“boe6\mini_cooper\meshes\boe6_mini_cooper_body_trim.mesh
”
Duplicate the entPhysicalMeshComponent
of the part you are copying. Rename it with the “name
” value to the one you used earlier, (body_02
). Set the “mesh
” value to the same mesh path.
Example:
I go into as much detail as I can, but try to be familiar with the project explorer, asset browser, and other windows.
.ent
file is the base file of a vehicle. It contains all settings and child files.
.app
is the vehicle’s appearances settings.
.mesh
is a 3d file, used for vehicle body parts.
.xbm
are raw texture files.
.mlsetup
has settings for texture sets
.mlmask
is zipped mask texture/finish layers, normal maps, etc.
.inkatlas
contain reference texture information
.inkwidget
configures texture layering, position, & animations
.rig
has 3D information for how the .mesh
files are linked and move.
.yaml
files configure TweakDB items.
.json
files list string localization information.
.phys
is used to set vehicle collision/interaction bounds
Int/Integer : whole numbers
Float : fractional numbers, decimal places
String : sequence of characters
Boolean : true or false
Array : collection or list of items of the same data type
Nothing : “null”
Object/edit mode
Object management
Object edits
Joining and separating models
Know the difference between object origin, world origin, and the blender cursor
Cyberpunk 2077 glTF Importer plugin / Wolvenkit resources plugin (View Options > plugins to install)
Must have support for .tga
files
Must have support for alpha channels
There are some python scripts and python commands to use.
Be able to edit and run a .py
script
Not necessary for most changes
Install these to your game before starting:
TweakXL (required, without it you won't be able to load Vehicle tweak at all)
Material and Texture override (unknown if required after 2077 version 2.0+)
Have a 3D vehicle model ready to use for converting into cyberpunk.
Important features are details parts, doors, trunk, etc. and textures.
Higher face/vertice count is better. Modeled interiors are a must.
The .rig
file ("armature" or "skeleton") is what makes your mesh move in the game. You can find more information about that on the Meshes and Armatures: Rigging page.
Parts that require a .rig
file will not import the same. For example, the bumper’s 3D models are not positioned in-line with the body_01
object in blender. Bumpers are centered around the origin of the 3D space. The part is then lined up correctly with the .rig
file, which is linked in the .ent
file.
To model the front bumper, we’ll first export the base game bumper as .glb
, and import into our blender project. As you can see, the game model is positioned around the origin, unlike the custom bumper.
Move it with the move tool, so the corner of the bumper is at the origin and apply all transforms.
If you do not have the rig file already, you can now add it to the project. Open your vehicle’s .ent
file and open this path: RDTDataViewModel > components > deformation_rig > rig
Add the file to the project, and rename/move it.
Example: “boe6\mini_cooper\boe6_mini_cooper.rig
”
Update the .ent
rig path with the new file:
You can also update the .rig path in your vehicle’s .anims file, as well as the rig references here:
.ent file > RDTDataViewModel > resolvedDependencies > 0 > .anims file path
.ent file > RDTDataViewModel > components > vehicle_rig > animations > gameplay > 0 > animSet
.ent file > RDTDataViewModel > components > deformation_rig > animations > gameplay > 0 > animSet
Add it to your project, rename and move it as usual. Then in the .anims
file, set the rig path to the new one we just created:
Additionally update these paths to your .anims
file.
Open the .rig file and notice these 3 arrays:
These map all the position data of each .mesh
file.
boneNames
tells you which index value is each item, boneParentIndex
tells you what each bone’s parent is (example; wheel_back_left
is a child of suspension_back_left
. If you change suspension position, wheel will move with it.), and boneTransforms
is the positional and rotational values for each bone.
For a reference of where the bones are in a model, you can export the vehicle’s .anims
file to .glb
and import it into blender. You can select each bone to see their name and properties.
Example:
Your 0,0,0
position should be in the center of the vehicle. Here is the translation data for each of the suspension wheels of a vanilla car (porsche):
These are frontLeft
, frontRight
, backLeft
, and backRight
respectively. The Translation values are XYZ
values:
(-) x
= left of car, driver side
(+) x
= right of car, passenger side
(-) y
= back of car
(+) y
= front of car
(-) z
= bottom of car
(+) z
= top of car.
It may help to visualize the car as if from a top-down ariel view, left is -x
, right is +x
, up is +y
, down is -y
.
You’ll need to update the .mlmask
and .mlsetup
files in these .mesh
files as well, however they may show up at a different location. If they are not listed under “localMaterialBuffer
”, check under the “preloadLocalMaterialInstances
” array. The masks set should be in there instead.
Modify the existing X Y Z
values for the bumper, changing one value at a time to see the amount changed. I suggest starting with a value of +/- 0.1.
For moving parts, such as hoods and doors, we start by replacing the 3D model. Notice the rotation point in-game is a pivot along the origin in 3d space:
In my case, I have my hood aligned forward, as the axis should be further behind in the real vehicle.
Your part will not align at first. If it is a large distance, try to adjust the 3D model first. Keep in mind the pivot will be the origin, so only adjust in the axis you can edit without messing with how the part will move(left/right for hood/trunk, up/down for doors). Once it’s close, you can adjust the .rig
file as well to get it positioned correctly:
Swap the .mesh
’s .mlmask
and .mlsetup
to the same as your body_01
has set. (or to whatever other file you need)
You can also change how components are connected in the vehicle rig.
For components that don’t have their own movement or bones, they are attached to another component that does. This can be changed to attach a model to another model’s movement.
.app
file “RDTDataViewModel > appearances > 0 > components > entPhysicalMeshComponent > parentTransform > bindName
”
Example:
Components that do have their own bones are set to the bindName “vehicle_slots
”, and their slotName
is set to a boneName
value in the .rig
file.
Example of body_01
in .app
:
If a component is linked to another component, we cannot use the .rig
file to move it. Instead, in the .app
file is each component’s localTransform
. This can be used to change the position and orientation relative to the parent component.
Example:
Notice that the x,y,z position is saved as Bits
, instead of as a float
. To edit the positon data here, we need to convert from a float value to a fixed bit value.
The latest release of Wolvenkit nightly seems to do the conversion automatically for you now.
The code for this conversion was written by Loomy(Loomy#3375) on the modding discord, here.
This can be run in a python console:
int( ( n * pow(2,32) ) ) >> 15
Where “n” is a float value.
If you do not have python installed, you can try an online console, such as here. (paste the command into the “shell” side on the right, and hit enter)
Rigging wheels requires a 2nd .rig
file that is used for vehicle handling. It is referenced here:
.ent file > RDTDataViewModel > components > vehicle_rig > rig (path to wheelbase rig)
You can add this new rig to your project and rename/move it.
Notice that the new rig file only has wheel related bones:
This is the rig file that is referenced for vehicle wheel handling. It controls where the wheels/suspension are located. This file overwrites the values in the deformation_rig
created earlier.
To update the wheels, generally you want to modify the suspension bones. Since the wheel_front_left
, wingarm_front_left
, and break_front_left
are all parented to suspension_front_left
, updating the location for suspension also keeps the brakes and swingarms in the correct position relative to the wheel.
If the suspension values are updated/changed in the vehicle_rig
, the deformation_rig
may cause issues in some cases. To avoid this, rename the boneNames
in the deformation_rig
that are the same as the vehicle_rig
. This ensures that the vehicle only uses the vehicle_rig
values. Do not rename front wheel suspension in the deformation_rig
, as it will break wheel turning.
For the Mini Cooper, I edited all 4 suspension positions in the vehicle_rig
to update wheel positions, so I renamed the 2 front in the deformation_rig
(excluding the front, as described above):
To change the size of the wheel, you need to change both the 3D models and the rig files. You can update the wheels by first replacing all the wheel and tire meshes with ones to match your vehicle. This is similar to how the doors, windows, and hood work. Make sure to have the center of the wheel as the mesh’s origin. Example:
Once the wheel model is updated, you need to update the .rig
files.
In both the deformation_rig
and vehicle_rig
, find the wheel bones (wheel_back_left
, wheel_front_right
, etc.) and update the Scale
values.
Example:
Please note that this is a “bodge” solution, it’s not the proper way to set wheel sizes, but it works.
The proxy mesh can be created once the vehicle is complete or close to complete.
There are many ways of converting a high-res model to a low-res model, but for this guide the “Remesh” modifier in Blender will be used.
Set all the parts visible and positioned correctly relative to the body in Blender. In my case I also added the wheel shadow meshes, as the rim holes caused issues with remeshing details. Join all the objects into one and select the object. Example:
The Remesh Modifier is available in the properties menu, under modifier properties: It
It is recommended to save before adding or changing settings in the modifier, as unintended settings can crash blender fairly easily. Here are the settings to adjust in Remesh:
Try to create a final proxy mesh with a low amount of faces/triangles. The face current face count visible in blender can be seen in the top left of the viewer. Example:
Most use less than 2 thousand faces for a proxy mesh. You’ll also need to update the blender model to be correctly position, as the .rig
will modify it. Translate your model with the reverse values used to translate the initial proxy mesh.
Once the mesh is ready, a texture is needed. I recommend this guide for texture painting:
Once the texture is ready, you can export it from Blender under Image > Save As.. >
Save it as a .png
, and then import it into the project as an .xbm
In the proxy.mesh
, update the texture path:
.mesh > RDTDataViewModel > preloadLocalMaterialInstances > 0 values > baseColor
Start to finish guide to add a standalone vehicle, in steps that are as specific as I can be.
In this guide I’ll be bringing a Mini Cooper into Cyberpunk 2077. All methods should translate to whatever vehicle you add. The guide assumes you’ve generally followed along in order. The later steps will skip basic info covered in previous steps, unless its written otherwise.
If at any point you get stuck & have questions, DM me on discord @boe6, or ask in one of the MODDING sections of the Cyberpunk 2077 Modding Discord. https://discord.gg/Epkq79kd96
If you discover info or additional details that you figured out during this guide, please right-click and add a comment in the original document so the guide can be updated! (do not ask questions in comments, use discord please)
This guide may not cover all 2.0 changes. Most everything should be correct, let me know if you find any issues.
For the visual learners among us, you can find a youtube video series by boe6 here:
Interaction collider .phys files control the physics size for the vehicle. This controls how it hits/collides with walls, players, etc.
Add the .phys
file to your project, rename, and move the file as appropriate. It is linked through the .ent
file.
Path:
.ent file > RDTDataViewModel > components > chassis > collisionResource
Convert the .phys
to a JSON
file:
Now you’ll need 2 python scripts created by “The Magnificent Doctor Presto!”. They can be downloaded on github here:
https://github.com/DoctorPresto/Cyberpunk-Helper-Scripts
Download both export_phys.py and import_.phys.py
In Blender, open the “Scripting” workspace:
At the top of the text editor that just openned, use the folder icon to find the importing .py
file we just downloaded.
This should open the .py
file into the text editor. It should look like this:
Edit the “physJsonPath
” value to the converted phys.json
file.
Example:
Once you’ve run the script, you should have vertices that look close to the outline of the original car model. Notice the outline similar to the porsche:
.phys
files don’t like complex meshes, so they use only vertices without faces or edges. Each “submesh” is a small object shape, with no concave angles. Each submesh is then joined to create the overall collision mesh.
Switch back to the Layout workspace.
The easiest way to edit these in blender is to enable your vehicle’s body mesh, is to switch to Wireframe shading, by holding Z, and moving your mouse to “Wireframe”. Example:
This will allow you to see your body mesh, but also see vertices behind and around it. Example:
Once done editing, select all the vertices objects and run the export_phys.py, similar to the import script.
The .phys
file should now be finished. Import from json
, update path references, and test in-game. Make sure to update the physJsonPath
again, as this is the path it will save the new .json
to.
Once the script has been run, you should have a new file with the “new_
” prefix added in the same folder as the phys.json
.
Example:
This file can now be converted back from json and used in game. Example:
Published: Nov 5, 2023 by Meta Pixel Last documented edit: Oct 15 2024 by
This guide is part of Boe6's Guide: new car from A to Z and covers the material part of it.
By default, cars use the multilayered shader. You can find guides about editing multilayered material linked at the top of the documenation page.
To learn more about materials, you can browse Cheat sheet: Materials and its sub-pages.
.mlmask
, .mlmaskset
& .masklist
To fix the odd textures that appear after importing your custom models, we have to edit the .mlmask
layers.
Find the .mlmask
corresponding file by opening a .mesh
in wkit to edit. Go to the path:
RDTDataViewModel > localMaterialBuffer > materials > 0 (...maskset) > values > 0 > value
Add the .mlmask
to the project.
Rename and move the .mlmask
file into your project filepath.
Example:
“boe6\mini_cooper\meshes\textures\boe6_mini_cooper_exterior_maskset.mlmask”
Export the .mlmask
file. It should result in a .masklist
file and a folder with .png
files inside:
Notice the first white image, and the remaining black images with white cutouts.
These textures don’t map onto the new vehicle’s mesh correctly. These can be edited to add more detail to your model.
As a temporary solution, we can edit the first image to be pure white, and the rest to be pure black. Example:
Import the .masklist
file
Update your .mesh
file with the new .mlmask
name/path file:
RDTDataViewModel > localMaterialBuffer > materials > 0 (...maskset) > values > 0 > value
Your vehicle should now be much smoother.
Note that you will need every .mesh file to have a corrected .mlmask path.
.mlsetup
& MLsetupBuilderTo change paint color, we need to edit the .mlsetup
file.
You will need the MLsetupBuilder plugin for editing these files. Install it in wkit:
HOME button (top left) > Plugins (left list) > MLsetupBuilder, right side, install.
Once installed, hit open. It will open a folder with a “MlsetupBuilder.exe” executable. Run it to open the program.
The .mlsetup
file we need is linked in the .mesh
file, similar to the .mlmask
:
RDTDataViewModel > localMaterialBuffer > materials > 0 / masksset > values > MultilayerSetup > Value
Add this .mlsetup
file to the project. Rename and move it.
Example:
“boe6\mini_cooper\meshes\textures\boe6_mini_cooper_exterior.mlsetup
”
Convert the .mlsetup
file into .json
by right clicking:
Open MlsetupBuilder.
Import the .mlsetup.json
file by hitting the arrow in the top left:
You can also right-click on the .mlsetup.json file and choose "Open in MlSetupbuilder". The file will be automatically loaded when it opens.
Once you open the file, a window will appear on the left with a list of materials. These are the texture layers of your mesh.
Note that layers 0,1,2,etc are linked the .mlmask layers.
At the top is a large orange button titled “Import the Multilayer Setup >>”. Click it to load the .mlsetup
file into the editor. It should populate the Layers list:
Now, assuming you created a .mlmask
file similar to our previous steps, with all but 1 black image (see below), we know only the first layer, Layer 0, is the only one that will currently render on our .mesh
Since Layer 0 is the visible layer, select it and it will load in the Material view:
This is where we edit the layer. If you only want to change the color to a common one, choose one from the lower color palette. Here I have chosen blue:
You can also change to another material texture here by clicking the preview ball:
This can be used to create different paint finishes, cloth texture, etc.
Once you have finished editing a layer, you can save it by clicking “Apply edits” at the top of the Layers panel:
Then you can select another layer and edit the next one if needed. If you select a new layer without applying the edits, the changes will be lost.
Once finished, you can Export to save it to your file explorer, and then load it in wkit by right clicking the .json
and selecting “convert from JSON”.
Example:
Save and Test in game
Working color:
Custom RGB value colors can be set with a custom .mltemplate
file. Here we will only set RGB values, but this file can control other material settings/textures as well.
Find the material you want to have a custom RGB value for in MlsetupBuilder, and click on the material name to show it’s file path. Example:
Add that file to the project, rename, and move it. Once it’s in place, you can copy it’s relative path and paste it into MlsetupBuilder for that layer:
Open the .mltemplate
file in wkit and navigate to the colorScale
array:
RDTDataViewModel > overrides > colorScale
This array contains all colors in the material. Open one of the Multilayers
and it’s “v
” values, which control the RGB values:
First, you can verify the color in . Copy the “n
” value here, and paste it into MlsetupBuilder in the Color filter:
Select the color, and it’s rgb values should match the values in the “v
” colorScale
in the .mltemplate
:
Edit the 0,1,2 (R,G,B) values to the color values needed and save. Make sure to save the .mlsetup as well with the layer set to the same color code as before.(“n” value, [00cf56_69039f])
Save and test.
To have multiple materials in a single .mesh file, the .glb export requires separate submeshes all selected in blender. Separate your blender .mesh into parts, and group them based on material.
First separate/split your meshes into small components, and then join them into groups:
For example, mine are grouped by body paint, silver trim, reflective black, matte black, and grey fabric. Take note, the order in your blender selection will be the order each submesh is called by the .mesh
file settings. This order is important.
With all the objects selected in blender, export it to .glb
and import it as you normally would.
You can save and test. If it worked properly, only the first object included in the blender export will render properly at the moment.
Example of a finished body mesh in blender:
Currently the other submeshes will not render properly. This is due to submesh materials being controlled by the chunkMaterials in your appearance settings (.app
file).
To correct this, start by duplicating an item in the chunkMaterials
list. Path:
.mesh > RDTDataViewModel > appearances > 0 > chunkMaterials
Make sure to edit the name to a unique name, as we are creating new materialEntries
. Repeat as needed. In my case I needed 6 extra layers:
Next, navigate to RDTDataViewModel > materialEntries
This list controls the names and index of each item in the localMaterialBuffer
, which we’ll edit soon. You’ll want to duplicate an item with similar qualities. If you are adding a plastic material, duplicate another plastic or solid material in the list. If you are adding glass, duplicate from a vehicle_lights
material, then edit as needed. Duplicate one for each new material.
Make sure to update the index values of each item in the materialEntries array. They must all be the same as their index in their current array. 4 to 4, and so on:
ALL items in the list update the index of both new items, and old ones that moved down the list
Next we’ll update the materialBuffer
with new .mlsetup
and .mlmask
files. Keep in mind the .mlmask
files can be edited to match the model to a texture. However we’ll be working around that by using the same method used previously, to have a .mlmask
file with one additional white layer.
Note the 0 layer will always be white.
Example:
Notice how I have a .mlmask
file dedicated to the 4th layer, so all other layers are muted. I have 20 (0 through 19) unique .mlmask
files, each selecting a different layer from a .mlsetup
file.
Speaking of .mlsetup
, I have created a .mlsetup
file with MlsetupBuilder. Layers 0 through 7 are used for the new materials:
Now to finish the materials setup, open the following .mesh
path:
.mesh > RDTDataViewModel > localMaterialBuffer > materials
This array contains every CMaterialInstance
for the .mesh
file. This array has it’s items named via the materialEntries
list we just edited. Since we added items to that list, those names now show in this localMaterialBuffer > materials
list. However the data in each item is still it’s original.
For example, in this picture, the array hasn’t been updated yet, however the names are updated. This means the index 1 item, …”masksset_texture
” has the properties of “reflector
”, which would normally be the 2nd item.
To fix this, duplicate the appropriate items, similar to before, once for each new material. Once done, the previous index 1 item will now be higher, but matched correctly. In my files, I duplicated “ml_v_sport2_porsche_911turbo_exterior_masksset
” 6 times, and they each pasted directly below the 1st.
You can tell if the materials are correct by opening them, and looking into the “values
” array. Each will have similar keyValuePairs
related to the material type. Glass
mentions GlassSpecular
, normal paint has many map layers, stickers dont have much, etc:
For each new material layer, update the paths:
CMaterialInstance > values > MultilayerMask > value
CMaterialInstance > values > MultilayerSetup > value
For mlsetup
, I use the previously mentioned file, with layers setup for individual materials.
For the mlmask
, use the “masksset_00
” for the layer you want to enable.
Submeshes should now load in order with matched materials.
Editing UV maps may be necessary for some meshes if they do not display correctly, or for custom editing. Broken & Fixed UV map example:
To edit UV maps in blender, select your object to modify and enter the UV Editing workspace:
The current UV map should display on the left:
In the right window in Edit Mode, select all vertices (“a” key in blender), and hit “U” for the UV mapping dialog. Select “Smart UV Project”, and hit OK:
This should update the UV mapping to an even spread map:
Save and test. Your meshes should have even material mapping now.
Some objects are better with different UV projection options. For example, a tire can be cleanly mapped by using the orthographic view and selecting Sphere Projection. Try some of these options to see what fits best for your use case.
Vehicle modding: landing page
This section collects information for vehicle modding.
For to Blender, check the Wolvenkit Wiki's step-by-step guide.
Boe6 has been so nice to share their (very detailed) and given full permission to transfer it to the wiki. However, the transfer is a work in progress (check )
A second guide is , although it is not very detailed.
For the visual learners among us, you can find a youtube video series by boe6 here:
Choosing the right way to shoot while driving a vehicle.
Most of the vehicles have the windows going down when you get your gun, allowing you to shoot without breaking it. Others have the door opening because the window can't retract itself, or because the glass is replaced by opaque screens. And special ones have mounted weapons, so no windows or doors, but big guns and missiles.
If you want to choose a specific opening for your custom vehicle, or just modifying existing ones, you can do it with couple of lines in a .yaml
file.
The string you need to change looks like this : DriverCombatTypes.Standard
There's 5 variations of it:
DriverCombatTypes.Standard
("Roll down driver and passenger windows for driver combat.")
DriverCombatTypes.Doors
("Open driver and passenger doors for driver combat.")
DriverCombatTypes.MountedWeapons
("Doors and windows do not open, player controls mounted weapon(s) on the vehicle.") PS: You need to add all needed weapons components to your vehicle to make it work, in .app, .ent and .mesh. I still need to explore this part)
DriverCombatTypes.Disabled
("Driver combat disabled.")
DriverCombatTypes.CrystalDome
("Unsupported for now")
Note : the MountedWeapons mode can only work if the vehicle has all the weapons and components equiped. If you try with no weapons, you will just have the visor.
(I will probably try to add more info about weapons if I have time to explore this)
Mounted weapons' locations are determined by the .ent file of the vehicle, and generally are identical to the base car .ent being used for the custom mesh. This can result in misplacement of the weapon emitters - the parts which actually shoot a bullet/missile - and requires some work to change. In WolvenKit, open the .ent file directly. In the file editor, expand RDTDataViewModel\components\vehicle_slots\slots
and scroll down, looking for the following entries, usually at the end:
VehiclePowerWeaponLeftSlotA
VehiclePowerWeaponRightSlotA
VehiclePowerWeaponLeftSlotB
VehiclePowerWeaponRightSlotB
VehiclePowerWeaponLeftSlotC
VehiclePowerWeaponRightSlotC
VehicleMissileLauncherA
VehicleMissileLauncherB
VehicleMissileLauncherC
In these records, slots can be assigned to specific bones in the rig, or have their position and rotation adjusted.
You can search for it using Tweak Browser inside WolvenKit.
Choose the vehicle name you want to find and look for this structure:
Vehicle.v_type_yourvehicle
Once you are inside, follow the path and unwrap the sub-categories. Here's an example with the Shion car.
The path looks like this :
▼ Vehicle.v_sport2_mizutani_shion_player
∟ ▼ vehDataPackage
∟ ▼ driverCombat
∟ enumeName : Standard
Two examples in Wolvenkit:
You can see that vehDataPackage
is also refered to Vehicle.v_type_yourvehicle_inlineX
( X = the line number inside the main file)
Result of the 2 previous example:
Vehicle.v_sport2_mizutani_shion_inline1
Vehicle.v_standard2_makigai_maimai_inline0
If you want to do some tests with various vehicles and see if it's working, you can do it using CET.
In the TweakDB Editor, search for your current vehicle and look for gamedataVehicleDataPackage_Record
.
You will have the content of the vehDataPackage
with the corresponding inline
number.
Look in the right column for the value of driverCombat
.
Expand the tab and write DriverCombatTypes
to see all the variations.
Now let's try to change the Porsche from Standard (windows) to Doors.
The door and the window will open at the same time if both have animations. (they probably use the same trigger)
You can now drive with the doors open, but without collisions on it.
If you want to add this setting on an existing car or inside your new custom vehicle, you can add some lines in your .yaml
file like this :
Lights are still somewhat of a mystery to me. That said, there are a couple routes to take for lights, depending on how you would like them to look.
Lights are using emissive (glowing) and glass materials. For more documentation about that, you can check .
Many lights, such as the headlights on the porsche, are reflective lights, with headlight lenses. These lenses require lots of lighting settings, such as IOR
, RefractionDepth
, FresnelBias
, and more.
Example:
The other common option, is to use solid lights, as many modded cars do. These lights avoid using the complex light options, and instead opt to use block-style lights. These are done by creating a custom mesh, setting it as a light material, and covering it with blank normal map lenses or glass. (IOR=1.0 & RefractionDepth<0.2) Example:
Here is an explanation of some light options, based on my own experience. Note that these values are not fully understood currently. If you learn something about these values, please share!
IOR
: “Index of Refraction”. This seems to be an on/off switch for enabling refraction. Values greater than 1 enable refraction.
RefractionDepth
FresnelBias
: Unknown. (does nothing?)
NormalStrength
: Strength of normal map on the mesh
NormalMapAffectsSpecular
: Strength of normal map on reflected light
Normal
: Path to normal map
GlassRoughnessBias
: n/a
BlurRadius
: n/a
For the emitted light beam, there are “vehicleLightComponent
”s in the .app
file components list. The localTransform
can be edited by the position xyz
bit values. Orientation will control which direction the light is aimed.
Example LightComponent
localTransform values:
To create a new light in a mesh that doesn’t have one already, you can duplicate the material in the mesh settings. Make sure to update the localMaterialBuffer
or preloadLocalMaterialInstances
, materialEntries
, and appearance chunkMaterials
.
See:
Once the vehicle_lights material is linked in the mesh, it also need to be linked to a vehicleLightComponent
in the .app
file. You can use an existing lightComponent
or duplicate a new one. The component has a parentTransform
entry, and a bindName
value which is used to set what mesh it is a part of.
Example:
For lights like brakes, headlights, or reverse lights - you need to edit the vertex paint in Blender. Select the light emitting object in blender and switch to "Vertex Paint" mode. Example:
Set the color to one that enables the brake behavior, for brakes this is red in hex:
You must use these specific hex colors for lights to be activated properly:
Headlights: #7C0101
Taillights: #e7010
Reverse lights: #cb0000
Use the Draw tool in Vertex Paint mode to draw on the color to the entire part. Example:
Lights should now be fully working.
Headlight colors are controlled in your vehicle's tweak file. You need to add this entry to your main Vehicle
record:
The values are RGBA respectively so the above changes the headlight color to blue
Adjusting the tweak file from the previous section, here's how it should all look like:
How to make interior lights work for your custom car
To make interior lights work, you first need to create the light boundary/shape which will contain the interior light. This is defined in an entLightChannelComponent
in your vehicle's ent
For eg in the porsche 911 .ent:
To create this shape, first we need to create a "cutout" of the interior mesh. Load your car's interior in a Blender project and join them all into a single mesh
Then you can create a cutout mesh by overlaying an Icosphere over your car's interior mesh and then using a Shrinkwrap modifier as shown:
Ensure the final cutout isn't high poly. Realistically, you don't need more than a few hundred vertices at maximum. High poly cutout may cause game crashes
You'll need to convert the .ent to JSON in wolvenkit, replace the indices and vertices array with the output of that script
Then you can convert the JSON back to .ent in wolvenkit
That's it! Like other light stuff (such as brake lights or headlights covered in other pages) - ensure you have an entLightChannelComponent in your .app parented to the interior.mesh (or whichever mesh has the interior light mesh + vehicle_lights
material + vertex colors assigned )
Adding CrystalCoat to your custom vehicle
CrystalCoat™ works in a unique way: they make use of Inkwidgets
Inkwidgets are used to create in-game UI - like car interior UI, in-game computers, etc
This is important to understand because if you're hearing inkwidgets for the first time - you have to understand, this is not what they are traditionally used for. The game cleverly hacked inkwidgets to use as a dynamic material. Imagine car paint but it's made of tiny LEDs. So while it's brilliant in theory, it has its own challenges when adding to a custom vehicle.
CrystalCoat is made possible with two core components:
The core inkwidget: this consists of two inkImageWidget
: primaryColor
and a secondaryColor
painted on a mask texture
The Crystal Coat .mt ( base\vehicles\common\materials\vehicle_modding_destruction.mt
): this multilayer shader works behind the scenes to load/unwrap the above inkwidget onto the car part, whereas traditional inkwidgets would need a separate submesh - CrystalCoat inkwidgets don't.
In your vehicle's .ent file, add a new appearance for Crystal Coat
We also need an entEffectSpawnerComponent for Crystal Coat fx effects. You can copy paste this as-is from the Aerondight .ent (base\vehicles\sport\v_sport1_rayfield_aerondight__basic_01.ent
): EffectSpawner3546
Define the appearance that you just created in the .ent: You can duplicate your default appearance to start with.
For all the meshes that will change color, we need to add a new mesh appearance that uses the .mt I mentioned earlier
To do this, simply duplicate your default mesh appearance, add a new customizable
material entry, and copy-paste the material definition from for eg. base\vehicles\sport\v_sport1_rayfield_aerondight\entities\meshes\v_sport1_rayfield_aerondight__ext01_hood_a_01.mesh
Remember to update the material values with your custom vehicle's mlsetup and mlmask paths
Set the customizable
material entry for all the submeshes that change color
WorldWidgetComponent
Go to Aerondight's .app file (base\vehicles\appearances\sport\rayfield_aerondight__basic.app
) and find its customizable appearance ( appearances > player_01_customizable_01)
Copy and paste all WorldWidgetComponent
s related to Crystal Coat (all of them will start with visual_customization_
) to your customizable appearance
Update the parentTransform
and meshTargetBinding
for each of the components to the respective car part in your appearance as needed (they don't necessarily need to match one to one: for e.g., you can use the fuel_cap WorldWidgetComponent for something that's not a fuel cap)
Add the following to your vehicle's tweak:
That's it! Install and launch - and you should see something happen.
Unless you're extremely lucky, you should start seeing issues. Roughly there are two buckets of issues:
What this is: Crystal Coat paint glitching to black or something in certain angles or certain TPP camera views
There are a few known causes and way to fix this:
You have more than 10 WorldWidgetComponent
s
The game currently for whatever reason (likely for resource management) glitches if you have more than 10 WorldWidgetComponent
s defined and used in your customizable appearance
To address this, for now, you can try joining meshes like the fuelcap doesn't need to be a separate mesh, it can be part of the body so you can save 1 WorldWidgetComponent
Angled meshes
Sometimes, or even randomly, certain meshes that are angled like door meshes for instance, can have a persistent glitch effect that comes and goes in certain angles
To address this, first try 'breaking' the component name
Crystal Coat identifies different car parts by looking at their component names, which are standard and common across cars like door_fl_a
, etc
So for example, if you're facing glitches on your left door, try renaming your door_fl_a
entPhysicalMeshComponent > door_fl_a_broken
Remember to update parentTransform
and meshTargetBinding
for the corresponding WorldWidgetComponent
and see if this fixes your issue
If it does, rememeber to also update the destruction
> detachableParts
tweak record in your vehicle's tweak file since the door component name is now different
What this is: rectangluar or something artifacts on the paint OR inconsistent or incorrect direction of gradient colors
To fix this, we need to understand how the core inkwidget functions
To do this let's look at the Crystal Coat inkwidget (WorldWidgetComponent
> widgetResource
> base\gameplay\vehicles\visual_customization\vvc_car_appearance_widget.inkwidget
)
Click into the "Widget Preview" tab > Export inkWidget as XML > save and open the XML file
The XML file (although currently Wkit doesn't support editing XML > inkwidget) will give you a good overview of how the inkwidget functionally works.
There are a lot of components (that aren't really needed -- we will see this in the next section) however the most important section is this one:
Here we see two InkImageWidgets
being defined for the two colors that can be set for Crystal Coat. We also see a textureAtlas
-> this is the mask which defines how the color is painted and controlled further with the layout
and renderTransform
values.
This is primarily what we're concerned with. The artifacts originate from these masks and any other color gradient issues as well
For most custom vehicles, it makes sense to have only one color i.e primaryColor as the customizable option. While it is possible to define both and somehow adjust the layout values to get the correct orientation, I have not tried that so let's focus on making this inkwidget paint ONLY the primaryColor
Tip if you're looking to customize this further and/or get correct gradients with secondaryColor: use the uvchecker.inkatlas
(this is already defined in the inkwidget) as a mask to see how the inkwidget wraps around a car part
To do this, go back to the edit view and go to libraryItems > Root > package > inkWidgetLibraryItemInstance > gameController
You should see primaryColor
and secondaryColor
From here you need to swap the mask with a completely white mask, adjust the layout an d renderTransform values so that primaryColor occupies the entire space. Then you need to make secondaryColor disappear similarly: you can delete the textureAtlas value and make the size = 1,1 which should make it disappear.
Remember to update the widgetResource
path in every WorldWidgetComponent
to the custom inkwidget in your project
Note - you might also notice glitchAnims (and a dedicated an .inkanim file): these are used when the car gets hit by a bullet or collission. Feel free to remove or tweak thos as well.
As of 2.12a, the base color (i.e. defined in the mlsetup of the customizable
mesh appearance) of the vehicle has a heavy influence on the final Crystal Coat color
For eg, if your base color is white, then it's impossible to get accurate darker colors. And vice versa.
The Crystal Coat GUI is primary controlled via this Inkwidget: base\gameplay\gui\widgets\notifications\vehicle_visual_customization.inkwidget
If you want to load a custom GUI for your custom vehicle, you can use this redscript:
You can replace assets by adding the respective .inkatlases to your project and linking them inside the inkwidget (fool proof of way of doing this is by converting the inkwidget to JSON, CTR + F and replace paths)
This way you can replace the default car preview with your own, remove the Rayfield logo, etc. You can also change the text by editing the Lockey
To remove the secondary color picker, CTR + F in the inkwidget JSON for inkCircleInnerWidget
, colorPickerInner
, pointerTargetSelectedInner
, inner-circle
, gradient_circle_inner
, color_wheel_inner
, CirclesInner
and set their opacity
values: 0. Also set the isInteractive
value to 0 (wherever it is set to 1 for the mentioned). You can then adjust the outer color picker as well, replace it with maybe a thicker color wheel like so in the inkatlas file:
In this section I will explain the minimal steps required in order to enable the feature on the vehicle. I will use the Mahir Supron vehicle as an example. You can buy this vehicle at the autofixer.
First you need to make your vehicle entity aware of the CrystalCoat feature. To do this you need to use TweakXL. Open WolvenKit and look into the Tweak Browser. Type your vehicle record name and right-click on it. Then create a TweakXL Override.
This will generate a new YAML file with the record name into your project at resources/r6/tweaks
. Open it and write this content. If needed, replace the vehicle record name at line 1 with yours. We will fill the customizableAppearance
field later.
This will tell the game to allow this vehicle to toggle CrystalCoat and to display the color picker widget. Now in the vehicle entity (*.ent
) file you need to create a new appearance dedicated to CrystalCoat for your vehicle.
The game will load the vehicle using its base appearance that is defined in the vehicle record at the field appearanceName
. This is what it does for all entities.
If the vehicle supports CrystalCoat, the game will assemble the entity during loading by swapping the appearance silently with the customizableAppearance
.
What is important to note is that the appearance being used will still be the base appearance. But the components being loaded and visually present on the screen will be the customizable ones.
This is why I am saying that the customizableAppearance
is being used silently.
When you toggle the CrystalCoat feature the game plays a distortion effect on the vehicle. This must be appended into the ENT file in the components > EffectSpawner3546 > effectDescs
array.
The easiest way is to copy the effect entry from the Rayfield Caliburn's ENT file and paste it into yours.
If you are editing an existing vehicle of the game, add the ENT file into your project and place it into a folder with a unique name to prevent collision with other mods.
Create a root folder named <mynickname>_modding
and place everything into it.
Open the ENT file in WolvenKit. Click on the appearances
array and then create a new appearance on the right.
Now define a unique name for the CrystalCoat appearance and write it down into the two fields:
appearanceName
name
It is not mandatory to use the same name in both fields but it will make things more simple. I would advise you to define a name that will not collide with another mod. You could for example use your nickname followed by the vehicle model followed by a CrystalCoat suffix:
Don't forget to write this appearance name into your YAML file in the customizableAppearance
field ! This field corresponds to the name
field of the entity appearance (not appearanceName
).
You must also set the path to the APP file of the vehicle which contains the actual appearance definition. The appearanceName
field must correspond to the appearance name that you will define into the APP file.
If you are modifying an existing vehicle of the game you should not replace the ENT file of the base game with yours or this will prevent other modders from adding new appearances to this vehicle.
Instead you shall use ArchiveXL to append your entity appearance to the base game ENT file dynamically. Create a new XL file by using the WolvenKit menu File > New File
. Click on ArchiveXL then select the XL file type.
This will create a new file into resources
folder. Add this content into it.
On the left side you must use the relative path to your own ENT file. Right-click on your ENT file in WolvenKit and copy its relative path then paste it here.
On the right side you must use the relative path to the base game ENT file. Find it using the Asset Browser and copy-paste its relative path.
Now you have created the top-level appearance you need to actually define it. This part is done into the APP file. Find the APP file using the Asset Browser and add it to your project. Then place it into your unique folder.
You shall duplicate the base appearance of the vehicle and change its name with what you have defined previously in the appearanceName
field of the entity appearance. Then remove all appearances from the file leaving only the one you have just created.
Then you shall add engine\ink\textures\4x4_transparent.xbm
into the resolvedDependencies
array of the appearance.
This page will show you how to create the base files that will tell your game about the new car.
Make sure you installed TweakXL.
.yaml
fileThis file contains the which will register your car with the game's database.
After creation, you can find it in your Wolvenkit project's tab.
Create a new tweakXL file by going to “New File” in the top left of wkit, just next to the HOME button.
Select TweakDB and TweakXL file. Name it something specific to your mod.
example: “boe6_mini_cooper.yaml
”
Open the .yaml
file in your favorite text editor. I used notepad++.
Create a new tweak entry for your model.
$base
:This should be set to the most similar in-game vehicle to your project. This is the tweak record that your car will mirror unless you set your own. You can find tweak names in the wKit tweak browser, search for the source car in the search bar on top. Look for Vehicle.v_NAME
, it’s first entry should be “gamedataVehicle_Record
”
example: “Vehicle.v_sport2_porsche_911turbo
”
appearanceName
:Needs to be set to the appearance name in the vehicle’s .ent
file. The .ent
file can be found inside your source vehicle’s tweak records. Locate the vehicle’s tweaks like before, Vehicle.v_sport2_porsche_911turbo
for example, and scroll down for an entry titled “entityTemplatePath
”. It should have an entry similar to this:
“Base\vehicles\sport\v_sport2_porsche_911turbo__basic_01.ent
”
displayName:
Set with your json
file below.
player_audio_resource
:This line is not necessary yet. It can be swapped for another vehicle’s engine sound later with whichever sounds closest to your desired vehicle.
entityTemplatePath
:This is the file we just added in the appearanceName
step earlier. To easily get this: in the project explorer, right click on the file and hit rename, or press F2. In the open window you can copy the path starting at base/…
You can also copy the relative path by right clicking the file and selecting “copy relative path”. Now you can paste it into the .yaml
entry.
Add your new vehicle tweak to the vehicle_list
tweak, so the game can find it.
Note the 2 spaces, followed by a dash, followed by another space, then the !entry
. Syntax is very important for .yaml
files
.json
fileThis file contains the translation strings, such as your car's name and description.
Create a .json
file in your project, the same as creating the .yaml
. The option is about half way down the “CR2W Files” category. Name it the same as your vehicle (e.g. “boe6_mini_cooper.json
”)
You’ll want to create your own project path at this point. With your mouse over the new .json
file, on the right side hit the yellow “open in explorer” button to find the .json
file in your file explorer, you should see just your .json
file and a “base
” folder. Create a new folder/path for your project files here. I created “boe6/mini_cooper/
”. Now in wkit, move your .json
into the new folder.
Your project file structure should now be similar to this:\
Open the .json
in wKit by double clicking.
Under RDTDataViewModel
, click on “root
” : handle:ISerializable
.
“root.root
” will appear on the right side. Next to handle:ISerializable
, click the yellow “Create Handle” button.
In the window that opens, click on the bottom text box to type in the following:
“localizationPersistenceOnScreenEntries
”
You can use the autofill. Click “create”
Notice that you have to manually save items in this window. Ctrl+S works. Same rule applies for .ent
files, .app
files, and any other files edited directly in wkit.
Now under RDTDataViewModel
, “root
” is expandable. Click on the entries array under “root
”.
Now on the right side, click “Create Item In Array”
Now we'll customize the new entry. Expand the item options and complete it to these values:
“primaryKey
” can be left at 0
, and “maleVarient
” can be left blank.
“secondaryKey
” is what your .yaml
is referencing. This is the name given that will link to the .yaml
.
Example: “boe6_mini_cooper_name
”
“femaleVariant
” is the actual display string. In this case, the vehicle’s name as it should be shown in the menus. Example: “Mini Cooper S
”
Create 2 more entries in the array by right clicking the first array entry we just created, and hit “duplicate item in array/buffer” as shown:\
Swap the 2nd and 3rd string values for your vehicle’s year
and description
text.
example:
2nd:
secondaryKey: boe6_mini_cooper_info
femaleVariant: fancy description
3rd:
secondaryKey: boe6_mini_cooper_year
femaleVarient: 2003
\
Final .json
should look similar this:
Return to your .yaml
file and make sure your vehicle’s “displayName
” is your secondaryKey
entry. See earlier .yaml
screenshot.
At this point you can test your mod to see if it loads in-game correctly. In general, you’ll want to launch the game and test as often as you can. I recommend changing one file at a time and testing each along the way to easily diagnose issues.
In wKit, at the end of the same line as “New File”, there is an “Install” button. For ease of testing, click the down arrow to show options and switch to “Install and Launch”. Now press “Install and Launch” to test the mod in-game.
Command Example:
It should show up as “TEST” in the vehicle call menu, as we haven't linked the json file to our mod yet. Once you call it, it should drive up with the model you are mirroring.
If at any point the result is not the one expected you can find the TweakXL log in `(Game Dir)\red4ext\plugins\TweakXL` and the ArchiveXL log in `(Game Dir)\red4ext\plugins\ArchiveXL`. It may not tell you what's wrong in great detail, but it will help you narrow down and fix the issue, or verify that there is none.
If the command does not add your vehicle to the vehicle call list, check if the Tweak Records
have been saved correctly. To do this, open the TweakDB Editor in the CET overlay. Search for your vehicle’s name, and look for the Vehicle.name
Record.
Note that the vehicle’s scan details will not be correct yet. The vehicle model and description will display as loading.
Move your source vehicle’s .ent
file into the same file path as your .json
file. Copy the file’s new path starting after “archive/…
”, and update your .yaml
tweak record so entityTemplatePath
is showing your new path.
Example:
Save the file, and test again by using the Install and launch button. Load a save from before you’ve used the summon command in CET console.
Rename the .ent
file and update the tweak in .yaml
, like last step.
Example:
“boe6\mini_cooper\boe6_mini_cooper_basic.ent
”
Now we’ll clean up the .ent
file. Open it in wkit by double clicking. Under “RDTDataViewModel
”, open the “appearances
” array.
Find the appearance you’re currently mirroring and make note of it. Then select all the other appearances with ctrl+click, then right click and hit “Delete Selection in Array/Buffer”. This will leave you with one appearance. We’ll duplicate this later when we get to adding different paint colors.
Edit the “name
” value to edit the array item. In my case, I change it from “porsche_911turbo__basic_johnny
” to “boe6_mini_cooper_basic
”
Also update your .yaml
tweak to this for the appearanceName
value.
Save, Install and Launch, test that it still works.
.app
fileAdd the .app
file to your project. Find it by navigating to the appearanceResource
in your main appearance, which links to an .app
file. Hit the yellow “Add File to Mod” button.
You’ll want to clean the appearances from this file as well. In the .ent
file, note the appearanceName
value. Then look at the .app
file for the matching item in the list. That is the appearance we want to keep. The other appearances can be deleted as you did with the .ent
file.
Now we can edit the appearanceName to an appropriate value related to our car.
Example:
And update it in your .app
file as well in the “name
” value:
Save and test. Get in the habit of testing often!
Notice the “components
” list inside the appearance settings. These are the main parts of your vehicle, and all 3D model parts are referenced through here, with a .mesh
file.
Move your .app
file and rename it to a relevant name.
Example:
“boe6\mini_cooper\boe6_mini_cooper.app
”
Update your .ent
file so the appearanceResource
value matches the new file name & path.
Install & Test.
Open up your .yaml file.
We’ll start with adding a logo record for the vehicle’s brand/manufacturer. Add a UIIcon
Tweak Record, with a $type: UIIcon_Record
, atlasPartName
, and atlasResourcePath
.
Example:
\
Copy the part name and path from your mirror car’s UIIcon_Record
Tweak, which you can find in the tweak browser in wKit:
We’ll update these to custom file names/paths soon.
Next create a Tweak in the .yaml
for the vehicle’s brand. This needs $type: VehicleManufacturer_Record
, and an enumName
.
Example:
Now we need a gui data Tweak Record for our vehicle, with $type: VehicleUIData
, productionYear
, and info: LocKey#[vehicle]_info
.
Example:
The LocKey# references your entry in the .json
file.
\
Now we create a UIIcon Record
for the vehicle’s image in the call menu, titled vehicle_icon
or similar.
Example:
Now these items can be referenced in the main vehicle tweak record. Add:
icon: UIIcon.[your_vehicle]_icon
manufacturer: Vehicle.[brand]
Example:
\
Add Virtual Car Dealer Tweak Values. Main settings are dealerPrice
, dealerCred
, dealerAtlasPath
, and dealerPartName
. We’ll just do the first 2 for now until we create the images for Virtual Car Dealer. These are added to you main vehicle.
Example:
Note that Tweak record order is important. Those that are referenced by another record need to be defined earlier in the document.
Recommended order:
UIIcon.Brand_Logo:
Vehicle.Brand:
Vehicle.vehicle_data:
UIIcon.vehicle_icon:
Vehicle.vehicle:
Vehicle.vehicle.[dealer settings]
Vehicle.vehicle_list.list:
Example of currently finished .yaml
:
.xl
fileNow we will add our .xl
file.
This file lets the game know to use our .json
file as localization.
Create a new file in wkit as done before. Under the ArchiveXL category, select “ArchiveXL file”. It should automatically open in your default text editor. Edit it with just 3 lines, a file structure for the .json
file.
Example:
Save and test.
The vehicle’s call menu should now show the vehicle’s name as specified in the .json
, as well as the description info text.
Rename your .xl
file to something appropriate. Files can cause issues if you use the same file name as another mod. Example:
“resources\boe6_mini_cooper.xl
”
Note that any/all .xl
and .yaml
files in the resources folder of your project will automatically load in-game.
\
There's multiple ways to write the inlines records, as mentionned on the by psiberx.
Once you have your cutout mesh, use this script to generate the indices and vertices for wolvenkit:
If you don't want to do all of this, feel free to download this inkwidget where I've already done all of this:
You can see a demonstration of this primaryColor-only functionality in this custom vehicle mod:
This is unfortunately not a problem that can be fixed in the core inkwidget, and needs messing with the shader itself which this mod by ShinnPL does: by (likely) changing how the inkwidget/crystal coat shaders work under the hood
Script credits thanks to
This gui inkwidget is also available at
After creation, you can find it in your Wolvenkit project's tab.
Once in-game, you can use the console to add the vehicle to your inventory. Swap “boe6_mini_cooper
” for your vehicle’s Tweak Record.
This file contains your car's components for the individual appearances. You can read up about at the link.
It's a problem that occurred when I created the vehicle mode and how to solve it
Each mesh component that you want to make colorable needs its own WorldWidgetComponent. For example you want the body_01_painted_custom
component to be colored so you need to create a new item in the component list with type WorldWidgetComponent.
Now we will set it up for CrystalCoat. Some of its fields shall use specific values.
For both the meshTargetBinding
and parentTransform
fields you shall create a handle. To do this click on the field, then on the right create a handle and select the only available type. Finally write the name of the mesh component into the bindName
field of it. In this example the component name is body_01_painted_custom
.
The name of the WorldWidgetComponent can use a pattern that will allow the script to handle any component for CrystalCoat. In order for this to work you must always use a name like this:
For this example the name will be visual_customization_body_01_painted_custom
.
Now for the widgetResource
field you must give it the InkWidget file that defines how the color will be wrapped onto the mesh.
The game uses a InkWidget that is customized for the Rayfield Caliburn or for the Rayfield Aerondight vehicles so it is unlikely to be usable as it is for any other vehicle without displaying unmatching patterns.
The details of how this InkWidget is structured and how to modify it will be covered into another paragraph. We will start in easy mode for now so we want a InkWidget that can be used on any part of any vehicle.
I have modified the InkWidget from the Rayfield Caliburn of the game v2.12a
so it will display only a primary color on the entire UV map.
I will explain into another paragraph how the InkWidget is structured, how it works and how you can modify it to define a secondary color for your specific vehicle.
This ZIP archive contains 6 files:
primary_color.inkatlas
: this file uses a simple white square texture from the base game in order to apply a primary color for widgets.
secondary_color.inkatlas
: this file will be useful to add a secondary color mask to the widget.
secondary_color.xbm
: the mask texture associated with the InkAtlas.
vvc_car_appearance_widget.inkwidget
: this widget is meant to apply both a primary color and a secondary color on the vehicle. The secondary color will be applied using a mask. This is the most advanced widget we will use at the end of the tutorial.
vvc_full_primary_only.inkwidget
: we will use this widget at the beginning. It only applies a primary color on the entire component it is bound to.
vvc_full_primary_and_secondary.inkwidget
: this widget applies both a primary color and a secondary color on the entire component it is bound to. It does not use a mask file for the secondary color.
You must deploy these files into your project's unique folder, for example into a widget
folder. Then we need to update the relative to the InkAtlas files into the three widgets.
When it comes to modifying a resource path into a InkWidget file it is more reliable to convert the widget into JSON and to modify the text file.
Right-click on the InkWidget file and click on Convert to JSON
. This will create a clear-copy of the file as readable JSON into the raw
folder. Open it with a text editor and replace the 3 occurrences of:
By your relative path to primary_color.inkatlas
(you must use double-backslashes \\
). Next, replace the 2 occurrences of:
By your relative path to secondary_color.inkatlas
(you must use double-backslashes \\
).
Then save the file and right-click on the readable JSON file in the raw
folder of the Project Explorer and click on Convert from JSON
to rebuild the InkWidget file.
Repeat this process with the other InkWidget files
vvc_full_primary_and_secondary.inkwidget
contains 5 occurrences of primary_color.inkatlas
and none of secondary_color.inkatlas
.
vvc_full_primary_only.inkwidget
contains 3 occurrences of primary_color.inkatlas
and none of secondary_color.inkatlas
.
Now your InkWidget files are ready to be used by any component. For now we will only use the vvc_full_primary_only.inkwidget
. Copy the relative path to your InkWidget file and write it into the widgetResource
field of the WorldWidgetComponent.
You now have a WorldWidgetComponent properly configured for CrystalCoat. All you have to do in order to work with another mesh component is to duplicate this WorldWidgetComponent and then update its meshTargetBinding > bindName
and parentTransform > bindName
fields with the new component name.
You can finally test your mod !
Wait ! My doors are not colored ! This is not good !
This is great ! Now we need to apply CrystalCoat to all 6 doors and the side mirrors which correspond to these components:
Driver door: door_fl_a
Front passenger door: door_fr_a
Back left door: door_bl_a
Back right door: door_br_a
Trunk left door: trunk_a
Trunk right door: trunk_b
Left mirror: mirror_fl_01
Right mirror: mirror_fr
All you have to do is to follow this tutorial for each part of the vehicle.
Wait ! This is cheap CrystalCoat ! What kind of green is this ?
As you can see the base color of the vehicle under the CrystalCoat is still present and modifies the resulting color. When applying CrystalCoat on a vehicle we shall use dark gray or black color as the base color. I will cover this modification later in the tutorial.
Now you have added all 6 doors and side mirrors to CrystalCoat, you need to make the new components detachable along with their original component.
Use the Tweak Browser and look into the vehicle record in the destruction > detachableParts
. Then look for the parts that correspond to the one you have duplicated.
In this picture you can see the TrunkLeft
detachable part that corresponds to the trunk_a
component. As we have duplicated this one to create the trunk_a_painted_custom
, we need to add it into the list of components for this detachable part.
In order to create a YAML file dedicated to detachable parts, right-click on the detachableParts
array and create a TweakXL override.
Then right-click on the TrunkLeft
element and select TweakXL > Copy to clipboard
. Finally erase the YAML file content with this data.
We want to append a new element to the components array.
Then repeat the same operation for all relevant detachable parts of the list. You should now have a YAML file with this content.
Concerning the trunk_a
part, you can see that it is also present into the destruction > deformableParts
array.
So you need to perform the same operation that we did for the body_01_painted_custom
component.
Let's say we want to give the user some choices to customize his vehicle. It would be great to allow for multiple paint aspects just like in real life.
If we want to allow the user to choose between multiple paint aspects we need to create a choice mechanism. Mod Settings is made for this purpose. So let's create a new settings class into our script.
Let me explain what lies into the following code snippet. The SettingsPackage
class contains the settings you will see in the Mod Settings menu of the game. Here you have two settings paintAspectEnabled
and paintAspect.
The first one is a toggle that allows to turn on the custom paint feature. Indeed you still want to be able to use the default CrystalCoat we have already created which is what the base game is doing. The second setting will be enabled only if the first one is enabled too because I have set a dependency
attribute (line 23).
The second setting uses a enumerated type EPaintAspect
which is defined on top of the snippet. This enum will contain any paint aspect we want to define. To begin I have added a single one that corresponds to a metallic paint.
The second class is the MyModSettings
class that we will call anywhere into the script to access our settings. This class contains the settings package instance into its settings
field.
In order for this code to work into your mod you need to perform some case-sensitive replacements. First replace occurrences of "MyModName"
(including quotes) by your mod name (with quotes) as you want it to appear in the ModSettings mods list. You should have replaced 2 occurrences.
Now you need to replace MyNickName-MyModName
by what you may have already used during this paragraph to define your secondary keys for translation. You should have replaced 7 occurrences.
Finally replace MyNickName.MyModName
by your module name which is at the very top of the script file. You should have replaced 1 occurrence.
You also need to add a new Painted
value into the EMeshAppearanceCC
enumerated type so we can handle paint materials.
Finally we need to revise our Utils.CustomizeMesh
method that actually swaps mesh appearances so it can accept a new one.
In the switch
statement at line 7 we look for the vehicle model. You may need to update it if you are not using the Mahir Supron.
What this code block does is select the relevant mesh appearance depending on the CrystalCoat state. If it is turned off then it will use the standard
mesh appearance. Otherwise if the custom paint toggle is enabled it will use the selected paint aspect (metallic
) and if the toggle is disabled it will use the default coated
mesh appearance you already know.
Then the for
loop at line 24 looks for a corresponding WorldWidgetComponent associated with the current mesh component if one exists.
And finally the final switch
statement at line 31 is actually affecting the new mesh appearance to the component.
Now the code is ready, we need to add new localized strings to our existing JSON files. In the following content you need to replace the MyNickName-MyModName
occurrences by what you have used previously. If you don't have created a translation file yet, then refer to this paragraph.
You should have replaced 6 occurrences.
We have just created the mechanism that allows the user to choose a different paint. Now we need to create the new paint material and add it into our mesh files.
Duplicate your existing coated.mlsetup
file and name it metallic.mlsetup
. Then convert it into JSON and open it in MlsetupBuilder (MLSB). Then in MLSB import layers to the editor by clicking on the orange button.
We know that our paint layer is 0 so we just need to modify this layer. Into the material template field use this one.
This is the default metallic car paint material of the game. Then set these values into the material settings. Apply edits on the left before doing anything else or your changes will be lost.
Finally export the file and erase the JSON file. Then convert the JSON file back into mlsetup in WolvenKit.
Add a new mesh appearance in the appearances
array with name metallic
containing a single chunk named metallic
too.
Now you need to update the mesh file for each of your *_painted_custom
components and add a new metallic
material into them.
To do this duplicate the last entry of the materialEntries
array and name it metallic
then right-click on the array and choose Recalculate child index properties
.
Then in the localMaterialBuffer > materials
array right-click on the coated
material and select Copy from Array/Buffer
. Then right-click on the array and select Paste into Array/Buffer
so it will be added to the end.
Now we need to set the material parameters. Open the metallic
material and modify its MultilayerSetup
field with the relative path to the new metallic.mlsetup
file you have created.
Next we need to add several parameters into this material. To do this click on the values
array and on the right click on Create Item In Array
. Then for each parameter you will need to use the relevant type.
Simple numbers like CoatFresnelBias
or Opacity
are using the Scalar
type.
RGBA colors like CoatTintFwd
are using the Color
type.
Fields that use a XBM file path like GlobalNormal
are using the Texture
type.
For each parameter you need to set its name and its value. Then you will be able to copy these parameters or even the entire metallic
material into the other mesh files as they all use the same mlmask and mlsetup in the case of the Mahir Supron.
Otherwise you need to copy the parameters into the existing material you have previously duplicated.
Now lets see the result.
Nice ! But I can see some stairs in the paint on the back doors !
As we have isolated all the painted parts it allows us to use a dedicated mlmask that will only allow the paint layer to be displayed. All of our painted components will use this new mlmask.
Copy any existing mlmask and rename it painted.mlmask
. For example use the one from the Mahir Supron.
Use the export tool Tools > Export Tool
to generate PNG images for each layer in the raw
folder. Then go into this folder and see that there are 20 layers as PNG files.
We know that the layer 0 is responsible for the painted areas. Then all we need to do is to use a full white image for layer 0 and a full black image for all the other layers so we are sure that they won't conflict with our paint.
In the case of the Mahir Supron the side stripes are created by the layer 2 so we need to keep the layer 2 image unchanged.
The layer 0 is already a full white image so there is no need to change it. Either find another layer that is already full black or create one with an image editor software using the same image size. Then duplicate this file for all the other layers except layer 2 and reuse their name.
Then import the layers back into the mlmask file using the Tools > Import Tool
.
Then affect your new mlmask file to all the painted components in the metallic
material in the field MultilayerMask
.
Then test the mod and see if the problem is solved.
Now all of our painted components are fixed. This metallic paint is beautiful !
For the Mahir Supron we could have kept the original mlmask file and instead turn the opacity for all layers except layer #0 and layer #2 to 0 into the mlsetup file. This could have worked because the Supron is using the base layer 0 as the paint layer and this layer is always a full white image so there is no UV drawing problem.
But some other vehicles use a specific layer for the paint, thus they draw all the painted parts into that layer. In this case, if the drawing is messy and lacks precision the only way to fix it is to modify the mlmask layer. This is why I have chosen to modify the mlmask so you can use a solution that works in all cases.
What if I want to add another paint aspect ? Like a glossy paint ?
The work is essentially the same as the metallic paint we have just created. As we already have created the user choice mechanism before we can now simply add a new paint type into the script.
At line 8 in this code snippet we need to add a new runtimeProperty
to translate the new enum value.
In the en-us.json
language file we need to add a new localized string entry. Do the same for any other language file you may have defined.
Duplicate the existing metallic.mlsetup
file and name it glossy.mlsetup
. Then open it in MLSB and replace layer 0's template file with this one.
Then set these parameters for layer 0.
Then save the file and import it back into WolvenKit.
For all painted components duplicate the metallic
appearance and replace its name and chunk with glossy
. Then duplicate the materialEntries
metallic
entry and name it glossy
then duplicate the associated localMaterialBuffer > materials
metallic
entry.
Finally assign the new glossy.mlsetup
file into this material. Keep all the parameters identical to the metallic paint.
All you have to do now is to install the mod and select the new paint aspect in the mod settings !
Wonderful ! I can't believe it ! Well do you think that I could add other premios stuff to this vehicle ?
This tutorial provides a brand new way to implement CrystalCoat for any kind of vehicle and to make any part of a vehicle customizable by the user during runtime. The only limit is your imagination !
Created: Jun ?? 2024 by @hgyi56 Last documented update: -
Modification policy
The author of this tutorial does not allow other users to modify it without his permission. Instead users shall contact the author using the links provided above and post comments on the relevant pages.
From the in-game perspective, this is a technology developed by Rayfield manufacturer that enables a vehicle to get colored dynamically. You can think of it either as a huge matrix composed of tiny LEDs that would cover most parts of the vehicle allowing its user to select a color at any time.
Or it could also be a holographic technology that would cover the vehicle with some kind of 3D projection of a new color.
Now from the modder's perspective, this feature is made possible by applying InkWidgets onto mesh components.
InkWidgets were initially developed in order to display animated content for TVs, computer monitors or vehicle UI. From game v2.11 they are now also used to display colored shapes wrapped on a mesh dynamically.
In order for your mod to work you need to install dependencies from Nexusmods:
Keep in mind that this is my way of implementing CrystalCoat and there may be other ways to do it.
This tutorial assumes that you and your users will install either Crystal Coat Fix v2.12 or Enhanced Vehicle System.
Either of these mods contain a script part that is currently required for CrystalCoat mods based on this tutorial to work perfectly.
They will still run without one of them but the result will be degraded.
Well it depends on what you intend to do. This tutorial is incremental. It means that you start at the beginning and you can eventually stop at some point if what you have created satisfies your goal.
Although I explain things step by step it may not be exhaustive otherwise this tutorial would become very long. Here is a list of skills that will help you to go through this tutorial.
Usage of WolvenKit. Using its tools, adding game files into your project, modifying array elements, knowing how to pack and install a mod, importing and exporting files from WolvenKit.
Usage of MlsetupBuilder (MLSB). Installing and preparing the tool, importing and exporting mlsetup files, modifying a material layer.
Usage of Blender. Installing the Cyberpunk related extension. Importing and exporting a entity or mesh file, creating a submesh.
Usage of Adobe Photoshop (or another image editor). Have a basic knowledge of layers, channels, know how to use some tools like the magic wand, gradient tool, fill a selection, create a selection, create a layer style, apply a filter.
Scripting basics. Know how to navigate into a JSON file, YAML file, REDS file, perform a replacement of a string, append a block by copy-pasting it from the tutorial.
Usage of a good text editor. I recommend using Visual Studio Code and installing the redscript-ide extension and the redscript syntax highlight extension.
You can test my mod Crystal Coat for Rayfield Caliburn to have an idea of what can be done.
Things you will be able to do after learning this tutorial:
Add CrystalCoat to any kind of vehicle.
Define a secondary color.
Customize the color picker widget with your brand and vehicle image.
Create multiple paint aspects and use any of them during runtime.
Create multiple appearances for all the vehicle parts and use any of them during runtime.
Clean the rust and dust from your vehicle parts and windows.
Modify window glass.
Create any amount of customizable components on your vehicle. They can be sub-parts of existing component.
Customize the vehicle appearance when CrystalCoat is ON and when it is OFF during runtime.
Allow anyone to add translations to your mod.
Display vehicle part images in the mod settings menu.
Create a default settings package for your mod settings to allow mod community admins to customize the mod's default settings for their users.
We have added customizable parts in the mod settings in this paragraph. So we can define parts appearance when CrystalCoat is ON and also when it is OFF. Most of the time, users will want to customize only one of the CrystalCoat states at a time.
So it would be nice to be able to hide all CrystalCoat ON/OFF settings. Mod Settings allows to do this natively using dependencies.
In the general settings category we need to add two toggle settings.
The toggle for CrystalCoat OFF settings is disabled by default. Because unless wanted by the user, you want your vehicle to revert to its original appearance and keep the custom settings for the CrystalCoat appearance.
You must replace the secondary keys and the mod name with yours. Then into your JSON language files you must define 4 new keys.
Then for each setting that concerns the CrystalCoat OFF state, we need to add a new runtimeProperty
to create a dependency with the OFF toggle setting we have just created.
Each setting that concerns the CrystalCoat ON state needs a new runtimeProperty
to create a dependency with the ON toggle setting we have just created.
When you disable either one of the toggle settings, their associated settings we be hidden from the mod settings page. This will allow for a better readability of your settings page.
Another cool feature would be to display the vehicle parts associated with each setting so the user easily understand what he is modifying.
This feature requires to use additional files to add to your project.
This ZIP archive contains 3 files:
vehicle_part.inkatlas
: this file acts like an organizer for the following texture so the script can select only a portion of it.
vehicle_part.xbm
: a texture that contains one vehicle picture for each setting.
vehicle_picture.inkwidget
: this widget will display the vehicle image into the mod settings page.
Regroup these files into a folder then open the InkAtlas file and update its slots
array by using the XBM texture file path into the texture
field of each array element.
In order to create pictures for your settings you can use the photo mode of the game while your vehicle is using a specific appearance. In the example picture of the Caliburn above I have used a black matte paint with only the relevant component using a red glossy appearance.
First lets create the matte paint aspect following this paragraph. I will only talk about the differences. Concerning the mlsetup, the base mltemplate file to use is this one.
And the mlsetup parameters are these ones.
Next, for each impacted mesh file in the matte
material remove all parameters except the MultilayerMask and MultilayerSetup.
Now we want all customizable components to be able to use a red glossy appearance. In order to do this follow this paragraph. I will only talk about the differences here.
Customizable components are the roof, the sun visor, the wheels inserts and the wheels painted areas.
In order to a have a deep color we need to create a custom mltemplate and define color codes into it. Then use this mltemplate file into the mlsetup instead of the original one. Add this file to your project and open it in WolvenKit.
Go into overrides > colorScale > c2162c_null > v
and set these values (1, 0, 0)
for a deep red color. It uses a RGB float code. Index 0 is red, index 1 is green and index 2 is blue. Value 0 is black, 1 is white. Now save the file and copy its relative path.
In the mlsetup file use the path to your custom mltemplate file and then use the red color code we have modified c2162c_null
. Then into the red material definition of the mesh files, set these parameters values.
Now our parts can all be turned into black matte paint and red except for the sun visor. So we need to allow the sun visor to use paint materials. Let's make all of our parts be able to use the same materials. It will offer more options to the users.
For this we need to modify the type used by the sun visor setting field related to CrystalCoat ON state.
Next we need to create a WorldWidgetComponent to enable CrystalCoat coloring on the sun visor.
Finally we need to copy the missing appearances
, materialEntries
and localMaterialBuffer > materials
entries from the roof component and paste them into the sun visor mesh file.
We can simply copy them because both components have been created from the same body_01
component so they share the same mlmask.
We need to copy the coated
, metallic
, glossy
and matte
appearances and materials. Now our vehicle is ready for a photo shoot.
We have added a file named vehicle_parts.xbm
to our project. This file contains a matrix of pictures for each of the customizable components of the vehicle. In order to populate this texture we will use Adobe Photoshop or another image editor.
Now take clean screenshots of the vehicle focused on the red part. We need one picture for each customizable component.
Into your image editor create a new project with a size of 2000x2000 pixels and a transparent background. We are going to append our pictures stick to each other from top to bottom and apply some color correction. As we only have 4 pictures we can simply put them one under the other.
Import your pictures one by one and resize them to 800px large by using Edit > Free Transform
. Then use the Image > Auto Tone
to fix the overall colors. You must have one picture per layer. So you need to apply the color correction on each of them seperately.
Now we need to enhance the red color to make it more visible. To do this select the areas that correspond to the red parts using the menu Select > Color Range
. Then use a tolerance of 100 and once you are done, remove any part of the selection that wouldn't correspond to the red areas of the vehicle component.
Next use the menu Image > Adjustments > Vibrance
and set a saturation of 100 and a vibrance of 50.
Next we need to crop the bottom corners so the images will fit into the widget. To do this create a rectangle selection with size 70x70 pixels. Then use Select > Transform Selection
to rotate it to 45° so you get a diamond-shaped selection. Now save your selection using the menu Select > Save Selection
and name it "big diamond".
Now move your selection to the bottom-right corner of the first picture so the center of the selection is aligned with the corner. Then select the corresponding layer, right-click on it and select Rasterize Layer
. Then hit the DEL key to remove the pixels. Repeat this operation for the other pictures.
Now create a new selection with size 16x16 pixels and repeat the same operation with the bottom-left corner of all the pictures. You should now have this final result.
Export the vehicle_parts.xbm file into PNG using the Tools > Export Tool
. Now erase the PNG file with your new texture. Then import it back into WolvenKit using these options.
The next step is to open the vehicle_parts.inkatlas
and define the different parts of the texture. To do this go into the slots > [0] > parts
array. Edit the part_name
element by changing its name with the component name body_01_roof_custom
.
Then into the clippingRectInUVCoords
field we need to define the texture area corresponding to the picture. UV coordinates goes from 0 to 1 in both axis. (0;0) is the top left corner of the texture, and (1;1) is the bottom-right corner. As we have defined 4 pictures vertically we must divide the parts as following.
Create duplicate this element in the parts array to create the other ones.
body_01_roof_custom: Bottom:0,25, Left:0, Right:1, Top:0
body_01_sunvisor_custom: Bottom:0,5, Left:0, Right:1, Top:0,25
wheel_01_painted_custom: Bottom:0,75, Left:0, Right:1, Top:0,5
wheel_01_inserts_custom: Bottom:1, Left:0, Right:1, Top:0,75
Now save the file and go into the Part Mapping
tab to check the result. You should be able to select your pictures individually.
In order for the mod settings menu to use your pictures and display a widget we need to add some code to create this new feature.
First add the new class into your script. Then perform some case-sensitive replacements into it.
Replace MyNickName.MyModName
with your module name (line 13).
Replace path\\to\\vehicle_picture.inkwidget
with the relative path to yours. You must use double-backslashes (\\
) at line 18.
Next we need to add these two methods (outside of any class). Then perform some case-sensitive replacements into it.
Replace MyModName
with the name of your mod that you have used in the runtimeProperty
elements of your mod settings (line 49). This should be the name as it appears in the mod list.
Replace path\\to\\vehicle_parts.inkatlas
by the relative path to yours. You must use double-backslashes (\\
) at line 62.
Then as you can see we are using if
blocks (lines 66-93) to identify settings category (line 66) and field (line 68) using secondary keys. You must use your own keys there. Then we assign the texture part name into the widget (line 70) so you may need to update these part names too with what you have set into the InkAtlas file.
You must trigger a vehicle part picture for both the CrystalCoat ON setting and the CrystalCoat OFF setting. Example at lines 68-69.
Now you can test your mod and see that when you hover on the settings they fade out the mod list and make a picture appear to illustrate what this setting is refering to.
In order to clean a vehicle part we need to add its mesh file to our project, then assign this new file to the component in the APP file.
Next, identify the material being used by this component by looking into the APP file at the meshAppearance
field of the component. Then in the mesh file find the relevant chunk.
Here are the vehicle parts to clean for the Mahir Supron:
tire_01_fl_a
tire_01_fr_a
tire_01_bl_a
tire_01_br_a
wheel_01_fl_a
and *_custom
wheel_01_fr_a
and *_custom
wheel_01_bl_a
and *_custom
wheel_01_br_a
and *_custom
bumper_f_01
bumper_b_01
engine
junk_fuelcell_01
body_01
and *_custom
door_fl_a
and *_custom
door_fr_a
and *_custom
door_bl_a
and *_custom
door_br_a
and *_custom
mirror_fl_01
and *_custom
mirror_fr
and *_custom
By chance the tires mesh file already contains a clean
appearance for the Mahir Supron so simply use it in the APP components meshAppearance
field.
For the other ones we need to edit their material setup.
Add the mlsetup being used by the material to your project and add a suffix to it so we know what it is all about: _clean
. Using its original name and appending a suffix tells us that this file is meant to be accurate to the original except that we remove the rust and dust from it.
Assign this new mlsetup file to the material definition. Then open the mlsetup file into MLSB. Load the 3D model of the corresponding mesh and identify dirt / rust / dust layers. Turn their opacity to 0 and export the file to JSON. Then import it back into WolvenKit.
Mlsetup files are shared between multiple components so once you have edited the mlsetup you will just have to assign it to the other components that use the same mlsetup.
Wait ! I can see yellow on the doors plastic parts ! Also the aluminium on wheels is messy !
We know that the Mahir Supron is using layer 0 as the paint layer which covers the entire vehicle. This implies that if additional layers are not precisely drew then the underlying layers will be visible. Depending on how the game developers have created layers it may be more or less accurate.
From what I have seen on multiple vehicles they seem to have used brush painting to create masks which I find it to be a messy way to create layers except for rust and dust which are messy by design.
Instead we must use Blender to select the mesh parts that shall be impacted by a layer and then export the UV layout associated with this selection.
Finally create a mlmask layer using this UV layout. The result will be perfect to the pixel. It also depends on the layer resolution because if the layer image is too small (like 256x256 on some vehicles) then it will lack precision. I prefer to use 1024x1024 layers which is the case for most vehicles anyway.
Open one of your doors mesh file and look in the poor_06
material. Add the mlmask file to your project and add the _clean
suffix to it.
Export the mlmask, the 6 doors mesh files and the body mesh file using the Tools > Export Tool
. Plastic parts are defined in the original component for each component, so you don't need to export _custom
components files.
Import all the mesh files into Blender, hide unused chunks and select all the chunks that include plastic parts (one per component). Then go into Edit mode
using the Tab
key and select all the areas affected by plastic. You can move the different mesh objects to make things easier.
Then use the UV Editing
view in the menu bar to display the UV layout that corresponds to your selection. Use this menu to export the layout to a PNG image UV > Export UV Layout
.
Then be sure to set a size of 1024x1024 and to set a Fill Opacity
of 1.00. Also name your PNG file layer11.png
so we know which layer is associated with this layout.
Now into a image editor like Adobe Photoshop open the mask layer 11 from your mlmask PNG files set. Then import the UV layout you have just created into that project and create a layer style on it to fill it with white.
Then merge layers and save the PNG file in-place. Import the mlmask layers set back into WolvenKit using the Tools > Import Tool
. Finally affect this new mlmask file to impacted components into their poor_06
material at localMaterialBuffer > materials > poor_06 > MultilayerMask
.
body_01
door_fl_a
door_fr_a
door_bl_a
door_br_a
As you can see the aluminium parts of wheels have some black artifacts. This is due to a messy layer mask. So you just need to repeat the same operation as above for the _insert
mesh file of the wheels.
You need to fix the layer 7 of the mlmask. Then assign the new mlmask to the coated
and poor_01
materials of the _inserts
mesh file.
We only need to update these 2 materials for wheels because these are the only materials that are using the original mlmask of the game.
You must update the AppearanceVisualController
list when you create a new component or if you modify its name, mesh appearance or mesh file path.
Refer to this paragraph to do so.
Now test your mod and see that all plastic parts and wheels aluminium parts are clean !
Awesome ! Now I want to create a secondary color !
In order to get a nice result for our CrystalCoat implementation we need to use some scripting. This script will also allow us to go deeper into the customization later.
Download this template, rename it with your mod name, and place it into your project at this location.
Then edit the script using VSCode. You need to perform some replacements into the file to make it work for your mod. To replace text use CTRL + H.
Perform case-sensitive replacements to avoid mistakes.
First you need to define the name of your module at the top of the file. The module name acts as a namespace for your types definition so they won't collide with another mod. It also allows other mods to check if your mod is installed.
You must use a unique combination of words to define your module name. I recommend you to use your nickname followed by your mod name.
In order to change this perform a global replacement using CTRL + H. Replace My_Nickname.My_Mod_Name
using the case-sensitive option.
You should have replaced 3 occurrences.
Next you need to use your vehicle record name. In this tutorial I am using the Mahir Supron with this record name Vehicle.v_standard25_mahir_supron_player
.
If you are working with another vehicle then perform a global replacement of this sequence with yours. You should have replaced 1 occurrence.
Do you remember what I told you about how does the game use appearances when it loads a vehicle ?
The script needs to monitor entity loading and it must hook only if this is the vehicle entity that your mod is working with. This is why you need to tell the script what is the entity ENT file and what is the entity appearance name to look for.
You need to replace mahir_supron__basic_player_01
by the base appearance name of the vehicle you can find in the vehicle record using the Tweak Browser at the field named appearanceName
. You should have replaced 1 occurrence.
You need to replace the entity file path by the one from the base game, that is to say by the one that is actually loaded by the game during runtime (not your own file !). Remember that our own ENT file only serves the purpose of having its content appended to the base game file.
You should have replaced 1 occurrence.
You must use double-backslashes (\\
) for file paths in the script file.
This script can be extended to support multiple vehicles so you will find an enumerated type.
If you are not creating a mod for the Mahir Supron you should replace the model name in this type definition and also into the code. Replace the type definition manually, then replace usage occurrences by looking for .Supron
You should have replaced 2 usage occurrences.
When you know how to implement CrystalCoat for one mesh component you can apply the same work on the other components.
I will use the example of the body_01
component which is the biggest one of the vehicle. The idea is always the same: submesh the component into multiple ones so you can customize as many sub-parts of the component as you wish and treat them automatically with a script.
On the picture above of the Mahir Supron, you only want to color the light brown areas which correspond to the painted parts of the vehicle. Coloring the entire mesh wouldn't make sense.
We will isolate the painted parts into a new component with a single chunk. First go into the APP file and duplicate the body_01
component into a new one called body_01_painted_custom
. Adding a common suffix to all our customizable components will be helpful for the scripting part of the mod.
The name of all the customizable components of the vehicle will end with _custom
. Add the mesh file of the body_01
to your project and duplicate it. Then add the _painted
suffix to the duplicated file so we know what it contains. Finally assign these files to the body_01
and body_01_painted_custom
components.
You should now have this project structure.
Export both mesh files and then open one of them in Blender. You can see that the interesting chunk is the first one with index 0 so you can hide the other ones.
Select the faces that correspond to the painted areas and press P
then choose Selection
to create the submesh. Now export the new submesh alone into the mesh file that corresponds to body_01_painted_custom
and then export the other submeshes to the base mesh file.
Be careful not to duplicate submeshes between multiple files !
Import both files back into WolvenKit. Now we need to configure the new mesh component to allow it to be colored.
First open the APP file and set the meshAppearance
of body_01_painted_custom
to standard
.
Then open the mesh file of body_01_painted_custom
and remove all appearances. Create two appearances named respectively standard
and coated
.
The standard
appearance will have a single chunk that uses the original material poor_06
. This appearance will be used when CrystalCoat is turned off. The coated
appearance will use a single chunk named coated
. This appearance will be used when CrystalCoat is turned on.
Next, we know that the original material for this mesh is named poor_06
so remove all entries from localMaterialBuffer > materials
and materialEntries
arrays except this one. Then duplicate it in both arrays and name the new ones coated
.
You must rebuild the index of the materialEntries
array after you have finished to reorganize it. Right-click on it and choose Recalculate child index properties
.
You should now have this appearance list.
Check coated
material and see that it only contains the MultilayerMask and MultilayerSetup parameters using the multilayer_vehicle_destruction.mi
as the base material.
CrystalCoat requires the mesh to use a specific base material in order to be colorable. So you need to modify the base material of coated
material with this one.
CrystalCoat is meant to apply a color on vehicle parts but it must look like something pretty, with a glossy coating. To do this add the following parameters in the values
array. The easiest way is to copy the existing parameters from the Rayfield Caliburn vehicle and paste them into yours.
Open this mesh file from the Rayfield Caliburn and go into localMaterialBuffer > materials > premium_ext_07_customizable > values
. Then copy this list of parameters by right-clicking on them and choosing Copy Selection From Array/Buffer
.
Then right-click on the values
array of your vehicle's body coated
material and choose Paste Selection To Array/Buffer
.
When you perform one of these operations on a component into the APP file you must terminate your work by updating the list of components in the entVisualControllerComponent
named AppearanceVisualController
at the end of the component list.
Add or remove a component
Modify a component's name, mesh file path or mesh appearance.
As we can modify a lot of components, the most reliable way to update the list is to right-click on the APP component list and then choose Regenerate Appearance Dependencies
. This will rebuild the entire list in the AppearanceVisualController
.
Each vehicle has a list of deformable parts. If you look into the vehicle record using the Tweak Browser into the destruction
element you will see that the body_01
component is a deformable part.
As we have divided it into multiple components we need to add the duplicates to the list of deformable parts. To do this we must create a TweakXL override. Right-click on the deformableParts
array and create a TweakXL override. This will create another TweakXL file into the folder resources/r6/tweaks
.
We want to create a new entry, but as you can see the entry contains other entries. We must proceed from bottom to top in order to create our new deformable part.
The lowest level of entry is the zones
array. Deploy it and right-click on the first entry then click on TweakXL > Copy to clipboard
.
Edit your new YAML file and replace its content with it. Lines starting with #
are comments. They help to visually organize our file so it can be read more easily.
This is an existing entry we want to duplicate so add the suffix _1
after its name. Do the same for the second zone entry.
Then we are going to duplicate the level above zones
which is the deformable part entry. Append the data into the file and append the suffix to its name. Then use the previous zone entries you have duplicated to populate the zones
array of this new deformable part. Don't forget to update the component name too.
You should now have this file content.
Now we have duplicated the deformable part entry, we can finally append our new entry to the deformableParts
array. Using !append
tells TweakXL not to erase the entire array content but instead append the element into it.
You see that the YAML file is defined in a reversed order. First we define low-level entries (zones), then as we go through the file we define the deformable part and finally we append it into the top-level array.
This is just like building a house: you build it from the basement up to the roof.
CrystalCoat consists of displaying a colored widget above a mesh. The problem is that the mesh itself uses a multilayered material definition defined in a mlsetup file.
This file defines the material layers applied on associated mask layers (mlmask file) to get the final result we have on the screen for this mesh. Each layer also uses a color.
The Mahir Supron uses a yellow / light brown color for the painted areas of the vehicle. We need to modify this color and to use a gray color instead so it will be neutral for any CrystalCoat tint.
Generally all the painted parts of the vehicle use the same mlsetup / mlmask combination. For the Mahir Supron you can find these files in the material definition in the parameters MultilayerMask
and MultilayerSetup
.
Add the mlsetup file to your project and place it into your unique folder. Then rename it with a simple name like coated.mlsetup
. Then assign it to all your *_painted_custom
components in their respective coated
material definition: localMaterialBuffer > materials > coated > MultilayeredSetup
.
Then right-click on the file and convert it to JSON. You must install the MlsetupBuilder plugin for WolvenKit in order to modify a mlsetup file.
To do so go into HOME in the menu bar and then Plugins
to display the plugins menu. Install MlsetupBuilder and restart WolvenKit. Follow this tutorial to properly setup this tool.
Then right-click on the coated.mlsetup.json
file and choose Open in MlsetupBuilder
. Import the layers into the editor by clicking on the orange button.
If you have correctly setup MlsetupBuilder you can display a 3D model of the vehicle in the tool. To do so click on the Models library
tab then search for "Supron" and click on the body part.
Go back into the Editor
tab and click on each layer until you find the one that corresponds to the painted areas. Layers are ordered from 0 (top) to 19 (bottom) and are added on top of each other (1 is applied above 0, etc). Each layer is linked with the mask that uses the same number of the mlmask file.
If layer 0 and layer 1 cover the same areas then layer 1 will cover layer 0. Sometimes the paint-related layer is the layer 0 that always covers everything, but then all additional layers will cover non-painted areas of the vehicle so in the end, by substracting these layers area to the layer 0 you get the painted areas. In this case we would be interested by the layer 0 to modify the paint color.
You can also look at the color used by the layer to find the relevant one. We know that the Mahir Supron uses a yellowish/brown paint.
For the Mahir Supron the layer responsible for the painted areas is the layer 0.
We shall use a dark gray or a somewhat black color to replace the base vehicle color.
For this particular case we want to keep the stripes on the sides of the vehicle so we need to have a lighter gray color for layer 0 and a darker gray color for layer 2.
You can use the code 720cfa_d42857
for layer 0 and 3eaf77_69039f
for layer 2. Enter these codes into the text input next to the colored rectangle for each layer then immediately hit ENTER to validate the new color and immediately click on Apply edits
at the top of the layers list before working on the next layer.
If you want to keep everything else identical you can just export the file using File > Mlsetup > Export
and erase the JSON file and then convert it back into mlsetup in WolvenKit.
On some computers when exiting MlsetupBuilder tool the computer may crash with a BSoD. In order to avoid this crash I have experienced that using CTRL + R to reload the instance and then using the menu to exit File > Exit
tends to avoid crashes.
Wait ! My vehicle still has rust and garbage all over it ! This is bad work !
As we are modifying the material definition we can also clean the painted parts.
The different rust marks on the vehicle are part of some layers. In order to remove them we only need to change the opacity of the relevant layers to 0. Click on each layer to identify messy ones and modify their Opacity
slider to 0 then click on Apply edits
before working with another layer.
This is the list of layers to make invisible: 7, 8, 13, 15, 16, 17, 18.
Export the file using File > Mlsetup > Export
and erase the JSON file. Finally convert it back into mlsetup in WolvenKit.
The paint is now fixed to keep a neutral color that will suit any CrystalCoat tint. I have also cleaned the rust and dust.
Wait ! Are you kidding ? My windows are covered with crap !
You may have noticed that windows are still dirty and their tint feels a bit too clear. In order to fix this we need to add all the window mesh files into the project and then modify their material definition.
Window components are the following:
body_01_window_bl_01
body_01_window_br_01
window_bl
window_br
window_fl
window_fr
window_f
trunk_a_window_01
trunk_b_window_01
Assign your custom files to these components and then open the window_fl
mesh file. Go into the preloadLocalMaterialInstances
array and look into the glass
material definition.
This mesh is using a MI file as the base material. MI files are instances of a MT file bundled with predefined parameters so it can be reused easily on multiple components.
Add this MI file to your project and rename it window_glass.mi
. Then edit the file and change these parameters into it.
Then save the file and assign it to all your window mesh files. Also remove any existing parameter present in the values
array of the material definition. To quickly empty an array right-click on it and select Delete All Items in Array/Buffer
.
Your windows are now clean and have a tint that feels more realistic / more visible.
Wait ! When I choose the color it says "CrystalCoat by Rayfield" and a sports car is displayed !
Also secondary color does not work !
Creating a custom color picker widget for your vehicle requires to handle an image editor like Adobe Photoshop or a free alternative and to have a bit of creativity in order to get a decent result. I will explain in details how I do so you can do it more easily.
The color picker widget displayed in the game is defined by a InkWidget file that you must copy into your project so we can modify it.
This InkWidget is a bit complicated to edit visually in WolvenKit so you will want to convert it to JSON and edit it with a text editor. Right-click on the InkWidget file and convert it to JSON.
Then into the readable JSON file perform the following search using a case-sensitive research.
You must include quote characters ( "
) into the search.
For each of these keywords (including quotes) you will find exactly 4 results.
Results 1 and 2 concern the inner color circle
Results 3 and 4 concern the outer color circle.
As we do not have defined a secondary color yet, we want to hide the inner circle. For results 1 and 2 of these researches you need to change the visible
field that is a few lines under the matching word. Change its value to "visible": 0
.
You should have performed 8 modifications.
Now we must make the inner circle unselectable both for mice and gamepads. To do this search for inkCircleInnerWidget
. You will find 2 occurrences. For each of them you need to go a few lines up from the matching word until you see isInteractive
field.
Change its value to "isInteractive": 0
. You should have performed 2 modifications.
But this won't be enough for gamepads because they trigger the circle activation from code.
We need to complete this by some code. Append this code into your project's script file. It will prevent gamepads from triggering the inner circle when switching modes.
We want to update the title of the widget so it will display CrystalCoat™ by <manufacturer>
for your vehicle. For this we need to create a localized resource that can be translated to other languages.
Create a new folder for localization data in your project, then add a File > New File
of JSON type named en-us.json
into that folder. Now append this text into your existing XL file to allow your mod to handle localized strings.
Update the relative path to your JSON file in this text.
Use English language as the default language if the user's language is not found. The first language code in the list will be the default fallback language. Then you can add any other language under it in any order with its own JSON file.
Convert your en-us.json
file into readable JSON by right-clicking on it then choose Convert to JSON
. Write this content into it and update these elements:
Update the manufacturer name by one of your choice if needed (line 23).
Create a new secondary key to use into the secondaryKey
field (line 26). A secondary key is a unique identifier to identify the localized string. It is the same key across all languages. You should use something unique like a combination of words separated by dashes. For example use your nickname followed by your mod name followed by what your key is referring to: mynickname-MyModName-colorpicker_title
You can then convert your readable JSON file back into CR2W by right-clicking on the en-us.json.json
file into the raw
folder then choose Convert from JSON
.
You can repeat the same process for any other language that you know:
Create a dedicated JSON file
Add the JSON file entry into the XL file using a new language code.
Convert the JSON into readable JSON.
Modify the JSON file content using the same secondary key as the other files.
Convert the readable JSON back into CR2W.
InkWidget files need to use a primary key in order to identify a localized string. So we cannot directly use the secondary key into the InkWidget file.
Instead, we need to generate a hash number of our secondary key using a specific algorithm and then use this resulting number into the InkWidget file.
To create the hash number go into WolvenKit and use the menu Tools > Hash Tool
. Copy your secondary key and paste it into the Text
field of the new window.
As soon as your enter a text value into the Text
field, you will see lot of numbers automatically generated into the other fields. This is what hashing is all about: translating an input data into a number that is unique and consistent regarding the input data.
This means that as long as you enter the same text you will always obtain the same hash number. Thus we can say that the hash number represents your input text. This operation is made possible by the hash algorithm (FNV1A64) that is a complex mathematical algorithm that you really don't want to know about.
What you need here is to copy the CName
field value. Then, in your readable JSON file corresponding to the InkWidget, search for LocKey#96050
and replace it with LocKey#<your hash number>
.
On the example above, the new LocKey would be LocKey#17052470697381502790
. You should have replaced 2 occurrences.
You can now convert your JSON file back into InkWidget.
You must not write the generated hash number into the en-us.json
file or into any other JSON language file.
Now we want the user to display our custom color picker widget when he his using the relevant vehicle. To do this append the following code to your script and replace the widget path (line 16) by the relative path to yours using double-backslashes (\\
).
You may also need to modify the vehicle model ESupportedVehicle.Supron
that is right above it (line 15).
Now you can test your mod.
Wait ! There is still a sports car displayed ! This is not my vehicle !
The most accurate way to represent the actual vehicle into the widget is to use the 3D model of the vehicle unless you would be an artist able to create a wonderful artwork by hand.
To do this we must export it from WolvenKit and import it into Blender.
Into WolvenKit create a new temporary project, find the ENT file of the vehicle in the Asset Browser then add it to your project.
Do not use your CrystalCoat project to perform this step or it will be populated with dozen of undesired files !
Select the ENT file in your project then go into the menu Tools > Script Manager
. Select Export_Vehicle_Ent
and click on the green arrow next to it to run the script. Then wait for it to finish adding files to your project.
Open Blender and in the import menu select Cyberpunk Entity (.json)
. Then in the dialog window find the *.ent.json
file of your vehicle into the raw
folder of the temporary project.
Before validating, be sure to enter the entity appearance to load in the upper right corner. For the Mahir Supron we want to load mahir_supron__basic_player_01
. This is the value in the appearanceName
field of the vehicle record.
Uncheck "With Materials" before validating or the import will be unnecessarily long.
Now hide the armature and remove all decals you see around the vehicle. The idea is to obtain a clean 3D model and take a screenshot of it. Then we will dump the project.
Decals are all these flat rectangle elements. Click on them and remove them all. Generally they are grouped together for each component so selecting one will select a group of decals.
If you notice that some elements are not placed correctly like the license plate you can simply remove them, if this is a window it is better to move it to the right place.
Once the vehicle is clean and ready, create a plane mesh under its wheels that we will use as a background. Then make this plane to display as green to make the image editing easier.
To display it as green, select the plane then on the right menu go into the materials tab (red sphere icon) click on New
to add a material to the plane.
Then in the material definition go into the Viewport Display
section and set the color to full green.
Now activate the TexturePaint
mode in the upper left corner of the viewport, place the camera from the top by clicking on the Z axis node in the upper right corner of the viewport. Zoom and center the view on the vehicle then take a screenshot of it. Be sure to keep a few space around the vehicle model.
I will use Adobe Photoshop in order to prepare the vehicle image but you can certainly do the same with another image editor. However you will have to find the corresponding tools.
First isolate the model by leaving a few space around. Then use the magic wand to select the green areas.
Some vehicles may also have holes from the top view. In this case do not forget to select these green areas too.
Once you have selected all the green areas, invert the selection by right-clicking on it and select "Invert" (I don't use English so I may be wrong on the exact word).
The selection will now select everything except the green areas. Press CTRL + C to copy the model then on the lower right corner create a new layer by using the +
icon. Then press CTRL + V to paste the model into it so you get rid of the green background.
You can now delete the layer containing the green background.
Now we want to make this model look a bit more like a drawing so what I do is first increase the brightness of the picture by 50%.
Next apply a filter on the picture to make it look a bit more like a drawing. Use the Filter > Filter Gallery...
menu. Then select Artistic > Paint Daubs
(French is "Barbouillage") with parameters Brush Size = 2
, Sharpness = 20
and Brush Type = Simple
.
Finally reduce the Saturation of the image by 100% to remove pink borders.
Our model is now ready to be used into WolvenKit.
Create a backup of this model picture so you will be able to reuse this picture later to create a mask for the secondary color.
In order to use our model into the color picker widget we need to use a XBM texture file and to modify it. Add this one to your project and then use the Tools > Export Tool
to convert it into TGA.
Open the TGA file into your image editor. You will see that it uses 5 channels: RGB, Red, Green, Blue and Alpha. When you click on the RGB channel it displays all the three colored channels together while if you click on the Alpha channel it will display another image with only gray shades.
When we modify something in the picture we need to modify it both in the RGB channel and also in the Alpha channel. When we manipulate layers they will only apply in the RGB channel.
On the picture we can see multiple parts. The idea is to replace the main model by our vehicle and then eventually to replace the secondary color mask with ours or to hide it. Finally replace the light beam, and both the headlight and tail light elements. We will leave the white square mask and the Cosmetic_Troll data code as they are.
We can eventually make the picture larger if our elements do not fit into it but we must keep the same vertical size so the widget will display them correctly. Especially concerning the vehicle and the secondary color mask parts.
Now use the RGB channel and paste your model layer in the image. Then resize it while keeping its aspect ratio and make it the same vertical size as the original image.
When your model is placed and resized correctly select the original layer eventually unlock it if necessary and then select a large area to cover the entire original vehicle part. Then fill the areas with black color to remove the original vehicle picture.
Then go into the Alpha channel and remove the vehicle part using a black area.
Now go back into the RGB channel, select your model layer then hold CTRL key and left-click on the layer image to create a selection around it.
Then go into the Alpha channel and fill your selection with white color. It should be placed at the exact same position as the model image on the RGB channel.
Finally go back into RGB channel, then layers tab and merge the model layer with the base layer.
Now you know how to modify one part of the texture for both the RGB and Alpha channels. As we currently don't have a secondary color we can remove the secondary color mask from both channels.
All you have to do is to fill the secondary color mask area with black color on both channels. The next step is to modify the headlight element.
First we want to know where is the element into the game when headlights are turned on. For the Mahir Supron there are several light elements the create.
To keep things simple, we will only create a single light component on the right of the vehicle. In the image editor let's create a simple shape in a new layer at the same position as the main headlight component.
Now we have a new headlight component in a dedicated layer, let's move it at the position of the existing one and replace it for the RGB channel like we did for the previous part.
Concerning the Alpha channel we will do differently. Once the element is placed correctly, duplicate the layer using CTRL + J in the RGB channel and create a style on it to make it full white. To do so right-click on the new layer and select the first element of the list. Then add a color coating with white color.
Then merge the style you have just created with the layer itself by right-clicking on it and choose this option.
Now your alpha element is placed right over the RGB element. Select the alpha element layer then hold CTRL and left click on the layer image to create a selection around your element. Then press CTRL + C.
Go into the Alpha channel and paste using CTRL + V. Your pasted element shall be at the same location as the one in the RGB channel. Now you can click anywhere to validate. Go back into the layers tab and delete the one you have just created.
Finally switch back and forth from RGB channel to Alpha channel to check that both elements are at the same location. You should see a white element in the Alpha channel while it is a little bit darker one in the RGB channel.
You can finally merge the headlight layer with the base layer.
Now we are going to repeat the same process for the tail light element. Except that instead of filling it with gray color, we are going to use the gradient tool with a dark red and a light red color.
Create a new layer, then create a simple selection at the tail light location and select the gradient tool. Select the basic gradient and fill the selection.
Now place the element at the location of the existing one. Right-click on the layer and add a new style with a red external glowing effect in order to create a similar effect as the original element.
Now remove the original tail light element as usual (both in RGB and Alpha channels) and place the new one at the same spot. Before merging the style with the layer, duplicate the layer and modify its style to have a white glowing effect and a white filling color for the alpha element.
Then merge the style of the alpha element, then select both this layer and the base layer and merge them.
Now create a rectangle selection that goes from the right border of the picture until you have the alpha element into it. Then copy the selection, go into the Alpha channel and paste it so it will be at the same location.
Go back into the RGB channel and delete the alpha element by selecting the spot and filling it with black color. Show the red tail light element back and merge its style then merge the layer with the base layer.
The last element we need to change is the light beam. Essential it will consist in modifying its base area so it complies with the shape of our headlight element. On some vehicles it is also necessary to enlarge it if the headlight component is larger.
First create a new layer containing a copy of the light beam by selecting a rectangle along the top and right borders.
Now click on the base layer and use the magic wand tool to select the headlight component area then move the selection by dragging it (not the headlight element) up to the light beam until it is all into it. Click on the new light beam layer. Also hide the base layer to better see the result. Then hit the DELETE key to remove pixels in the selection for our new layer. Then delete the remainings below it too.
Then toggle the visibility of the light beam element to OFF and turn the base layer back ON. Remove the original light beam both in the RGB and Alpha channels as we did for the previous parts.
Then toggle the new light beam layer back ON and merge it with the base layer. The select the area of the light beam and paste it in the Alpha channel.
You should now have this final result.
Your image should not be larger than 1200 pixels or the InkWidget won't display the texture parts correctly.
Now save the picture as TARGA / TGA image and be sure to select 32-bits/pixel and import it back into WolvenKit using the Tools > Import Tool
.
Now you have imported the TGA file back into XBM we must use a InkAtlas file in order to create texture parts from our XBM file.
Add this file into your project at the same location as the XBM.
Then open it and set the relative path to your XBM file in place of the three elements of the slots
array.
Now go into the Part Mapping
tab and see that eventually all parts are already well placed so you won't have to modify parts. In the case you need to modify some of them. Let's explain how it works.
In the Part Mapping tab, click on a part then see on the right there are informations like Left, Top, Width, Height using pixel unit. The rectangle of the texture part is defined like this:
Left: number of pixels between the left border of the image and the left border of the part
Width: number of pixels between the left border of the part and the right border of the part
Top: number of pixels between the top border of the image and the top border of the part
Height: number of pixels between the top border of the part and the bottom border of the part
But the issue is that you cannot set the new part area from here, you can only play with the pixel values to define a new visual area that you find better. You cannot save this information like this. But instead once you have finished to define your new part area, remember the pixel values of Left, Width, Top, Height fields.
Then know the size of your texture image. Let's say the picture size is 960 x 850 pixels. You can calculate the ratio between your part area borders and the image size.
Let's use the example of the headlight element. Its rectangle area is Left: 770, Width: 170, Top: 240 and Height: 40 then you have to calculate into a 0:1 UV space the equivalent of these coordinates.
On the horizontal axis, 0 is the left border of the image, 1 is the right border of the image. On the vertical axis, 0 is the top border of the image, 1 is the bottom border of the image. Then you find this result:
Left: 770 / 960 = 0,8020833333333333
Right: (770 + 170) / 960 = 0,9791666666666667
Top: 240 / 850 = 0,2823529411764706
Bottom: (240 + 40) / 850 = 0,3294117647058824
The decimals are important in order to keep precision. Then go back into the first tab and open the first slot element then update the fields with these values to define the new part area.
If you want to check the new visual area after you have modified the coordinates you need to save the file and reopen it. Do this for all required parts until none of them overlaps another.
Finally remove the elements 1 and 2 of the slots
array and duplicate the first element twice so they are all 3 identical.
Before we can test the result, we need to actually use the InkAtlas file into the InkWidget file. To do this convert your InkWidget into JSON then replace all occurrences of this path by the relative path to your InkAtlas file using double-backslashes (\\
).
Then convert your JSON file back into InkWidget. You can now test your mod.
Wait ! Light components do not correspond to what is on the actual vehicle !
Well we are almost there ! As you can see the widget automatically duplicate the light components symetrically for headlights, tail lights and light beams. We can also see that it applies a mirror effect on half of them.
For this vehicle we need to use only one of each and to place them differently. This must be done into the InkWidget file.
For this step we can edit the widget directly in WolvenKit. Open the file and go into this element hierarchy libraryItems > package > rootWidget > children > container > children > CenterGroup > children > Car > children
.
Here you have the list of elements related to the vehicle.
CarColorPrintC1
is the main model
CarColorPrintC2
is the secondary color mask
CarColorPrintLights1
and 2 are the tail lights
CarColorPrintLightsFront1
and 2 are the headlights
CarColorPrintLightsFront1Beam
and 2Beam are the light beams.
We will keep the right headlight component as it is the closest to the actual location we need. But as you noticed it has been mirrored so we will change this and its location.
Go into CarColorPrintLightsFront1
which is the left headlight and uncheck the visible
field so it will be hidden.
Into the CarColorPrintLightsFront2
set mirrorType
to NoMirror
. Then into layout > margin
you will set its relative position in pixels. There is no fast method to find the right values you need to try new values and save the file, install the mod and test the mod and so on until it matches.
If you are creating the same mod as me, I will save you some time and give you my values for the Mahir Supron. Left: 99, Top: -432.
Go into CarColorPrintLights1
which is the left tail light and uncheck the visible
field so it will be hidden.
Into the CarColorPrintLights2
set mirrorType
to NoMirror
. Then modify the position into layout > margin
. For the Mahir Supron I have used Left: 138, Top: 352.
Go into CarColorPrintLightsFront1Beam
which is the left light beam and uncheck the visible
field so it will be hidden.
Into the CarColorPrintLightsFront2Beam
set mirrorType
to NoMirror
. Then modify the position into layout > margin
. For the Mahir Supron I have used Left: 109, Top: -528.
If you test the mod now you may see that details of the outer circle have disappeared. This may occur if you modify the file from WolvenKit. To fix this convert your InkWidget into JSON and refer to this paragraph. For each of the keywords, set the field visible: 1
for results 3 and 4.
Now let's see the result.
Wonderful ! My color picker widget is now perfect !
But wait ! I am a bit disappointed with my CrystalCoat. I would like to have a more premios result !
My Supron feels cheap !
The CrystalCoat feature allows to define a primary color that shall cover the painted areas of the vehicle and then a secondary color that goes on top of the primary color.
The idea of the secondary color is to create shapes, patterns or to cover only some parts of the vehicle. There are two approaches that you can combine as you wish in order to create a secondary color.
The most simple way is to apply the secondary color on a complete mesh so the combination of multiple components will give a nice result. This can work especially with vehicles that use a organic design such as Rayfield vehicles.
If a component subset could make sense to use the secondary color then you can submesh it and apply this approach with it.
On this picture, the silver front wings of the vehicle have been submeshed to they can be colored entirely without affecting the rest of the body part.
With conventional vehicles that consist in a box with 4 square doors, this approach won't give a nice result most of the time. Except for wheels.
The approach that allows the most possibilities is to work with a UV layout.
When you want to apply a 2D resource onto a 3D model you need to project your mesh onto a plane. Think of it as a spotlight that projects the shadow of an object on the ground. Although this is not exactly what is happening.
The mesh is made of vertices so you simply decide where you want to place these vertices on the plane. The classic way to use this technic looks a bit like a spotlight projection as you can see on the picture above. It also makes it easier for a human to understand what is going on.
This type of projection is particularly useful if you want to apply 2D resources across multiple parts at once. In the case of CrystalCoat you may want to create oblique stripes across the body, roof and doors of the vehicle. You can do it easily with the Mahir Supron.
The red stripes on this picture would not necessarily be red but they represent shapes that you can create to define a secondary color pattern.
The easiest way to create such mask is to use a image editor like Adobe Photoshop to create shapes and to transform them (rotate, shear, scale, translate) onto the UV layout of the component.
Most of the time the components of a vehicle are sharing the same UV layout that is to say they are all sharing the UV space without overlapping each other. In this case they use the same mlmask file in their material definition.
You will notice that wheels are always using a dedicated UV layout so you won't be able to use the same InkWidget to apply a secondary color for both the painted areas of the vehicle and for its wheels.
In the case your vehicle uses a different UV layout for each component it becomes more difficult to define shapes that will apply across multiple parts.
And moreover when the UV layout is different you always need to use a new InkWidget because your patterns are bound to a specific UV layout. The red stripes on the picture above make sense only for this unique UV layout. Any other one wouldn't suit.
There is a third way to create a secondary color pattern. It is the same idea as to create a mask but instead of editing a picture into a image editor you can create shapes directly into the InkWidget file. This is what is being done for the Rayfield vehicles in the base game.
The InkWidget file for the Rayfield Caliburn contains 16 shapes for the secondary color pattern.
This way of creating shapes is the most difficult because you can't even work with a UV layout to know what you are doing. You must try and test multiple times until the shapes are applying correctly on the vehicle. Also there is no benefit to use this approach.
I recommend you to work on a mask using a image editor.
You could use the mlmask file from the game and extract the mask layer corresponding to the vehicle paint. But you will notice that masks from the base game are quite messy and even contain imperfections. Also if the paint layer is the layer 0 then you won't be able to use this technic.
In order to create a clean UV layout for your secondary color pattern, you should create your own mask using the mesh files. Export all the mesh files involved in the vehicle paint using the menu Tools > Export Tool
.
In the case of the Mahir Supron I will export all the _painted
files except the wheel one. Then I will also export the roof mesh file. Import them all into Blender and uncheck the With Materials
option so the import will be faster.
You can only import mesh components that are sharing the same UV layout together. That is to say they use the same mlmask file. Otherwise they may overlap on one another.
Select all the meshes using the A
key then press TAB
to go into Edit mode
. Finally select the UV Editing
view in the menu bar.
You can see on the left that your UV layout has been generated. Export it to PNG using the menu UV > Export UV Layout
. Define the size to 1024x1024 and set the Fill Opacity
to 1.00.
Open Adobe Photoshop or another image editor and create a new project with a image size of 1024x1024. Then fill it with black color by using the menu Edit > Fill
. Import the PNG file you have just created with Blender. It should be imported into a new layer.
Create a new style on the UV layout to fill it with white. Now you have created your mask for the painted areas of the vehicle.
The next part is to define your secondary color pattern by adding shapes on top of your mask using dynamic objects (like the Rectangle tool) or any other technic as long as they belong to a dedicated layer. I recommend that you use red shapes so you can see them better.
Before exporting the mask we will fill them with white.
You can also use any other tool to create shapes such as the magic wand to select color areas or other selection tools. You can even use external images, logos and even text. Once your pattern is ready create a style on it to fill it with white just like you did previously. Then hide all the other layers except the pattern so you have something like this.
Although you can use any kind of shape to define a secondary color pattern like logos and text you should keep in mind that the InkWidget resolution on the vehicle is low so you won't have a lot of details.
Finally export the image as PNG. Now it is time to export your secondary_color.xbm
texture to PNG using the Tools > Export Tool
. Erase the PNG export with your PNG mask using the same file name.
Then use the Tools > Import Tool
with the following settings to import your mask back into the texture file.
From the beginning of this tutorial you are using the vvc_full_primary_only.inkwidget
into all of your WorldWidgetComponents so you can only display a primary color on your components.
Now you have defined a secondary color pattern, you must use the vvc_car_appearance_widget.inkwidget
for all components except for wheels. You need to update the widgetResource
field of your WorldWidgetComponents using the relative path to this InkWidget.
The _painted
WorldWidgetComponents for wheels will still be using the vvc_full_primary_only.inkwidget
.
We want to apply the secondary color on wheels inserts too. But as the wheels are using a dedicated mlmask (UV layout) we would need to duplicate the InkWidget file, and also the secondary_color.xbm
and the secondary_color.inkatlas
files.
Then update their relative path into the new dedicated InkWidget and finally create a mask for the wheels using a image editor.
We can obtain the same result by simply using the vvc_full_primary_and_secondary.inkwidget
on the _inserts
WorldWidgetComponents of the wheels.
visual_customization_wheel_01_fl_a_inserts_custom
visual_customization_wheel_01_fr_a_inserts_custom
visual_customization_wheel_01_bl_a_inserts_custom
visual_customization_wheel_01_br_a_inserts_custom
At the beginning of this tutorial we have removed the ability for the user to select a secondary color. Now we are going to restore this feature.
Remove the code you have added in this paragraph of the tutorial.
Right-click on the vehicle_visual_customization.inkwidget
and export it into JSON. Then perform these replacements in the opposite way to restore the inner circle of the widget. Finally import the JSON file back into InkWidget.
Now let's test the mod again.
For a component to use the secondary color you must set its appearance to either Coated
or Painted
in the mod settings. Concerning wheels parts you need to set their appearances to eitherMetallic
or Glossy
in the mod settings.
Wait ! I don't see the secondary color pattern on the color picker !
Remember that the texture vehicle_visual_customization_car_preview.xbm
that contains the vehicle model into the color picker widget has a part dedicated to the secondary color.
All you have to do is to follow this paragraph to modify the secondary color mask instead of the vehicle model. The secondary color mask should be a selection of the original model. The following picture is the mask I have created. Visible parts of the model will use the secondary color if it is defined.
The most accurate way to create the mask is to duplicate the section that contains the vehicle model. Then keep only the areas that should be affected by the secondary color.
Your image should not be larger than 1200 pixels or the InkWidget won't display the texture parts correctly.
Concerning the secondary color pattern I have choosen for the Mahir Supron, it cannot be easily seen on the color picker. This will depend on your choices.
I have added a lot of customizable parts in the mod settings how can I make it more user-friendly ?
All we need to do is to perform the same steps as we did for the roof. Except that we won't make this component colorable with CrystalCoat. Instead it will only be able to use predefined appearances.
Duplicate the body_01_roof_custom
component and rename it to body_01_sunvisor_custom
, then duplicate its mesh file and replace its suffix by _sunvisor
. Affect this new mesh file to the new component.
Export the new mesh file and the body_01
mesh file. Open the body_01
mesh file into Blender and submesh the component. Export the new submesh alone into the sun visor mesh file. Finally export all the other submeshes into the base mesh file. Finally import both files back into WolvenKit.
Open the new mesh file and remove all appearances except standard
and black
. Remove the corresponding materials from localMaterialBuffer > materials
and materialEntries
arrays. When you are done, right-click on the materialEntries
array and refresh the index by choosing Recalculate child index properties
.
You must update the AppearanceVisualController
list when you create a new component or if you modify its name, mesh appearance or mesh file path.
Refer to this paragraph to do so.
Now we need to add two settings into our SettingsPackage
class to handle both CrystalCoat ON and OFF states.
Notice that we are using the EMeshAppearanceStandard
enumerated type for both fields. This is because we don't want the user to be able to choose Coated
or Painted
values in any case.
We need to add new localized strings for our new settings. Export your en-us.json
file then add these elements into it and update their secondary keys.
Then import it back into CR2W by right-clicking on it and select Convert from JSON
. Repeat this process with any other language you have set.
As the sun visor component comes from the body_01
component it must be added to the list of deformable parts of the vehicle. Follow this paragraph to do so. Except that you will add a _3
suffix to your new elements in the YAML file.
The _1
suffix is already being used for body_01_painted_custom
component. And the _2
suffix is already being used for body_01_roof_custom
component.
As our component is individually customizable by the user we need to connect the mod settings with the components processing just like we did for the roof.
To do this modify the Utils.CustomizeMesh
method. In the first switch
block of the method, add a new else if
block under the existing if
block. The new content is on the lines 7 to 9.
Now you can test your mod and see that the sun visor can be customized for both CrystalCoat states.
My vehicle is so fancy now ! But wait ! Those wheels feel very cheap !
Concerning the wheels we want to make them colorable with CrystalCoat but we would like to select their paint aspect independently of the rest of the vehicle. So we can have a vehicle with metallic paint and wheels using the glossy paint.
For the Mahir Supron all the wheels use the same mesh file so it will be easier. The wheel components are these ones:
wheel_01_fl_a
wheel_01_fr_a
wheel_01_bl_a
wheel_01_br_a
Duplicate the wheel_01_fl_a
component to create two new components named wheel_01_fl_a_painted
and wheel_01_fl_a_inserts
. Set their meshAppearance
to standard
.
Add the mesh file to your project and duplicate it twice. Add theses suffixes to your new mesh files _painted
and _inserts
. Assign all three files to your three components:
wheel_01_fl_a
wheel_01_fl_a_painted
wheel_01_fl_a_inserts
Repeat this process for the other wheels using the same mesh files. If your front wheels and back wheels are using two different mesh files then repeat this process for both groups.
Export your three mesh files and import one of them into Blender. Hide all submeshes except the relevant one you want to work with.
Submesh these parts for the _inserts
component.
Select this new submesh alone and export it to the _inserts
mesh file. Submesh these parts for the _painted
component.
Select this new submesh alone and export it to the _painted
mesh file. Then select all the other submeshes and export them to the base mesh file.
Import your three mesh files back into WolvenKit. Now duplicate one of your WorldWidgetComponents named visual_customization_<some_component>
for the painted component and another one for the inserts component. Rename them and update their meshTargetBinding > bindName
and parentTransform > bindName
fields with their new component name.
You must update the AppearanceVisualController
list when you create a new component or if you modify its name, mesh appearance or mesh file path.
Refer to this paragraph to do so.
We need to create four mod settings to handle wheels. Two for CrystalCoat OFF state and two more for CrystalCoat ON state.
As usual in the following code you need to replace the mod name and secondary keys for translation. See that CrystalCoat OFF settings are using EMeshAppearanceStandard
type while CrystalCoat ON settings are using EWheelAppearanceCC
type because we want to replace the Painted
value with a list of paint aspects directly. So we also need to use the relevant translation entries above the field.
Then create the new EWheelAppearanceCC
enumerated type.
As we have 4 wheels to link with our new settings we need to add this code (lines 10-21) in the Utils.CustomizeMesh
method.
We need to add new localized strings for our new settings. Export your en-us.json
file then add these elements into it and update their secondary keys.
Then import it back into CR2W by right-clicking on it and select Convert from JSON
. Repeat this process with any other language you have set.
Open your _inserts
mesh file so we can create the materials and appearances. Delete all appearances and create these ones with a single chunk (appearance name -> chunk name).
standard
-> poor_01
metallic
-> metallic
glossy
-> glossy
coated
-> coated
black
-> black
In the materialEntries
and localMaterialBuffer > materials
arrays remove all entries except poor_01
. Then copy the existing materials from another of your mesh files concerning metallic
, glossy
, coated
and black
materials.
You must rebuild the index of the materialEntries
array after you have finished to reorganize it. Right-click on it and choose Recalculate child index properties
.
For the coated
material, reuse the original mlmask file from the poor_01
material. Add its associated mlsetup file to your project and rename it to wheel_coated.mlsetup
. Assign this new file to localMaterialBuffer > materials > coated > MultilayerSetup
. Then right-click on the file and select Convert to JSON
.
Open it in MLSB and disable the layers that make the wheel dirty: 15, 17, 18. To do so turn their opacity to 0. Then export the file to JSON and import it back into WolvenKit.
Concerning this one as this is a textured material we need to define its Tiles
value into the mlsetup file because depending on the mesh UV, the texture resolution will be different.
Duplicate the existing metallic.mlsetup
and name it wheel_metallic.mlsetup
. Then assign it to localMaterialBuffer > materials > metallic > MultilayerSetup
.
Edit the file into MLSB and set its Tiles
value to 8 on the layer 0. Save the file and import it back into WolvenKit. Now we also need to define a new mlmask file for the wheels.
We cannot reuse the existing painted.mlmask
file because it uses layer 0 and layer 2. Indeed the Mahir Supron has side stripes thanks to layer 2.
Duplicate the painted.mlmask
file and name it wheel_painted.mlmask
. Export it and replace the layer 2 image by a black image using the same size and name. Then import the mlmask back into WolvenKit.
Assign it to your metallic
material into localMaterialBuffer > materials > metallic > MultilayerMask
.
For this one we don't need to duplicate the mlsetup. Simply assign your wheel_painted.mlmask
file into localMaterialBuffer > materials > glossy > MultilayerMask
.
Finally concerning the black
material this is exactly the same process as the glossy
one.
Now you can test your mod and see the result !
If your wheels stay black when you activate CrystalCoat, switch between the third-person perspective (TPP) and the first-person perspective (FPP) so widgets will be updated.
This is fantastic ! My vehicle now looks premios ! But wait ! It is all dirty on the tires and around the vehicle !
Once we have created our customization mechanism we can create as many options as we want and extend this to any sub-part of the vehicle.
It would be a great addition to allow the roof to use a glossy black plastic finish don't you think ?
First duplicate the mesh file of the body_01_painted_custom
component. Modify its file name suffix from _painted
to _roof
. Then export both the body_01
mesh file and this new file.
Then import the body_01
mesh file into Blender so we can submesh it.
Select all the faces of the roof component then press P
and select Selection
to create the new submesh. Export this submesh alone into the new roof mesh file. Then export all the other submeshes to the body_01
mesh file.
Then import both mesh files back into WolvenKit. Now in the APP file duplicate the body_01_painted_custom
component into a new one named body_01_roof_custom
.
Assign your new mesh file to it. Also duplicate its widget named visual_customization_body_01_painted_custom
into a new one named visual_customization_body_01_roof_custom
. This widget will allow us to eventually apply paint or coating on the roof.
Then into this new widget modify the meshTargetBinding > bindName
and parentTransform > bindName
with the new component name.
If you test your mod you will see that the roof will now be synchronized with all the other painted components. Although this is a cool feature, this is not exactly what we want.
What we want is to be able to choose what to do with the roof. So we need to append something to our script and to mod settings.
You must update the AppearanceVisualController
list when you create a new component or if you modify its name, mesh appearance or mesh file path.
Refer to this paragraph to do so.
First we want to allow the user to choose the roof appearance. Append this code snippet into your SettingsPackage
class at the end of it. Notice that I have added a new translation entry for the Black
value at line 13.
As usual replace the secondary keys prefix and the mod name with yours. You can see that the roofAppearance
field is using the EMeshAppearanceCC
type which is the list of available appearances we have defined before.
We want to allow the roof to use a glossy black material, so add a Black
value to the EMeshAppearanceCC
type.
Now in the Utils.CustomizeMesh
method, we need to add some intelligence in order to treat each customizable component independently from the others. At least for some of them. Replace the first switch
block by this new one.
Replace the vehicle model at line 2 if necessary. You can see that we are looking for a specific component named body_01_roof_custom
, so we can make the link with its corresponding mod setting.
For all the other components we use the else
block that works like before. Now if you look closely at line 5 you can see that if CrystalCoat is disabled we force the roof appearance to be standard. If CrystalCoat is enabled we use the mod setting value.
As we have created a new mod setting and a new mesh appearance we need to add new localized strings into our en-us.json
file and also in any other language file you have defined. Right-click on your en-us.json
file and choose Convert to JSON
.
Edit the clear-copy created in the raw
folder and add these new entries into it. Replace the secondary keys prefix with yours.
Then right-click on the en-us.json.json
file from the raw
folder and choose Convert from JSON
to save your changes.
Finally we need to edit the roof mesh file to add the new black appearance into it and to create the corresponding material. Open the file and duplicate one of the existing appearances. Then name it black
and rename its chunk to black
.
Now into the materialEntries
array duplicate the last item and name it black
. In the localMaterialBuffer > materials
array right-click on the glossy
entry and choose Copy From Array/Buffer
. Then right-click on the array and choose Paste Into Array/Buffer
in order to append it to the end.
We are using the glossy
material because it is the closest to the one we want to create as we want to create a glossy black material. But we need to edit two parameters to get a better result CoatTintFwd
and CoatTintSide
. We should set colors that are close to the one we want. As we are creating a black material set CoatTintFwd = (red:0, green:0, blue:0, alpha:255)
and CoatTintSide = (red:77, green:77, blue:77, alpha:255)
.
Also as this material is not colorable by CrystalCoat, use the basic multilayer file in the base material field.
Now duplicate the glossy.mlsetup
file and name it black.mlsetup
. Then assign it to the material into its MultilayerSetup parameter. Right-click on the black.mlsetup
file and convert it to JSON. Open it in MlsetupBuilder by right-clicking on it.
We are going to use only the first layer and turn all the other ones invisible by setting their opacity to 0. By chance the glossy paint layer #0 is already configured properly. Simply export the file using File > Mlsetup > Export
and erase the JSON file. Then import it back into mlsetup in WolvenKit.
You can use either painted.mlmask
or the original mlmask file of the vehicle for this material because both files use the layer 0. In any case we will ignore the other layers because their opacity is null. We only need a mlmask that uses the layer 0, and this layer is used by all mlmask files because the layer 0 is the base layer.
For a better clarity I recommend you to use the original mlmask file when it is possible so if you come back later to modify your mod you won't be asking yourself why you would have used that mlmask.
As the roof component comes from the body_01
component it must be added to the list of deformable parts of the vehicle. Follow this paragraph to do so. Except that you will add a _2
suffix to your new elements in the YAML file.
The _1
suffix is already being used for body_01_painted_custom
component.
Now you can test your mod and play with the new mod setting so you can see how it behaves.
Wait ! What if I want to have a customized roof while CrystalCoat is turned off ?
Well this raises the question of customizing the vehicle for two contexts: with and without CrystalCoat. It is possible to do this with some modifications to the script.
First rename the roofAppearance
field to roofAppearanceCC
using a case-sensitive replacement. This CC
suffix means that it concerns the CrystalCoat ON state. You should have replaced 2 occurrences.
Next, modify your existing secondary keys for this field so they have a -on-
part into them.
Modify your secondary keys in your languages JSON files too and add new keys for the OFF state.
Import your JSON files back into CR2W. Now add a new roofAppearance
field by duplicating the existing one and change the secondary keys of it so they have a -off-
part into them.
Wait !
We are defining a new mod setting for the CrystalCoat OFF state but the values Coated
and Painted
can be chosen by the user. These values make no sense if CrystalCoat is turned off.
To fix this we need to create a second enumerated type that excludes these values.
Create the new mod setting for CrystalCoat OFF state.
Then into the Utils.CustomizeMesh
method in the if
block that concerns the body_01_roof_custom
component change the line by this one (line 2).
Now you can test your mod and configure the roof appearance for both the CrystalCoat ON and OFF states.
This is really great ! I think the sun visor feels cheap ! Can you do something for this ?
When you install the mod and you go into the mod settings menu, you will see that you have default values. When you modify these values you can then click on a button to reset all values to default.
These default values are defined by what is written into the script for each field. It is up to you to define default values that you find relevant for your mod.
Some users are community administrators that deploy a collection of mods to a lot of users. They may want to add your mod to their collection, but for some reason they would prefer to customize the default settings for their community.
This is the purpose of this chapter.
If you look at the script you will find a SettingsPackage
class. This class contains the mod settings definition. Default values are what stands after the =
sign for each field.
Creating a custom settings package requires to create a new mod just like translations also require to create a standalone mod.
These extension mods shall not contain anything from the original mod otherwise it will cause trouble with updates, and it will also violate intellectual property of the original mod author.
When you create a translation mod you simply need to create a XL file and one JSON file per additional language you are providing. This way you don't duplicate anything from the original mod and you won't erase any file from it.
Settings package mod works in a similar way.
If you want other mods to be able to override your default settings you need to add a few changes to your script. At the top of your file, remember that you have defined a module name. This is what uniquely identifies your mod for any other script.
Right below the module statement, you must add the following code snippet. The if
line checks if another installed mod is defining a new settings package. If none is installed then the import
statement won't be executed.
If one is installed then it will replace your SettingsPackage
class with the one defined into the other mod. You must replace the MyNickName.MyModName
string by your module name.
Next, just before the SettingsPackage
class definition you must add a new line. This line is the opposite of the previous one above. If the settings package mod is not installed then your default class will be used.
That's it ! Your mod is now able to replace its default settings values with those of another mod. Now you should create a ready-to-use template file that other users will be able to download.
To do so create a new script file that you will make available for download to your users into a ZIP archive for example SettingsPackageTemplate.zip
.
In this REDS file you will simply add this content. Use the same module name as your original script except that you add .SettingsPackage
at the end.
Then the import
statement loads all the types definition from your original mod so the class below can use these types. And finally the SettingsPackage
class must be a exact duplicate of the original mod's class.
If you update the original mod and you rename / add / remove / change type of any setting field then you must update the template and any user that has created a custom settings package mod must update its mod with your new template.
Rename the REDS file with your new mod name and place it into resources/r6/scripts/<mod name>/
folder.
Then modify the fields to define the default values. Default values are what stands after the =
sign of each field. If you need more details on the types used by fields, refer to the original mod's script.
That's it ! Finally package your mod and install it so it will override the default values. When installing or removing a settings package mod, it will erase the custom values defined by the user and revert to the new default values.
You can name your custom settings mod as you want because the mod loading order won't matter.
How to set the vehicle taillight
There are four types of taillights
Position light
The position light is always on with the light on
Brake light
It's basically the same as position light
but It gets brighter when the user presses the brake
Reverse light
This is a light that brightens up when the vehicle is reversing It's off by default
Turn signal light
I don't think it's working
so I'm using it where the light should be off
How to set it up
1.
Select the object to be set in the blender and change it to vertex paint mode
2.
Select a color
In hex, enter the code
Position light: 7C0101
Brake light: E70100
Reverse light: CB0101
Turn signal light: A90101
3.
Draw a color on the vertex where want it to be
4.
in the .app Adjust the position of the "vehicle light component" to match the position of each light
Separating the interior and exterior of the vehicle
If the reflection of the vehicle is strange in rainy weather Or if it rains inside the vehicle and water flows inside the interior
This phenomenon is caused by the mismatch between indoor and outdoor boundaries
and
What distinguishes the interior and exterior of the vehicle is the "_cutout" mesh
"cutout" can be found in "RDTDataViewMode -> appearances -> components" within ".app"
Make a mesh to fit the vehicle and replace it
There are things to watch out for when making the "cutout" mesh It should be smaller than the outdoors But if it's too small, the water will enter the inside There's some tolerance But it's better to be as accurate as possible When the mesh is done You can replace it with the old mesh
In order to create a new settings package you need to create a new mod project, then add a script file. You can either create it by following the or you can simply download the template file made available by the author on the original mod's page.
How to extend the vehicle chassis to fit your custom car.
Created: Aug 15 2024 by TheTacticalNuke Last documented edit: Aug 15 2024 by manavortex
This guide features the steps I discovered to extend the chassis to fit my custom car. If you know of a simpler/smarter way of modifying the chassis, please do suggest a change in the #mod-dev-chat
on the redmodding Discord server, or sign up and improve it!
This guide assumes you have been following Boe6's Guide and have read through "Boe6's vehicles: Import/export meshes" chapter. This will teach you how to import and export parts to WolvenKit from Blender.
From your vehicle's .app
file, access the chassis file by following this path and clone it to your project directory:
Rename this mesh to suit your needs and export this to the raw directory using the export tool. This will be exported as a .glb
file.
Import the mesh into blender using the Cyberpunk GLTF
add-on. For this tutorial, I will be using the Chevillon Thrax chassis:
Make sure your custom car is scaled properly to the dimensions of the original car. You can do this easily by importing the main body of the base car. Align the wheel arch of your custom car to the wheel arch of your base car:
This will cause the front axle of the chassis to be aligned with the front section of the car from the beginning. Once the chassis has been imported and the body is made visible, it should look similar to this:
Do not shift the chassis down to fit it with the main body. Like many meshes that have animations attached, the chassis must remain where it is when it is imported.
We can see that the front axle is already in the correct position. However, the back axle falls short of where it is meant to be. We will need to extend the back half of the chassis to make it fit with the full length of the car. This can be done in the following way as visualised below:
Using the bisect tool, split the chassis down the center and seperate them by selecting one half of all vertices, pressing P
and seperating by selection. Make sure X-Ray is turned on to select all vertices that are also hidden from view.
The chassis itself contains multiple meshes, but only one of them actually runs down the middle. If you have multiple meshes like this, bisect them as well. There are meshes that hold the texture which can be split easily, simply go into edit mode and select and split by selection. Do this for all such meshes:
You should now be able to move the front and back half of the chassis with all corresponding meshes indepedently.
Now select all of the back meshes and start moving it to fit with the back wheel arch of the car:
Make sure you make a note of how much you moved the chassis back. You will need to modify the rig by this number to match the physics of the car to the new chassis.
Now merge the half meshes back together by joining them. You should now have the same number of meshes as you started with, but with a gap in the middle.
Make sure the Data attribute of the meshes follows the correct name format *_LOD_1
Go into edit mode and connect all corresponding vertices with an edge by pressing F
. Then select all vertices and edges and press F
again. Make sure to select all faces and triangulate them.
Great! We have now created a chassis that matches the full length of our car. But we cannot simply export this into the game yet, as I will explain in the next section.
If we simply export this chassis and replace the original with it, we will run into a rather amusing problem when we use the car in game:
The reason this happens is that while we shifted the chassis mesh back, the actual animation that rotates the mesh is still in the same place!
From my experience so far, changing the armature to make it fit with the new chassis does not do anything to fix this problem.
Lets go back to Blender. Under the data pane we can see a few vertex groups that are associated with different pieces of the mesh:
Each vertex group defines a specific part of the mesh which the rig file will use to act upon. The rig file contains entries that share the same names as these vertex groups. It is used to define where each part of the car goes.
However, it assumes that the center of the animation is at the center of the mesh it acts upon. We have actually offset the mesh away from this center point, which is why our entire brake disc is rotating around this point rather than spinning in position. We will have to return these specifc vertex groups to the original position for the mesh and animation to be aligned.
Re-import the original chassis back into Blender. Select a mesh in your chassis collection and within the vertex groups pane, select all vertex groups relating to the back portion of the chassis:
Now move the faces in the opposite direction you moved them when you where extending the chassis. Use the value you noted down before to return it back into its exact place:
Do this for all the meshes that are associated with your chassis. You will now have a chassis with the back axle looking very skewed forward:
Now select the collection containing your chassis meshes and hit export. Make sure to disable Static Prop. Import the new chassis back into WolvenKit. Verify the chassis using the preview pane:
Within your .app
file, replace the original chassis path with the path your new chassis is located at. Now let's launch the game and see how it looks:
Success! Our chassis is now rotating as intended. However, we still have to move the back axle back in order to fit our car. This is done in the rig file.
This part of the guide is already mentioned in Boe6's Rigging chapter. However, I will go into detail as to how to modify the suspension settings to match the car.
There are two rig files you will need to clone. These are located within the .ent
file of our custom car and is under the following directories:
RDTDataViewModel > components > entAnimatedComponent deformation_rig > rig
RDTDataViewModel > components > entAnimatedComponent vehicle_rig > rig
Clone this to your project directory and modify the file path to your new rig files.
The deformation_rig
looks at all the parts of the car while the vehicle_rig
specifically handles the wheel physics. As mentioned in Boe6's guide, in the deformation_rig
file, change the back suspension attribute names to suspension_back_left_
and suspension_back_right_
.
Open the vehicle_rig
file and take a look at the back suspension variables:
Open the Translation attribute and you will see 4 fields W, X, Y, Z. When we moved the chassis back, we were translating it in the Y axis in the negative direction. We will need to subtract our offset from the Y value. Do this for both the left and right back suspension.
Now lets open up the game and see how our chassis is running:
Congratulations! You have successfully made a custom chassis for your own custom car!
Shadow Mesh for Non-RT Environment
The shadow mesh is This is the mesh required for the vehicle to create shadows in an environment where ray tracing shadows are not used
Shadow mesh doesn't need a lot of polygons. It only needs to have a minimal shape to create a shadow
You don't need stickers, decals, and glass when you make a shadow mesh
The shadow mesh must have the same position value as the original mesh
Shadow mesh
Original mesh
If the following problems occur
If you made a shadow mesh and put it in
and
If it looks like the picture above
In the "~shadow.mesh" file
renderResourceBlob -> header -> renderChunkInfos -> rendChunk -> render Mask
Turn off options except "MCF_RenderInShadows"
To modify the hand position on the steering wheel
It can be modified from the ".rig" file
"steering_right" the position of one's right hand
"steering_left" the position of one's left hand
The position center of the hand is calculated based on the information below
X: X on steering wheel
Y: Y on steering wheel
Z: Self
If you want to see the exact location in Blander
Add the above location information to each hand position
"steering_wheel_zro"This is the steering position
"steering_wheel"This is the steering angle