Jump to content
The Dark Mod Forums

Revelator's TDM Branch


nbohr1more

Recommended Posts

Ahaaa !!! seems i finally cracked how to get ARB_depth_clamp working :D

 

Problem: when combined with depthbounds testing it causes really bad artifacting in shadows.

 

Solution: I created code paths to either use depth clamping or depthbounds testing (also for light volumes -> RBDoom3 code).

 

So far it seems to be a good tad faster than depthbounds testing but ill test a bit further before comitting it to darkmod :)

  • Like 1
Link to comment
Share on other sites

Nice one. I've been studying shadow volume rasterization myself this evening too.

 

I have to admit I'm confused about how the engine deals with the near plane cutting into a shadow volume. I can't see any evidence of depth clamping, and I can't see any evidence of it drawing caps at the near plane. In fact, I'm pretty sure it doesn't.

 

The only thing I can see that might be substituting for both is the call to glPolygonOffset in RB_T_Shadow:

if ( r_shadowPolygonFactor.GetFloat() || r_shadowPolygonOffset.GetFloat() ) {
       qglPolygonOffset( r_shadowPolygonFactor.GetFloat(), -r_shadowPolygonOffset.GetFloat() );
       qglEnable( GL_POLYGON_OFFSET_FILL );
   }

r_shadowPolygonFactor defaults to 0, so off, but r_shadowPolygonOffset defaults to -1, so it's active. But I can't find any evidence via google searches of polygonoffset being useful for capping the near plane either. Have you any idea how this works currently? :wacko:

  • Like 1
Link to comment
Share on other sites

If i understood correctly it just adds a small offset to the shadow volumen to avoid Z fighting (its kind of a hack but hard to get around without when using stencil volumes).

 

Btw i just got SSAO / SSIL / and softshadows working with the new depth renderer but it causes massive problems with sikkpins sunshaft code if its enabled, if not it works like a charm :blink:

 

The sunshafts causes some really nasty overdraw making the scene look like you are looking through a lens directly at the sun ouch my eyes :huh: .

 

znoe3c.jpg

 

Looks like this without sunshafts :)

Link to comment
Share on other sites

Nice work again!

 

The near cap thing remains a mystery then. I'll report back when I get to the bottom of it. There's no depth clamping, no capping, but also none of the errors that all the articles I can find say we should expect when the front of a shadow volume gets partically clipped by the near plane.

Link to comment
Share on other sites

I think i heard mention of what you describe at the inside3d forums, ill see if i can find the post :)

 

ARB_depth_clamp was actually a nvidia extension back in the days but got added to the OpenGL spec from version 2.0 i think.

Old extention was named NV_depth_clamp and was only avaliable on nvidia cards, not sure why the documentation for what it does is so hard to find :huh: .

Link to comment
Share on other sites

Ok fixed sunshafts :) take a look at the hackedview function in the game code it tries to use CaptureRenderToImage("_CurrentRender") to emulate a depth copy so i changed it to CaptureDepthToImage("_CurrentDepth") instead and it looks like it works now yay.

 

Sadly sikkpins ssao shader uses a hack to make the depth image and it still causes artifacts on heathaze / portals and some particles (ghostly outlines on the effects but less visible now).

Link to comment
Share on other sites

Nice work again! The near cap thing remains a mystery then. I'll report back when I get to the bottom of it. There's no depth clamping, no capping, but also none of the errors that all the articles I can find say we should expect when the front of a shadow volume gets partically clipped by the near plane.

*Facepalm* I spent probably 2 hours in total looking for the elusive capping of near plane shadow volumes before remembering that the engine does -- and always has -- used a z-fail-type workaround to handle the situation. So there's no capping of the front plane.

 

Ahaaa !!! seems i finally cracked how to get ARB_depth_clamp working :D Problem: when combined with depthbounds testing it causes really bad artifacting in shadows. Solution: I created code paths to either use depth clamping or depthbounds testing (also for light volumes -> RBDoom3 code). So far it seems to be a good tad faster than depthbounds testing but ill test a bit further before comitting it to darkmod :)

