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.
If you don't want to do all of this, feel free to download this inkwidget where I've already done all of this: https://www.nexusmods.com/cyberpunk2077/mods/13947
Remember to update the widgetResource
path in every WorldWidgetComponent
to the custom inkwidget in your project
You can see a demonstration of this primaryColor-only functionality in this custom vehicle mod: https://www.nexusmods.com/cyberpunk2077/mods/13396
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.
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: https://www.nexusmods.com/cyberpunk2077/mods/13685 by (likely) changing how the inkwidget/crystal coat shaders work under the hood
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:
Script credits thanks to hgyi56
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:
This gui inkwidget is also available at https://www.nexusmods.com/cyberpunk2077/mods/13947