Jump to content
The Dark Mod Forums

Newbie DarkRadiant Questions


demagogue
 Share

Recommended Posts

@HMart you should be able to have detail textures with certain material keywords. You can do this by e.g. stacking normalmaps with addnormals, or by using gl blend modes. I'm using something a la detail noise texture in Rage, since one of the gl blend modes seems to work like Overlay mode in photoshop:

Clipboard01.thumb.jpg.0a0a43aa5e6c6fc7a4886a6db8736b5b.jpg

Edit: sorry, you can't really make detail normalmaps with addnormals, since this is image programs territory and there is no option to tile the second normalmap there. It's quite confusing, because there's a scale keyword for image stage, which is responsible for tiling, and there's a scale image program function, which acts as a multiplier for image RGBA values. Two completely different things.

Edited by peter_spy
Link to comment
Share on other sites

@HMart So the engine should support this I understand? However we don't have any such textures by default, do we? Note that I'm not suggesting adding extra textures for this purpose: We already have good terrain images for most use cases, only suggesting a shader to blend multiple ones based on surface direction to obtain more realistic detail.

Edited by MirceaKitsune
Link to comment
Share on other sites

On 7/28/2021 at 4:20 PM, MirceaKitsune said:

Meaning terrain materials that alternate between images based on the surface normals, blending one image horizontally and another vertically for more realism

The term for this is called triplanar mapping.  Before getting your hopes up, the answer is no, TDM does not have this capability.

 

That said, I once did an experiment some time ago, I achieved some degree of success but there were some major drawbacks:

 

Spoiler

Other than the trees, this terrain is a single mesh with one material applied to it:splat.thumb.jpg.fa40804ab5ac18dfb58780e7d33c9ac0.jpg

The features:

  • Using an RGB splatmap, I could paint 3 different textures onto the ground.
  • A 4th texture would automatically be used for vertical areas using triplanar mapping.
  • Where different textures meet, instead of linearly blending them together, they are blended based on heightmap.  Lacking actual heightmap data for the textures in use here, I substituted a grayscale map of the texture and still got good results.

Why you shouldn't do this:

  • Custom shaders are fun to toy around with, but they're likely to get broken in future releases.  There was another thread for discussion of this here.
  • This isn't great for performance, it's a heavy material that will get run on large portions of your screen.
  • All the magic happens by modulating the diffuse texture.  You can't have normal or specular maps.
  • Like 1
Link to comment
Share on other sites

Thanks @jonri that clarifies it better. Yeah triplanar mapping is a common and great way of doing it. I figured it might still be possible by manually painting a grayscale blend image, but with terrain patches that you need to constantly adjust and move around that isn't really an option. And if it breaks the normal or specular map it's a big no-no, each terrain shader should still have its own active on its own portion of the surface. Wonder what could be done for our engine to support even a simple version of this system.

Link to comment
Share on other sites

33 minutes ago, MirceaKitsune said:

Thanks @jonri that clarifies it better. Yeah triplanar mapping is a common and great way of doing it. I figured it might still be possible by manually painting a grayscale blend image, but with terrain patches that you need to constantly adjust and move around that isn't really an option. And if it breaks the normal or specular map it's a big no-no, each terrain shader should still have its own active on its own portion of the surface. Wonder what could be done for our engine to support even a simple version of this system.

@MirceaKitsuneto be clear, you can still use vertex color blending to blend two materials and that shouldn't break the other maps, it was just my experimental approach to terrain that did that.

And you are correct, it wouldn't be something you could attempt with patches, you'd want to build it all in a 3d modeler.

Link to comment
Share on other sites

Could someone point me to the sound shader and/or ogg file that has the sound of a fly circling over a chamberpot? I know I've seen this in FMs in the past, but can't seem find it.

Link to comment
Share on other sites

19 minutes ago, Geep said:

Could someone point me to the sound shader and/or ogg file that has the sound of a fly circling over a chamberpot? I know I've seen this in FMs in the past, but can't seem find it.

I've got such a speaker underneath the northwestern external stairs on the ship in Perilous Refuge - can't checl that myself atm.

  • Like 1
Link to comment
Share on other sites

Need some help. Nearing the end of my list of fixes, and this sprang up at random, thankfully I caught it by chance. 

I've got an issue with some locked doors and corresponding keys. At first they worked perfect, but now the key(s) won't unlock the door(s).

Here's the set up. I've got a room with three doors into it. Two of them are locked. There is a key outside the room on a guard, and one inside the room hidden somewhere. Attached is a screenshot of the properties of the keys (top row) and the doors they correspond to (bottom row.)

