Skip to main content
GameDev.net gamedev.net
🔒 Locked

Weird stretched textures

Started by Marc aka Foddex Jun 23, 2004 at 9:33 AM 8 replies 3.2k views
Original Post
Marc aka Foddex
Marc aka Foddex
Context: [DirectX 7] [Direct3D] [Windows 2000] Hi, I'm having some trouble with textures/texturecoordinates in the Direct3D version of my application. I'm working on a so called serious-gaming-engine (3D engine for educational purposes). When I load a world for the first time, everything works as planned. See the screenshot below: http://130.89.227.64/foddex/it/1.jpg Please ignore the lighting errors (big white line and black faces behind the guy) :) When I load the world again, the result is the following: http://130.89.227.64/foddex/it/2.jpg The textures are all stretched! And also, the textures *move over the faces* in a weird way when the camera moves (which ofcourse can't be shown with one screenshot alone ;)). I have no clue at all about what is going wrong. The same loading and rendering code is simply used two times, but the first everything works, and the second time it doesn't. Do you guys have any idea what might be going wrong? Any clues as to what I should check (Direct3D states maybe?) I have an OpenGL version of my application as well, which uses the exact same abstract rendering/loading code (just implemented in a OpenGL fashion in stead of a Direct3D fashion), and there everything works just fine, so I'm pretty sure the texture coordinate data is correct. Here's my Direct3D texture loading and unloading code. Maybe you guys can find something there? Remember, it's DirectX 7, I can't use anything else!

int TextureEngine_Direct3D::CreateTexture(char* description, unsigned char* data, unsigned int width, unsigned int height, ENGINE_TEXTURE_OPTIONS options)
{
	int id = 0;
	// search for a texture with the given properties
	ENGINE_TEXTURE_STRUCT_t* s = FindTexture(description, options.texclass, options.mipmapped, options.quality, options.format);

	// if it doesn't exist yet, create it
	if (!s) {
		// apply quality
		data = ChangeBitmapQuality(data, options.quality, options.format, &width, &height);

		/** initialize parameters */
		LPDIRECTDRAWSURFACE7 tex = NULL;
		DWORD flags = (options.mipmapped ? 0 : D3DX_TEXTURE_NOMIPMAP);
		DWORD w = width;
		DWORD h = height;
		DWORD mipmapcount = (options.mipmapped ? 5 : 0);
		D3DX_SURFACEFORMAT f = (options.format==ENGINE_TEXTURE_FORMAT_RGB ? D3DX_SF_R8G8B8 : D3DX_SF_A8R8G8B8);

		/** set clamping options */
		m_d3ddevice->SetRenderState(D3DRENDERSTATE_WRAP0, options.clamp ? D3DWRAPCOORD_0 | D3DWRAPCOORD_1 : 0);

		/** create texture in Direct3D */
		HRESULT hr;
		if (hr = D3DXCreateTexture(m_d3ddevice, &flags, &w, &h, &f, NULL, &tex, options.mipmapped ? &mipmapcount : NULL)<S_OK) {
			ENGINE_LOG(("createtexture: D3DXCreateTexture failed with error code: %d", hr));
			return 0;
		}

		/** check if we have a valid LPDIRECTDRAWSURFACE7 pointer */
		if (!tex) {
			ENGINE_LOG(("createtexture: failed to create a LPDIRECTDRAWSURFACE7 object"));
			return 0;
		}

		/** check if what we got is at least what we wanted */
		if (options.format==ENGINE_TEXTURE_FORMAT_RGBA && (f==D3DX_SF_R8G8B8 || f==D3DX_SF_X8R8G8B8)) {
			ENGINE_LOG(("createtexture: user wants alpha texture for '%s', but we got RGB only", description));
			return 0;
		}

		/** we only support certain Direct3D formats */
		if (f!=D3DX_SF_R8G8B8 && f!=D3DX_SF_X8R8G8B8 && f!=D3DX_SF_A8R8G8B8) {
			ENGINE_LOG(("createtexture: unsupported Direct3D texture format: 0x%x", f));
			return 0;
		}

		/** resize original data to the size Direct3D requires */
		data = ResizeBitmap(data, options.format, width, height, w, h, false);
		width = w;
		height = h;

		/** lock surface */
		DDSURFACEDESC2 lock;
		tex->Lock(NULL, &lock, DDLOCK_WAIT, NULL);

		/** copy texture data into the surface  */
		// this is the only case we can 'mass copy' all data at once
		if (options.format==ENGINE_TEXTURE_FORMAT_RGB && f==D3DX_SF_R8G8B8) {
			memcpy(lock.lpSurface, data, width*height*3);
		} else {
			// once we've reached this point, we know sure that the *target* pixelsize is always 4 bytes!
			// (the wanted format here will always be XRGB or ARGB)
			// the *source* pixelsize might either be 3 or 4
			
			#define SOURCEPIXEL		(&data[sourcepixelsize*(x + w*y)])
			#define TARGETPIXEL		(&base[targetpixelsize*(x + w*y)])
			
			int sourcepixelsize = (options.format==ENGINE_TEXTURE_FORMAT_RGB ? 3 : 4);
			int targetpixelsize = (f==D3DX_SF_R8G8B8 ? 3 : 4);
			unsigned char* base = (unsigned char*)lock.lpSurface;
			for (unsigned int y=0; y<height; y++) {
				for (unsigned int x=0; x<width; x++) {
					unsigned char* sp = SOURCEPIXEL;
					unsigned char* tp = TARGETPIXEL;
					DWORD pixel;
					switch (options.format) {
						case ENGINE_TEXTURE_FORMAT_RGB: {
							pixel = ((*sp) << 16) + ((*(sp+1))<<8) + (*(sp+2));
							break;
						}

						case ENGINE_TEXTURE_FORMAT_RGBA: {
							pixel = ((*(sp+3))<<24) + ((*sp) << 16) + ((*(sp+1))<<8) + (*(sp+2));
							break;
						}
					}
					memcpy(TARGETPIXEL, &pixel, sizeof(pixel));
				}
			}
		}

		/** unlock surface */
		tex->Unlock(NULL);

		/** let parent class add a new item in the linked list of textures, with the pointer to the surface as id */
		id = TextureEngine::AddTexture(description, width, height, options, (int)tex);

		// add id to valid list
		ASSERT(AddTextureIdentifierToValidList(id), ("createtexture: error adding id %d to list of valid texture ids", id));
	} else {
		// if it exists already, return its id
		s->refcount++;
		id = s->id;
	}
	// free temporary pixel data
	AXEFREE(data);
	return id;
}