Is it depthbounds causing the problem I wonder, or one of the z-fail workarounds? You'd expect depth bounds to remove all or 0 shadows for any given screen pixel. But I've no idea how it interacts with depth clamping.

 

It's good to know that we can support both solutions at the same time. We're going to need to keep the depth bounds test for soft stencil shadows I think. The fragment shader will be doing a lot more work while drawing shadows, so we will need to cull more shadow not less. At least, that's how it's done by the three penumbra wedge methods I've been reading up on. But I'm wanting to try something different first, using the stencil buffer only for the inner umbra (which could be drawn by either method), followed by drawing the penumbra directly into the alpha channel of the color buffer before any surfaces that get hit by the light get drawn. It'll still probably run faster with depth bounds testing than with front face capping, but I'll try it both ways when I get that far. The first problem I have to tackle is building wedges in the first place without increasing the number of verts that get positioned and uploaded to the GPU every frame.

 

BTW I questioned what was GLEW? etc above when you said you'd implemented it. I've now got round to giving it a try and done my own little openGL app with the aid of a tutorial. Glew is a nice addition :) And I should have done this exercise at the beginning. It's really helped me get my head around the options for wedges, as well as about some of the improvements you've put in your branch.

Link to comment
Share on other sites

Oh i might have given you to much hope then :( no they do not run well together so either you use depthbounds testing (settable with a cvar) else it will use depth clamping.

Im not 100 but it might be caused by the support code for depthbounds testing, anyway if we end up needing depthbounds testing we could easily create a simple boolean switch to alternate between the two

methods instead of a cvar, so far sikkmods soft shadows seem to work just fine with depth clamping.

Link to comment
Share on other sites

Not sure why it would be needed for a screenshot of some work in progress :) but if you wonder yes soft shadows still suck FPS wise,

mostly because the shader needs work to use the new depthbuffer access code correctly, atm both SSAO and soft shadows use a physical image to emulate a depthmap instead of the one copied off the depthbuffer and as you can see it causes all sorts of problems ;)

Link to comment
Share on other sites

Oh i might have given you to much hope then :( no they do not run well together so either you use depthbounds testing (settable with a cvar) else it will use depth clamping.

Im not 100 but it might be caused by the support code for depthbounds testing, anyway if we end up needing depthbounds testing we could easily create a simple boolean switch to alternate between the two

methods instead of a cvar, so far sikkmods soft shadows seem to work just fine with depth clamping.

 

No worries, I understood that perfectly from your post. We will use one or the other in any given draw, not both combined.

 

We tried out the Sikkmod soft shadows method earlier in the 2.03 dev cycle, thanks to a lot of hard work by Obsttorte, but the method turned out to have too many flaws that would have been really tough to overcome: it causes bizarre light bleed in odd situations that would be really difficult to fix. IIRC the main problem was that lights shine through walls from neighbouring visleafs when you render without shadows, so you get a whole load of surfaces lit from new directions that wouldn't have had any direct light at all from those directions when rendering normally with shadows. Within a room, blending between the shadowed image of a statue or an AI and and the non-shadowed image of that statue is ok to make soft shadows. But when a light is shining through a wall, blending between the shadowed and non-shadowed images leaves you with a strange and unexplained glowing bits on the surfaces hit by a light though the wall. It worked well in some maps, where the mapper had happened to use mostly central lights in each room, but not in others that had lots of lights positioned on walls.

Link to comment
Share on other sites

2zp8sg7.jpg

 

SSAO now without the outline bug :D breaks sunshafts though (massive lightbleed) so unless someone can fix that part it would have to stay off.

 

Aye sikkmods softshadows are a bit buggy especially on ATI/AMD where the shadows turn up green :blink: in gausian and box but atleast they work pretty well with the poisson filter allbeit ultra slowish.

Link to comment
Share on other sites

Ok to get this far and because it might be off interrest to others create this in Image_Init.cpp

static void R_DepthBufferImage(idImage *image) {
	byte	data[DEFAULT_SIZE][DEFAULT_SIZE][4];
	memset(data, 0, sizeof(data));
	data[0][0][0] = 16;
	data[0][0][1] = 32;
	data[0][0][2] = 48;
	data[0][0][3] = 96;
	image->GenerateImage((byte *)data, DEFAULT_SIZE, DEFAULT_SIZE, TF_LINEAR, false, TR_CLAMP_TO_BORDER, TD_HIGH_QUALITY);
	// now reset it to a depth image
	glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, DEFAULT_SIZE, DEFAULT_SIZE, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, data);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE_ARB, GL_COMPARE_R_TO_TEXTURE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC_ARB, GL_LEQUAL);
	// explicit zero depth border
	float color[4];
	color[0] = color[1] = color[2] = color[3] = 0.0f;
	glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, color);
}

