module cg;
private {
import core.dllStuff;
import core.plugin;
import core.common;
import renderer.renderer;
import geometry.vertex;
import geometry.vertexData;
import derelict.opengl.gl;
import glExtLoader;
import derelict.cg.cg;
import derelict.cgGL.cgGL;
import core.maths;
import std.string;
import subsystems.texture;
import renderer.gpuShader;
import renderer.typedefs;
import renderer.vertexKernel;
import renderer.gpuShaderKernel;
import renderer.gpuShaderState;
import renderer.textureKernel;
import renderer.textureState;
import renderer.renderContextBuilder;
import core.engine;
import core.engineSubsystem;
import core.systemCaps;
import subsystems.render;
import subsystems.gpuShader;
}
pragma (lib, "derelictGL.lib");
pragma (lib, "derelictCg.lib");
pragma (lib, "derelictCgGL.lib");
pragma (lib, "derelictUtil.lib");
class CgException : Exception
{
this (char[] str) {
super(str);
}
}
private class CgCaps : SystemCaps
{
this ()
{
set("loaded", true);
engine.systemCaps.extend(this, "Cg");
}
}
private class CgShaderSubsystem : GpuShaderSubsystem
{
private static void cgErrorCallback()
{
CGerror error = cgGetError();
char* errStr;
if (error == CGerror.COMPILER_ERROR) {
errStr = cgGetLastListing(cgContext);
} else {
errStr = cgGetErrorString(error);
}
throw new CgException("cgError: " ~ errStr[0 .. strlen(errStr)]);
}
this ()
{
//writefln("Initializing Cg");
DerelictCG_Load();
DerelictCGGL_Load();
cgContext = cgCreateContext();
cgSetErrorCallback(&cgErrorCallback);
}
private bool loadCgProgram(char[] programString, CGprofile profile, out CGprogram cgProg)
{
if (!cgGLIsProfileSupported(profile)) {
return false;
}
char[] progStr = programString.replace("/+", "/*").replace("+/", "*/") ~ '\0';
CGprogram prog;
try {
prog = cgCreateProgram(cgContext, CGenum.SOURCE, progStr, profile, "main", null);
cgGLLoadProgram(prog);
}
catch (CgException err) {
writefln(err);
return false;
}
finally {
delete progStr;
}
cgProg = prog;
return true;
}
IVertexShader loadVertexShader(char[] programString)
{
CGprogram prog;
if (CgShaderKernel.vertexShadersSupported && loadCgProgram(programString, vertexProfile, prog)) {
return new CgVertexShader(prog);
} else {
return null;
}
}
IFragmentShader loadFragmentShader(char[] programString)
{
CGprogram prog;
if (CgShaderKernel.fragmentShadersSupported && loadCgProgram(programString, fragmentProfile, prog)) {
return new CgFragmentShader(prog);
} else {
return null;
}
}
package {
static CGcontext cgContext;
static CGprofile vertexProfile;
static CGprofile fragmentProfile;
}
}
private class CgVertexKernel : VertexKernel
{
private VertexKernel old;
this (VertexKernel old) {
this.old = old;
}
void enableArray(VertexDataType vdt, VertexStoreType vst, uint stride, void* ptr)
{
if (!CgShaderKernel.vertexShaderBound) {
return old.enableArray(vdt, vst, stride, ptr);
}
uint components;
uint type;
switch (vst) {
case VertexStoreType.FLOAT:
case VertexStoreType.FLOAT2:
case VertexStoreType.FLOAT3:
case VertexStoreType.FLOAT4:
{
type = GL_FLOAT;
components = vst - VertexStoreType.FLOAT + 1;
} break;
case VertexStoreType.INT:
{
type = GL_INT;
components = 1;
} break;
case VertexStoreType.INT1:
case VertexStoreType.INT2:
case VertexStoreType.INT3:
case VertexStoreType.INT4:
{
type = GL_INT;
components = vst - VertexStoreType.INT1 + 1;
} break;
case VertexStoreType.BYTE1:
case VertexStoreType.BYTE2:
case VertexStoreType.BYTE3:
case VertexStoreType.BYTE4:
{
type = GL_BYTE;
components = vst - VertexStoreType.BYTE1 + 1;
} break;
default:
throw new Exception("*Renderer: invalid vertex attribute format");
}
enableArray(vdt);
cgGLSetParameterPointer(cgParams[vdt - arrayOffset], components, type, stride, ptr);
}
private void enableArray(VertexDataType vdt)
{
CGparameter cgParam = cgParams[vdt - arrayOffset];
if (cgParam) {
debug writefln("disable vdt: %s cg param: %s", cast(uint)vdt, cast(uint)cgParam);
cgGLEnableClientState(cgParam);
}
}
void disableArray(VertexDataType vdt)
{
if (!CgShaderKernel.vertexShaderBound) {
return old.disableArray(vdt);
}
CGparameter cgParam = cgParams[vdt - arrayOffset];
if (cgParam) {
debug writefln("disable vdt: %s cg param: %s", cast(uint)vdt, cast(uint)cgParam);
cgGLDisableClientState(cgParam);
}
}
package {
static const uint arraySize = cast(uint)(VertexDataType.max - VertexDataType.min + 1);
static const uint arrayOffset = cast(uint)VertexDataType.min;
static CGparameter[arraySize] cgParams;
}
}
class CgShaderKernel : GPUShaderKernel
{
private GPUShaderKernel old;
this(GPUShaderKernel old)
{
this.old = old;
if (!cgInitialized) {
writefln("Initializing CgGL");
DerelictGL_Load();
loadAllOpenGLExtensions();
try {
CgShaderSubsystem.vertexProfile = cgGLGetLatestProfile(CGGLenum.VERTEX);
CgShaderSubsystem.fragmentProfile = cgGLGetLatestProfile(CGGLenum.FRAGMENT);
if (cgGLIsProfileSupported(CgShaderSubsystem.vertexProfile)) {
cgGLSetOptimalOptions(CgShaderSubsystem.vertexProfile);
vertexShadersSupported = true;
}
if (cgGLIsProfileSupported(CgShaderSubsystem.fragmentProfile)) {
cgGLSetOptimalOptions(CgShaderSubsystem.fragmentProfile);
fragmentShadersSupported = true;
}
writefln("CgGL initialization finished");
}
catch (CgException err) {
writefln("Cg is not supported\nError: ", err);
}
cgInitialized = true;
}
}
void apply(inout GPUShaderState state)
{
old.apply(state);
with (state) {
if (!applyState) {
return;
}
vertexShaderBound = (vertexShader !is null);
fragmentShaderBound = (fragmentShader !is null);
}
}
void unapply(inout GPUShaderState state)
{
old.unapply(state);
with (state) {
if (!applyState) {
return;
}
vertexShaderBound = false;
fragmentShaderBound = false;
}
}
private {
static bool cgInitialized = false;
}
package {
static bool vertexShadersSupported = false;
static bool fragmentShadersSupported = false;
static bool vertexShaderBound = false;
static bool fragmentShaderBound = false;
}
}
class CgTextureKernel : TextureKernel
{
private TextureKernel old;
this(TextureKernel old) {
this.old = old;
}
void apply(inout TextureState state)
{
with (state) {
if (!applyState) {
return;
}
foreach (uint i, Texture t; texUnitTextures) {
if (t !is null) {
state.texUnits[i].bindTexture(t);
}
}
}
old.apply(state);
}
void unapply(inout TextureState state)
{
foreach (inout Texture t; texUnitTextures) {
t = null;
}
old.unapply(state);
}
package {
static Texture[TextureState.maxTexUnits] texUnitTextures;
}
}
private template MCgProgram()
{
ShaderParam getParam(char[] name) {
return cgGetNamedParameter(cgProgram, name ~ '\0');
}
void setParam(ShaderParam param, float val) {
cgGLSetParameter1f(cast(CGparameter)param, val);
}
void setParam(ShaderParam param, vec2 val) {
cgGLSetParameter2fv(cast(CGparameter)param, &val.x);
}
void setParam(ShaderParam param, vec3 val) {
cgGLSetParameter3fv(cast(CGparameter)param, &val.x);
}
void setParam(ShaderParam param, vec4 val) {
cgGLSetParameter4fv(cast(CGparameter)param, &val.x);
}
void setParam(ShaderParam param, mat4 val) {
cgGLSetMatrixParameterfr(cast(CGparameter)param, &val.data[0]);
}
void setStateMatrixParam(ShaderParam param, StateMatrix sm, MatrixTransform mt)
{
cgGLSetStateMatrixParameter(cast(CGparameter)param, cast(CGGLenum)sm, cast(CGGLenum)mt);
}
package {
CGprogram cgProgram;
}
}
class CgVertexShader : IVertexShader
{
mixin MCgProgram;
private void extractVaryingParams()
{
CGparameter leaf = cgGetFirstLeafParameter(cgProgram, CGenum.PROGRAM);
while (leaf)
{
CGenum var = cgGetParameterVariability(leaf);
CGenum dir = cgGetParameterDirection(leaf);
if (var == CGenum.VARYING && dir != CGenum.OUT && dir != CGenum.ERROR) {
CGresource res = cgGetParameterResource(leaf);
if (res >= VertexDataType.min && res <= VertexDataType.max) {
VertexDataType vdt = cast(VertexDataType)res;
varyingParams ~= VaryingParam(leaf, vdt);
debug writefln("extracted a param: ", cast(uint)vdt);
}
}
leaf = cgGetNextLeafParameter(leaf);
}
}
void bind() {
foreach (inout VaryingParam vpar; varyingParams) {
CgVertexKernel.cgParams[vpar.succ - CgVertexKernel.arrayOffset] = vpar.pred;
}
cgGLEnableProfile(CgShaderSubsystem.vertexProfile);
cgGLBindProgram(cgProgram);
}
void unbind() {
foreach (inout VaryingParam vpar; varyingParams) {
CgVertexKernel.cgParams[vpar.succ - CgVertexKernel.arrayOffset] = null;
}
cgGLUnbindProgram(CgShaderSubsystem.vertexProfile);
cgGLDisableProfile(CgShaderSubsystem.vertexProfile);
}
this (CGprogram prog)
{
this.cgProgram = prog;
extractVaryingParams();
}
bool isVertexDataNeeded(VertexDataType vdt)
{
foreach (inout VaryingParam vp; varyingParams) {
if (vp.succ == vdt) {
return true;
}
}
return false;
}
void setTexture(ShaderParam param, Texture tex) {
assert (false); // not implemented
}
package {
alias tuple!(CGparameter, VertexDataType) VaryingParam;
VaryingParam[] varyingParams;
}
}
class CgFragmentShader : IFragmentShader
{
mixin MCgProgram;
private void extractTexBindings()
{
CGparameter leaf = cgGetFirstLeafParameter(cgProgram, CGenum.PROGRAM);
while (leaf)
{
CGenum var = cgGetParameterVariability(leaf);
CGenum dir = cgGetParameterDirection(leaf);
if (var != CGenum.VARYING && dir != CGenum.OUT && dir != CGenum.ERROR) {
CGresource res = cgGetParameterResource(leaf);
if (res >= CGresource.TEXUNIT0 && res <= CGresource.TEXUNIT15) {
texBindings ~= TexBinding(leaf, res - CGresource.TEXUNIT0);
}
}
leaf = cgGetNextLeafParameter(leaf);
}
}
this (CGprogram prog)
{
this.cgProgram = prog;
extractTexBindings();
}
void setTexture(ShaderParam param, Texture tex) {
if (tex !is null && tex.id != -1) {
foreach (inout TexBinding tb; texBindings) {
if (tb.pred == cast(CGparameter)param) {
CgTextureKernel.texUnitTextures[tb.succ] = tex;
return;
}
}
}
}
void bind() {
cgGLEnableProfile(CgShaderSubsystem.fragmentProfile);
cgGLBindProgram(cgProgram);
}
void unbind() {
cgGLUnbindProgram(CgShaderSubsystem.fragmentProfile);
cgGLDisableProfile(CgShaderSubsystem.fragmentProfile);
}
package {
alias tuple!(CGparameter, uint) TexBinding;
TexBinding[] texBindings;
}
}
class CgVertexKernelFactory : VertexKernelFactory
{
VertexKernel create() {
return new CgVertexKernel(old.create);
}
this (VertexKernelFactory old) {
this.old = old;
}
private VertexKernelFactory old;
}
class CgShaderKernelFactory : GPUShaderKernelFactory
{
GPUShaderKernel create() {
return new CgShaderKernel(old.create);
}
this (GPUShaderKernelFactory old) {
this.old = old;
}
private GPUShaderKernelFactory old;
}
class CgTextureKernelFactory : TextureKernelFactory
{
TextureKernel create() {
return new CgTextureKernel(old.create);
}
this (TextureKernelFactory old) {
this.old = old;
}
private TextureKernelFactory old;
}
void initializeCg()
{
engine.registerSubsystem(new CgShaderSubsystem);
with (subsystem!(RenderSubsystem).renderContextBuilder()) {
vertexKF = new CgVertexKernelFactory(vertexKF);
gpuShaderKF = new CgShaderKernelFactory(gpuShaderKF);
textureKF = new CgTextureKernelFactory(textureKF);
}
new CgCaps; // tell the engine that Cg is supported from now on
}
class CgPlugin : IPlugin
{
static class CgComponent : IPluginComponent
{
char[] name() {
return "opengl.cg";
}
char[][] dependencies() {
static char[][] deps = ["opengl.window", "opengl.renderer"];
return deps;
}
bool initialize(IPluginInterface pi) {
initializeCg();
return true;
}
}
IPluginComponent[] components()
{
static IPluginComponent[1] comp;
if (comp[0] is null) {
comp[0] = new CgComponent;
}
return comp;
}
}
export extern(C) IPlugin createPlugin()
{
//writefln("cg.createPlugin called");
return new CgPlugin;
}