void TextureEngine_Direct3D::RemoveTexture(ENGINE_TEXTURE_STRUCT_t *s)
{
	ASSERT(s!=NULL, ("removetexture: s is NULL"));
	ASSERT(RemoveTextureIdentifierFromValidList(s->id), ("removetexture: error removing id %d from list of valid texture ids", s->id));
	LPDIRECTDRAWSURFACE7 tex = (LPDIRECTDRAWSURFACE7) s->id;
	ASSERT(tex!=NULL, ("removetexture: tex is NULL"));

	if (tex) {
		tex->Release();
		tex = NULL;
		s->id = NULL;
	}
	s->id = 0;
}
Thanks in advance for any help! Marc
jtmerchant
jtmerchant
Just from a quick look at your code, I can tell there aren't many comments. Personally, I like to put a comment every line, but you don't have to go that extreme. Anyway, I think you need to show us where you set the UV coordinates etc, as I don't see anything here.
Marc aka Foddex
Marc aka Foddex
Quote:
Original post by DrunkenHyena
Have you checked the information in the DirectX Debug Output?
Debug Info

I'm trying the debug version of Direct3D right now (d3dxd.lib in stead of d3dx.lib).

But I can't find the DirectX control panel in my win2k control panels list. The page you referred to mentioned the classic view, but AFAICT I have the classic view... is the DirectX control panel also in DirectX 7, which I'm using?

[edit]

Hmm I don't see any debug messages. I've tried debugging the debug version, but it's soo damn slow due to the large amounts of data being moved (which are all checked), and I don't see any messages.
If I execute the debug version (without debugging), it always ends up in the ntsd program, since I've installed the Microsoft Windows Application Verificiation Toolkit. That debugger doesn't show any messages from DirectX. Since I don't have the DirectX control panel, I can't set the messages mode higher. So either the DirectX debug stuff actually works, but simply no errors are detected, or it doesn't work and there may be errors... hmmm :(