I should note, initially I started with just one key outside the room, placed on the guard. While it technically worked, beta feedback dictated a second key to be found inside the room. No problem, the first time I implemented it and dmapped it worked exactly as intended, with my new second key unlocking both doors, and vice versa for the key on the guard. So I moved on and didn't think about it for months. But randomly I noticed they don't do shit no more! I swear I haven't changed the spawnargs since I got it working the way I wanted, but clearly something is wrong and now I'm perplexed.

Thanks,

-Jedi

bedroomkey.png

Edited by Jedi_Wannabe

As my father used to say, "A grenade a day, keeps the enemy at bay!"

Link to comment
Share on other sites

There's an excess comma at the end of the key name in the "used_by" spawnarg. Maybe that's what tripped up the door script for both keys.

Also, it might be worth making the 2 keys stackable with inv_stackable 1, since they're identical. Might need to change the used_by spawnargs to point to the inv_names in that case.

  • Thanks 1
Link to comment
Share on other sites

Well I can't explain the existence of the comma haha, but it's absence cleared up my problem.

They stack by default, even without the inv_stackable 1 spawnarg. Unless they break again I'm just gonna leave it lol.

Thanks for the help @Dragofer!

  • Like 1

As my father used to say, "A grenade a day, keeps the enemy at bay!"

Link to comment
Share on other sites

Another sound issue. Primarily for conjectural performance reasons, I was trying to replace large area speakers with info_location ambience instead, using the same looping sound shaders (e.g., city_sounds_varied). But it appears that if the sound shader has multiple sound files, only 1 ever gets played & looped. Sucks. Since this is not mission-critical, I'll probably revert the build, unless someone has an easy fix.

Update: Got a mix of smaller speakers and info_locations that are working good enough for me, so moving on. (BTW, the manifestation with city_sounds_varied as a location ambient seems to be that a random ogg is played the first time, but then it loops forevermore on the first ogg in the list. So back to a speaker, with "random 1" "s_looping 0" for that one.)

Edited by Geep
update
Link to comment
Share on other sites

Yes, if a sound shader is looping then it will only ever loop on one sound file. Otherwise how would it know when to switch from one to another, and how would it do so without a jarring interruption of the sound?

In theory it would be possible to implement fading between multiple sounds in a single looping shader, but it would require additional work on the engine and extra fields in the shader to control things like how many seconds it should play each file for, and how often it should switch to another one.

But I'm not sure this would be a very important feature, because the expectation is that if you want a load of random things in a single looping ambient, you just bake them in to the looping sound file using your audio editor.

I very much doubt you will get any performance benefit from combining multiple location speakers into a single ambient. Playing sounds isn't the performance-affecting element in a game engine.

Link to comment
Share on other sites

@OrbWeaver

With a speaker, there's already a "random" keyword that does the magic, with expectation that each constituent ogg file (which when playing will play in its entirety) begins and ends with silence, so flows OK. I'm just observing that it would be nice if location-based ambience had this capability likewise. A bit of a lift to have to go to the audio editor to compose a pseudo-random track, at least for me.

What you are describing is more elaborate than what I considered, with fades between constituent ogg files and control over duration of each.

As for performance, some forum posts reported that large-extent speakers (not multiple) were a problem, and should be replaced by location zones. Having just gone through that exercise, maybe there's a couple fps to be had... but there's more visportals to support locationseparators, so that contributes too. Not a controlled experiment.

Link to comment
Share on other sites

1 hour ago, Geep said:

With a speaker, there's already a "random" keyword that does the magic, with expectation that each constituent ogg file (which when playing will play in its entirety) begins and ends with silence, so flows OK.

As far as I know, random only applies to sound shaders which are triggered repeatedly. I've never heard of s_looping do anything other than loop a single sound forever; if it has gained the capability to also switch between random sounds, that is news to me.

Link to comment
Share on other sites

"s_looping" is not involved. It's "wait" (when applied to a speaker) that specifies a time interval to repeatedly, automatically start up one of the ogg files. "random" provides variation to that time interval. "nodup" to prevent the same ogg file from being played twice in a row.

Probably not a built-in way to do what I was originally bloviating about: repeatedly pick an ogg, play it by itself from beginning to end, pick another, repeat forever.

Link to comment
Share on other sites

I'm wondering if there's a way to set spawnargs on heads of AIs:

Normally for anything that's def_attached, you can use "set x on y", where x is the name of the spawnarg and y is the name of the attachment (name_attach). So i.e.

"set light_radius on flame" "120 120 120"

on candle holders changes the radius of the def_attached light entity ("name_attach" "flame"). This is important because in DR it's not possible to select or apply spawnargs to def_attached entities directly.

Problem is, it seems heads are attached differently. You use def_head instead of def_attach, and there seems to be no name_attach given to the head (only "head_bodyname" "head" and "head_jointname" "head"). This doesn't work:

"set model on head" "head_skeleton_skull"

These "set x on y" spawnargs seem to be parsed in Entity.cpp from line 12886. Maybe we should add separate handling for "set x on head"?