second change currentDepthImage = ImageFromFunction("_currentDepth", R_RGBA8Image);

to currentDepthImage = ImageFromFunction("_currentDepth", R_DepthBufferImage);

 

This example taken from draw_exp.cpp

 

ok next off

 

change the old CopyDepthBuffer to

void idImage::CopyDepthbuffer(int x, int y, int imageWidth, int imageHeight, bool useOversizedBuffer) {
	this->Bind();
	// if the size isn't a power of 2, the image must be increased in size
	int	potWidth, potHeight;
	IMAGE_ROUND_POWER2(imageWidth, potWidth);
	IMAGE_ROUND_POWER2(imageHeight, potHeight);
	// Ensure we are reading from the back buffer
	glReadBuffer(GL_BACK);
	// only resize if the current dimensions can't hold it at all,
	// otherwise subview renderings could thrash this
	if ((useOversizedBuffer && (uploadWidth < potWidth || uploadHeight < potHeight)) || (!useOversizedBuffer && (uploadWidth != potWidth || uploadHeight != potHeight))) {
		uploadWidth = potWidth;
		uploadHeight = potHeight;
		if (potWidth == imageWidth && potHeight == imageHeight) {
			glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, x, y, imageWidth, imageHeight, NULL);
		} else {
			// This bit runs once only at map start, because it tests whether the image is too small to hold the screen.
			// It resizes the texture to a power of two that can hold the screen,
			// and then subsequent captures to the texture put the depth component into the RGB channels: (steveL)
			glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24_ARB, potWidth, potHeight, 0, GL_DEPTH_COMPONENT, GL_UNSIGNED_BYTE, NULL);
			glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight);
		}
	}
	else {
		// otherwise, just subimage upload it so that drivers can tell if we are going to be changing
		// it and don't try and do a texture compression or some other silliness.
		glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x, y, imageWidth, imageHeight);
	}
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	backEnd.c_copyDepthBuffer++;
}

I know this seems like something that should not be but we cannot guarantee that we did copy a depthbuffer so just do it :)

 

lastly if you use sikkmods shaders find the RenderDepth function and change it to this

void idPlayerView::RenderDepth(bool bCrop) {
	// modify player related models in depth render.
	if (!player->IsHidden() && !pm_thirdPerson.GetBool()) {
		player->ToggleSuppression(true);
	}
	if (!bDepthRendered) {
		if (bCrop) {
			int	nWidth = renderSystem->GetScreenWidth() / 2.0f;
			int	nHeight = renderSystem->GetScreenHeight() / 2.0f;
			renderSystem->CropRenderSize(nWidth, nHeight, true);
			// set our depthView parms
			renderView_t depthView = hackedView;
			depthView.viewID = -8;
			depthView.globalMaterial = depthMaterial;
			// render scene
			gameRenderWorld->RenderScene(&depthView);
			// capture image for our depth buffer
			renderSystem->CaptureDepthToImage("_depth");
			renderSystem->UnCrop();
		}
		else if (!bCrop) {	// uncropped depth is used specifically for soft shadows
			// set our depthView parms
			renderView_t depthView = hackedView;
			depthView.viewID = -8;
			depthView.globalMaterial = depthMaterial;
			// render scene
			gameRenderWorld->RenderScene(&depthView);
			// capture image for our depth buffer
			renderSystem->CaptureDepthToImage("_ssDepth");
		}
		bDepthRendered = true;
	}
	// Restore player models
	if (!player->IsHidden() && !pm_thirdPerson.GetBool() && player->bViewModelsModified) {
		player->ToggleSuppression(false);
	}
}