I also noted that the guy and the plant in the background aren't rendered incorrectly. Since they are models and can change (through animations), they're not rendered through hardware vertex buffers like the more static world faces. Maybe something's wrong with my vertex buffer code. I therefor list the code for the vertexbuffers:

/** Vertex buffer commands */int RendererEngine_Direct3D::CreateHardwareVertexBuffer(VertexBuffer* vb){	// create struct	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)AXEMALLOC(sizeof(ENGINE_RENDERER_DIRECT3D_HWB));	memset(hwb, 0, sizeof(ENGINE_RENDERER_DIRECT3D_HWB));	hwb->vb = vb;	hwb->vertexcount = vb->GetVertexCount();	// setup vertex buffer descriptor    ZeroMemory(&hwb->desc, sizeof(D3DVERTEXBUFFERDESC));    hwb->desc.dwSize		= sizeof(D3DVERTEXBUFFERDESC);    hwb->desc.dwCaps		= D3DVBCAPS_WRITEONLY;    hwb->desc.dwFVF			= m_vertexformat;    hwb->desc.dwNumVertices	= vb->GetVertexCount();	// try to create the buffer in Direct3D	if (FAILED(m_d3d->CreateVertexBuffer(&hwb->desc, &hwb->source, 0L))) {		return -1;	}	// lock buffer, fill it, unlock it	void* bufferdata;	DWORD buffersize;	if (FAILED(hwb->source->Lock(DDLOCK_WAIT | DDLOCK_WRITEONLY, &bufferdata, &buffersize))) {		ENGINE_LOG(("createhardwarevertexbuffer: failed to lock vertex buffer"));		hwb->source->Release();		AXEFREE(hwb);		return -1;	}	unsigned int totalsize = hwb->vertexcount*(sizeof(float) * (3 + 3 + 2*m_texturecount));	if (totalsize != buffersize) {		ENGINE_LOG(("createhardwarevertexbuffer: invalid vertex buffer size (it's %d, we need %d): is the multitexture count correctly set?"));		hwb->source->Unlock();		hwb->source->Release();		AXEFREE(hwb);		return -1;	}	memcpy(bufferdata, vb->GetBuffer(), totalsize);	hwb->source->Unlock();	// optimize buffer for the current device	hwb->source->Optimize(m_d3ddevice, NULL);	// create target buffer, which will contain the transformed, processed vertices	hwb->desc.dwFVF = D3DFVF_XYZRHW | (D3DFVF_TEX1 * m_texturecount);	if (FAILED(m_d3d->CreateVertexBuffer(&hwb->desc, &hwb->target, 0L))) {		hwb->source->Release();		AXEFREE(hwb);		return -1;	}		// return pointer to our hardware vertex buffer struct converted to int	return (int)hwb;}void RendererEngine_Direct3D::ProcessHardwareVertexBuffer(int vertexbuffer){	FUNCCALL_COUNTER(m_funccall_processhvb);	// for performance reasons, we're not checking the input	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)vertexbuffer;	if FAILED(hwb->target->ProcessVertices(D3DVOP_TRANSFORM, 0, hwb->vertexcount, hwb->source, 0, m_d3ddevice, 0)) {		ENGINE_LOG(("processhardwarevertexbuffer: ProcessVertices failed"));	}}void RendererEngine_Direct3D::DestroyHardwareVertexBuffer(int vertexbuffer){	ENGINE_RENDERER_DIRECT3D_HWB* hwb = (ENGINE_RENDERER_DIRECT3D_HWB*)vertexbuffer;	if (hwb) {		if (hwb->source) hwb->source->Release();		if (hwb->target) hwb->target->Release();		AXEFREE(hwb);	}}


[Edited by - Marc aka Foddex on June 24, 2004 5:41:55 AM]
Chris Jungen
Chris Jungen
The textures "move over the faces" .. when the camera moves
sounds like if some texgen is switched on.

Please check if the texture stage state D3DTSS_TEXTURETRANSFORMFLAGS is D3DTTFF_DISABLE.

Marc aka Foddex
Marc aka Foddex
Quote:
Original post by Chris Jungen
The textures "move over the faces" .. when the camera moves
sounds like if some texgen is switched on.

Please check if the texture stage state D3DTSS_TEXTURETRANSFORMFLAGS is D3DTTFF_DISABLE.

Thank you for your suggestion! Unfortunately, I never set the texture transform flags, and still the first time I load a map everything works, but the second time (and following times) it doesn't work :(

Nevertheless I tried your suggestion, to no effect. Any other ideas?



[edit]
I was reading your post again. What exactly do you mean with "texgen"?
Marc aka Foddex
Marc aka Foddex
YEAH BABY!

/** set clamping options */// m_d3ddevice->SetRenderState(D3DRENDERSTATE_WRAP0, options.clamp ? D3DWRAPCOORD_0 | D3DWRAPCOORD_1 : 0);


I *thought* this was the Direct3D equivalent for the following OpenGL code:
		if (options.clamp) {			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);		} else {			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);			glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);		}