Spoiler
// tels: parse all "set .." spawnargs
const idKeyValue *kv_set = from->MatchPrefix( "set ", NULL );
while ( kv_set )
{
	// "set FOO on BAR" "0.5 0.5 0"
	// means set "_color" "0.5 0.5 0" on the entity attached to the attachment point
	// named "BAR" (defined with "name_attach" "BAR" on the original entity)

	// Get the value
	// example: "0.5 0.5 0"
	idStr SpawnargValue(kv_set->GetValue());

	// "set FOO on BAR"
	idStr SetAttName(kv_set->GetKey());
	// "set FOO on BAR" => "FOO on BAR"
	SetAttName = SetAttName.Right( kv_set->GetKey().Length() - 4 );

	// "FOO on BAR"
	idStr SpawnargName(SetAttName);

	// find position of first ' '	
	int PosSpace = SetAttName.Find( ' ', 0, -1);

	if (PosSpace == -1)
	{
		gameLocal.Warning(
			"%s of class %s: Spawnarg '%s' (value '%s') w/o attachment name. Applying to to all attachments.",
			from->GetString("name", "[unknown]"), from->GetString("classname", "[unknown]"),
			kv_set->GetKey().c_str(), kv_set->GetValue().c_str()
		);
		//kv_set = from->MatchPrefix( "set ", kv_set );
		//continue;		
		// pretend "set _color" "0.1 0.2 0.3" means "set _color on BAR" where BAR is the
		// current attachement. So it applies to all of them.
		// SpawnargName is already right
		SetAttName = AttNameValue;
	}
	else
	{
		// "FOO on BAR" => "FOO"
		SpawnargName = SpawnargName.Left( PosSpace );
		// "FOO on BAR" => "BAR"
		SetAttName = SetAttName.Right( SetAttName.Length() - (PosSpace + 4) );
	}

	// gameLocal.Printf("SetAttName '%s'\n", SetAttName.c_str());
	// gameLocal.Printf("AttNameValue '%s'\n", AttNameValue.c_str());
	// gameLocal.Printf("SpawnargName '%s'\n", SpawnargName.c_str());

	// does this spawnarg apply to the newly spawned entity?
	if (SetAttName == AttNameValue)
	{
		// it matches, so this spawnarg must be applied directly to this entity
		//gameLocal.Printf("Match: Setting '%s' to '%s'\n", SpawnargName.c_str(), SpawnargValue.c_str() );
		args.Set( SpawnargName, SpawnargValue );
	}
	else
	{
		// pass along the original "set ..." spawnarg, it might apply to an
		// def_attached entity of the newly spawned one
		//gameLocal.Printf("No match: Passing along '%s' ('%s')\n", kv_set->GetKey().c_str(), SpawnargValue.c_str() );
		args.Set( kv_set->GetKey(), SpawnargValue );
	}

	kv_set = from->MatchPrefix( "set ", kv_set );
	// end while ( kv_set )
}

 

 

What I want to use this for is to set "model_xray" on heads so that they get replaced with a skull when seen through an xray screen. This would be much more convenient than having to create a new head entity just to change a couple spawnargs.

 

 

Edit: might be better to modify idActor::SetupHead() in Actor.cpp since that's where def_head is handled, while the code I pasted from Entity.cpp only handles spawnargs for def_attach entities before spawning them.

