Custom Feature Art

S3rgeus

Emperor
Joined
Apr 10, 2011
Messages
1,270
Location
London, United Kingdom
This has been an interesting process. I'm trying to get a new feature into the game that uses its own custom art. Ideally I'd like it to show up immediately when it's been placed by a function (like fallout does) but firstly I'd like to get the art definition working at all (even after save/load).

Now, I've made some progress. I'm using Indiestone Nexus Buddy to modify the gr2 textures (based off the marsh), but it doesn't seem to have the right configuration for features, just for improvements. I've attached a screenshot of the feature as it's rendered now.

As you can see, it's rendered as a square, which I think is because features have a "stamp" image which looks a whole lot like a transparency mask, but Nexus Buddy doesn't have a corresponding "stamp" field for the material. I believe I tried using the spec field, but that didn't seem to do anything. In fact, only the diff field seems to make any difference at all. Applying the stamp to the diff texture directly as a mask and then saving that as a dds, importing it into the gr2 with Nexus Buddy didn't seem to have any effect, which I thought was very odd. So transparency must be managed in an entirely different way within the gr2. The existing material also shows up (the one from the original marsh) in the selection drop down list, but not in the material menu to be editable, which makes me think Nexus Buddy doesn't understand its structure. The existing material is called "Material #25" for whatever reason.

To get an idea of what I was going for, I based the feature off of the existing marsh feature, basically what I want is a colorized version that's a sickly yellow-ish and black color. I know custom features have been something CiV has been lacking for a long while, so this may be unsolvable, but hopefully someone has some ideas for how to make it work. (I'll look more into the DLL end of this sometime to see if it can be tricked into treating another feature like fallout to get in-game updating.)

I've also attached all of the xml, fxsxml, gr2, and dds files relevant to this feature, in case anyone else has some good ideas and would like to test them.

As an aside, is there source code for Nexus Buddy available anywhere? This seems like something that should be very fixable with the right tweaks to create a new Nexus Buddy material profile.

EDIT: Oh, I should mention I was using "LandmarkShader" as the material in Nexus Buddy, "LandmarkShader_Stencil" also looks like it might be interesting but I haven't had time to try it out.
 

Attachments

  • Blight Texture Test.jpg
    Blight Texture Test.jpg
    319.7 KB · Views: 318
  • Blight.7z
    2.9 MB · Views: 141
As an aside, is there source code for Nexus Buddy available anywhere? This seems like something that should be very fixable with the right tweaks to create a new Nexus Buddy material profile.

As mentioned here, the source code for all of lemmy101 and CaptainBinky's tools has been lost.
 
As mentioned here, the source code for all of lemmy101 and CaptainBinky's tools has been lost.

So, were I to have used a .NET decompiler to retrieve the source code from a copy of NexusBuddy I found on the forums, that would be good, yes? :mischief:

I'm familiar with C# and this doesn't look too complicated at a glance (may be deceptive) but I'll take a look and see if I can't adapt NexusBuddy for feature materials.

EDIT: And this thing does not like newer versions of .NET. Currently wrangling with getting it to compile at all.

