public idImage(string name, TextureType type, TextureFilter filter, TextureRepeat repeat, TextureDepth depth, CubeFiles cubeMap, bool allowDownSize) { _name = name; _type = type; _filter = filter; _repeat = repeat; _depth = depth; _cubeFiles = cubeMap; _allowDownSize = allowDownSize; }
// background loading information. /*private idImage _partialImage; // shrunken, space-saving version * private bool _isPartialImage; // true if this is pointed to by another image*/ #endregion #region Constructor public idImage(string name, ImageLoadCallback generator) { _name = name; _generator = generator; _type = TextureType.Disabled; _filter = TextureFilter.Default; _repeat = TextureRepeat.Repeat; _depth = TextureDepth.Default; _cubeFiles = CubeFiles.TwoD; }
/// <summary> /// Finds or loads the given image, always returning a valid image pointer. /// Loading of the image may be deferred for dynamic loading. /// </summary> /// <param name="name"></param> /// <param name="filter"></param> /// <param name="allowDownSize"></param> /// <param name="repeat"></param> /// <param name="depth"></param> /// <param name="cubeMap"></param> /// <returns></returns> public idImage ImageFromFile(string name, TextureFilter filter, bool allowDownSize, TextureRepeat repeat, TextureDepth depth, CubeFiles cubeMap) { if((name == null) || (name == string.Empty) || (name.Equals("default", StringComparison.OrdinalIgnoreCase) == true) || (name.Equals("_default", StringComparison.OrdinalIgnoreCase) == true)) { idE.DeclManager.MediaPrint("DEFAULTED"); return this.DefaultImage; } idImage image; // strip any .tga file extensions from anywhere in the _name, including image program parameters name = name.Replace(".tga", ""); // // see if the image is already loaded, unless we // are in a reloadImages call // if(_imageDictionary.TryGetValue(name, out image) == true) { // the built in's, like _white and _flat always match the other options if(name.StartsWith("_") == true) { return image; } if(image.CubeFiles != cubeMap) { idConsole.Error("Image '{0}' has been referenced with conflicting cube map states", name); } if((image.Filter != filter) || (image.Repeat != repeat)) { // we might want to have the system reset these parameters on every bind and // share the image data // FIXME: this might be the wrong behaviour. original d3 would return a new image but our dictionary // requires unique keys. return image; } else { if((image.AllowDownSize == allowDownSize) && (image.Depth == depth)) { // note that it is used this level load image.LevelLoadReferenced = true; return image; } // the same image is being requested, but with a different allowDownSize or depth // so pick the highest of the two and reload the old image with those parameters if(image.AllowDownSize == false) { allowDownSize = false; } if(image.Depth > depth) { depth = image.Depth; } if((image.AllowDownSize == allowDownSize) && (image.Depth == depth)) { // the already created one is already the highest quality image.LevelLoadReferenced = true; return image; } /*image.AllowDownSize = allowDownSize; image.Depth = depth;*/ image.LevelLoadReferenced = true; if((idE.CvarSystem.GetBool("image_preload") == true) && (_insideLevelLoad == false)) { image.ReferencedOutsideLevelLoad = true; image.ActuallyLoadImage(true, false); // check for precompressed, load is from front end idE.DeclManager.MediaPrint("{0}x{1} {1} (reload for mixed referneces)", image.Width, image.Height, image.Name); } return image; } } // HACK: to allow keep fonts from being mip'd, as new ones will be introduced with localization // this keeps us from having to make a material for each font tga if(name.Contains("fontImage_") == true) { allowDownSize = false; } // // create a new image // image = CreateImage(name, TextureType.TwoD, filter, repeat, depth, cubeMap, allowDownSize); image.LevelLoadReferenced = true; // load it if we aren't in a level preload if((idE.CvarSystem.GetBool("image_preload") == true) && (_insideLevelLoad == false)) { image.ReferencedOutsideLevelLoad = true; image.ActuallyLoadImage(true, false); // check for precompressed, load is from front end idE.DeclManager.MediaPrint("{0}x{1} {2}", image.Width, image.Height, image.Name); } else { idE.DeclManager.MediaPrint(image.Name); } return image; }
/// <summary> /// Finds or loads the given image, always returning a valid image pointer. /// Loading of the image may be deferred for dynamic loading. /// </summary> /// <param name="name"></param> /// <param name="filter"></param> /// <param name="allowDownSize"></param> /// <param name="repeat"></param> /// <param name="depth"></param> /// <returns></returns> public idImage ImageFromFile(string name, TextureFilter filter, bool allowDownSize, TextureRepeat repeat, TextureDepth depth) { return ImageFromFile(name, filter, allowDownSize, repeat, depth, CubeFiles.TwoD); }
private idImage CreateImage(string name, TextureType type, TextureFilter filter, TextureRepeat repeat, TextureDepth depth, CubeFiles cubeMap, bool allowDownSize) { idImage image = new idImage(name, type, filter, repeat, depth, cubeMap, allowDownSize); _images.Add(image); _imageDictionary.Add(name, image); return image; }
/// <summary> /// Adds implicit stages to the material. /// </summary> /// <remarks> /// If a material has diffuse or specular stages without any /// bump stage, add an implicit _flat bumpmap stage. /// <p/> /// It is valid to have either a diffuse or specular without the other. /// <p/> /// It is valid to have a reflection map and a bump map for bumpy reflection. /// </remarks> /// <param name="textureRepeatDefault"></param> private void AddImplicitStages(TextureRepeat textureRepeatDefault = TextureRepeat.Repeat) { bool hasDiffuse = false; bool hasSpecular = false; bool hasBump = false; bool hasReflection = false; int count = _parsingData.Stages.Count; for(int i = 0; i < count; i++) { switch(_parsingData.Stages[i].Lighting) { case StageLighting.Bump: hasBump = true; break; case StageLighting.Diffuse: hasDiffuse = true; break; case StageLighting.Specular: hasSpecular = true; break; } if(_parsingData.Stages[i].Texture.TextureCoordinates == TextureCoordinateGeneration.ReflectCube) { hasReflection = true; } } // if it doesn't have an interaction at all, don't add anything if((hasBump == false) && (hasDiffuse == false) && (hasSpecular == false)) { return; } idLexer lexer = new idLexer(LexerOptions.NoFatalErrors | LexerOptions.NoStringConcatination | LexerOptions.NoStringEscapeCharacters | LexerOptions.AllowPathNames); if(hasBump == false) { string bump = "blend bumpmap\nmap _flat\n}\n"; lexer.LoadMemory(bump, "bumpmap"); ParseStage(lexer, textureRepeatDefault); } if((hasDiffuse == false) && (hasSpecular == false) && (hasReflection == false)) { string bump = "blend bumpmap\nmap _white\n}\n"; lexer.LoadMemory(bump, "diffusemap"); ParseStage(lexer, textureRepeatDefault); } }
private void ParseStage(idLexer lexer, TextureRepeat textureRepeatDefault) { TextureFilter textureFilter = TextureFilter.Default; TextureRepeat textureRepeat = textureRepeatDefault; TextureDepth textureDepth = TextureDepth.Default; CubeFiles cubeMap = CubeFiles.TwoD; bool allowPicmip = true; string imageName = string.Empty; NewMaterialStage newStage = new NewMaterialStage(); newStage.VertexParameters = new int[4, 4]; MaterialStage materialStage = new MaterialStage(); materialStage.ConditionRegister = GetExpressionConstant(1); materialStage.Color.Registers = new int[] { GetExpressionConstant(1), GetExpressionConstant(1), GetExpressionConstant(1), GetExpressionConstant(1) }; int[,] matrix = new int[2, 3]; idToken token; int a, b; string tokenValue; string tokenLower; while(true) { if(TestMaterialFlag(MaterialFlags.Defaulted) == true) { // we have a parse error return; } else if((token = lexer.ExpectAnyToken()) == null) { this.MaterialFlag = MaterialFlags.Defaulted; return; } tokenValue = token.ToString(); tokenLower = tokenValue.ToLower(); // the close brace for the entire material ends the draw block if(tokenLower == "}") { break; } // BSM Nerve: Added for stage naming in the material editor else if(tokenLower == "name") { lexer.SkipRestOfLine(); } // image options else if(tokenLower == "blend") { ParseBlend(lexer, ref materialStage); } else if(tokenLower == "map") { imageName = ParsePastImageProgram(lexer); } else if(tokenLower == "remoterendermap") { materialStage.Texture.Dynamic = DynamicImageType.RemoteRender; materialStage.Texture.Width = lexer.ParseInt(); materialStage.Texture.Height = lexer.ParseInt(); } else if(tokenLower == "mirrorrendermap") { materialStage.Texture.Dynamic = DynamicImageType.MirrorRender; materialStage.Texture.Width = lexer.ParseInt(); materialStage.Texture.Height = lexer.ParseInt(); materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.Screen; } else if(tokenLower == "xrayrendermap") { materialStage.Texture.Dynamic = DynamicImageType.XRayRender; materialStage.Texture.Width = lexer.ParseInt(); materialStage.Texture.Height = lexer.ParseInt(); materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.Screen; } else if(tokenLower == "screen") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.Screen; } else if(tokenLower == "screen2") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.Screen; } else if(tokenLower == "glasswarp") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.GlassWarp; } else if(tokenLower == "videomap") { // note that videomaps will always be in clamp mode, so texture // coordinates had better be in the 0 to 1 range if((token = lexer.ReadToken()) == null) { idConsole.Warning("missing parameter for 'videoMap' keyword in material '{0}'", this.Name); } else { bool loop = false; if(token.ToString().Equals("loop", StringComparison.OrdinalIgnoreCase) == true) { loop = true; if((token = lexer.ReadToken()) == null) { idConsole.Warning("missing parameter for 'videoMap' keyword in material '{0}'", this.Name); continue; } } idConsole.Warning("TODO: material videoMap keyword"); // TODO: cinematic /*ts->cinematic = idCinematic::Alloc(); ts->cinematic->InitFromFile( token.c_str(), loop );*/ } } else if(tokenLower == "soundmap") { if((token = lexer.ReadToken()) == null) { idConsole.Warning("missing parameter for 'soundMap' keyword in material '{0}'", this.Name); } else { idConsole.Warning("TODO: material soundMap keyword"); // TODO /*ts->cinematic = new idSndWindow(); ts->cinematic->InitFromFile( token.c_str(), true );*/ } } else if(tokenLower == "cubemap") { imageName = ParsePastImageProgram(lexer); cubeMap = CubeFiles.Native; } else if(tokenLower == "cameracubemap") { imageName = ParsePastImageProgram(lexer); cubeMap = CubeFiles.Camera; } else if(tokenLower == "ignorealphatest") { materialStage.IgnoreAlphaTest = true; } else if(tokenLower == "nearest") { textureFilter = TextureFilter.Nearest; } else if(tokenLower == "linear") { textureFilter = TextureFilter.Linear; } else if(tokenLower == "clamp") { textureRepeat = TextureRepeat.Clamp; } else if(tokenLower == "noclamp") { textureRepeat = TextureRepeat.Repeat; } else if(tokenLower == "zeroclamp") { textureRepeat = TextureRepeat.ClampToZero; } else if(tokenLower == "alphazeroclamp") { textureRepeat = TextureRepeat.ClampToZeroAlpha; } else if((tokenLower == "uncompressed") || (tokenLower == "highquality")) { if(idE.CvarSystem.GetInteger("image_ignoreHighQuality") == 0) { textureDepth = TextureDepth.HighQuality; } } else if(tokenLower == "forcehighquality") { textureDepth = TextureDepth.HighQuality; } else if(tokenLower == "nopicmip") { allowPicmip = false; } else if(tokenLower == "vertexcolor") { materialStage.VertexColor = StageVertexColor.Modulate; } else if(tokenLower == "inversevertexcolor") { materialStage.VertexColor = StageVertexColor.InverseModulate; } // privatePolygonOffset else if(tokenLower == "privatepolygonoffset") { if((token = lexer.ReadTokenOnLine()) == null) { materialStage.PrivatePolygonOffset = 1; } else { // explict larger (or negative) offset lexer.UnreadToken = token; materialStage.PrivatePolygonOffset = lexer.ParseFloat(); } } // texture coordinate generation else if(tokenLower == "texgen") { token = lexer.ExpectAnyToken(); tokenValue = token.ToString(); tokenLower = tokenValue.ToLower(); if(tokenLower == "normal") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.DiffuseCube; } else if(tokenLower == "reflect") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.ReflectCube; } else if(tokenLower == "skybox") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.SkyboxCube; } else if(tokenLower == "wobblesky") { materialStage.Texture.TextureCoordinates = TextureCoordinateGeneration.WobbleSkyCube; _texGenRegisters = new int[4]; _texGenRegisters[0] = ParseExpression(lexer); _texGenRegisters[1] = ParseExpression(lexer); _texGenRegisters[2] = ParseExpression(lexer); } else { idConsole.Warning("bad texGen '{0}' in material {1}", tokenValue, this.Name); this.MaterialFlag = MaterialFlags.Defaulted; } } else if((tokenLower == "scroll") || (tokenLower == "translate")) { a = ParseExpression(lexer); MatchToken(lexer, ","); b = ParseExpression(lexer); matrix[0, 0] = GetExpressionConstant(1); matrix[0, 1] = GetExpressionConstant(0); matrix[0, 2] = a; matrix[1, 0] = GetExpressionConstant(0); matrix[1, 1] = GetExpressionConstant(1); matrix[1, 2] = b; MultiplyTextureMatrix(ref materialStage.Texture, matrix); } else if(tokenLower == "scale") { a = ParseExpression(lexer); MatchToken(lexer, ","); b = ParseExpression(lexer); // this just scales without a centering matrix[0, 0] = a; matrix[0, 1] = GetExpressionConstant(0); matrix[0, 2] = GetExpressionConstant(0); matrix[1, 0] = GetExpressionConstant(0); matrix[1, 1] = b; matrix[1, 2] = GetExpressionConstant(0); MultiplyTextureMatrix(ref materialStage.Texture, matrix); } else if(tokenLower == "centerscale") { a = ParseExpression(lexer); MatchToken(lexer, ","); b = ParseExpression(lexer); // this subtracts 0.5, then scales, then adds 0.5 matrix[0, 0] = a; matrix[0, 1] = GetExpressionConstant(0); matrix[0, 2] = EmitOp(GetExpressionConstant(0.5f), EmitOp(GetExpressionConstant(0.5f), a, ExpressionOperationType.Multiply), ExpressionOperationType.Subtract); matrix[1, 0] = GetExpressionConstant(0); matrix[1, 1] = b; matrix[1, 2] = EmitOp(GetExpressionConstant(0.5f), EmitOp(GetExpressionConstant(0.5f), b, ExpressionOperationType.Multiply), ExpressionOperationType.Subtract); MultiplyTextureMatrix(ref materialStage.Texture, matrix); } else if(tokenLower == "shear") { a = ParseExpression(lexer); MatchToken(lexer, ","); b = ParseExpression(lexer); // this subtracts 0.5, then shears, then adds 0.5 matrix[0, 0] = GetExpressionConstant(1); matrix[0, 1] = a; matrix[0, 2] = EmitOp(GetExpressionConstant(-0.5f), a, ExpressionOperationType.Multiply); matrix[1, 0] = b; matrix[1, 1] = GetExpressionConstant(1); matrix[1, 2] = EmitOp(GetExpressionConstant(-0.5f), b, ExpressionOperationType.Multiply); MultiplyTextureMatrix(ref materialStage.Texture, matrix); } else if(tokenLower == "rotate") { int sinReg, cosReg; // in cycles a = ParseExpression(lexer); idDeclTable table = idE.DeclManager.FindType<idDeclTable>(DeclType.Table, "sinTable", false); if(table == null) { idConsole.Warning("no sinTable for rotate defined"); this.MaterialFlag = MaterialFlags.Defaulted; return; } sinReg = EmitOp(table.Index, a, ExpressionOperationType.Table); table = idE.DeclManager.FindType<idDeclTable>(DeclType.Table, "cosTable", false); if(table == null) { idConsole.Warning("no cosTable for rotate defined"); this.MaterialFlag = MaterialFlags.Defaulted; return; } cosReg = EmitOp(table.Index, a, ExpressionOperationType.Table); // this subtracts 0.5, then rotates, then adds 0.5 matrix[0, 0] = cosReg; matrix[0, 1] = EmitOp(GetExpressionConstant(0), sinReg, ExpressionOperationType.Subtract); matrix[0, 2] = EmitOp(EmitOp(EmitOp(GetExpressionConstant(-0.5f), cosReg, ExpressionOperationType.Multiply), EmitOp(GetExpressionConstant(0.5f), sinReg, ExpressionOperationType.Multiply), ExpressionOperationType.Add), GetExpressionConstant(0.5f), ExpressionOperationType.Add); matrix[1, 0] = sinReg; matrix[1, 1] = cosReg; matrix[1, 2] = EmitOp(EmitOp(EmitOp(GetExpressionConstant(-0.5f), sinReg, ExpressionOperationType.Multiply), EmitOp(GetExpressionConstant(-0.5f), cosReg, ExpressionOperationType.Multiply), ExpressionOperationType.Add), GetExpressionConstant(0.5f), ExpressionOperationType.Add); MultiplyTextureMatrix(ref materialStage.Texture, matrix); } // color mask options else if(tokenLower == "maskred") { materialStage.DrawStateBits |= MaterialStates.RedMask; } else if(tokenLower == "maskgreen") { materialStage.DrawStateBits |= MaterialStates.GreenMask; } else if(tokenLower == "maskblue") { materialStage.DrawStateBits |= MaterialStates.BlueMask; } else if(tokenLower == "maskalpha") { materialStage.DrawStateBits |= MaterialStates.AlphaMask; } else if(tokenLower == "maskcolor") { materialStage.DrawStateBits |= MaterialStates.ColorMask; } else if(tokenLower == "maskdepth") { materialStage.DrawStateBits |= MaterialStates.DepthMask; } else if(tokenLower == "alphatest") { materialStage.HasAlphaTest = true; materialStage.AlphaTestRegister = ParseExpression(lexer); _coverage = MaterialCoverage.Perforated; } // shorthand for 2D modulated else if(tokenLower == "colored") { materialStage.Color.Registers[0] = (int) ExpressionRegister.Parm0; materialStage.Color.Registers[1] = (int) ExpressionRegister.Parm1; materialStage.Color.Registers[2] = (int) ExpressionRegister.Parm2; materialStage.Color.Registers[3] = (int) ExpressionRegister.Parm3; _parsingData.RegistersAreConstant = false; } else if(tokenLower == "color") { materialStage.Color.Registers[0] = ParseExpression(lexer); MatchToken(lexer, ","); materialStage.Color.Registers[1] = ParseExpression(lexer); MatchToken(lexer, ","); materialStage.Color.Registers[2] = ParseExpression(lexer); MatchToken(lexer, ","); materialStage.Color.Registers[3] = ParseExpression(lexer); } else if(tokenLower == "red") { materialStage.Color.Registers[0] = ParseExpression(lexer); } else if(tokenLower == "green") { materialStage.Color.Registers[1] = ParseExpression(lexer); } else if(tokenLower == "blue") { materialStage.Color.Registers[2] = ParseExpression(lexer); } else if(tokenLower == "alpha") { materialStage.Color.Registers[3] = ParseExpression(lexer); } else if(tokenLower == "rgb") { materialStage.Color.Registers[0] = materialStage.Color.Registers[1] = materialStage.Color.Registers[2] = ParseExpression(lexer); } else if(tokenLower == "rgba") { materialStage.Color.Registers[0] = materialStage.Color.Registers[1] = materialStage.Color.Registers[2] = materialStage.Color.Registers[3] = ParseExpression(lexer); } else if(tokenLower == "if") { materialStage.ConditionRegister = ParseExpression(lexer); } else if(tokenLower == "program") { if((token = lexer.ReadTokenOnLine()) != null) { idConsole.Warning("TODO: material program keyword"); // TODO /*newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() ); newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() );*/ } } else if(tokenLower == "fragmentprogram") { if((token = lexer.ReadTokenOnLine()) != null) { idConsole.Warning("TODO: material fragmentProgram keyword"); // TODO //newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, token.c_str() ); } } else if(tokenLower == "vertexprogram") { if((token = lexer.ReadTokenOnLine()) != null) { idConsole.Warning("TODO: material vertexProgram keyword"); // TODO //newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, token.c_str() ); } } else if(tokenLower == "megatexture") { if((token = lexer.ReadTokenOnLine()) != null) { idConsole.Warning("TODO: material megaTexture keyword"); // TODO /*newStage.megaTexture = new idMegaTexture; if ( !newStage.megaTexture->InitFromMegaFile( token.c_str() ) ) { delete newStage.megaTexture; SetMaterialFlag( MF_DEFAULTED ); continue; } newStage.vertexProgram = R_FindARBProgram( GL_VERTEX_PROGRAM_ARB, "megaTexture.vfp" ); newStage.fragmentProgram = R_FindARBProgram( GL_FRAGMENT_PROGRAM_ARB, "megaTexture.vfp" );*/ } } else if(tokenLower == "vertexparm") { ParseVertexParameter(lexer, ref newStage); } else if(tokenLower == "fragmentmap") { ParseFragmentMap(lexer, ref newStage); } else { idConsole.Warning("unknown token '{0}' in material '{1}'", tokenValue, this.Name); this.MaterialFlag = MaterialFlags.Defaulted; return; } } // if we are using newStage, allocate a copy of it if((newStage.FragmentProgram != 0) || (newStage.VertexProgram != 0)) { materialStage.NewStage = newStage; } // select a compressed depth based on what the stage is if(textureDepth == TextureDepth.Default) { switch(materialStage.Lighting) { case StageLighting.Bump: textureDepth = TextureDepth.Bump; break; case StageLighting.Diffuse: textureDepth = TextureDepth.Diffuse; break; case StageLighting.Specular: textureDepth = TextureDepth.Specular; break; } } // now load the image with all the parms we parsed if((imageName != null) && (imageName != string.Empty)) { materialStage.Texture.Image = idE.ImageManager.ImageFromFile(imageName, textureFilter, allowPicmip, textureRepeat, textureDepth, cubeMap); if(materialStage.Texture.Image == null) { materialStage.Texture.Image = idE.ImageManager.DefaultImage; } } else if(/*TODO: !ts->cinematic &&*/ (materialStage.Texture.Dynamic == 0) && (materialStage.NewStage.IsEmpty == true)) { idConsole.Warning("material '{0}' had stage with no image", this.Name); materialStage.Texture.Image = idE.ImageManager.DefaultImage; } // successfully parsed a stage. _parsingData.Stages.Add(materialStage); }
/// <summary> /// Used by callback functions to specify the actual data /// data goes from the bottom to the top line of the image, as OpenGL expects it /// These perform an implicit Bind() on the current texture unit. /// </summary> /// <remarks> /// The alpha channel bytes should be 255 if you don't want the channel. /// We need a material characteristic to ask for specific texture modes. /// Designed limitations of flexibility: /// No support for texture borders. /// No support for texture border color. /// No support for texture environment colors or GL_BLEND or GL_DECAL /// texture environments, because the automatic optimization to single /// or dual component textures makes those modes potentially undefined. /// No non-power-of-two images. /// No palettized textures. /// There is no way to specify separate wrap/clamp values for S and T. /// There is no way to specify explicit mip map levels. /// </remarks> /// <param name="data"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="filter"></param> /// <param name="allowDownSize"></param> /// <param name="repeat"></param> /// <param name="depth"></param> public void Generate(byte[] data, int width, int height, TextureFilter filter, bool allowDownSize, TextureRepeat repeat, TextureDepth depth) { if(this.Disposed == true) { throw new ObjectDisposedException(this.GetType().Name); } // FIXME: should we implement cinematics this way, instead of with explicit calls? /*Purge(); _filter = filter; _allowDownSize = allowDownSize; _repeat = repeat; _depth = depth;*/ idConsole.Warning("TODO: generate"); // if we don't have a rendering context, just return after we // have filled in the parms. We must have the values set, or // an image match from a shader before OpenGL starts would miss // the generated texture if(idE.RenderSystem.IsRunning == false) { return; } // don't let mip mapping smear the texture into the clamped border /*bool preserveBorder = (_repeat == TextureRepeat.ClampToZero); // make sure it is a power of 2 int scaledWidth = idHelper.MakePowerOfTwo(width); int scaledHeight = idHelper.MakePowerOfTwo(height); if((scaledWidth != width) || (scaledHeight != height)) { idConsole.Error("Image.Generate: not a power of 2 image."); } // Optionally modify our width/height based on options/hardware GetDownSize(ref scaledWidth, ref scaledHeight); byte[] scaledBuffer = null; /*idE.RenderSystem.CheckOpenGLErrors(); uint[] tex = new uint[1]; //Gl.glGenTextures(1, tex); _texNumber = (int) tex[0]; idE.RenderSystem.CheckOpenGLErrors();*/ /*_loaded = true; // select proper internal format before we resample _internalFormat = Gl.GL_RGB8; // SelectInternalFormat(data, 1, width, height, depth, out _isMonochrome); // copy or resample data as appropriate for first MIP level. if((scaledWidth == width) && (scaledHeight == height)) { // we must copy even if unchanged, because the border zeroing // would otherwise modify const data scaledBuffer = data; } else { idConsole.Warning("TODO: DONT SUPPORT MIMAP RIGHT NOW"); // resample down as needed (FIXME: this doesn't seem like it resamples anymore!) // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 ); /*scaledBuffer = R_MipMap( pic, width, height, preserveBorder ); width >>= 1; height >>= 1; if ( width < 1 ) { width = 1; } if ( height < 1 ) { height = 1; } while ( width > scaled_width || height > scaled_height ) { shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder ); R_StaticFree( scaledBuffer ); scaledBuffer = shrunk; width >>= 1; height >>= 1; if ( width < 1 ) { width = 1; } if ( height < 1 ) { height = 1; } } // one might have shrunk down below the target size scaled_width = width; scaled_height = height;*/ /*}*/ /*_uploadWidth = scaledWidth; _uploadHeight = scaledHeight; _type = TextureType.TwoD; // zero the border if desired, allowing clamped projection textures // even after picmip resampling or careless artists. if(repeat == TextureRepeat.ClampToZero) { byte[] rgba = new byte[4] { 0, 0, 0, 255 }; SetBorderTexels(scaledBuffer, width, height, rgba); } else if(repeat == TextureRepeat.ClampToZeroAlpha) { byte[] rgba = new byte[4] { 255, 255, 255, 0 }; SetBorderTexels(scaledBuffer, width, height, rgba); }*/ /*if((_generator == null) && ((_depth == TextureDepth.Bump) && (idE.CvarSystem.GetBool("image_writeNormalTGA") == true) || (_depth != TextureDepth.Bump) && (idE.CvarSystem.GetBool("image_writeTGA") == true))) { idConsole.Warning("TODO: gen = null && bump && write"); // Optionally write out the texture to a .tga /*char filename[MAX_IMAGE_NAME]; ImageProgramStringToCompressedFileName( imgName, filename ); char *ext = strrchr(filename, '.'); if ( ext ) { strcpy( ext, ".tga" ); // swap the red/alpha for the write /* if ( depth == TD_BUMP ) { for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) { scaledBuffer[ i ] = scaledBuffer[ i + 3 ]; scaledBuffer[ i + 3 ] = 0; } } */ // TODO: R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false ); // put it back /* if ( depth == TD_BUMP ) { for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) { scaledBuffer[ i + 3 ] = scaledBuffer[ i ]; scaledBuffer[ i ] = 0; } } */ /*}*/ /* }*/ // swap the red and alpha for rxgb support // do this even on tga normal maps so we only have to use // one fragment program. // if the image is precompressed (either in palletized mode or true rxgb mode) // then it is loaded above and the swap never happens here. /*if((depth == TextureDepth.Bump) && (idE.CvarSystem.GetInteger("image_useNormalCompression") != 1)) { for(int i = 0; i < scaledWidth * scaledHeight * 4; i += 4) { scaledBuffer[i + 3] = scaledBuffer[i]; scaledBuffer[i] = 0; } } // upload the main image level Bind(); if(_internalFormat == Gl.GL_COLOR_INDEX8_EXT) { idConsole.Warning("TODO: UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );"); } else { //Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, _internalFormat, scaledWidth, scaledHeight, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, scaledBuffer); } // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed /*int miplevel = 0; // TODO: remove this if not needed while((scaledWidth > 1) || (scaledHeight > 1)) { // preserve the border after mip map unless repeating scaledBuffer = MipMap(scaledBuffer, scaledWidth, scaledHeight, preserveBorder); scaledWidth >>= 1; scaledHeight >>= 1; if(scaledWidth < 1) { scaledWidth = 1; } if(scaledHeight < 1) { scaledHeight = 1; } miplevel++; // this is a visualization tool that shades each mip map // level with a different color so you can see the // rasterizer's texture level selection algorithm // Changing the color doesn't help with lumminance/alpha/intensity formats... // TODO /*if ( depth == TD_DIFFUSE && globalImages->image_colorMipLevels.GetBool() ) { R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); }*/ // upload the mip map /*if(_internalFormat == Gl.GL_COLOR_INDEX8_EXT) { idConsole.Warning("TODO: UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );"); } else { //Gl.glTexImage2D(Gl.GL_TEXTURE_2D, miplevel, _internalFormat, scaledWidth, scaledHeight, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, scaledBuffer); } }*/ //SetImageFilterAndRepeat(); }
// background loading information. /*private idImage _partialImage; // shrunken, space-saving version private bool _isPartialImage; // true if this is pointed to by another image*/ #endregion #region Constructor public idImage(string name, ImageLoadCallback generator) { _name = name; _generator = generator; _type = TextureType.Disabled; _filter = TextureFilter.Default; _repeat = TextureRepeat.Repeat; _depth = TextureDepth.Default; _cubeFiles = CubeFiles.TwoD; }
/// <summary> /// Used by callback functions to specify the actual data /// data goes from the bottom to the top line of the image, as OpenGL expects it /// These perform an implicit Bind() on the current texture unit. /// </summary> /// <remarks> /// The alpha channel bytes should be 255 if you don't want the channel. /// We need a material characteristic to ask for specific texture modes. /// Designed limitations of flexibility: /// No support for texture borders. /// No support for texture border color. /// No support for texture environment colors or GL_BLEND or GL_DECAL /// texture environments, because the automatic optimization to single /// or dual component textures makes those modes potentially undefined. /// No non-power-of-two images. /// No palettized textures. /// There is no way to specify separate wrap/clamp values for S and T. /// There is no way to specify explicit mip map levels. /// </remarks> /// <param name="data"></param> /// <param name="width"></param> /// <param name="height"></param> /// <param name="filter"></param> /// <param name="allowDownSize"></param> /// <param name="repeat"></param> /// <param name="depth"></param> public void Generate(byte[] data, int width, int height, TextureFilter filter, bool allowDownSize, TextureRepeat repeat, TextureDepth depth) { if (this.Disposed == true) { throw new ObjectDisposedException(this.GetType().Name); } // FIXME: should we implement cinematics this way, instead of with explicit calls? /*Purge(); * * _filter = filter; * _allowDownSize = allowDownSize; * _repeat = repeat; * _depth = depth;*/ idConsole.Warning("TODO: generate"); // if we don't have a rendering context, just return after we // have filled in the parms. We must have the values set, or // an image match from a shader before OpenGL starts would miss // the generated texture if (idE.RenderSystem.IsRunning == false) { return; } // don't let mip mapping smear the texture into the clamped border /*bool preserveBorder = (_repeat == TextureRepeat.ClampToZero); * * // make sure it is a power of 2 * int scaledWidth = idHelper.MakePowerOfTwo(width); * int scaledHeight = idHelper.MakePowerOfTwo(height); * * if((scaledWidth != width) || (scaledHeight != height)) * { * idConsole.Error("Image.Generate: not a power of 2 image."); * } * * // Optionally modify our width/height based on options/hardware * GetDownSize(ref scaledWidth, ref scaledHeight); * * byte[] scaledBuffer = null; * /*idE.RenderSystem.CheckOpenGLErrors(); * uint[] tex = new uint[1]; * //Gl.glGenTextures(1, tex); * _texNumber = (int) tex[0]; * idE.RenderSystem.CheckOpenGLErrors();*/ /*_loaded = true; * * // select proper internal format before we resample * _internalFormat = Gl.GL_RGB8; // SelectInternalFormat(data, 1, width, height, depth, out _isMonochrome); * * // copy or resample data as appropriate for first MIP level. * if((scaledWidth == width) && (scaledHeight == height)) * { * // we must copy even if unchanged, because the border zeroing * // would otherwise modify const data * scaledBuffer = data; * } * else * { * idConsole.Warning("TODO: DONT SUPPORT MIMAP RIGHT NOW"); * * // resample down as needed (FIXME: this doesn't seem like it resamples anymore!) * // scaledBuffer = R_ResampleTexture( pic, width, height, width >>= 1, height >>= 1 ); * /*scaledBuffer = R_MipMap( pic, width, height, preserveBorder ); * width >>= 1; * height >>= 1; * if ( width < 1 ) { * width = 1; * } * if ( height < 1 ) { * height = 1; * } * * while ( width > scaled_width || height > scaled_height ) { * shrunk = R_MipMap( scaledBuffer, width, height, preserveBorder ); * R_StaticFree( scaledBuffer ); * scaledBuffer = shrunk; * * width >>= 1; * height >>= 1; * if ( width < 1 ) { * width = 1; * } * if ( height < 1 ) { * height = 1; * } * } * * // one might have shrunk down below the target size * scaled_width = width; * scaled_height = height;*/ /*}*/ /*_uploadWidth = scaledWidth; * _uploadHeight = scaledHeight; * _type = TextureType.TwoD; * * // zero the border if desired, allowing clamped projection textures * // even after picmip resampling or careless artists. * if(repeat == TextureRepeat.ClampToZero) * { * byte[] rgba = new byte[4] { 0, 0, 0, 255 }; * SetBorderTexels(scaledBuffer, width, height, rgba); * } * else if(repeat == TextureRepeat.ClampToZeroAlpha) * { * byte[] rgba = new byte[4] { 255, 255, 255, 0 }; * SetBorderTexels(scaledBuffer, width, height, rgba); * }*/ /*if((_generator == null) && ((_depth == TextureDepth.Bump) && (idE.CvarSystem.GetBool("image_writeNormalTGA") == true) || (_depth != TextureDepth.Bump) && (idE.CvarSystem.GetBool("image_writeTGA") == true))) * { * idConsole.Warning("TODO: gen = null && bump && write"); * // Optionally write out the texture to a .tga * /*char filename[MAX_IMAGE_NAME]; * ImageProgramStringToCompressedFileName( imgName, filename ); * char *ext = strrchr(filename, '.'); * if ( ext ) { * strcpy( ext, ".tga" ); * // swap the red/alpha for the write * /* * if ( depth == TD_BUMP ) { * for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) { * scaledBuffer[ i ] = scaledBuffer[ i + 3 ]; * scaledBuffer[ i + 3 ] = 0; * } * } */ // TODO: R_WriteTGA( filename, scaledBuffer, scaled_width, scaled_height, false ); // put it back /* * if ( depth == TD_BUMP ) { * for ( int i = 0; i < scaled_width * scaled_height * 4; i += 4 ) { * scaledBuffer[ i + 3 ] = scaledBuffer[ i ]; * scaledBuffer[ i ] = 0; * } * } */ /*}*/ /* }*/ // swap the red and alpha for rxgb support // do this even on tga normal maps so we only have to use // one fragment program. // if the image is precompressed (either in palletized mode or true rxgb mode) // then it is loaded above and the swap never happens here. /*if((depth == TextureDepth.Bump) && (idE.CvarSystem.GetInteger("image_useNormalCompression") != 1)) * { * for(int i = 0; i < scaledWidth * scaledHeight * 4; i += 4) * { * scaledBuffer[i + 3] = scaledBuffer[i]; * scaledBuffer[i] = 0; * } * } * * // upload the main image level * Bind(); * * if(_internalFormat == Gl.GL_COLOR_INDEX8_EXT) * { * idConsole.Warning("TODO: UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, 0 );"); * } * else * { * //Gl.glTexImage2D(Gl.GL_TEXTURE_2D, 0, _internalFormat, scaledWidth, scaledHeight, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, scaledBuffer); * } * * // create and upload the mip map levels, which we do in all cases, even if we don't think they are needed * /*int miplevel = 0; * // TODO: remove this if not needed * while((scaledWidth > 1) || (scaledHeight > 1)) * { * // preserve the border after mip map unless repeating * scaledBuffer = MipMap(scaledBuffer, scaledWidth, scaledHeight, preserveBorder); * scaledWidth >>= 1; * scaledHeight >>= 1; * * if(scaledWidth < 1) * { * scaledWidth = 1; * } * * if(scaledHeight < 1) * { * scaledHeight = 1; * } * * miplevel++; * * // this is a visualization tool that shades each mip map * // level with a different color so you can see the * // rasterizer's texture level selection algorithm * // Changing the color doesn't help with lumminance/alpha/intensity formats... * // TODO * /*if ( depth == TD_DIFFUSE && globalImages->image_colorMipLevels.GetBool() ) { * R_BlendOverTexture( (byte *)scaledBuffer, scaled_width * scaled_height, mipBlendColors[miplevel] ); * }*/ // upload the mip map /*if(_internalFormat == Gl.GL_COLOR_INDEX8_EXT) * { * idConsole.Warning("TODO: UploadCompressedNormalMap( scaled_width, scaled_height, scaledBuffer, miplevel );"); * } * else * { * //Gl.glTexImage2D(Gl.GL_TEXTURE_2D, miplevel, _internalFormat, scaledWidth, scaledHeight, 0, Gl.GL_RGBA, Gl.GL_UNSIGNED_BYTE, scaledBuffer); * } * }*/ //SetImageFilterAndRepeat(); }