/// <summary> /// R_TextureAnimation /// Returns the proper texture for a given time and base texture /// </summary> private static texture_t TextureAnimation(texture_t t) { if (_CurrentEntity.frame != 0) { if (t.alternate_anims != null) { t = t.alternate_anims; } } if (t.anim_total == 0) { return(t); } int reletive = (int)(client.cl.time * 10) % t.anim_total; int count = 0; while (t.anim_min > reletive || t.anim_max <= reletive) { t = t.anim_next; if (t == null) { sys.Error("R_TextureAnimation: broken cycle"); } if (++count > 100) { sys.Error("R_TextureAnimation: infinite cycle"); } } return(t); }
/// <summary> /// R_RenderBrushPoly /// </summary> private static void RenderBrushPoly(msurface_t fa) { _BrushPolys++; if ((fa.flags & Surf.SURF_DRAWSKY) != 0) { // warp texture, no lightmaps EmitBothSkyLayers(fa); return; } texture_t t = TextureAnimation(fa.texinfo.texture); Drawer.Bind(t.gl_texturenum); if ((fa.flags & Surf.SURF_DRAWTURB) != 0) { // warp texture, no lightmaps EmitWaterPolys(fa); return; } if ((fa.flags & Surf.SURF_UNDERWATER) != 0) { DrawGLWaterPoly(fa.polys); } else { DrawGLPoly(fa.polys); } // add the poly to the proper lightmap chain fa.polys.chain = _LightMapPolys[fa.lightmaptexturenum]; _LightMapPolys[fa.lightmaptexturenum] = fa.polys; // check for lightmap modification bool modified = false; for (int maps = 0; maps < bsp_file.MAXLIGHTMAPS && fa.styles[maps] != 255; maps++) { if (_LightStyleValue[fa.styles[maps]] != fa.cached_light[maps]) { modified = true; break; } } if (modified || fa.dlightframe == _FrameCount || // dynamic this frame fa.cached_dlight) // dynamic previously { if (_Dynamic.Value != 0) { _LightMapModified[fa.lightmaptexturenum] = true; UpdateRect(fa, ref _LightMapRectChange[fa.lightmaptexturenum]); int offset = fa.lightmaptexturenum * _LightMapBytes * BLOCK_WIDTH * BLOCK_HEIGHT; offset += fa.light_t * BLOCK_WIDTH * _LightMapBytes + fa.light_s * _LightMapBytes; BuildLightMap(fa, new ByteArraySegment(_LightMaps, offset), BLOCK_WIDTH * _LightMapBytes); } } }
static void DrawTextureChains() { if (_glTexSort.Value == 0) { DisableMultitexture(); if (_SkyChain != null) { DrawSkyChain(_SkyChain); _SkyChain = null; } return; } Model world = Client.Cl.worldmodel; for (int i = 0; i < world.numtextures; i++) { texture_t t = world.textures[i]; if (t == null) { continue; } msurface_t s = t.texturechain; if (s == null) { continue; } if (i == _SkyTextureNum) { DrawSkyChain(s); } else if (i == _MirrorTextureNum && _MirrorAlpha.Value != 1.0f) { MirrorChain(s); continue; } else { if ((s.flags & Surf.SURF_DRAWTURB) != 0 && _WaterAlpha.Value != 1.0f) { continue; // draw translucent water later } for (; s != null; s = s.texturechain) { RenderBrushPoly(s); } } t.texturechain = null; } }
/// <summary> /// R_TextureAnimation /// Returns the proper texture for a given time and base texture /// </summary> static texture_t TextureAnimation(texture_t t) { if (_CurrentEntity.frame != 0) { if (t.alternate_anims != null) t = t.alternate_anims; } if (t.anim_total == 0) return t; int reletive = (int)(Client.cl.time * 10) % t.anim_total; int count = 0; while (t.anim_min > reletive || t.anim_max <= reletive) { t = t.anim_next; if (t == null) Sys.Error("R_TextureAnimation: broken cycle"); if (++count > 100) Sys.Error("R_TextureAnimation: infinite cycle"); } return t; }
/// <summary> /// R_DrawWaterSurfaces /// </summary> private static void DrawWaterSurfaces() { if (_WaterAlpha.Value == 1.0f && _glTexSort.Value != 0) { return; } // // go back to the world matrix // GL.LoadMatrix(ref _WorldMatrix); if (_WaterAlpha.Value < 1.0) { GL.Enable(EnableCap.Blend); GL.Color4(1, 1, 1, _WaterAlpha.Value); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Modulate); } if (_glTexSort.Value == 0) { if (_WaterChain == null) { return; } for (msurface_t s = _WaterChain; s != null; s = s.texturechain) { Drawer.Bind(s.texinfo.texture.gl_texturenum); EmitWaterPolys(s); } _WaterChain = null; } else { for (int i = 0; i < client.cl.worldmodel.numtextures; i++) { texture_t t = client.cl.worldmodel.textures[i]; if (t == null) { continue; } msurface_t s = t.texturechain; if (s == null) { continue; } if ((s.flags & Surf.SURF_DRAWTURB) == 0) { continue; } // set modulate mode explicitly Drawer.Bind(t.gl_texturenum); for ( ; s != null; s = s.texturechain) { EmitWaterPolys(s); } t.texturechain = null; } } if (_WaterAlpha.Value < 1.0) { GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); GL.Color4(1f, 1, 1, 1); GL.Disable(EnableCap.Blend); } }
/// <summary> /// R_DrawSequentialPoly /// Systems that have fast state and texture changes can /// just do everything as it passes with no need to sort /// </summary> private static void DrawSequentialPoly(msurface_t s) { // // normal lightmaped poly // if ((s.flags & (Surf.SURF_DRAWSKY | Surf.SURF_DRAWTURB | Surf.SURF_UNDERWATER)) == 0) { RenderDynamicLightmaps(s); glpoly_t p = s.polys; texture_t t = TextureAnimation(s.texinfo.texture); if (vid.glMTexable) { // Binds world to texture env 0 Drawer.SelectTexture(MTexTarget.TEXTURE0_SGIS); Drawer.Bind(t.gl_texturenum); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); // Binds lightmap to texenv 1 EnableMultitexture(); // Same as SelectTexture (TEXTURE1) Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); int i = s.lightmaptexturenum; if (_LightMapModified[i]) { CommitLightmap(i); } GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Blend); GL.Begin(PrimitiveType.Polygon); for (i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.MultiTexCoord2(TextureUnit.Texture0, v[3], v[4]); GL.MultiTexCoord2(TextureUnit.Texture1, v[5], v[6]); GL.Vertex3(v); } GL.End(); return; } else { Drawer.Bind(t.gl_texturenum); GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.TexCoord2(v[3], v[4]); GL.Vertex3(v); } GL.End(); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); GL.Enable(EnableCap.Blend); GL.Begin(PrimitiveType.Polygon); for (int i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.TexCoord2(v[5], v[6]); GL.Vertex3(v); } GL.End(); GL.Disable(EnableCap.Blend); } return; } // // subdivided water surface warp // if ((s.flags & Surf.SURF_DRAWTURB) != 0) { DisableMultitexture(); Drawer.Bind(s.texinfo.texture.gl_texturenum); EmitWaterPolys(s); return; } // // subdivided sky warp // if ((s.flags & Surf.SURF_DRAWSKY) != 0) { DisableMultitexture(); Drawer.Bind(_SolidSkyTexture); _SpeedScale = (float)host.RealTime * 8; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(s); GL.Enable(EnableCap.Blend); Drawer.Bind(_AlphaSkyTexture); _SpeedScale = (float)host.RealTime * 16; _SpeedScale -= (int)_SpeedScale & ~127; EmitSkyPolys(s); GL.Disable(EnableCap.Blend); return; } // // underwater warped with lightmap // RenderDynamicLightmaps(s); if (vid.glMTexable) { texture_t t = TextureAnimation(s.texinfo.texture); Drawer.SelectTexture(MTexTarget.TEXTURE0_SGIS); Drawer.Bind(t.gl_texturenum); GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Replace); EnableMultitexture(); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); int i = s.lightmaptexturenum; if (_LightMapModified[i]) { CommitLightmap(i); } GL.TexEnv(TextureEnvTarget.TextureEnv, TextureEnvParameter.TextureEnvMode, (int)TextureEnvMode.Blend); GL.Begin(PrimitiveType.TriangleFan); glpoly_t p = s.polys; float[] nv = new float[3]; for (i = 0; i < p.numverts; i++) { float[] v = p.verts[i]; GL.MultiTexCoord2(TextureUnit.Texture0, v[3], v[4]); GL.MultiTexCoord2(TextureUnit.Texture1, v[5], v[6]); nv[0] = (float)(v[0] + 8 * Math.Sin(v[1] * 0.05 + host.RealTime) * Math.Sin(v[2] * 0.05 + host.RealTime)); nv[1] = (float)(v[1] + 8 * Math.Sin(v[0] * 0.05 + host.RealTime) * Math.Sin(v[2] * 0.05 + host.RealTime)); nv[2] = v[2]; GL.Vertex3(nv); } GL.End(); } else { glpoly_t p = s.polys; texture_t t = TextureAnimation(s.texinfo.texture); Drawer.Bind(t.gl_texturenum); DrawGLWaterPoly(p); Drawer.Bind(_LightMapTextures + s.lightmaptexturenum); GL.Enable(EnableCap.Blend); DrawGLWaterPolyLightmap(p); GL.Disable(EnableCap.Blend); } }
// R_InitTextures public static void InitTextures() { // create a simple checkerboard texture for the default _NoTextureMip = new texture_t(); _NoTextureMip.pixels = new byte[16 * 16 + 8 * 8 + 4 * 4 + 2 * 2]; _NoTextureMip.width = _NoTextureMip.height = 16; int offset = 0; _NoTextureMip.offsets[0] = offset; offset += 16 * 16; _NoTextureMip.offsets[1] = offset; offset += 8 * 8; _NoTextureMip.offsets[2] = offset; offset += 4 * 4; _NoTextureMip.offsets[3] = offset; byte[] dest = _NoTextureMip.pixels; for (int m = 0; m < 4; m++) { offset = _NoTextureMip.offsets[m]; for (int y = 0; y < (16 >> m); y++) for (int x = 0; x < (16 >> m); x++) { if ((y < (8 >> m)) ^ (x < (8 >> m))) dest[offset] = 0; else dest[offset] = 0xff; offset++; } } }
static msurface_t _WarpFace; // used by SubdivideSurface() #endregion Fields #region Methods /// <summary> /// R_InitSky /// called at level load /// A sky texture is 256*128, with the right side being a masked overlay /// </summary> public static void InitSky(texture_t mt) { byte[] src = mt.pixels; int offset = mt.offsets[0]; // make an average value for the back to avoid // a fringe on the top level const int size = 128 * 128; uint[] trans = new uint[size]; uint[] v8to24 = Vid.Table8to24; int r = 0; int g = 0; int b = 0; Union4b rgba = Union4b.Empty; for (int i=0 ; i<128 ; i++) for (int j=0 ; j<128 ; j++) { int p = src[offset + i*256 + j + 128]; rgba.ui0 = v8to24[p]; trans[(i * 128) + j] = rgba.ui0; r += rgba.b0; g += rgba.b1; b += rgba.b2; } rgba.b0 = (byte)(r / size); rgba.b1 = (byte)(g / size); rgba.b2 = (byte)(b / size); rgba.b3 = 0; uint transpix = rgba.ui0; if (_SolidSkyTexture == 0) _SolidSkyTexture = Drawer.GenerateTextureNumber(); Drawer.Bind (_SolidSkyTexture ); GL.TexImage2D(TextureTarget.Texture2D, 0, Drawer.SolidFormat, 128, 128, 0, PixelFormat.Rgba, PixelType.UnsignedByte, trans); Drawer.SetTextureFilters(TextureMinFilter.Linear, TextureMagFilter.Linear); for (int i=0 ; i<128 ; i++) for (int j=0 ; j<128 ; j++) { int p = src[offset + i*256 + j]; if (p == 0) trans[(i*128) + j] = transpix; else trans[(i*128) + j] = v8to24[p]; } if (_AlphaSkyTexture == 0) _AlphaSkyTexture = Drawer.GenerateTextureNumber(); Drawer.Bind(_AlphaSkyTexture); GL.TexImage2D(TextureTarget.Texture2D, 0, Drawer.AlphaFormat, 128, 128, 0, PixelFormat.Rgba, PixelType.UnsignedByte, trans); Drawer.SetTextureFilters(TextureMinFilter.Linear, TextureMagFilter.Linear); }
static msurface_t _WarpFace; // used by SubdivideSurface() /// <summary> /// R_InitSky /// called at level load /// A sky texture is 256*128, with the right side being a masked overlay /// </summary> public static void InitSky(texture_t mt) { byte[] src = mt.pixels; int offset = mt.offsets[0]; // make an average value for the back to avoid // a fringe on the top level const int size = 128 * 128; uint[] trans = new uint[size]; uint[] v8to24 = Vid.Table8to24; int r = 0; int g = 0; int b = 0; Union4b rgba = Union4b.Empty; for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { int p = src[offset + i * 256 + j + 128]; rgba.ui0 = v8to24[p]; trans[(i * 128) + j] = rgba.ui0; r += rgba.b0; g += rgba.b1; b += rgba.b2; } } rgba.b0 = (byte)(r / size); rgba.b1 = (byte)(g / size); rgba.b2 = (byte)(b / size); rgba.b3 = 0; uint transpix = rgba.ui0; if (_SolidSkyTexture == 0) { _SolidSkyTexture = Drawer.GenerateTextureNumber(); } Drawer.Bind(_SolidSkyTexture); GL.TexImage2D(TextureTarget.Texture2D, 0, Drawer.SolidFormat, 128, 128, 0, PixelFormat.Rgba, PixelType.UnsignedByte, trans); Drawer.SetTextureFilters(TextureMinFilter.Linear, TextureMagFilter.Linear); for (int i = 0; i < 128; i++) { for (int j = 0; j < 128; j++) { int p = src[offset + i * 256 + j]; if (p == 0) { trans[(i * 128) + j] = transpix; } else { trans[(i * 128) + j] = v8to24[p]; } } } if (_AlphaSkyTexture == 0) { _AlphaSkyTexture = Drawer.GenerateTextureNumber(); } Drawer.Bind(_AlphaSkyTexture); GL.TexImage2D(TextureTarget.Texture2D, 0, Drawer.AlphaFormat, 128, 128, 0, PixelFormat.Rgba, PixelType.UnsignedByte, trans); Drawer.SetTextureFilters(TextureMinFilter.Linear, TextureMagFilter.Linear); }
/// <summary> /// Mod_LoadTextures /// </summary> static void LoadTextures(ref lump_t l) { if (l.filelen == 0) { _LoadModel.textures = null; return; } dmiptexlump_t m = Sys.BytesToStructure<dmiptexlump_t>(_ModBase, l.fileofs);// (dmiptexlump_t *)(mod_base + l.fileofs); m.nummiptex = Common.LittleLong(m.nummiptex); int[] dataofs = new int[m.nummiptex]; Buffer.BlockCopy(_ModBase, l.fileofs + dmiptexlump_t.SizeInBytes, dataofs, 0, dataofs.Length * sizeof(int)); _LoadModel.numtextures = m.nummiptex; _LoadModel.textures = new texture_t[m.nummiptex]; // Hunk_AllocName (m->nummiptex * sizeof(*loadmodel->textures) , loadname); for (int i = 0; i < m.nummiptex; i++) { dataofs[i] = Common.LittleLong(dataofs[i]); if (dataofs[i] == -1) continue; int mtOffset = l.fileofs + dataofs[i]; miptex_t mt = Sys.BytesToStructure<miptex_t>(_ModBase, mtOffset); //mt = (miptex_t *)((byte *)m + m.dataofs[i]); mt.width = (uint)Common.LittleLong((int)mt.width); mt.height = (uint)Common.LittleLong((int)mt.height); for (int j = 0; j < BspFile.MIPLEVELS; j++) mt.offsets[j] = (uint)Common.LittleLong((int)mt.offsets[j]); if ((mt.width & 15) != 0 || (mt.height & 15) != 0) Sys.Error("Texture {0} is not 16 aligned", mt.name); int pixels = (int)(mt.width * mt.height / 64 * 85); texture_t tx = new texture_t();// Hunk_AllocName(sizeof(texture_t) + pixels, loadname); _LoadModel.textures[i] = tx; tx.name = Common.GetString(mt.name);// memcpy (tx->name, mt->name, sizeof(tx.name)); tx.width = mt.width; tx.height = mt.height; for (int j = 0; j < BspFile.MIPLEVELS; j++) tx.offsets[j] = (int)mt.offsets[j] - miptex_t.SizeInBytes; // the pixels immediately follow the structures tx.pixels = new byte[pixels]; Buffer.BlockCopy(_ModBase, mtOffset + miptex_t.SizeInBytes, tx.pixels, 0, pixels); if (tx.name != null && tx.name.StartsWith("sky"))// !Q_strncmp(mt->name,"sky",3)) Render.InitSky(tx); else { tx.gl_texturenum = Drawer.LoadTexture(tx.name, (int)tx.width, (int)tx.height, new ByteArraySegment(tx.pixels), true, false); } } // // sequence the animations // texture_t[] anims = new texture_t[10]; texture_t[] altanims = new texture_t[10]; for (int i = 0; i < m.nummiptex; i++) { texture_t tx = _LoadModel.textures[i]; if (tx == null || !tx.name.StartsWith("+"))// [0] != '+') continue; if (tx.anim_next != null) continue; // allready sequenced // find the number of frames in the animation Array.Clear(anims, 0, anims.Length); Array.Clear(altanims, 0, altanims.Length); int max = tx.name[1]; int altmax = 0; if (max >= 'a' && max <= 'z') max -= 'a' - 'A'; if (max >= '0' && max <= '9') { max -= '0'; altmax = 0; anims[max] = tx; max++; } else if (max >= 'A' && max <= 'J') { altmax = max - 'A'; max = 0; altanims[altmax] = tx; altmax++; } else Sys.Error("Bad animating texture {0}", tx.name); for (int j = i + 1; j < m.nummiptex; j++) { texture_t tx2 = _LoadModel.textures[j]; if (tx2 == null || !tx2.name.StartsWith("+"))// tx2->name[0] != '+') continue; if (String.Compare(tx2.name, 2, tx.name, 2, Math.Min(tx.name.Length, tx2.name.Length)) != 0)// strcmp (tx2->name+2, tx->name+2)) continue; int num = tx2.name[1]; if (num >= 'a' && num <= 'z') num -= 'a' - 'A'; if (num >= '0' && num <= '9') { num -= '0'; anims[num] = tx2; if (num + 1 > max) max = num + 1; } else if (num >= 'A' && num <= 'J') { num = num - 'A'; altanims[num] = tx2; if (num + 1 > altmax) altmax = num + 1; } else Sys.Error("Bad animating texture {0}", tx2.name); } // link them all together for (int j = 0; j < max; j++) { texture_t tx2 = anims[j]; if (tx2 == null) Sys.Error("Missing frame {0} of {1}", j, tx.name); tx2.anim_total = max * ANIM_CYCLE; tx2.anim_min = j * ANIM_CYCLE; tx2.anim_max = (j + 1) * ANIM_CYCLE; tx2.anim_next = anims[(j + 1) % max]; if (altmax != 0) tx2.alternate_anims = altanims[0]; } for (int j = 0; j < altmax; j++) { texture_t tx2 = altanims[j]; if (tx2 == null) Sys.Error("Missing frame {0} of {1}", j, tx2.name); tx2.anim_total = altmax * ANIM_CYCLE; tx2.anim_min = j * ANIM_CYCLE; tx2.anim_max = (j + 1) * ANIM_CYCLE; tx2.anim_next = altanims[(j + 1) % altmax]; if (max != 0) tx2.alternate_anims = anims[0]; } } }