and find RB_STD_DrawShaderPasses and make it look like this.

static int RB_STD_DrawShaderPasses(drawSurf_t **drawSurfs, int numDrawSurfs) {
	int		shaderPasses;
	// only obey skipAmbient if we are rendering a view
	if (backEnd.viewDef->viewEntitys && r_skipAmbient.GetBool()) {
		return numDrawSurfs;
	}
	// if we are about to draw the first surface that needs
	// the rendering in a texture, copy it over
	if (drawSurfs[0]->material->GetSort() >= SS_POST_PROCESS) {
		if (r_skipPostProcess.GetBool()) {
			return 0;
		}
		// only dump if in a 3d view
		if (backEnd.viewDef->viewEntitys) {
			// copy of framebuffer revelator.
			globalImages->currentRenderImage->CopyFramebuffer(
			backEnd.viewDef->viewport.x1,
			backEnd.viewDef->viewport.y1,
			backEnd.viewDef->viewport.x2 -
			backEnd.viewDef->viewport.x1 + 1,
			backEnd.viewDef->viewport.y2 -
			backEnd.viewDef->viewport.y1 + 1, true);
		}
		backEnd.currentRenderCopied = true;
	}
	// copy of depthbuffer revelator.
	// might also be used by post process shaders.
	// viewID -8 is sikkmods id for the depthbuffer.
	if (backEnd.viewDef->renderView.viewID <= -8) {
		if(r_skipDepthCopy.GetBool()) {
			return 0;
		}
		// only dump if in a 3d view
		if (backEnd.viewDef->viewEntitys) {
			globalImages->currentDepthImage->CopyDepthbuffer(
			backEnd.viewDef->viewport.x1,
			backEnd.viewDef->viewport.y1,
			backEnd.viewDef->viewport.x2 -
			backEnd.viewDef->viewport.x1 + 1,
			backEnd.viewDef->viewport.y2 -
			backEnd.viewDef->viewport.y1 + 1, true);
		}
	}
	GL_SelectTexture(1);
	globalImages->BindNull();
	GL_SelectTexture(0);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	RB_SetProgramEnvironment();
	// we don't use RB_RenderDrawSurfListWithFunction()
	// because we want to defer the matrix load because many
	// surfaces won't draw any ambient passes
	backEnd.currentSpace = NULL;
	for (shaderPasses = 0; shaderPasses < numDrawSurfs; shaderPasses++) {
		if (drawSurfs[shaderPasses]->material->SuppressInSubview()) {
			continue;
		}
		if (backEnd.viewDef->isXraySubview && drawSurfs[shaderPasses]->space->entityDef) {
			if (drawSurfs[shaderPasses]->space->entityDef->parms.xrayIndex != 2) {
				continue;
			}
		}
		// we need to draw the post process shaders after we have drawn the fog lights
		if (drawSurfs[shaderPasses]->material->GetSort() >= SS_POST_PROCESS && !backEnd.currentRenderCopied) {
			break;
		}
		RB_STD_T_RenderShaderPasses(drawSurfs[shaderPasses]);
	}
	GL_Cull(CT_FRONT_SIDED);
	GL_Color(1.0f, 1.0f, 1.0f);
	return shaderPasses;
}

and delete the old CopyDepthbuffer code bit from RB_STD_FillDepthBuffer.

 

now SSAO works allmost without bugs :) but be aware that the change to use the real depthbuffer breaks sunshafts.

Link to comment
Share on other sites

It doesn't seem to compile for me either. Apparently scons doesn't detect the 32bit libraries, although they are there. For example, it detects /usr/lib64/libGLU.so, notices it's incompatible, but doesn't find an /usr/lib/libGLU.so. This appears to be because for some reason, openSUSE names the symlink for "lib64" libglu.so but the one for "lib" libglu.so.1. As for the other one, lib64/libglu.so versus lib/libglu.so.1.10. Likely improper packaging, but since I don't wish to hack system files this can hopefully be fixed in the scons settings so it detects the libraries differently. I am now able to compile the mainstream engine from SVN, so I imagine the issue is specific to this fork.