EDIT 2: Ok, only one function refuses to compile now, though it looks quite important. If anyone can make heads or tails of what the function actually does, that would be remarkably helpful. (It's only ever called from the save functions that create the final gr2 file.)

Code:
private unsafe void FixupMaterials()
{
	foreach (IGrannyMesh current in Form1.loadedFile.Meshes)
	{
		granny_mesh* mesh = (current as GrannyMesh).GetMesh();
		int* ptr = (int*)mesh;
		int arg_33_0 = *(IntPtr)(*(IntPtr)ptr[(IntPtr)40 / 4]);
		for (int i = 0; i < 8; i++)
		{
			int* ptr2 = *(IntPtr)ptr[(IntPtr)40 / 4] + (IntPtr)i;
			*(IntPtr)(*ptr2) = 0;
		}
	}
}

Honestly, I didn't know you could do stuff like this with C#. (Never used the unsafe keyword before.) Visual Studio has a couple of problems with this function, most of which are likely because it doesn't like the types granny_mesh and GrannyMesh. The former it claims it can't find, the latter it claims is not visible due to its protection level. And given that the GrannyMesh class is in the Firaxis.Framework.Granny DLL, I don't see how one would ever have gotten around that.
 
I found a working(ish) Visual Tech Tree Editor if you get in the mind to do more decompiling and updating. :p

I downloaded a copy of that earlier, planning to take a look after I've looked through NexusBuddy. ;)

EDIT: IT LIVES! I've managed to compile NexusBuddy and run it from Visual Studio. My workaround for the above function has probably broken the program though. (I commented out the blasted function.) More experimentation to come.

EDIT 2: Ok, still no idea what the magic unsafe function is supposed to do, but the new NexusBuddy does still appear to work without it. I just created a new gr2 file that used a base unit texture and mesh and loaded it up in a mod and the unit displayed correctly.
 
That will make some people happy :)

:bowdown::bowdown:

For when I look at it, what exactly is broken in the visual tech tree editor? I've read part of its thread (and would definitely like to use the program) but it escapes me what went wrong with it.

Regarding NexusBuddy, I have some ideas about how to integrate feature gr2's into it, we'll see if they pan out.
 
I wasn't around to use it, and all the tutorial pictures are dead links. But, when I ran it, it didn't crash but was confusing. There was two of everything like iron for example. Moving iron to an early tech worked but left one of the two irons back at the original tech.

I don't know for sure, but since I didn't set any directories or put it anyplace special it probably is scanning the base directory inward and getting what it needs. Finding duplicates of everything under the expansion DLC is confusing it.

Complete guesses though.
 