Spoiler
void idActor::SetupHead()
{
	idStr headModelDefName = spawnArgs.GetString( "def_head", "" );

	if (!headModelDefName.IsEmpty())
	{
		// We look if the head model is defined as a key to have a specific offset.
		// If that is not the case, then we use the default value, if it exists, 
		// otherwise there is no offset at all.
		mHeadModelOffset = spawnArgs.GetVector(headModelDefName, "0 0 0");

		// greebo: Regardless what happens, the offsetHeadModel vector always gets added to the offset
		mHeadModelOffset += spawnArgs.GetVector("offsetHeadModel", "0 0 0");

		idStr jointName = spawnArgs.GetString( "head_joint" );
		jointHandle_t joint = animator.GetJointHandle( jointName );
		if ( joint == INVALID_JOINT ) {
			gameLocal.Error( "Joint '%s' not found for 'head_joint' on '%s'", jointName.c_str(), name.c_str() );
		}

		// set the damage joint to be part of the head damage group (if possible)
		jointHandle_t damageJoint = joint;

		for (int i = 0; i < damageGroups.Num(); i++ )
		{
			if ( damageGroups[ i ] == "head" ) {
				damageJoint = static_cast<jointHandle_t>( i );
				break;
			}
		}

		// Setup the default spawnargs for all heads
		idDict args;

		const idDeclEntityDef* def = gameLocal.FindEntityDef(headModelDefName, false);

		if (def == NULL)
		{
			gameLocal.Warning("Could not find head entityDef %s!", headModelDefName.c_str());

			// Try to fallback on the default head entityDef
			def = gameLocal.FindEntityDef(TDM_HEAD_ENTITYDEF, false);
		}

		if (def != NULL)
		{
			// Make a copy of the default spawnargs
			args = def->dict;
		}
		else
		{
			gameLocal.Warning("Could not find head entityDef %s or %s!", headModelDefName.c_str(), TDM_HEAD_ENTITYDEF);
		}		
		
		// Copy any sounds in case we have frame commands on the head
		for (const idKeyValue* kv = spawnArgs.MatchPrefix("snd_", NULL); kv != NULL; kv = spawnArgs.MatchPrefix("snd_", kv)) 
		{
			args.Set(kv->GetKey(), kv->GetValue());
		}

		// Spawn the head entity
		idEntity* ent = gameLocal.SpawnEntityType(idAFAttachment::Type, &args);
		idAFAttachment* headEnt = static_cast<idAFAttachment*>(ent);
		headEnt->SetName( name + "_head" );

		// Retrieve the actual model from the head entityDef
		idStr headModel = args.GetString("model");
		if (headModel.IsEmpty())
		{
			gameLocal.Warning("No 'model' spawnarg on head entityDef: %s", headModelDefName.c_str());
		}
		headEnt->SetBody( this, headModel, damageJoint );
		headEnt->SetCombatModel();

		// Store the head locally
		head = headEnt;

		DM_LOG(LC_AI, LT_DEBUG)LOGSTRING("SETBODY: Actor %s : damage joint %d for attached head is part of damage group %s\r", name.c_str(), (int) damageJoint, GetDamageGroup( damageJoint ) );

		// Add the head as attachment
		idVec3 origin;
		idMat3 axis;
		CAttachInfo& attach = m_Attachments.Alloc();

		attach.channel = animator.GetChannelForJoint( joint );
		animator.GetJointTransform( joint, gameLocal.time, origin, axis );
		origin = renderEntity.origin + ( origin + modelOffset + mHeadModelOffset ) * renderEntity.axis;
		attach.ent = headEnt;
		attach.posName = jointName; // grayman #2603

		headEnt->SetOrigin( origin );
		headEnt->SetAxis( renderEntity.axis );
		headEnt->BindToJoint( this, joint, true );

		// greebo: Setup the frob-peer relationship between head and body
		m_FrobPeers.AddUnique(headEnt->name);
	}
}

 

 

Link to comment
Share on other sites

I've added support for "set x on head" spawnargs for AI heads, adding a simplifed version of the general method from idEntity::ParseAttachmentSpawnargs() to idActor::SetupHead(). Rev 9557

  • Thanks 1
Link to comment
Share on other sites

Scripting question. For script variables that are at file level (not within a function and not part of a script object), are their values automatically handled correctly during game Save/Load? Or does the script author need to do something explicit?

Link to comment
Share on other sites

32 minutes ago, Geep said:

Scripting question. For script variables that are at file level (not within a function and not part of a script object), are their values automatically handled correctly during game Save/Load? Or does the script author need to do something explicit?

Yes to question 1, I've used such variables for the new secret tracking system and encountered no problems when testing loading and saving.

  • Thanks 1
Link to comment
Share on other sites

Since this is the newbie question thread, here's one that I feel really should fit; (I believe I should know this, but no, I do not 😒) You probably think I'm nuts but is there any simple way to see/know exactly where every material definition and skins and such are stored?

I would like to "Hey, I like this model I found in DR, but I would like to tinker with it (add a new skin to it or change the graphics in some way). I wonder where the material definition and graphical textures are stored..."

My solution this far has been: extract every single pk4-folder and search through them one by one and open the text files I believe contain the stuff I need and after 30 minutes my head hurts and I want to kill everything within 50 meters...🤨

It would be golden to be able to enter the Skin name, from the model inspector in DR, somewhere and get a pointer to which folder the material files/data is stored...

 

Link to comment
Share on other sites

With a copy of a TDM distribution where you have expanded all the pk4s, you can search from the directory root with a tool that does full-text search across file. With Windows, I use TextPad to do this... look for Search/Find in Files. You can specify what string to look for and in what files, e.g., "*.def". It's not perfect, but usually gets the job done.

To apply it to FMs, you'd have to expand those pk4s too.

  • Thanks 1
Link to comment
Share on other sites

I might add, if what you are searching for is not within a file, but within a filename, then you can use the built-in File Explorer search box in the upper right corner. Navigate to the tree of interest and enter the search term (e.g., rug*.dds) . This will also work for compressed directories, but you have to first rename them from .pk4 to .zip for Windows to understand.

  • Thanks 1
Link to comment
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

 Share


×
×
  • Create New...