Replies: 0 (Who?), Viewed: 109 times.
Staff - Moderatorstaff: moderator
#1 21st Apr 2021 at 8:39 PM Last edited by zoe22 : 22nd Apr 2021 at 1:46 PM.
Thanks: 1274 in 11 Posts
Adding Material States to your Custom ObjectWhat is this tutorial?
This is an overview of how to add material states to custom objects, in order to change parts of an object's appearance in-game with the object's custom script.
The way it's done is through editing the MLOD/MODL to add a new Material State and Material to go with it.
It can also be done externally, but I don't know how (yet?)
An example of using material states is when objects get burnt or dirty - in the code it says SetMaterial("burnt") etc, and the "burnt" material is applied to the object.
Another example is treadmills, which have their own Material States, for example they use SetMaterial("TreadmillRun") in the code for one of the speeds. "TreadMillRun" is the name of the Material State that links to the Material where the fast speed texture is applied to the treadmill.
There are probably infinite ways that you could use material states for a custom object, but for reference here are some examples:
Inge Jones' OFB object uses material states to change the appearance of the screen, depending on if the object is turned off, if the business is opened, or closed.
My Toddler Food Bowls uses material states to change the food texture depending on what type of food the bowl is filled with.
My Functional Washboard (which will be used as a reference for this tutorial) uses material states to change the water from clean to dirty.
There are some advantages to using materials over textures (like changing an overlay).
The biggest one is that they are very flexible with how you can make them look, as you can use different shaders to get different effects, like shiny, or transparent, or glass, which cannot be done with using an overlay or stencil texture.
This is what allows me to have a transparent water texture for the washboard, because it uses the PhongAlpha shader which allows for see-through textures, while the rest of the object is Phong, just a normal solid mesh.
The food bowls on the other hand, only use plain images with no interesting shader effects, so theoretically could be done with an overlay. However, the other advantage of using materials is that mesh groups with their own shader have their own UV maps, so the whole mesh does not have to be squished on to one UV map.
However, the drawback of using materials is that they aren't recolourable with CASt.
So you will need take into account these things when deciding if using materials and material states is right for your project.
Who is this tutorial for?
This tutorial won't teach you how to make script mods for sims 3 or how to make objects, so you will need to be able to make an object mod in order to use this tutorial in a useful way, and ideally a bit of knowledge on object creation - namely mesh groups and shaders/materials. You don't need to be an expert (I'm not ) but you will need to have some understanding of these areas for this to make any sense, and some practice in making mods and objects.
So if you have a bit of experience with both areas, this tutorial will hopefully be helpful in teaching you how to add material states to a custom object.
You also will need to be comfortable using S3PE.
This tutorial is really aimed at someone in the position where they have a custom object, with a custom script, and the object needs to change appearance in-game with the code, but they don't know how to make it so.
So if that's the case for you, you will hopefully have an object in a similar position to this:
With the part of the object that needs to change in its own group. Note that my object also has geostates, which is when you have different versions of the same mesh which can change in-game. You can ignore those though, because they aren't relevant to this tutorial.
Ideally it will also have a shader/material info that's similar to the material you want to be able to change it into. In my case, I have the PhongAlpha shader, with the water texture for the diffuse map. I want to be able to turn the clean water into dirty water, so I will need a second material with the same shader info, but with a dirty water texture for the diffuse map.
The reason it's better that the two materials are similar (eg the same apart from the diffuse map), is because we have to add all this extra info in S3PE, which is a little harder to get the shader info right than in TSRW, especially if like me, you don't have that much experience with shaders. If the shaders and material info is the same apart from the image texture we are using, we can clone the existing material and point to the different image for the new diffuse map.
On to the Actual Instructions, but first: a Brief Outline:
To help understand what we are doing, I will just go over how the mesh links to the material states which link to materials.
The mesh is split up into groups. Each group can have its own set of Material States. Each Material State has its own Material.
Therefore my washboard has a group for the water part of the mesh. This group may have two material states: "clean" and "dirty". The "clean" state has a material with a clean water texture. The "dirty" state has a material with a dirty water texture.
In the code, toggling between states "clean" and "dirty" will apply the appropriate material to the water plane of the object.
So first we need to open the package with S3PE, and look at the high detail MLOD with Grid view.
You will see tabs called Resources and ChunkEntries. ChunkEntries has all the important info of the object's mesh, including the Material States and Materials. The data in ChunkEntries references the Resources tab, or what's inside it for any resources used by the object (eg images), and the Resources tab references the Resource Keys of the resources themselves.
For my washboard, I will need to add the dirty water image to the Resources tab, but I will do that after I have added the Material States and Material. If you are using an image as part of your second material you will need to do the same, but for now let's get on with the rest, and look in ChunkEntries. Open the ChunkEntries grid view by clicking the 3 dots on the right at the end of the row of the ChunkEntries header.
First we need to find the mesh group that we want to add states to. The mesh groups are usually in ChunkEntry.
You should know which group is the one you want from either the poly/vertex count, or from TSRW.
For me it's group (the second in the list), and that's the water plane I'm looking at and want to add states to.
If you expand MaterialIndex, you can see a hex number next to TGIBlockIndex. This references where the material states are for this group, in a ChunkEntry which is an MTST chunk. The important thing to note is that the number is 1 less than the ChunkEntry index. In my case it's 5, so the MTST is ChunkEntry. If it says B then the chunk entry is ChunkEntry[0C] etc.
So that's where we go next - here I'm looking at ChunkEntry, the MTST chunk that has the Material States for the water plane's group, and if I expand some tabs out I can see there are two Material States already defined
- "Default" and "Burnt".
Because my object only needs the two states, I'm going to be lazy and reuse these. "Default" will already be set up for clean water and "Burnt" will be the new one for dirty water. Note that the names "default" and "burnt" will be what's used in the code to change material.
You could rename them, or reuse them like I'm doing if that suits you. You could also use them and add more too, depending on how many material states you need.
To add new ones or change their names, click the three dots on the right next to the Entries row:
If you are going to add new states, you need to click Copy rather than Add to copy an existing one and change the values, otherwise you can't add the important info and will get an error.
Pick an existing option for the name of the MaterialState, or add your own by putting in the FNV32 hash of the name you want to use. If you want it called DirtyWater, you'd input 0x93F95D10 and "DirtyWater" is what you'd use in the code to change to this state.
Looking at the index tab, the TGIBlockIndex references the material itself. Again, it's one less than the ChunkEntry index of the chunk that contains the material info.
If you are reusing a second state (after the "Default" one), it will already point to a ChunkEntry for the material's info, which you can use/replace instead of making a new one. For example, I'm using the "Burnt" one that already exists. If I look at ChunkEntry[0E] (the material state says D) I will see the MATD (Material Definition, the material info) for the "burnt" state of the water plane.
If there isn't already a second state, or you have added more Material States entries, you will need to add a new MATD for each new state.
Because you will be adding a new ChunkEntry (or more), you know that its index will be the number after the last already existing ChunkEntry. So if there are 10 ChunkEntries currently, the indexes will go from 00 to 09, and so when you add a new one it will be at index 0A. So you can reference it now by putting one less than that index (so input the index of the last currently existing ChunkEntry) into the TGIBlockIndex box.
So to actually add the new MATD, we need to clone the MATD it's based on. To do that, we need to find which ChunkEntry it's contained in. This should be the ChunkEntry that the "Default" material references.
In my case, it says 15, so I need ChunkEntry. Click on it, and then Copy, and you'll see a new ChunkEntry added at the bottom of the list. This is where the new material will be defined.
However, like I said earlier, you may already have a MATD ChunkEntry that you just want to change (in my case the burnt one). Instead of adding a new chunk by cloning the base MATD, you can export the base MATD and import into to the ChunkEntry you want to change.
Either way, we have a MATD for our new MaterialState.
However, it's currently a clone of the clean water MATD, so we need to change 2 things.
First, the MaterialNameHash has to be different. I have just added 1 to it, so before it was 0x1550FA07 and now it's 0x1550FA08.
The next thing to change is actually the data in order to make it look different. For me, I'm just changing the image. You might want to do something more complex with shaders or brightness or transparency. If you have a shader and material info you want from another project or another part of your object here, you could export and import that into this MATD like I did earlier. Just remember that the MaterialNameHash should be unique to this object.
To add the new image, you need to find the DiffuseMap tab, which for me is in SData. Currently the TGIBlockIndex is 6, and if you remember from earlier, it refers to what's in the Resources tab. Closing ChunkEntries for now, you will see that Resources has a TGI number, and that is the resource key of the image it references. In my case, that's the clean water image. Note that the Resources index in the TGIBlockIndex field is the same index as the entry in Resources that it refers to, NOT one less like in the other two cases.
So if it says 6, it means Resources.
Now we need to add the image to the package, and add an entry for it in Resources, then we can reference it in the ChunkEntry for the MATD.
Close Grid view, making sure to click Commit, and add your image resource. I'm just going to duplicate the clean water image resource, change the instance name, and replace the image with the dirty water texture.
Then, you need to copy the Resource Key of the new resource to add it to the Resources tab.
Open the MLOD back up, and open Resources. Click Add, then Paste RK, to add the resource. Then Save, and a new entry is added. The entry doesn't seem to show up in the view mode until you click Commit and re-enter the MLOD again, but it's there, and you will see that where it says TGI Blocks, there is now one higher value than before. The index of the new resource will be one less than that value (as the index starts from 0, which is the 1st entry), so you can remember that to add to the DiffuseMap's tab from before. If you can't remember which ChunkEntry the new MATD was, you can find it by following the path of the mesh group>MTST (material states for that mesh)>MATD (material definition).
Add your resource's index to the DiffuseMap tab and that's actually it for adding the material state!
The only thing left is to change the states in the code. To test, you could just do an immediate interaction for each state, for example SetClean and SetDirty, and those interactions will set the states by using the line of code:
Setmaterial("MaterialStateName"). For me it's SetMaterial("Default") for clean and SetMaterial("Burnt") for dirty.
Test in game!