private void CreateShaderMaterials(Quake3Level q3lvl, SceneManager sm) { // NB this only works for the 'default' shaders for now // i.e. those that don't have a .shader script and thus default // to just texture + lightmap // TODO: pre-parse all .shader files and create lookup for next stage (use ROGL shader_file_t) // Material names are shadername#lightmapnumber // This is because I like to define materials up front completely // rather than combine lightmap and shader dynamically (it's // more generic). It results in more materials, but they're small // beer anyway. Texture duplication is prevented by infrastructure. // To do this I actually need to parse the faces since they have the // shader/lightmap combo (lightmap number is not in the shader since // it can be used with multiple lightmaps) string shaderName; int face = q3lvl.Faces.Length; while(face-- > 0) { // Check to see if existing material // Format shader#lightmap int shadIdx = q3lvl.Faces[face].shader; shaderName = String.Format("{0}#{1}", q3lvl.Shaders[shadIdx].name, q3lvl.Faces[face].lmTexture); Material shadMat = sm.GetMaterial(shaderName); if (shadMat == null && !bspOptions.useLightmaps) { // try the no-lightmap material shaderName = String.Format("{0}#n", q3lvl.Shaders[shadIdx].name); shadMat = sm.GetMaterial(shaderName); } if(shadMat == null) { // Colour layer // NB no extension in Q3A(doh), have to try shader, .jpg, .tga string tryName = q3lvl.Shaders[shadIdx].name; // Try shader first Quake3Shader shader = (Quake3Shader) Quake3ShaderManager.Instance.GetByName(tryName); if(shader != null) { shadMat = shader.CreateAsMaterial(sm, q3lvl.Faces[face].lmTexture); } else { // No shader script, try default type texture shadMat = sm.CreateMaterial(shaderName); Pass shadPass = shadMat.GetTechnique(0).GetPass(0); // Try jpg TextureUnitState tex = shadPass.CreateTextureUnitState(tryName + ".jpg"); tex.Load(); if(tex.IsBlank) { // Try tga tex.SetTextureName(tryName + ".tga"); } // Set replace on all first layer textures for now tex.SetColorOperation(LayerBlendOperation.Replace); tex.TextureAddressing = TextureAddressing.Wrap; // for ambient lighting tex.ColorBlendMode.source2 = LayerBlendSource.Manual; if(bspOptions.useLightmaps && q3lvl.Faces[face].lmTexture != -1) { // Add lightmap, additive blending tex = shadPass.CreateTextureUnitState(String.Format("@lightmap{0}", q3lvl.Faces[face].lmTexture)); // Blend tex.SetColorOperation(LayerBlendOperation.Modulate); // Use 2nd texture co-ordinate set tex.TextureCoordSet = 1; // Clamp tex.TextureAddressing = TextureAddressing.Clamp; } shadMat.CullingMode = CullingMode.None; shadMat.Lighting = false; } } shadMat.Load(); // Copy face data BspStaticFaceGroup dest = CopyShaderFaceData(q3lvl, face, shadMat, shadIdx); faceGroups[face] = dest; } }
/// <summary> /// Creates this shader as an OGRE material. /// </summary> /// <remarks> /// Creates a new material based on this shaders settings and registers it with the /// SceneManager passed in. /// Material name is in the format of: shader#lightmap. /// </remarks> /// <param name="sm">SceneManager to register the material with.</param> /// <param name="lightmapNumber">Lightmap number</param> public Material CreateAsMaterial(SceneManager sm, int lightmapNumber) { string materialName = String.Format("{0}#{1}", name, lightmapNumber); Material material = sm.CreateMaterial(materialName); LogManager.Instance.Write("Using Q3 shader {0}", name); for(int p = 0; p < pass.Count; ++p) { TextureUnitState t; // Create basic texture t = LoadMaterialTextures(p, lightmapNumber, material); // Blending if(p == 0) { // scene blend material.SetSceneBlending(pass[p].blendSrc, pass[p].blendDest); if(material.IsTransparent && (pass[p].blendSrc != SceneBlendFactor.SourceAlpha)) material.DepthWrite = false; t.SetColorOperation(LayerBlendOperation.Replace); } else { if(pass[p].customBlend) { // Fallback for now t.SetColorOperation(LayerBlendOperation.Modulate); } else { t.SetColorOperation(pass[p].blend); } } // Tex coords if(pass[p].texGen == ShaderTextureGen.Base) t.TextureCoordSet = 0; else if(pass[p].texGen == ShaderTextureGen.Lightmap) t.TextureCoordSet = 1; else if(pass[p].texGen == ShaderTextureGen.Environment) t.SetEnvironmentMap(true, EnvironmentMap.Planar); // Tex mod // Scale t.SetTextureScaleU(pass[p].tcModScale[0]); t.SetTextureScaleV(pass[p].tcModScale[1]); CreateProceduralTextureMods(p, t); // Address mode t.TextureAddressing = pass[p].addressMode; // Alpha mode t.SetAlphaRejectSettings(pass[p].alphaFunc, pass[p].alphaVal); } // Do farbox (create new material) // Do skydome (use this material) if(skyDome) { float halfAngle = 0.5f * (0.5f * (4.0f * (float) Math.Atan(1.0f))); float sin = (float) Math.Sin(halfAngle); // Quake3 is always aligned with Z upwards Quaternion q = new Quaternion( (float) Math.Cos(halfAngle), sin * Vector3.UnitX.x, sin * Vector3.UnitY.y, sin * Vector3.UnitX.z ); // Also draw last, and make close to camera (far clip plane is shorter) sm.SetSkyDome(true, materialName, 20 - (cloudHeight / 256 * 18), 12, 2000, false, q); } material.CullingMode = Axiom.Graphics.CullingMode.None; material.ManualCullMode = cullMode; material.Lighting = false; material.Load(); return material; }