/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../libGLU.so when searching for -lGLU
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/libGLU.so when searching for -lGLU
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: cannot find -lGLU
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/gcc/x86_64-suse-linux/4.8/../../../libGLEW.so when searching for -lGLEW
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: skipping incompatible /usr/lib64/libGLEW.so when searching for -lGLEW
/usr/lib64/gcc/x86_64-suse-linux/4.8/../../../../x86_64-suse-linux/bin/ld: cannot find -lGLEW
collect2: error: ld returned 1 exit status
scons: *** [build/release/core/sys/scons/doom] Error 1
scons: building terminated because of errors.

real    4m37.949s
user    8m43.874s
sys     0m36.100s
cp: cannot stat ‘thedarkmod.x86’: No such file or directory
cp: cannot stat ‘thedarkmod.x86’: No such file or directory
rm: cannot remove ‘/usr/include/zlib.h’: Permission denied
mv: cannot stat ‘/usr/include/zlib.h.bak’: No such file or directory
mircea@linux-qz0r:~/Games/Quake/TheDarkMod/darkmod_experimental_GIT/src> 
Link to comment
Share on other sites

Ok to get this far and because it might be off interrest to others create this in Image_Init.cpp

________________________________________________

Nice work as always. What's the advantage of the tweaks in R_DepthBufferImage? Other than tidier, easier to read code of course. I'm curious to know how the border etc help.

 

One small problem with the above layout: we're going to need the depth image to be available to shaders while light interactions are being drawn in 2.04 so we can't delay capturing it till we draw the non-lit shader passes. I know it looks tidier where you put it and untidy where I put it, but we do need it earlier :) Would that cause a problem for your sikkmod ssao implementation?

Link to comment
Share on other sites

This is pretty much as early as you can get in the chain :) try following the function call list to see what i mean.

 

R_DepthBufferImage was created from the code in draw_exp.cpp for rendering to a depth image for shadow mapping, the border code renders the image borders as all black to avoid seams.

 

draw_exp.cpp did not allow downsizing either (might cause artifacts i guess) .

 

@MirceaKitsune.

 

Linux port still needs work Unfortunatly i use Kubuntu so i cant test if what works there will also work on other distros, but if someone with opensuse can fill in the blanks ill be happy to implement it.

Link to comment
Share on other sites

ok reverted but also refined it a bit.

 

new function ->

static void RB_T_CopyDepthBuffer(const viewDef_t *viewDef) {
    // Wooh mama this is sooooooooooo sloooooooooow mblgrblrblr.
    bool depthCopied = false;
    for (viewEntity_t *viewEnt = viewDef->viewEntitys; viewEnt; viewEnt = viewEnt->next) {
        idRenderEntityLocal *ent = viewEnt->entityDef;
        // copy once update on change.
        if (depthCopied) {
            continue;
        }
        // check if depth view is suppressed.
        if (!ent->parms.suppressSurfaceInViewID && (ent->parms.suppressSurfaceInViewID != viewDef->renderView.viewID)) {
            globalImages->currentDepthImage->CopyDepthbuffer(
            backEnd.viewDef->viewport.x1,
            backEnd.viewDef->viewport.y1,
            backEnd.viewDef->viewport.x2 -
            backEnd.viewDef->viewport.x1 + 1,
            backEnd.viewDef->viewport.y2 -
            backEnd.viewDef->viewport.y1 + 1, true);
            // refresh status.
            depthCopied = true;
        }
    }
}

check if viewID is suppressed by game code and only do a full depthcopy if not. This lets us control unruly things that might else interfere or cause artifacts.

 

and change RB_STD_FillDepthBuffer to this.