but it turns out that it isn't. Clamping (or what I thought was clamping) was left on after the 2nd time I loaded a map. And that fucked up my scene!

*idiot mode*
so w00t, 1 f33l lik3 a 1337 h4x0r :D

:)
Chris Jungen
Chris Jungen
Quote:
Original post by Marc aka Foddex
Clamping (or what I thought was clamping) was left on after the 2nd time I loaded a map. And that fucked up my scene!


What irritates me, is why the texture moved over the polygons with the camera.

This usually happens with texgen on:
(D3DTSS_TCI_CAMERASPACEPOSITION, D3DTSS_TCI_CAMERASPACEPOSITION,
and D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR) or with some
vertex shaders.

Be sure, that the texture stage states D3DTSS_TEXCOORDINDEX are not set to one of the above D3DTSS_TCI_CAMERASPACE* states (I forgot that).

Perhabs this behavior could also come from some strange moiree effect.
Zipster
Zipster
Marc aka Foddex! It's you! :)

Something you need to know about in Direct3D, so please don't leave just yet :) The texture wrapping you're doing in the Direct3D code is not the same type of texture wrapping you're doing in OpenGL (as you have found out). Direct3D actually has a different concept of "texture wrapping." In Direct3D, texture wrapping changes how the rasterizer looks at texture coordinates. Basically, it sees 0 and 1 as the same position, which changes how it interpolates texels by inducing a wrapping effect. If you have the coord (0.1, 0.1) on the upper left of the quad and (0.9, 0.9) on the lower right, without texture wrapping that would mean you have a texture interpolated across the quad from somewhere in it's upper-left to somehwere in its lower-right. However, with texture wrapping in Direct3D, the shortest way to get from 0.1 from 0.9 isn't to increase, it's to decrease, since when you get to 0, you wrap back around to 1. So using texture wrapping, going backwards is the shortest interpolation distance (0.2), as opposed to 0.8 going forward. Thus when Direct3D rasters with texture wrapping, it interpolates backwards, giving you a reversed texture, which would be highly stretched (since you're interpolating less of the texture, 0.2, across the same quad size than you were before, with 0.8). MSDN has a good little section on this, with some pictures, if you're really interested.

This is why you were experiencing problems. If you have 0.0 on one end of a quad, and 1.0 on the other, with texture wrapping that's the same position! It's like having both be 0.0 or both be 1.0 in a non-texture wrapping environment. Or if you texture coordinates are close to the ends of the 0..1 range, they interpolate backwards and cause major stretching (like what you were seeing).

OpenGL doesn't have this. Instead, what OpenGL calls "texture wrapping" is really called "texture address mode" in Direct3D. Wrapping (GL_REPEAT in OpenGL, D3DTADDRESS_WRAP in Direct3D) is the default texture address mode in Direct3D. So by default, everything works fine without you messing with special Direct3D texture wrapping :)

However, if you really do want to maintain clamp support in your Direct3D version, you would use this code:
if(options.clamp){   m_d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);   m_d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);}else{   m_d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_WRAP);   m_d3ddevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_WRAP);}


P.S So you left RCS to work on engine design, eh? Just wait until I tell everyone! ;)

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.