Jump to content
The Dark Mod Forums

Penumbra HPL1 engine for a Thief like game?


HMart

Recommended Posts

I was joking on the DarkRadiant thread about using the HPL1 engine for the dark Mod but it made me think of one thing, all the functionality for a stealth based FPS game is there!

 

Manteling is there, object interaction is there, reading papers is there, opening doors and drawers is there and is much more realistic, Dynamic light and shadows ala idtech 4 is there, more importantly all the stealth based gameplay (hiding in the shadows and behind obstacles, etc) and stealth based enemies AI is already there, weapon manipulation is there and other extra things like a inventory system is also there. If the development tools were better, HPL1 would make for a very nice engine to make a TDM like game.

 

I always found it a bit sad how this engine now open sourced has been so neglected, there's no mapping tool like Dark Radiant for it (they used Maya for the maps and objects, but any collada tool works) and the rest of the tools are also basic that is perhaps why, but TDM used their own tools and id tech 4 tools aren't noob friendly either, so the lack of tools don't seem to be the principal cause, perhaps the bigger cause is the saturation with now free AAA game engines out there that don't give any chance to "small" ones like HPL1.

Edited by HMart
Link to comment
Share on other sites

The problem with using some "free" engine from an AAA studio is that they will never EVER release the code. This means in 7-8 years when they no longer give a crap about the engine that they wrote and allowed you to use for "free", they will not fix bugs. Since you don't have access to the code, neither will you be able to fix the bugs.

 

HPL would indeed be great for a stealth game, particularly because of the awesome unified physics, fast performance, and open code.

 

We got lucky with id though because they always released their code which has allowed TDM to become as great as it is and stand-alone.

--- War does not decide who is right, war decides who is left.

Link to comment
Share on other sites

  • 2 weeks later...

There isn't manteling support in HPL1 by the way. It could probably be implemented without much difficulty but just thought I'd clarify that. The problem with HPL1 and HPL2 for that matter is that there is very little support for wide open areas. Attempts have been made by modders to stretch the limits, but it is still nothing compared to a more solid engine like id Tech 4. You may have noticed that in Amnesia and Penumbra that the areas were separated by doors that transport you to new levels, and while this is excusable some of the time in a long game with an unbroken narrative, it is really a sign of the engine's deficiencies. In a game like Thief, which is praised for its amazing immersion this would be unacceptable, and this exact thing was done in Thief: Deadly Shadows. There it was just horribly distracting in my opinion. Ironically enough, in Amnesia and Penumbra, the games that use the HPL engine, this really isn't such a big deal for some reason. And yet I cannot imagine playing Thief like this at all. It just isn't the experience we all love. I think it would be better to adapt the id Tech engine to whatever purposes you need. I keep hearing about how beautifully coded and how organized the source code is.

Link to comment
Share on other sites

Hmm, HPL2 has Deferred Rendering and a dynamic culling system which should extend it's ability to render large areas. Perhaps the implementation is poor?

Does HPL2 support portals for culling like HPL1 as well? (Meaning: could a good mapper squeeze more performance than the typical under-optimized map?)

 

Hmm, HPL3 is in-development and has an LOD system... This could also be a factor (lacking LOD in the previous versions).

 

Has anyone done a high light-count map on this engine to test it's comparable performance?

 

Ah, looks like the shadow silhouettes are done on the CPU (just like Id Tech 4)... this is probably the performance killer in HPL1.

(someday... hopefully... we will be unshackled from this too...)

 