/*
=====================
RB_STD_FillDepthBuffer

If we are rendering a subview with a near clip plane, use a second texture
to force the alpha test to fail when behind that clip plane
=====================
*/
static void RB_STD_FillDepthBuffer(drawSurf_t **drawSurfs, int numDrawSurfs) {
	// if we are just doing 2D rendering, no need to fill the depth buffer
	if (!backEnd.viewDef->viewEntitys) {
		return;
	}
	// Early copy off depth buffer.
	RB_T_CopyDepthBuffer(backEnd.viewDef);
	// enable the second texture for mirror plane clipping if needed
	if (backEnd.viewDef->numClipPlanes) {
		GL_SelectTexture(1);
		globalImages->alphaNotchImage->Bind();
		glDisableClientState(GL_TEXTURE_COORD_ARRAY);
		glEnable(GL_TEXTURE_GEN_S);
		glTexCoord2f(1.0f, 0.5f);
	}
	// the first texture will be used for alpha tested surfaces
	GL_SelectTexture(0);
	glEnableClientState(GL_TEXTURE_COORD_ARRAY);
	// decal surfaces may enable polygon offset
	glPolygonOffset(r_offsetFactor.GetFloat(), r_offsetUnits.GetFloat());
	GL_State(GLS_DEPTHFUNC_LESS);
	// Enable stencil test if we are going to be using it for shadows.
	// If we didn't do this, it would be legal behavior to get z fighting
	// from the ambient pass and the light passes.
	glEnable(GL_STENCIL_TEST);
	glStencilFunc(GL_ALWAYS, 1, 255);
	RB_RenderDrawSurfListWithFunction(drawSurfs, numDrawSurfs, RB_T_FillDepthBuffer);
	if (backEnd.viewDef->numClipPlanes) {
		GL_SelectTexture(1);
		globalImages->BindNull();
		glDisable(GL_TEXTURE_GEN_S);
		GL_SelectTexture(0);
	}
}

This way SSAO still works and it seems to work ok with darkmod also.

  • Like 1
Link to comment
Share on other sites

Btw the check for suppression can be used for lightgems to :)

The depthcopy does take its toll though nothing alarming but you will feel it.

I suspect checking for suppression is quite expensive.

 

Btw im not quite sure R_DepthBufferImage is 100% correct as the data array was normally just Mem_Alloc(width*height) (not quite sure how a static representation of that would look ?)

width and height had a value of 1024 so rather big.

Link to comment
Share on other sites

  • 2 weeks later...
  • 1 month later...

I got your branch up and running with 2.03 last night by the way, so I'm about to try one of (1) drawing interactions using a glsl version of our shader, or (2) drawing our shadow stencils into a framebuffer object so we can mess with them. Not quite decided which to try first yet :)

I haven't sent a pull request to your repo because it's not a full merge of the 2.03 engine code, I just did the minimum hack so I could try out OpenGL3 techniques in TDM, and you already have GLEW in here :) And I'd rather spend time moving the good stuff from here into the trunk rather than the other way round!

 

For the record, the minimal changes needed to make the branch compile and run with 2.03 are

  • replace the /game/ folder with the 2.04 version
  • merge the changes from trunk revision #6142 to RenderWorld.h, tr_local.h, tr_light.cpp, and draw_common.cpp
  • add the following game files to the solution:
    • game/SearchManager.h/cpp
    • game/ai/Tasks/GuardSpotTask.h/cpp

The reason the build has to be changed at all when we have a new release is the interdependency between game code and AI scripts. Something that grayman and I plan to simplify or even break completely in 2.04.

 

EDIT: changed the first three mentions of "2.04" to "2.03". I'm getting ahead of myself!

Edited by SteveL
  • Like 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.


  • Recent Status Updates

    • 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
       
      · 2 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
    • The Black Arrow

      I wanna play Doom 3, but fhDoom has much better features than dhewm3, yet fhDoom is old, outdated and probably not supported. Damn!
      Makes me think that TDM engine for Doom 3 itself would actually be perfect.
      · 6 replies
    • Petike the Taffer

      Maybe a bit of advice ? In the FM series I'm preparing, the two main characters have the given names Toby and Agnes (it's the protagonist and deuteragonist, respectively), I've been toying with the idea of giving them family names as well, since many of the FM series have named protagonists who have surnames. Toby's from a family who were usually farriers, though he eventually wound up working as a cobbler (this serves as a daylight "front" for his night time thieving). Would it make sense if the man's popularly accepted family name was Farrier ? It's an existing, though less common English surname, and it directly refers to the profession practiced by his relatives. Your suggestions ?
      · 9 replies
×
×
  • Create New...