Right now I'm struggling with how lemmy knew what to call the parameters within the gr2. I can retrieve the set but without a human readable name I don't know what they represent. You can do lookups by strings, so they must be stored in here somewhere. (The strings I'm looking for are the "BaseTextureMap" labels you see in the right hand window of the NexusBuddy material maker.)
 
The feature gr2's don't seem to be as sensibly defined as the units or landmarks. For instance, why would a feature gr2 have a "Civ5LeaderheadTextures" parameter set? The shader type reports its name to be "SimpleShader" which makes me think it might be a generic shader Firaxis used for a bunch of different art defines. Several of the available parameter sets seem to be uninitialized because trying to access them causes crashes, but it varies from improvement to improvement. The Hadrian's Wall 'feature' that Firaxis added with the Viking DLC seemed like a good bet, but it just seems to use the standard landmark and building materials.

EDIT: The more information I get, the less sense this makes. Why is Krakatoa's shader type "Colossus"?
 
All right, getting somewhere! Turns out, for features, you can actually use the BuildingShader material already in NexusBuddy. (Yay!) If it's all right with the right people, I'll publish NexusBuddy's source code on the forum here so someone else might pick up developing it further? I'll look into it if I need it for anything else, but I feel I don't know enough about the 3D modelling side of this program to make any very complex additions.

As for custom feature art, I've attached a screenshot of my new custom feature rendered in game! Now I have to convince the graphics engine to update when the feature changes mid-game, which doesn't seem to be a very easy task. The graphics update for fallout and forests, but nothing else I've tried.

Still, if people want new features that can be added by mapscripts, (so they don't need to be changed mid-game), then I could write a short tutorial if there's any demand for that. (What I've done isn't that complicated, there are just a lot of choices along the way that don't work, so it was a lot of trial and error.)
 

Attachments

  • Blight Texture.jpg
    Blight Texture.jpg
    361.3 KB · Views: 269
All right, getting somewhere! Turns out, for features, you can actually use the BuildingShader material already in NexusBuddy. (Yay!) If it's all right with the right people, I'll publish NexusBuddy's source code on the forum here so someone else might pick up developing it further? I'll look into it if I need it for anything else, but I feel I don't know enough about the 3D modelling side of this program to make any very complex additions.

As for custom feature art, I've attached a screenshot of my new custom feature rendered in game! Now I have to convince the graphics engine to update when the feature changes mid-game, which doesn't seem to be a very easy task. The graphics update for fallout and forests, but nothing else I've tried.

Still, if people want new features that can be added by mapscripts, (so they don't need to be changed mid-game), then I could write a short tutorial if there's any demand for that. (What I've done isn't that complicated, there are just a lot of choices along the way that don't work, so it was a lot of trial and error.)

Are you modifying the DLL for this? I do believe graphical updates occur in the source code, especially for units and features. I can double check when I go home.
 
Are you modifying the DLL for this? I do believe graphical updates occur in the source code, especially for units and features. I can double check when I go home.

All of the above I've done without modifying the DLL, but my mod has a modified DLL anyway since it's a total conversion.

I've been exploring how to force a graphics refresh from the DLL and it's looking like bad news. I think the features types are hard coded in the graphics DLL while the game is running. (I've also been trying, unsuccessfully so far, to destroy and reconstruct a plot object to see if that forces a refresh like we see at game load, because the art works then.)

To see what effect it had, I swapped out the art define for marsh with my blight art define, then used Plot.SetFeatureType from FireTuner to create a marsh on a plot (which should now look like blight). The marsh graphics appeared anyway, which really surprised me. I expected to either get nothing or my new graphics, not the old ones. I checked the DB using SQLite and the marsh's art define tag was definitely changed.

If you look at CvPlot::SetFeatureType in the DLL, it calls gDLL->GameplayFeatureChanged(pDllPlot.get(), eNewFeature). The enum that defines FeatureTypes (in CvEnums.h) is hard coded in there, unlike others which start out as empty and are 'populated' from the DB. This makes me think the gDLL actually looks at the integer it gets as a parameter there instead of making any queries about how the game may have been changed. (Adding a corresponding entry to the FeatureTypes enum doesn't fix the issue.)
 
All of the above I've done without modifying the DLL, but my mod has a modified DLL anyway since it's a total conversion.

I've been exploring how to force a graphics refresh from the DLL and it's looking like bad news. I think the features types are hard coded in the graphics DLL while the game is running. (I've also been trying, unsuccessfully so far, to destroy and reconstruct a plot object to see if that forces a refresh like we see at game load, because the art works then.)

To see what effect it had, I swapped out the art define for marsh with my blight art define, then used Plot.SetFeatureType from FireTuner to create a marsh on a plot (which should now look like blight). The marsh graphics appeared anyway, which really surprised me. I expected to either get nothing or my new graphics, not the old ones. I checked the DB using SQLite and the marsh's art define tag was definitely changed.

If you look at CvPlot::SetFeatureType in the DLL, it calls gDLL->GameplayFeatureChanged(pDllPlot.get(), eNewFeature). The enum that defines FeatureTypes (in CvEnums.h) is hard coded in there, unlike others which start out as empty and are 'populated' from the DB. This makes me think the gDLL actually looks at the integer it gets as a parameter there instead of making any queries about how the game may have been changed. (Adding a corresponding entry to the FeatureTypes enum doesn't fix the issue.)

Argh, that's annoying. I can't understand the logic there.
 
Hm. How does it work for forests and fallout, then?

It works the same way for all existing features (ones that are in the base game). Calling SetFeatureType calls the gDLL method with the enum corresponding to the feature type you've changed the plot to. As long as you place a feature on a valid plot, it will be redrawn and rendered correctly. (So placing atolls on land does nothing.) If you give it any different values (> 7, the last one is FEATURE_NATURAL_WONDER) it doesn't seem to do anything.

The only thing I find encouraging is that reloading the game does load correct graphics for new feature types, it's just the feature changing mid-game that seems to be hard coded. But that strikes me as a very strange decision, to hard code a function like that, when it does do a lookup on game load.
 
I'm getting the distinct impression that the gDLL method doesn't work like it really should. You can pass natural wonder's feature ID's as arguments to CvPlot::SetFeatureType as well and they suffer from the same problem as custom features. The yields update and the tooltip displays the right information, but the graphics don't change. :(

Does anyone know if they added a feature via DLC? I'm combing through those now to see if there's any insight to be gained there.
 
I'm getting the distinct impression that the gDLL method doesn't work like it really should. You can pass natural wonder's feature ID's as arguments to CvPlot::SetFeatureType as well and they suffer from the same problem as custom features. The yields update and the tooltip displays the right information, but the graphics don't change. :(

Does anyone know if they added a feature via DLC? I'm combing through those now to see if there's any insight to be gained there.

The Spain/Inca Natural Wonders? El Dorado, Fountain of Youth, etc.?
 
The Spain/Inca Natural Wonders? El Dorado, Fountain of Youth, etc.?

Well I've taken a thorough look through how they did the DLC natural wonders and it looks like they never fixed this problem even for themselves. You can't make the natural wonder graphics show up mid-game, they've got the same necessity for reloading the game as new features. In fact, any feature that isn't one of the original 8 can't be brought in mid-game. (Even when placed on legal hexes, atolls don't show up.) I think all of their new features can only be 'newly placed' before the game has loaded graphics up, which seems quite a shame.

I may just stick with this for my mod long term, but I'd have quite liked blight that could spread. Though it could still be done and would have an effect on the game as it should, I don't think it really plays properly if newly spread blight is invisible.

The only last, tiny glimmer of hope, is this comment between some function declarations in ICvDLLUtility.h:

Code:
virtual void GameplayFeatureChanged(_In_ ICvPlot1* pThisPlot, FeatureTypes newFeature) = 0;

// the last set of functions will shortly be deprecated, this is the replacement
virtual void GameplayPlotStateChange(_In_ const ICvPlot1* pThisPlot, const ResourceTypes eRevealedResource, const ImprovementTypes eRevealedImprovement, 
        const int iRevealedImprovementState, const RouteTypes eRevealedRoute, const int iRouteState) = 0;
virtual void GameplayPlotIconStateChange(_In_ const ICvPlot1* pThisPlot, const ResourceTypes eRevealedResource, 
        const ImprovementTypes eRevealedImprovement, const int iRevealedImprovementState) = 0;

I read that comment as suggesting you should be able to use GameplayPlotStateChange in place of GameplayFeatureChanged, but I can't figure out how. I may try exposing a simple function to lua that passes parameters to GameplayPlotStateChange so I can test it from FireTuner tomorrow, or I may move on to looking at the Visual Tech Tree Editor/reworking the notification system.

As an aside, I have noticed that my new feature "floats" above the land. When units stand on it their legs disappear into it, like in forests and such. That's probably a byproduct of how the gr2 was set up, and it doesn't really bother me enough to look into changing it.

A further aside, the graphics DLL seems to handle the natural wonder graphics entirely separately from the feature graphics (even though, in theory, natural wonders are features). If you call SetFeatureType on a tile that currently is rendered as a natural wonder and change it to something else, the new feature overlaps with the wonder.

Eg. Say you call Plot:SetFeatureType(GameInfoTypes.FEATURE_MARSH) on a tile that has Uluru on it. The marsh appears underneath Uluru and even though the yields update (and the CvPlot code does remove the natural wonder explicitly in SetFeatureType), Uluru's graphics stick around.

Also, successive calls to gDLL->GamePlayFeatureChanged with valid (0 <= i < 8) feature IDs will also let the graphics of those features overlap. (Forest, jungle, and fallout all on the same tile, it makes for a busy hex.) But of course it's only graphics, so it doesn't affect yields/movement or any of that.

Man, I'm gonna be dreaming of this stuff when I get to sleep.
 
Edit: Never mind. Redundant with your post above.


Edit2: I've had some luck affecting various graphics by invoking Events (tutorial here). You can, for example, have full control of city graphic state, including size, era and art style. Also FOW state (though the integer codes are offset from FogOfWarModeTypes so you have to figure it out purely by trial and error). But these are things that normally do change in game, so this may not be relevant here...
 
Top Bottom