https://github.com/F...ndingVolume.cpp

 

 

 



	cShadowVolumeBV* cBoundingVolume::GetShadowVolume(const cVector3f& avLightPos,
																											float afLightRange,bool abForceUpdate)
	{
			if(cMath::PointBVCollision(avLightPos, *this)) return NULL;

			if(!abForceUpdate && !mbShadowPlanesNeedUpdate) return &mShadowVolume;

			//Set size 0.
			mShadowVolume.mvPoints.resize(0);

			//Get the corners.
			cVector3f vMax = GetMax();
			cVector3f vMin = GetMin();
			cVector3f vCorners[8];
			vCorners[0] = cVector3f(vMax.x,vMax.y,vMax.z);
			vCorners[1] = cVector3f(vMax.x,vMax.y,vMin.z);
			vCorners[2] = cVector3f(vMax.x,vMin.y,vMax.z);
			vCorners[3] = cVector3f(vMax.x,vMin.y,vMin.z);

			vCorners[4] = cVector3f(vMin.x,vMax.y,vMax.z);
			vCorners[5] = cVector3f(vMin.x,vMax.y,vMin.z);
			vCorners[6] = cVector3f(vMin.x,vMin.y,vMax.z);
			vCorners[7] = cVector3f(vMin.x,vMin.y,vMin.z);

			/////////////////////////////////////////////////////////////////////
			//Iterate the faces and check which ones are facing the light.
			int lNearPoint =-1;
			mShadowVolume.mlPlaneCount=0;
			for(int face=0; face< 6; face++)
			{
					gvFaces[face].facingLight = cMath::Vector3Dot(gvFaces[face].normal,
																											vCorners[kvFacePoints[face]] - avLightPos)<0;

					//Get a point for the near plane. (any edge point will do)
					if(gvFaces[face].facingLight)
					{
							mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount] = cPlanef(
																							gvFaces[face].normal*-1.0f,vCorners[kvFacePoints[face]]);
							mShadowVolume.mlPlaneCount++;
					}
			}

			mShadowVolume.mlCapPlanes = mShadowVolume.mlPlaneCount;

			//The direction a point is pushed away in
			cVector3f vDir;

			//The length to push the shadow points.
			float fPushLength = afLightRange*kSqrt2f;

			//////////////////////////////////////////////////////////
			//Iterate the edges and build quads from the silhouette
			for(int edge=0; edge< 12;edge++)
			{
					const cTriEdge& Edge = kvBVEdges[edge];

					cTriangleData &Face1 = gvFaces[Edge.tri1];
					cTriangleData &Face2 = gvFaces[Edge.tri2];

					if((Face1.facingLight && !Face2.facingLight) ||		(Face2.facingLight && !Face1.facingLight))
					{
							if(Face1.facingLight)
							{
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point1]);
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point2]);

									vDir = (vCorners[Edge.point2]-avLightPos); vDir.Normalise();
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point2] + vDir*fPushLength);

									vDir = (vCorners[Edge.point1]-avLightPos); vDir.Normalise();
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point1] + vDir*fPushLength);
							}
							else
							{
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point2]);
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point1]);

									vDir = (vCorners[Edge.point1]-avLightPos); vDir.Normalise();
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point1] + vDir*fPushLength);

									vDir = (vCorners[Edge.point2]-avLightPos); vDir.Normalise();
									mShadowVolume.mvPoints.push_back(vCorners[Edge.point2] + vDir*fPushLength);
							}
					}
			}

			/////////////////////////////////////
			//Create the side planes:

			for(int i=0; i< (int)mShadowVolume.mvPoints.size(); i+=4)
			{
					//Normal should point inwards
					cVector3f vNormal = cMath::Vector3Cross(
															mShadowVolume.mvPoints[i+1] - mShadowVolume.mvPoints[i+0],
															mShadowVolume.mvPoints[i+2] - mShadowVolume.mvPoints[i+0]);
					mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount].FromNormalPoint(vNormal,
																													 mShadowVolume.mvPoints[i+0]);
					mShadowVolume.mvPlanes[mShadowVolume.mlPlaneCount].Normalise();

					mShadowVolume.mlPlaneCount++;
			}

			return &mShadowVolume;
	}

	//-----------------------------------------------------------------------

	void cBoundingVolume::DrawEdges(const cVector3f& avLightPos,float afLightRange, iLowLevelGraphics *apLowLevelGraphics)
	{
			cShadowVolumeBV *pVolume = GetShadowVolume(avLightPos, afLightRange, false);

			apLowLevelGraphics->SetBlendActive(true);
			apLowLevelGraphics->SetBlendFunc(eBlendFunc_One,eBlendFunc_One);
			apLowLevelGraphics->SetDepthWriteActive(false);
			tVertexVec vVtx;
			vVtx.resize(4);

			for(int capplane=0; capplane<mShadowVolume.mlCapPlanes; capplane++)
			{
					mShadowVolume.mvPlanes[capplane].CalcNormal();
					apLowLevelGraphics->DrawLine(GetWorldCenter(),GetWorldCenter() +
																	mShadowVolume.mvPlanes[capplane].normal*-0.5f, cColor(1,1,1,1));
			}

			int lPlane = mShadowVolume.mlCapPlanes;
			for(int quad = 0; quad < (int)pVolume->mvPoints.size(); quad+=4)
			{

					for(int i=0; i<4; i++)
							vVtx[i].pos = pVolume->mvPoints[quad+i];

					apLowLevelGraphics->DrawQuad(vVtx,cColor(0.2f,0,0.2f));

					cVector3f vCenter = (vVtx[1].pos + vVtx[0].pos)*0.5f;
					mShadowVolume.mvPlanes[lPlane].CalcNormal();
					apLowLevelGraphics->DrawLine(vCenter,vCenter +
							mShadowVolume.mvPlanes[lPlane].normal*-0.5f, cColor(1,1,1,1));
					lPlane++;
			}

			apLowLevelGraphics->SetBlendActive(false);
			apLowLevelGraphics->SetDepthWriteActive(true);
	}

	//-----------------------------------------------------------------------

	void cBoundingVolume::AddArrayPoints(const float *apArray, int alNumOfVectors)
	{
			cBVTempArray temp;
			temp.mpArray = apArray;
			temp.mlSize = alNumOfVectors;

			mlstArrays.push_back(temp);
	}

	//-----------------------------------------------------------------------

	void cBoundingVolume::CreateFromPoints(int alStride)
	{
			mvLocalMax = cVector3f(-100000, -100000, -100000);
			mvLocalMin = cVector3f(100000, 100000, 100000);

			for(tBVTempArrayListIt it= mlstArrays.begin();it != mlstArrays.end(); it++)
			{
					//Loop through all the vectors and find min and max
					const float *apVec = it->mpArray;
					int lNumOfVectors = it->mlSize;
					while(lNumOfVectors)
					{
							//Min and max X
							if(apVec[0] < mvLocalMin.x) mvLocalMin.x = apVec[0];
							if(apVec[0] > mvLocalMax.x) mvLocalMax.x = apVec[0];

							//Min and max Y
							if(apVec[1] < mvLocalMin.y) mvLocalMin.y = apVec[1];
							if(apVec[1] > mvLocalMax.y) mvLocalMax.y = apVec[1];

							//Min and max Z
							if(apVec[2] < mvLocalMin.z) mvLocalMin.z = apVec[2];
							if(apVec[2] > mvLocalMax.z) mvLocalMax.z = apVec[2];

							apVec += alStride;
							lNumOfVectors--;
					}
			}
			mlstArrays.clear();

			//Update the used size
			mbPositionUpdated = true;
			mbSizeUpdated = true;
	}

 

 

 

If it's that problematic with open areas, it's probably a side-grade if anything other than the possible physics improvements (which are

also somewhat dubious compared with tuning the existing physics in Doom 3)...

 

HPL1 is open source so I think lost_soul is a little lost... (couldn't resist)...

Edited by nbohr1more

Please visit TDM's IndieDB site and help promote the mod:

 

http://www.indiedb.com/mods/the-dark-mod

 

(Yeah, shameless promotion... but traffic is traffic folks...)

Link to comment
Share on other sites

I'm really not as knowledgeable about this as I probably led you to believe. I'm only relaying to you discussion I've read on the forums that I easily could have misinterpreted. A friend of mine who is more familiar with the development part of the engine suggested that it could also be because almost every entity in the game is interactable, at least more so than in TDM, that includes every door, every drawer, dozens of different types of random useless objects scattered around. In large maps it could probably be too much to handle without the segregation. It was also suggested that it's mainly to minimize loading times by spreading them around rather than having less frequent and longer loading times, which also makes sense.

 

Notwithstanding, I have heard people complain about poor performance in large levels they design and also, perhaps more importantly, I have heard that outdoor areas have not been very succesful because of the lighting engine which isn't designed for that. There was the short outdoor snowy part in Overture and another one in Black Plague which I found to be cool, but in general most Thief levels take place at least partly outdoors and it isn't optimal I've heard. That being said outdoor levels were done with great success in A Machine for Pigs (at least what I've seen so far - haven't finished it) I found, so it can obviously be implemented (there are also much larger maps in that than the original Amnesia, however entity interaction was significantly reduced in that).

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.

  • Recent Status Updates

    • nbohr1more

      The FAQ wiki is almost a proper FAQ now. Probably need to spin-off a bunch of the "remedies" for playing older TDM versions into their own article.
      · 1 reply
    • nbohr1more

      Was checking out old translation packs and decided to fire up TDM 1.07. Rightful Property with sub-20 FPS areas yay! ( same areas run at 180FPS with cranked eye candy on 2.12 )
      · 3 replies
    • taffernicus

      i am so euphoric to see new FMs keep coming out and I am keen to try it out in my leisure time, then suddenly my PC is spouting a couple of S.M.A.R.T errors...
      tbf i cannot afford myself to miss my network emulator image file&progress, important ebooks, hyper-v checkpoint & hyper-v export and the precious thief & TDM gamesaves. Don't fall yourself into & lay your hands on crappy SSD
       
      · 7 replies
    • OrbWeaver

      Does anyone actually use the Normalise button in the Surface inspector? Even after looking at the code I'm not quite sure what it's for.
      · 7 replies
    • Ansome

      Turns out my 15th anniversary mission idea has already been done once or twice before! I've been beaten to the punch once again, but I suppose that's to be expected when there's over 170 FMs out there, eh? I'm not complaining though, I love learning new tricks and taking inspiration from past FMs. Best of luck on your own fan missions!
      · 4 replies
×
×
  • Create New...