diff --git a/bin/C/Imogen.h b/bin/C/Imogen.h index b007a81b..a003a909 100644 --- a/bin/C/Imogen.h +++ b/bin/C/Imogen.h @@ -10,10 +10,33 @@ typedef struct Image_t typedef struct Evaluation_t { int targetIndex; - int inputIndices[8]; int forcedDirty; + int uiPass; + int padding; + float mouse[4]; + int inputIndices[8]; } Evaluation; +enum BlendOp +{ + ZERO, + ONE, + SRC_COLOR, + ONE_MINUS_SRC_COLOR, + DST_COLOR, + ONE_MINUS_DST_COLOR, + SRC_ALPHA, + ONE_MINUS_SRC_ALPHA, + DST_ALPHA, + ONE_MINUS_DST_ALPHA, + CONSTANT_COLOR, + ONE_MINUS_CONSTANT_COLOR, + CONSTANT_ALPHA, + ONE_MINUS_CONSTANT_ALPHA, + SRC_ALPHA_SATURATE, + BLEND_LAST +}; + // call FreeImage when done int ReadImage(char *filename, Image *image); // writes an allocated image @@ -35,5 +58,9 @@ int SetThumbnailImage(Image *image); // no guarantee that the resulting Image will have that size. int Evaluate(int target, int width, int height, Image *image); +void SetBlendingMode(int target, int blendSrc, int blendDst); +int GetEvaluationSize(int target, int *imageWidth, int *imageHeight); +int SetEvaluationSize(int target, int imageWidth, int imageHeight); + #define EVAL_OK 0 #define EVAL_ERR 1 \ No newline at end of file diff --git a/bin/C/Paint2D.c b/bin/C/Paint2D.c new file mode 100644 index 00000000..2f838183 --- /dev/null +++ b/bin/C/Paint2D.c @@ -0,0 +1,13 @@ +#include "Imogen.h" + +typedef struct Paint2D_t +{ + int size; +} Paint2D; + +int main(Paint2D *param, Evaluation *evaluation) +{ + SetEvaluationSize(evaluation->targetIndex, 256<size, 256<size); + SetBlendingMode(evaluation->targetIndex, ONE, ONE_MINUS_SRC_ALPHA); + return EVAL_OK; +} \ No newline at end of file diff --git a/bin/GLSL/Crop.glsl b/bin/GLSL/Crop.glsl new file mode 100644 index 00000000..dc62b47c --- /dev/null +++ b/bin/GLSL/Crop.glsl @@ -0,0 +1,23 @@ +layout (std140) uniform CropBlock +{ + vec4 quad; +} CropParam; + +vec4 Crop() +{ + if (EvaluationParam.uiPass == 1) + { + vec4 q = vec4(min(CropParam.quad.x, CropParam.quad.z), + min(CropParam.quad.y, CropParam.quad.w), + max(CropParam.quad.x, CropParam.quad.z), + max(CropParam.quad.y, CropParam.quad.w)); + float barx = min(step(q.x, vUV.x), step(vUV.x, q.z)); + float bary = min(step(q.y, vUV.y), step(vUV.y, q.w)); + float colFactor = min(barx, bary); + return texture(Sampler0, vUV) * max(colFactor, 0.5); + } + + vec4 q = CropParam.quad; + vec2 uv = vec2(mix(min(q.x, q.z), max(q.x, q.z), vUV.x), mix(min(q.y, q.w), max(q.y, q.w), vUV.y)); + return texture(Sampler0, uv); +} \ No newline at end of file diff --git a/bin/GLSL/LambertMaterial.glsl b/bin/GLSL/LambertMaterial.glsl index 1feba240..9ac713b0 100644 --- a/bin/GLSL/LambertMaterial.glsl +++ b/bin/GLSL/LambertMaterial.glsl @@ -34,7 +34,7 @@ layout (std140) uniform LambertMaterialBlock vec4 LambertMaterial() { - vec2 p = vUV *vec2(2.0,-2.0) +vec2(- 1.0, 1.0); + vec2 p = vUV * 2.0 - 1.0; // camera movement float an = LambertMaterialParam.view.x * PI * 2.0; diff --git a/bin/GLSL/PBR.glsl b/bin/GLSL/PBR.glsl index 457bc37a..4dda7c0e 100644 --- a/bin/GLSL/PBR.glsl +++ b/bin/GLSL/PBR.glsl @@ -222,7 +222,7 @@ layout (std140) uniform PBRBlock vec4 PBR() { - vec2 p = vUV *vec2(2.0,-2.0) +vec2(- 1.0, 1.0); + vec2 p = vUV * 2.0 - 1.0; // camera movement float an = PBRParam.view.x * PI * 2.0; diff --git a/bin/GLSL/Paint2D.glsl b/bin/GLSL/Paint2D.glsl new file mode 100644 index 00000000..72fdc520 --- /dev/null +++ b/bin/GLSL/Paint2D.glsl @@ -0,0 +1,33 @@ +layout (std140) uniform Paint2DBlock +{ + int size; // 1<<(size+8) +} Paint2DParam; + +vec4 brushSample(vec2 uv, float radius) +{ + vec2 nuv = (uv) / radius + vec2(0.5); + float alphaMul = min(min(step(0.0, nuv.x), step(nuv.x, 1.0)), min(step(0.0, nuv.y), step(nuv.y, 1.0))); + return texture(Sampler0, nuv) * alphaMul; +} +vec4 Paint2D() +{ + vec4 res = vec4(0.0); + float brushRadius = 0.25; + vec4 brush = brushSample(vUV-EvaluationParam.mouse.xy, brushRadius); + if (EvaluationParam.uiPass == 1) + { + res = brush; + } + // paint pass + if (EvaluationParam.mouse.z > 0.0) + { + res = brush; + } + if (EvaluationParam.mouse.w > 0.0) + { + res = brush; + res.xyz = vec3(0.0); + } + + return vec4(res.xyz*res.w, res.w); +} \ No newline at end of file diff --git a/bin/GLSL/Pixelize.glsl b/bin/GLSL/Pixelize.glsl index 63bdb473..d5ef3204 100644 --- a/bin/GLSL/Pixelize.glsl +++ b/bin/GLSL/Pixelize.glsl @@ -5,6 +5,6 @@ layout (std140) uniform PixelizeBlock vec4 Pixelize() { - vec4 tex = texture(Sampler0, floor(vUV*PixelizeParam.scale)/PixelizeParam.scale); + vec4 tex = texture(Sampler0, floor(vUV*PixelizeParam.scale+0.5)/PixelizeParam.scale); return tex; } diff --git a/bin/GLSL/PolarCoords.glsl b/bin/GLSL/PolarCoords.glsl index 30f0003c..625ffe10 100644 --- a/bin/GLSL/PolarCoords.glsl +++ b/bin/GLSL/PolarCoords.glsl @@ -3,24 +3,22 @@ layout (std140) uniform PolarCoordsBlock int op; } PolarCoordsParam; -#define M_PI 3.1415926535897932384626433832795 - vec4 PolarCoords() { vec2 uvin = vUV - vec2(0.5,0.5); vec2 uv; if (PolarCoordsParam.op == 1) { - uv.x = cos(uvin.x * M_PI * 2 + M_PI / 2) * (1 - (uvin.y + 0.5)) / 2 + 0.5; - uv.y = sin(uvin.x * M_PI * 2 + M_PI / 2) * (1 - (uvin.y + 0.5)) / 2 + 0.5; + uv.x = cos(uvin.x * PI * 2 + PI / 2) * (1 - (uvin.y + 0.5)) / 2 + 0.5; + uv.y = sin(uvin.x * PI * 2 + PI / 2) * (1 - (uvin.y + 0.5)) / 2 + 0.5; } else { uv.x = atan(uvin.y, uvin.x); - uv.x += M_PI / 2; + uv.x += PI / 2; if (uv.x < 0) - uv.x += M_PI * 2; - uv.x /= M_PI * 2; + uv.x += PI * 2; + uv.x /= PI * 2; uv.y = 1 - length(uvin) * 2; } vec4 tex = texture(Sampler0, uv); diff --git a/bin/GLSL/Shader.glsl b/bin/GLSL/Shader.glsl index 418891f6..6e8d74ca 100644 --- a/bin/GLSL/Shader.glsl +++ b/bin/GLSL/Shader.glsl @@ -1,4 +1,6 @@ #define PI 3.14159265359 +#define SQRT2 1.414213562373095 + #define TwoPI (PI*2) #ifdef VERTEX_SHADER @@ -17,6 +19,18 @@ void main() #ifdef FRAGMENT_SHADER +layout (std140) uniform EvaluationBlock +{ + int targetIndex; + int forcedDirty; + int uiPass; + int padding; + vec4 mouse; // x,y, lbut down, rbut down + int inputIndices[8]; + +} EvaluationParam; + + layout(location=0) out vec4 outPixDiffuse; in vec2 vUV; @@ -75,7 +89,6 @@ __NODE__ void main() { outPixDiffuse = vec4(__FUNCTION__); - outPixDiffuse.a = 1.0; } #endif diff --git a/bin/GLSL/Swirl.glsl b/bin/GLSL/Swirl.glsl new file mode 100644 index 00000000..9bcbf67c --- /dev/null +++ b/bin/GLSL/Swirl.glsl @@ -0,0 +1,15 @@ +layout (std140) uniform SwirlBlock +{ + vec2 angles; +} SwirlParam; + +vec4 Swirl() +{ + vec2 uv = vUV - vec2(0.5); + float len = length(uv) / (SQRT2 * 0.5); + float angle = mix(SwirlParam.angles.x, SwirlParam.angles.y, len); + vec2 nuv = Rotate2D(uv, angle) + vec2(0.5); + vec4 tex = texture(Sampler0, nuv); + + return tex; +} diff --git a/bin/Imogen.exe b/bin/Imogen.exe index 8aa73ed1..a2c68022 100644 Binary files a/bin/Imogen.exe and b/bin/Imogen.exe differ diff --git a/bin/Pictures/saint-seiya.jpg b/bin/Pictures/saint-seiya.jpg new file mode 100644 index 00000000..9a4d8e00 Binary files /dev/null and b/bin/Pictures/saint-seiya.jpg differ diff --git a/bin/library.dat b/bin/library.dat index 9e40d1e0..c0817cd6 100644 Binary files a/bin/library.dat and b/bin/library.dat differ diff --git a/changelog.txt b/changelog.txt index 5cdd4412..ad94d325 100644 --- a/changelog.txt +++ b/changelog.txt @@ -1,3 +1,31 @@ +------------------------------------------------------------------------------- +Imogen 0.4 - Andromeda Shun + +New: + +Changed: + +Fixed: + +------------------------------------------------------------------------------- +Imogen 0.3 - codename Cygnus Hyoga + +New: +- C nodes can change the size and blending mode of the texture target +- Nodes can mix C and GLSL +- Paint2D node +- Swirl node +- Crop node +- UI mode for nodes : nodes can display user information +- Nodes can handle mouse pos and buttons +- openGL debug output in the console +- image ratio support +- better image resize in the NodeEdit panel + +Changed: +- more consistent texV management +- Threaded decoding and loading of node textures (paint2d Surface) + ------------------------------------------------------------------------------- Imogen 0.2 - codename Dragon Shiryu diff --git a/src/Evaluation.cpp b/src/Evaluation.cpp index e495be14..c6b1dda4 100644 --- a/src/Evaluation.cpp +++ b/src/Evaluation.cpp @@ -34,7 +34,7 @@ std::string Evaluation::GetEvaluator(const std::string& filename) return mEvaluatorScripts[filename].mText; } -Evaluation::Evaluation() : mDirtyCount(0), mEvaluationMode(-1) +Evaluation::Evaluation() : mDirtyCount(0), mEvaluationMode(-1), mEvaluationStateGLSLBuffer(0) { } @@ -51,41 +51,43 @@ void Evaluation::Finish() size_t Evaluation::AddEvaluation(size_t nodeType, const std::string& nodeName) { + EvaluationStage evaluation; + evaluation.mTarget = NULL; + evaluation.mUseCountByOthers = 0; + evaluation.mbDirty = true; + evaluation.mbForceEval = false; + evaluation.mNodeType = nodeType; + evaluation.mParametersBuffer = 0; + evaluation.mEvaluationMask = 0; + evaluation.mBlendingSrc = ONE; + evaluation.mBlendingDst = ZERO; + + bool valid(false); auto iter = mEvaluatorScripts.find(nodeName+".glsl"); if (iter != mEvaluatorScripts.end()) { - EvaluationStage evaluation; + evaluation.mEvaluationMask |= EvaluationGLSL; + iter->second.mNodeType = int(nodeType); evaluation.mTarget = new RenderTarget; mAllocatedRenderTargets.push_back(evaluation.mTarget); - evaluation.mUseCountByOthers = 0; - evaluation.mbDirty = true; - evaluation.mbForceEval = false; - evaluation.mNodeType = nodeType; - evaluation.mParametersBuffer = 0; - evaluation.mEvaluationType = 0; - iter->second.mNodeType = int(nodeType); mEvaluatorPerNodeType[nodeType].mGLSLProgram = iter->second.mProgram; - mDirtyCount++; - mEvaluations.push_back(evaluation); - return mEvaluations.size() - 1; + valid = true; } iter = mEvaluatorScripts.find(nodeName + ".c"); if (iter != mEvaluatorScripts.end()) { - EvaluationStage evaluation; - evaluation.mTarget = NULL; - evaluation.mUseCountByOthers = 0; - evaluation.mbDirty = true; - evaluation.mbForceEval = false; - evaluation.mNodeType = nodeType; - evaluation.mParametersBuffer = 0; - evaluation.mEvaluationType = 1; + evaluation.mEvaluationMask |= EvaluationC; iter->second.mNodeType = int(nodeType); mEvaluatorPerNodeType[nodeType].mCFunction = iter->second.mCFunction; mEvaluatorPerNodeType[nodeType].mMem = iter->second.mMem; + valid = true; + } + + if (valid) + { mDirtyCount++; - mEvaluations.push_back(evaluation); - return mEvaluations.size() - 1; + mEvaluationStages.push_back(evaluation); + return mEvaluationStages.size() - 1; } Log("Could not find node name \"%s\" \n", nodeName.c_str()); return -1; @@ -94,12 +96,12 @@ size_t Evaluation::AddEvaluation(size_t nodeType, const std::string& nodeName) void Evaluation::DelEvaluationTarget(size_t target) { SetTargetDirty(target); - EvaluationStage& ev = mEvaluations[target]; + EvaluationStage& ev = mEvaluationStages[target]; ev.Clear(); - mEvaluations.erase(mEvaluations.begin() + target); + mEvaluationStages.erase(mEvaluationStages.begin() + target); // shift all connections - for (auto& evaluation : mEvaluations) + for (auto& evaluation : mEvaluationStages) { for (auto& inp : evaluation.mInput.mInputs) { @@ -111,24 +113,26 @@ void Evaluation::DelEvaluationTarget(size_t target) unsigned int Evaluation::GetEvaluationTexture(size_t target) { - return mEvaluations[target].mTarget->mGLTexID; + if (!mEvaluationStages[target].mTarget) + return 0; + return mEvaluationStages[target].mTarget->mGLTexID; } void Evaluation::SetEvaluationParameters(size_t target, void *parameters, size_t parametersSize) { - EvaluationStage& stage = mEvaluations[target]; + EvaluationStage& stage = mEvaluationStages[target]; stage.mParameters = parameters; stage.mParametersSize = parametersSize; - if (stage.mEvaluationType == EVALUATOR_GLSL) + if (stage.mEvaluationMask&EvaluationGLSL) BindGLSLParameters(stage); SetTargetDirty(target); } -void Evaluation::PerformEvaluationForNode(size_t index, int width, int height, bool force) +void Evaluation::PerformEvaluationForNode(size_t index, int width, int height, bool force, EvaluationInfo& evaluationInfo) { - EvaluationStage& evaluation = mEvaluations[index]; + EvaluationStage& evaluation = mEvaluationStages[index]; if (force) { @@ -136,15 +140,10 @@ void Evaluation::PerformEvaluationForNode(size_t index, int width, int height, b SetTargetDirty(index); } - switch (evaluation.mEvaluationType) - { - case 0: // GLSL - EvaluateGLSL(evaluation); - break; - case 1: // C - EvaluateC(evaluation, index); - break; - } + if (evaluation.mEvaluationMask&EvaluationC) + EvaluateC(evaluation, index, evaluationInfo); + if (evaluation.mEvaluationMask&EvaluationGLSL) + EvaluateGLSL(evaluation, evaluationInfo); } void Evaluation::SetEvaluationMemoryMode(int evaluationMode) @@ -164,35 +163,34 @@ void Evaluation::SetEvaluationMemoryMode(int evaluationMode) if (evaluationMode == 0) // edit mode { - for (size_t i = 0; i < mEvaluations.size(); i++) + for (size_t i = 0; i < mEvaluationStages.size(); i++) { - EvaluationStage& evaluation = mEvaluations[i]; - switch (evaluation.mEvaluationType) + EvaluationStage& evaluation = mEvaluationStages[i]; + if (evaluation.mEvaluationMask&EvaluationGLSL) { - case 0: // glsl evaluation.mTarget = new RenderTarget; mAllocatedRenderTargets.push_back(evaluation.mTarget); - break; - case 1: // C + } + if (evaluation.mEvaluationMask&EvaluationC) + { evaluation.mTarget = 0; - break; } } } else // baking mode { std::vector freeRenderTargets; - std::vector useCount(mEvaluations.size(), 0); - for (size_t i = 0; i < mEvaluations.size(); i++) + std::vector useCount(mEvaluationStages.size(), 0); + for (size_t i = 0; i < mEvaluationStages.size(); i++) { - useCount[i] = mEvaluations[i].mUseCountByOthers; + useCount[i] = mEvaluationStages[i].mUseCountByOthers; } for (size_t i = 0; i < mEvaluationOrderList.size(); i++) { size_t index = mEvaluationOrderList[i]; - EvaluationStage& evaluation = mEvaluations[index]; + EvaluationStage& evaluation = mEvaluationStages[index]; if (!evaluation.mUseCountByOthers) continue; @@ -217,7 +215,7 @@ void Evaluation::SetEvaluationMemoryMode(int evaluationMode) useCount[targetIndex]--; if (!useCount[targetIndex]) { - freeRenderTargets.push_back(mEvaluations[targetIndex].mTarget); + freeRenderTargets.push_back(mEvaluationStages[targetIndex].mTarget); } } } @@ -233,21 +231,26 @@ void Evaluation::RunEvaluation(int width, int height, bool forceEvaluation) if (!mDirtyCount && !forceEvaluation) return; + EvaluationInfo evaluationInfo; + evaluationInfo.forcedDirty = forceEvaluation ? 1 : 0; + evaluationInfo.uiPass = 0; for (size_t i = 0; i < mEvaluationOrderList.size(); i++) { size_t index = mEvaluationOrderList[i]; - EvaluationStage& evaluation = mEvaluations[index]; + EvaluationStage& evaluation = mEvaluationStages[index]; if (!evaluation.mbDirty && !forceEvaluation) continue; if (evaluation.mTarget && !evaluation.mTarget->mGLTexID) + { evaluation.mTarget->initBuffer(width, height, false); + } - PerformEvaluationForNode(index, width, height, false); + PerformEvaluationForNode(index, width, height, false, evaluationInfo); } - for (auto& evaluation : mEvaluations) + for (auto& evaluation : mEvaluationStages) { if (evaluation.mbDirty) { @@ -262,21 +265,21 @@ void Evaluation::RunEvaluation(int width, int height, bool forceEvaluation) void Evaluation::SetEvaluationSampler(size_t target, const std::vector& inputSamplers) { - mEvaluations[target].mInputSamplers = inputSamplers; + mEvaluationStages[target].mInputSamplers = inputSamplers; SetTargetDirty(target); } void Evaluation::AddEvaluationInput(size_t target, int slot, int source) { - mEvaluations[target].mInput.mInputs[slot] = source; - mEvaluations[source].mUseCountByOthers++; + mEvaluationStages[target].mInput.mInputs[slot] = source; + mEvaluationStages[source].mUseCountByOthers++; SetTargetDirty(target); } void Evaluation::DelEvaluationInput(size_t target, int slot) { - mEvaluations[mEvaluations[target].mInput.mInputs[slot]].mUseCountByOthers--; - mEvaluations[target].mInput.mInputs[slot] = -1; + mEvaluationStages[mEvaluationStages[target].mInput.mInputs[slot]].mUseCountByOthers--; + mEvaluationStages[target].mInput.mInputs[slot] = -1; SetTargetDirty(target); } @@ -287,10 +290,10 @@ void Evaluation::SetEvaluationOrder(const std::vector nodeOrderList) void Evaluation::SetTargetDirty(size_t target) { - if (!mEvaluations[target].mbDirty) + if (!mEvaluationStages[target].mbDirty) { mDirtyCount++; - mEvaluations[target].mbDirty = true; + mEvaluationStages[target].mbDirty = true; } for (size_t i = 0; i < mEvaluationOrderList.size(); i++) { @@ -299,13 +302,13 @@ void Evaluation::SetTargetDirty(size_t target) for (i++; i < mEvaluationOrderList.size(); i++) { - EvaluationStage& currentEvaluation = mEvaluations[mEvaluationOrderList[i]]; + EvaluationStage& currentEvaluation = mEvaluationStages[mEvaluationOrderList[i]]; if (currentEvaluation.mbDirty) continue; for (auto inp : currentEvaluation.mInput.mInputs) { - if (inp >= 0 && mEvaluations[inp].mbDirty) + if (inp >= 0 && mEvaluationStages[inp].mbDirty) { mDirtyCount++; currentEvaluation.mbDirty = true; @@ -318,10 +321,25 @@ void Evaluation::SetTargetDirty(size_t target) void Evaluation::Clear() { - for (auto& ev : mEvaluations) + for (auto& ev : mEvaluationStages) ev.Clear(); - mEvaluations.clear(); + mEvaluationStages.clear(); mEvaluationOrderList.clear(); } +void Evaluation::SetMouse(int target, float rx, float ry, bool lButDown, bool rButDown) +{ + for (auto& ev : mEvaluationStages) + { + ev.mRx = -9999.f; + ev.mRy = -9999.f; + ev.mLButDown = false; + ev.mRButDown = false; + } + auto& ev = mEvaluationStages[target]; + ev.mRx = rx; + ev.mRy = 1.f - ry; // inverted for UI + ev.mLButDown = lButDown; + ev.mRButDown = rButDown; +} \ No newline at end of file diff --git a/src/Evaluation.h b/src/Evaluation.h index 788babae..c9c18ce8 100644 --- a/src/Evaluation.h +++ b/src/Evaluation.h @@ -36,6 +36,29 @@ extern int Log(const char *szFormat, ...); typedef unsigned int TextureID; +struct ImDrawList; +struct ImDrawCmd; + +enum BlendOp +{ + ZERO, + ONE, + SRC_COLOR, + ONE_MINUS_SRC_COLOR, + DST_COLOR, + ONE_MINUS_DST_COLOR, + SRC_ALPHA, + ONE_MINUS_SRC_ALPHA, + DST_ALPHA, + ONE_MINUS_DST_ALPHA, + CONSTANT_COLOR, + ONE_MINUS_CONSTANT_COLOR, + CONSTANT_ALPHA, + ONE_MINUS_CONSTANT_ALPHA, + SRC_ALPHA_SATURATE, + BLEND_LAST +}; + enum EvaluationStatus { EVAL_OK, @@ -45,8 +68,11 @@ enum EvaluationStatus struct EvaluationInfo { int targetIndex; - int inputIndices[8]; int forcedDirty; + int uiPass; + int padding; + float mouse[4]; + int inputIndices[8]; }; typedef struct Image_t @@ -71,8 +97,6 @@ class RenderTarget void initBuffer(int width, int height, bool hasZBuffer); void bindAsTarget() const; - void clear(); - TextureID txDepth; unsigned int mGLTexID; int mWidth, mHeight; @@ -101,18 +125,19 @@ struct Evaluation void DelEvaluationTarget(size_t target); unsigned int GetEvaluationTexture(size_t target); void SetEvaluationParameters(size_t target, void *parameters, size_t parametersSize); - void PerformEvaluationForNode(size_t index, int width, int height, bool force); + void PerformEvaluationForNode(size_t index, int width, int height, bool force, EvaluationInfo& evaluationInfo); void SetEvaluationSampler(size_t target, const std::vector& inputSamplers); void AddEvaluationInput(size_t target, int slot, int source); void DelEvaluationInput(size_t target, int slot); void RunEvaluation(int width, int height, bool forceEvaluation); void SetEvaluationOrder(const std::vector nodeOrderList); void SetTargetDirty(size_t target); - + void SetMouse(int target, float rx, float ry, bool lButDown, bool rButDown); void Clear(); // API static int ReadImage(const char *filename, Image *image); + static int ReadImageMem(unsigned char *data, size_t dataSize, Image *image); static int WriteImage(const char *filename, Image *image, int format, int quality); static int GetEvaluationImage(int target, Image *image); static int SetEvaluationImage(int target, Image *image); @@ -121,7 +146,13 @@ struct Evaluation static int FreeImage(Image *image); static unsigned int UploadImage(Image *image); static int Evaluate(int target, int width, int height, Image *image); + static void SetBlendingMode(int target, int blendSrc, int blendDst); + static int EncodePng(Image *image, std::vector &pngImage); + static int SetNodeImage(int target, Image *image); + static int GetEvaluationSize(int target, int *imageWidth, int *imageHeight); + static int SetEvaluationSize(int target, int imageWidth, int imageHeight); + static void NodeUICallBack(const ImDrawList* parent_list, const ImDrawCmd* cmd); // synchronous texture cache // use for simple textures(stock) or to replace with a more efficient one unsigned int GetTexture(const std::string& filename); @@ -167,6 +198,11 @@ struct Evaluation int mInputs[8]; }; + enum EvaluationMask + { + EvaluationC = 1 << 0, + EvaluationGLSL = 1 << 1, + }; struct EvaluationStage { RenderTarget *mTarget; @@ -178,18 +214,26 @@ struct Evaluation std::vector mInputSamplers; bool mbDirty; bool mbForceEval; - int mEvaluationType; // 0 = GLSL. 1 = CPU C + int mEvaluationMask; // see EvaluationMask int mUseCountByOthers; + int mBlendingSrc; + int mBlendingDst; + // mouse + float mRx; + float mRy; + bool mLButDown; + bool mRButDown; void Clear(); }; - - std::vector mEvaluations; + unsigned int mEvaluationStateGLSLBuffer; + std::vector mEvaluationStages; std::vector mEvaluationOrderList; - void BindGLSLParameters(EvaluationStage& stage); - void EvaluateGLSL(EvaluationStage& evaluation); - void EvaluateC(EvaluationStage& evaluation, size_t index); + void SetMouseInfos(EvaluationInfo &evaluationInfo, EvaluationStage &evaluationStage) const; + void BindGLSLParameters(EvaluationStage& evaluationStage); + void EvaluateGLSL(EvaluationStage& evaluationStage, EvaluationInfo& evaluationInfo); + void EvaluateC(EvaluationStage& evaluationStage, size_t index, EvaluationInfo& evaluationInfo); void FinishEvaluation(); std::vector mAllocatedRenderTargets; diff --git a/src/EvaluationAPI.cpp b/src/EvaluationAPI.cpp index ab994bcd..09cd2852 100644 --- a/src/EvaluationAPI.cpp +++ b/src/EvaluationAPI.cpp @@ -35,12 +35,15 @@ #include "stb_image_write.h" #include #include - +#include "imgui.h" +#include "imgui_internal.h" static const int SemUV0 = 0; static const unsigned int wrap[] = { GL_REPEAT, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_BORDER, GL_MIRRORED_REPEAT }; static const unsigned int filter[] = { GL_LINEAR, GL_NEAREST }; static const char* samplerName[] = { "Sampler0", "Sampler1", "Sampler2", "Sampler3", "Sampler4", "Sampler5", "Sampler6", "Sampler7" }; +static const unsigned int GLBlends[] = { GL_ZERO, GL_ONE, GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_DST_COLOR, GL_ONE_MINUS_DST_COLOR,GL_SRC_ALPHA, + GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA, GL_ONE_MINUS_DST_ALPHA, GL_CONSTANT_COLOR, GL_ONE_MINUS_CONSTANT_COLOR, GL_CONSTANT_ALPHA, GL_ONE_MINUS_CONSTANT_ALPHA, GL_SRC_ALPHA_SATURATE }; inline void TexParam(TextureID MinFilter, TextureID MagFilter, TextureID WrapS, TextureID WrapT, TextureID texMode) { @@ -69,11 +72,6 @@ void RenderTarget::destroy() mGLTexID = 0; } -void RenderTarget::clear() -{ - glClear(GL_COLOR_BUFFER_BIT | (depthbuffer ? GL_DEPTH_BUFFER_BIT : 0)); -} - void RenderTarget::initBuffer(int width, int height, bool hasZBuffer) { if ((width == mWidth) && (mHeight == height) && (!(hasZBuffer ^ (depthbuffer != 0)))) @@ -107,6 +105,11 @@ void RenderTarget::initBuffer(int width, int height, bool hasZBuffer) glDrawBuffers(sizeof(DrawBuffers) / sizeof(GLenum), DrawBuffers); checkFBO(); + bindAsTarget(); + glClearColor(0, 0, 0, 0); + glClear(GL_COLOR_BUFFER_BIT); + glBindFramebuffer(GL_FRAMEBUFFER, 0); + } void RenderTarget::checkFBO() @@ -311,23 +314,23 @@ struct EValuationFunction void *function; }; -/* -typedef struct Evaluation_t -{ -int targetIndex; -int inputIndices[8]; -int forcedDirty; -} Evaluation; -*/ - extern Evaluation gEvaluation; int Evaluation::ReadImage(const char *filename, Image *image) { - unsigned char *data = stbi_load(filename, &image->width, &image->height, &image->components, 0); - if (!data) + unsigned char *bits = stbi_load(filename, &image->width, &image->height, &image->components, 0); + if (!bits) + return EVAL_ERR; + image->bits = bits; + return EVAL_OK; +} + +int Evaluation::ReadImageMem(unsigned char *data, size_t dataSize, Image *image) +{ + unsigned char *bits = stbi_load_from_memory(data, int(dataSize), &image->width, &image->height, &image->components, 0); + if (!bits) return EVAL_ERR; - image->bits = data; + image->bits = bits; return EVAL_OK; } @@ -361,10 +364,10 @@ int Evaluation::WriteImage(const char *filename, Image *image, int format, int q int Evaluation::GetEvaluationImage(int target, Image *image) { - if (target == -1 || target >= gEvaluation.mEvaluations.size()) + if (target == -1 || target >= gEvaluation.mEvaluationStages.size()) return EVAL_ERR; - Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluations[target]; + Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluationStages[target]; RenderTarget& tgt = *evaluation.mTarget; image->components = 4; image->width = tgt.mWidth; @@ -378,7 +381,7 @@ int Evaluation::GetEvaluationImage(int target, Image *image) int Evaluation::SetEvaluationImage(int target, Image *image) { - Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluations[target]; + Evaluation::EvaluationStage &evaluation = gEvaluation.mEvaluationStages[target]; //gEvaluation.UnreferenceRenderTarget(&evaluation.mTarget); if (!evaluation.mTarget) { @@ -414,15 +417,25 @@ int Evaluation::FreeImage(Image *image) return EVAL_OK; } -int Evaluation::SetThumbnailImage(Image *image) +int Evaluation::EncodePng(Image *image, std::vector &pngImage) { int outlen; unsigned char *bits = stbi_write_png_to_mem((unsigned char*)image->bits, image->width * image->components, image->width, image->height, image->components, &outlen); if (!bits) return EVAL_ERR; - std::vector pngImage(outlen); + pngImage.resize(outlen); memcpy(pngImage.data(), bits, outlen); + free(bits); + return EVAL_OK; +} + +int Evaluation::SetThumbnailImage(Image *image) +{ + std::vector pngImage; + if (EncodePng(image, pngImage) == EVAL_ERR) + return EVAL_ERR; + extern Library library; extern Imogen imogen; @@ -433,9 +446,25 @@ int Evaluation::SetThumbnailImage(Image *image) return EVAL_OK; } +int Evaluation::SetNodeImage(int target, Image *image) +{ + std::vector pngImage; + if (EncodePng(image, pngImage) == EVAL_ERR) + return EVAL_ERR; + + extern Library library; + extern Imogen imogen; + + int materialIndex = imogen.GetCurrentMaterialIndex(); + Material & material = library.mMaterials[materialIndex]; + material.mMaterialNodes[target].mImage = pngImage; + + return EVAL_OK; +} + void Evaluation::RecurseGetUse(size_t target, std::vector& usedNodes) { - EvaluationStage& evaluation = mEvaluations[target]; + EvaluationStage& evaluation = mEvaluationStages[target]; const Input& input = evaluation.mInput; std::vector usingTargets; @@ -479,9 +508,20 @@ static const EValuationFunction evaluationFunctions[] = { { "AllocateImage", (void*)Evaluation::AllocateImage }, { "FreeImage", (void*)Evaluation::FreeImage }, { "SetThumbnailImage", (void*)Evaluation::SetThumbnailImage }, - { "Evaluate", (void*)Evaluation::Evaluate} + { "Evaluate", (void*)Evaluation::Evaluate}, + { "SetBlendingMode", (void*)Evaluation::SetBlendingMode}, + { "GetEvaluationSize", (void*)Evaluation::GetEvaluationSize}, + { "SetEvaluationSize", (void*)Evaluation::SetEvaluationSize }, }; +void Evaluation::SetBlendingMode(int target, int blendSrc, int blendDst) +{ + EvaluationStage& evaluation = gEvaluation.mEvaluationStages[target]; + + evaluation.mBlendingSrc = blendSrc; + evaluation.mBlendingDst = blendDst; +} + std::string ReplaceAll(std::string str, const std::string& from, const std::string& to) { size_t start_pos = 0; @@ -492,7 +532,6 @@ std::string ReplaceAll(std::string str, const std::string& from, const std::stri return str; } - void Evaluation::ClearEvaluators() { // clear @@ -546,73 +585,94 @@ void Evaluation::SetEvaluators(const std::vector& evaluatorfilena shaderText = ReplaceAll(shaderText, "__FUNCTION__", nodeName + "()"); unsigned int program = LoadShader(shaderText, filename.c_str()); - unsigned int parameterBlockIndex = glGetUniformBlockIndex(program, (nodeName + "Block").c_str()); - glUniformBlockBinding(program, parameterBlockIndex, 1); + + int parameterBlockIndex = glGetUniformBlockIndex(program, (nodeName + "Block").c_str()); + if (parameterBlockIndex != -1) + glUniformBlockBinding(program, parameterBlockIndex, 1); + + parameterBlockIndex = glGetUniformBlockIndex(program, "EvaluationBlock"); + if (parameterBlockIndex != -1) + glUniformBlockBinding(program, parameterBlockIndex, 2); shader.mProgram = program; if (shader.mNodeType != -1) mEvaluatorPerNodeType[shader.mNodeType].mGLSLProgram = program; } + if (!gEvaluation.mEvaluationStateGLSLBuffer) + { + glGenBuffers(1, &mEvaluationStateGLSLBuffer); + glBindBuffer(GL_UNIFORM_BUFFER, mEvaluationStateGLSLBuffer); + + glBufferData(GL_UNIFORM_BUFFER, sizeof(EvaluationInfo), NULL, GL_DYNAMIC_DRAW); + glBindBufferBase(GL_UNIFORM_BUFFER, 2, mEvaluationStateGLSLBuffer); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + } + // C for (auto& file : evaluatorfilenames) { if (file.mEvaluatorType != EVALUATOR_C) continue; const std::string filename = file.mFilename; - - std::ifstream t(file.mDirectory + filename); - if (!t.good()) + try { - Log("%s - Unable to load file.\n", filename.c_str()); - continue; - } - std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); - if (mEvaluatorScripts.find(filename) == mEvaluatorScripts.end()) - mEvaluatorScripts[filename] = EvaluatorScript(str); - else - mEvaluatorScripts[filename].mText = str; + std::ifstream t(file.mDirectory + filename); + if (!t.good()) + { + Log("%s - Unable to load file.\n", filename.c_str()); + continue; + } + std::string str((std::istreambuf_iterator(t)), std::istreambuf_iterator()); + if (mEvaluatorScripts.find(filename) == mEvaluatorScripts.end()) + mEvaluatorScripts[filename] = EvaluatorScript(str); + else + mEvaluatorScripts[filename].mText = str; - EvaluatorScript& program = mEvaluatorScripts[filename]; - TCCState *s = tcc_new(); + EvaluatorScript& program = mEvaluatorScripts[filename]; + TCCState *s = tcc_new(); - int *noLib = (int*)s; - noLib[2] = 1; // no stdlib + int *noLib = (int*)s; + noLib[2] = 1; // no stdlib - tcc_set_error_func(s, 0, libtccErrorFunc); - tcc_add_include_path(s, "C\\"); - tcc_set_output_type(s, TCC_OUTPUT_MEMORY); + tcc_set_error_func(s, 0, libtccErrorFunc); + tcc_add_include_path(s, "C\\"); + tcc_set_output_type(s, TCC_OUTPUT_MEMORY); - if (tcc_compile_string(s, program.mText.c_str()) != 0) - { - Log("%s - Compilation error!\n", filename.c_str()); - continue; - } + if (tcc_compile_string(s, program.mText.c_str()) != 0) + { + Log("%s - Compilation error!\n", filename.c_str()); + continue; + } - for (auto& evaluationFunction : evaluationFunctions) - tcc_add_symbol(s, evaluationFunction.szFunctionName, evaluationFunction.function); + for (auto& evaluationFunction : evaluationFunctions) + tcc_add_symbol(s, evaluationFunction.szFunctionName, evaluationFunction.function); - int size = tcc_relocate(s, NULL); - if (size == -1) - { - Log("%s - Libtcc unable to relocate program!\n", filename.c_str()); - continue; - } - program.mMem = malloc(size); - tcc_relocate(s, program.mMem); + int size = tcc_relocate(s, NULL); + if (size == -1) + { + Log("%s - Libtcc unable to relocate program!\n", filename.c_str()); + continue; + } + program.mMem = malloc(size); + tcc_relocate(s, program.mMem); - *(void**)(&program.mCFunction) = tcc_get_symbol(s, "main"); - if (!program.mCFunction) - { - Log("%s - No main function!\n", filename.c_str()); - } - tcc_delete(s); + *(void**)(&program.mCFunction) = tcc_get_symbol(s, "main"); + if (!program.mCFunction) + { + Log("%s - No main function!\n", filename.c_str()); + } + tcc_delete(s); - if (program.mNodeType != -1) + if (program.mNodeType != -1) + { + mEvaluatorPerNodeType[program.mNodeType].mCFunction = program.mCFunction; + mEvaluatorPerNodeType[program.mNodeType].mMem = program.mMem; + } + } + catch (...) { - mEvaluatorPerNodeType[program.mNodeType].mCFunction = program.mCFunction; - mEvaluatorPerNodeType[program.mNodeType].mMem = program.mMem; + Log("Error at compiling %s", filename.c_str()); } - } } @@ -624,27 +684,58 @@ void Evaluation::BindGLSLParameters(EvaluationStage& stage) glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); glBufferData(GL_UNIFORM_BUFFER, stage.mParametersSize, stage.mParameters, GL_DYNAMIC_DRAW); - glBindBufferBase(GL_UNIFORM_BUFFER, 0, stage.mParametersBuffer); + glBindBufferBase(GL_UNIFORM_BUFFER, 1, stage.mParametersBuffer); glBindBuffer(GL_UNIFORM_BUFFER, 0); } else { glBindBuffer(GL_UNIFORM_BUFFER, stage.mParametersBuffer); - glBufferSubData(GL_UNIFORM_BUFFER, 0, stage.mParametersSize, stage.mParameters); + glBufferData(GL_UNIFORM_BUFFER, stage.mParametersSize, stage.mParameters, GL_DYNAMIC_DRAW); glBindBuffer(GL_UNIFORM_BUFFER, 0); } } -void Evaluation::EvaluateGLSL(EvaluationStage& evaluation) +void Evaluation::SetMouseInfos(EvaluationInfo &evaluationInfo, EvaluationStage &evaluationStage) const { - const Input& input = evaluation.mInput; + evaluationInfo.mouse[0] = evaluationStage.mRx; + evaluationInfo.mouse[1] = evaluationStage.mRy; + evaluationInfo.mouse[2] = evaluationStage.mLButDown ? 1.f : 0.f; + evaluationInfo.mouse[3] = evaluationStage.mRButDown ? 1.f : 0.f; +} +void Evaluation::EvaluateGLSL(EvaluationStage& evaluationStage, EvaluationInfo& evaluationInfo) +{ + const Input& input = evaluationStage.mInput; + + if (!evaluationInfo.uiPass) + evaluationStage.mTarget->bindAsTarget(); + unsigned int program = mEvaluatorPerNodeType[evaluationStage.mNodeType].mGLSLProgram; + const int blendOps[] = { evaluationStage.mBlendingSrc, evaluationStage.mBlendingDst }; + unsigned int blend[] = { GL_ONE, GL_ZERO }; + + + for (int i = 0; i < 2; i++) + { + if (blendOps[i] < BLEND_LAST) + blend[i] = GLBlends[blendOps[i]]; + } + + evaluationInfo.targetIndex = 0; + memcpy(evaluationInfo.inputIndices, input.mInputs, sizeof(evaluationInfo.inputIndices)); + evaluationInfo.forcedDirty = evaluationStage.mbForceEval ? 1 : 0; + SetMouseInfos(evaluationInfo, evaluationStage); + //evaluationInfo.uiPass = 1; + glBindBuffer(GL_UNIFORM_BUFFER, mEvaluationStateGLSLBuffer); + glBufferData(GL_UNIFORM_BUFFER, sizeof(EvaluationInfo), &evaluationInfo, GL_DYNAMIC_DRAW); + glBindBuffer(GL_UNIFORM_BUFFER, 0); + - evaluation.mTarget->bindAsTarget(); - unsigned int program = mEvaluatorPerNodeType[evaluation.mNodeType].mGLSLProgram; + glEnable(GL_BLEND); + glBlendFunc(blend[0], blend[1]); glUseProgram(program); - glBindBufferBase(GL_UNIFORM_BUFFER, 1, evaluation.mParametersBuffer); + glBindBufferBase(GL_UNIFORM_BUFFER, 1, evaluationStage.mParametersBuffer); + glBindBufferBase(GL_UNIFORM_BUFFER, 2, mEvaluationStateGLSLBuffer); int samplerIndex = 0; for (size_t inputIndex = 0; inputIndex < 8; inputIndex++) @@ -662,26 +753,29 @@ void Evaluation::EvaluateGLSL(EvaluationStage& evaluation) continue; } //assert(!mEvaluations[targetIndex].mbDirty); - glBindTexture(GL_TEXTURE_2D, mEvaluations[targetIndex].mTarget->mGLTexID); + glBindTexture(GL_TEXTURE_2D, mEvaluationStages[targetIndex].mTarget->mGLTexID); - const InputSampler& inputSampler = evaluation.mInputSamplers[inputIndex]; + const InputSampler& inputSampler = evaluationStage.mInputSamplers[inputIndex]; TexParam(filter[inputSampler.mFilterMin], filter[inputSampler.mFilterMag], wrap[inputSampler.mWrapU], wrap[inputSampler.mWrapV], GL_TEXTURE_2D); } // mFSQuad.Render(); + glDisable(GL_BLEND); } -void Evaluation::EvaluateC(EvaluationStage& evaluation, size_t index) +void Evaluation::EvaluateC(EvaluationStage& evaluationStage, size_t index, EvaluationInfo& evaluationInfo) { - const Input& input = evaluation.mInput; + const Input& input = evaluationStage.mInput; - EvaluationInfo evaluationInfo; + //EvaluationInfo evaluationInfo; evaluationInfo.targetIndex = int(index); memcpy(evaluationInfo.inputIndices, input.mInputs, sizeof(evaluationInfo.inputIndices)); - evaluationInfo.forcedDirty = evaluation.mbForceEval ? 1 : 0; + SetMouseInfos(evaluationInfo, evaluationStage); + //evaluationInfo.forcedDirty = evaluation.mbForceEval ? 1 : 0; + //evaluationInfo.uiPass = false; try // todo: find a better solution than a try catch { - mEvaluatorPerNodeType[evaluation.mNodeType].mCFunction(evaluation.mParameters, &evaluationInfo); + mEvaluatorPerNodeType[evaluationStage.mNodeType].mCFunction(evaluationStage.mParameters, &evaluationInfo); } catch (...) { @@ -691,7 +785,7 @@ void Evaluation::EvaluateC(EvaluationStage& evaluation, size_t index) void Evaluation::EvaluationStage::Clear() { - if (mEvaluationType == EVALUATOR_GLSL) + if (mEvaluationMask&EvaluationGLSL) glDeleteBuffers(1, &mParametersBuffer); //gEvaluation.UnreferenceRenderTarget(&mTarget); } @@ -739,3 +833,150 @@ unsigned int Evaluation::GetTexture(const std::string& filename) mSynchronousTextureCache[filename] = textureId; return textureId; } + + +void Evaluation::NodeUICallBack(const ImDrawList* parent_list, const ImDrawCmd* cmd) +{ + // Backup GL state + GLenum last_active_texture; glGetIntegerv(GL_ACTIVE_TEXTURE, (GLint*)&last_active_texture); + glActiveTexture(GL_TEXTURE0); + GLint last_program; glGetIntegerv(GL_CURRENT_PROGRAM, &last_program); + GLint last_texture; glGetIntegerv(GL_TEXTURE_BINDING_2D, &last_texture); +#ifdef GL_SAMPLER_BINDING + GLint last_sampler; glGetIntegerv(GL_SAMPLER_BINDING, &last_sampler); +#endif + GLint last_array_buffer; glGetIntegerv(GL_ARRAY_BUFFER_BINDING, &last_array_buffer); + GLint last_vertex_array; glGetIntegerv(GL_VERTEX_ARRAY_BINDING, &last_vertex_array); +#ifdef GL_POLYGON_MODE + GLint last_polygon_mode[2]; glGetIntegerv(GL_POLYGON_MODE, last_polygon_mode); +#endif + GLint last_viewport[4]; glGetIntegerv(GL_VIEWPORT, last_viewport); + GLint last_scissor_box[4]; glGetIntegerv(GL_SCISSOR_BOX, last_scissor_box); + GLenum last_blend_src_rgb; glGetIntegerv(GL_BLEND_SRC_RGB, (GLint*)&last_blend_src_rgb); + GLenum last_blend_dst_rgb; glGetIntegerv(GL_BLEND_DST_RGB, (GLint*)&last_blend_dst_rgb); + GLenum last_blend_src_alpha; glGetIntegerv(GL_BLEND_SRC_ALPHA, (GLint*)&last_blend_src_alpha); + GLenum last_blend_dst_alpha; glGetIntegerv(GL_BLEND_DST_ALPHA, (GLint*)&last_blend_dst_alpha); + GLenum last_blend_equation_rgb; glGetIntegerv(GL_BLEND_EQUATION_RGB, (GLint*)&last_blend_equation_rgb); + GLenum last_blend_equation_alpha; glGetIntegerv(GL_BLEND_EQUATION_ALPHA, (GLint*)&last_blend_equation_alpha); + GLboolean last_enable_blend = glIsEnabled(GL_BLEND); + GLboolean last_enable_cull_face = glIsEnabled(GL_CULL_FACE); + GLboolean last_enable_depth_test = glIsEnabled(GL_DEPTH_TEST); + GLboolean last_enable_scissor_test = glIsEnabled(GL_SCISSOR_TEST); + ImGuiIO& io = ImGui::GetIO(); + + const ImogenDrawCallback& cb = mCallbackRects[intptr_t(cmd->UserCallbackData)]; + + ImRect cbRect = cb.mRect; + float h = cbRect.Max.y - cbRect.Min.y; + float w = cbRect.Max.x - cbRect.Min.x; + glViewport(int(cbRect.Min.x), int(io.DisplaySize.y - cbRect.Max.y), int(w), int(h)); + + cbRect.Min.x = ImMax(cbRect.Min.x, cmd->ClipRect.x); + + glScissor(int(cbRect.Min.x), int(io.DisplaySize.y - cbRect.Max.y), int(cbRect.Max.x - cbRect.Min.x), int(cbRect.Max.y - cbRect.Min.y)); + glEnable(GL_SCISSOR_TEST); + + if (cb.mNodeIndex == -1) + { + // processing UI + static int waitingShader = 0; + if (waitingShader == 0) + { + static const char *waitinShaderScript = { +"#ifdef VERTEX_SHADER\n" +"layout(location = 0)in vec2 inUV;\n" +"out vec2 vUV;\n" +"void main(){ gl_Position = vec4(inUV.xy*2.0 - 1.0,0.5,1.0); vUV = inUV; }\n" +"#endif\n" +"#ifdef FRAGMENT_SHADER\n" +"uniform float time;\n" +"#define PI 3.1415926\n" +"layout(location = 0) out vec4 outPixDiffuse;\n" +"in vec2 vUV;\n" +"void main() {\n" +//"float time = 0.0;\n" +"vec2 npos = vUV-0.5;\n" +"float mixcontrol = sin(time);\n" +"if (mixcontrol < 0.0) { mixcontrol = pow(1.0 - abs(mixcontrol), 3.0) - 1.0; } \n" +"else { mixcontrol = 1.0 - pow(1.0 - mixcontrol, 3.0); }\n" +"mixcontrol = mixcontrol * 0.5 + 0.5;\n" +"float c1time = time * 2.0;\n" +"vec2 c1pos = npos + vec2(sin(c1time), cos(c1time)) * 0.24;\n" +"float c1size = 0.05;\n" +"float c2time = time * 2.0 + PI;\n" +"vec2 c2pos = npos + vec2(sin(c2time), cos(c2time)) * 0.24;\n" +"float c2size = 0.05;\n" +"c1pos = mix(npos, c1pos, mixcontrol);\n" +"c1size = mix(0.18, c1size, mixcontrol);\n" +"c2pos = mix(npos, c2pos, 1.0 - mixcontrol);\n" +"c2size = mix(0.18, c2size, 1.0 - mixcontrol);\n" +"vec3 colorbg = vec3(32.0 / 255.0);\n" +"vec3 colorfg = vec3(250.0 / 255.0);\n" +"vec4 col = vec4(colorbg, 1.0);\n" +"if (length(npos) < 0.3) { col = vec4(colorfg, 1.0); }\n" +"if (length(c1pos) < c1size) { col = vec4(colorbg, 1.0); }\n" +"if (length(c2pos) < c2size) { col = vec4(colorbg, 1.0); }\n" +"outPixDiffuse = vec4(col.rgb, 1.0); }\n" +"#endif\n" + }; + waitingShader = LoadShader(waitinShaderScript, "WaitingShader"); + } + glUseProgram(waitingShader); + static float gGlobalTime = 0.f; + gGlobalTime += 0.03f; + glUniform1f(glGetUniformLocation(waitingShader, "time"), gGlobalTime); + mFSQuad.Render(); + } + else + { + EvaluationInfo evaluationInfo; + evaluationInfo.forcedDirty = 1; + evaluationInfo.uiPass = 1; + gEvaluation.PerformEvaluationForNode(cb.mNodeIndex, int(w), int(h), true, evaluationInfo); + } + + // Restore modified GL state + glUseProgram(last_program); + glBindTexture(GL_TEXTURE_2D, last_texture); +#ifdef GL_SAMPLER_BINDING + glBindSampler(0, last_sampler); +#endif + glActiveTexture(last_active_texture); + glBindVertexArray(last_vertex_array); + glBindBuffer(GL_ARRAY_BUFFER, last_array_buffer); + glBlendEquationSeparate(last_blend_equation_rgb, last_blend_equation_alpha); + glBlendFuncSeparate(last_blend_src_rgb, last_blend_dst_rgb, last_blend_src_alpha, last_blend_dst_alpha); + if (last_enable_blend) glEnable(GL_BLEND); else glDisable(GL_BLEND); + if (last_enable_cull_face) glEnable(GL_CULL_FACE); else glDisable(GL_CULL_FACE); + if (last_enable_depth_test) glEnable(GL_DEPTH_TEST); else glDisable(GL_DEPTH_TEST); + if (last_enable_scissor_test) glEnable(GL_SCISSOR_TEST); else glDisable(GL_SCISSOR_TEST); +#ifdef GL_POLYGON_MODE + glPolygonMode(GL_FRONT_AND_BACK, (GLenum)last_polygon_mode[0]); +#endif + glViewport(last_viewport[0], last_viewport[1], (GLsizei)last_viewport[2], (GLsizei)last_viewport[3]); + glScissor(last_scissor_box[0], last_scissor_box[1], (GLsizei)last_scissor_box[2], (GLsizei)last_scissor_box[3]); + glBindFramebuffer(GL_FRAMEBUFFER, 0); +} + +int Evaluation::GetEvaluationSize(int target, int *imageWidth, int *imageHeight) +{ + if (target < 0 || target >= gEvaluation.mEvaluationStages.size()) + return EVAL_ERR; + RenderTarget* renderTarget = gEvaluation.mEvaluationStages[target].mTarget; + if (!renderTarget) + return EVAL_ERR; + *imageWidth = renderTarget->mWidth; + *imageHeight = renderTarget->mHeight; + return EVAL_OK; +} + +int Evaluation::SetEvaluationSize(int target, int imageWidth, int imageHeight) +{ + if (target < 0 || target >= gEvaluation.mEvaluationStages.size()) + return EVAL_ERR; + RenderTarget* renderTarget = gEvaluation.mEvaluationStages[target].mTarget; + if (!renderTarget) + return EVAL_ERR; + renderTarget->initBuffer(imageWidth, imageHeight, false); + return EVAL_OK; +} \ No newline at end of file diff --git a/src/Imogen.cpp b/src/Imogen.cpp index 92d5ebf0..dc5b4160 100644 --- a/src/Imogen.cpp +++ b/src/Imogen.cpp @@ -36,6 +36,9 @@ #include "TaskScheduler.h" #include "stb_image.h" #include "tinydir.h" +#include "stb_image.h" +unsigned char *stbi_write_png_to_mem(unsigned char *pixels, int stride_bytes, int x, int y, int n, int *out_len); +extern Evaluation gEvaluation; extern enki::TaskScheduler g_TS; @@ -103,6 +106,17 @@ struct ImguiAppLog } }; +std::vector mCallbackRects; +void InitCallbackRects() +{ + mCallbackRects.clear(); +} +size_t AddNodeUICallbackRect(const ImRect& rect, size_t nodeIndex) +{ + mCallbackRects.push_back({ rect, nodeIndex }); + return mCallbackRects.size() - 1; +} + ImguiAppLog *ImguiAppLog::Log = NULL; ImguiAppLog logger; TextEditor editor; @@ -168,17 +182,35 @@ void NodeEdit(TileNodeEditGraphDelegate& nodeGraphDelegate, Evaluation& evaluati if (ImGui::CollapsingHeader("Preview", 0, ImGuiTreeNodeFlags_DefaultOpen)) { ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); - ImGui::ImageButton(ImTextureID((selNode != -1) ? evaluation.GetEvaluationTexture(selNode) : 0), ImVec2(256, 256)); + ImGui::PushStyleColor(ImGuiCol_ButtonActive, 0xFF000000); + ImGui::PushStyleColor(ImGuiCol_ButtonHovered, 0xFF000000); + ImGui::PushStyleColor(ImGuiCol_Button, 0xFF000000); + float w = ImGui::GetWindowContentRegionWidth(); + int imageWidth(1), imageHeight(1); + Evaluation::GetEvaluationSize(selNode, &imageWidth, &imageHeight); + float ratio = float(imageHeight)/float(imageWidth); + float h = w * ratio; + ImVec2 p = ImGui::GetCursorPos() + ImGui::GetWindowPos(); + + ImGui::ImageButton((ImTextureID)(int64_t)((selNode != -1) ? evaluation.GetEvaluationTexture(selNode) : 0), ImVec2(w, h), ImVec2(0, 1), ImVec2(1, 0)); + ImGui::PopStyleColor(3); ImGui::PopStyleVar(1); ImRect rc(ImGui::GetItemRectMin(), ImGui::GetItemRectMax()); + + if (selNode != -1 && nodeGraphDelegate.NodeHasUI(selNode)) + { + ImDrawList* draw_list = ImGui::GetWindowDrawList(); + draw_list->AddCallback((ImDrawCallback)(Evaluation::NodeUICallBack), (void*)(AddNodeUICallbackRect(rc, selNode))); + } if (rc.Contains(io.MousePos)) { - if (io.MouseDown[0]) - { - ImVec2 ratio((io.MousePos.x - rc.Min.x) / rc.GetSize().x, (io.MousePos.y - rc.Min.y) / rc.GetSize().y); - ImVec2 deltaRatio((io.MouseDelta.x) / rc.GetSize().x, (io.MouseDelta.y) / rc.GetSize().y); - nodeGraphDelegate.SetMouseRatios(ratio.x, ratio.y, deltaRatio.x, deltaRatio.y); - } + ImVec2 ratio((io.MousePos.x - rc.Min.x) / rc.GetSize().x, (io.MousePos.y - rc.Min.y) / rc.GetSize().y); + ImVec2 deltaRatio((io.MouseDelta.x) / rc.GetSize().x, (io.MouseDelta.y) / rc.GetSize().y); + nodeGraphDelegate.SetMouse(ratio.x, ratio.y, deltaRatio.x, deltaRatio.y, io.MouseDown[0], io.MouseDown[1]); + } + else + { + nodeGraphDelegate.SetMouse(-9999.f, -9999.f, -9999.f, -9999.f, false, false); } } @@ -188,7 +220,6 @@ void NodeEdit(TileNodeEditGraphDelegate& nodeGraphDelegate, Evaluation& evaluati nodeGraphDelegate.EditNode(); } - template struct SortedResource { SortedResource() {} @@ -237,57 +268,111 @@ std::string GetName(const std::string &name) struct PinnedTaskUploadImage : enki::IPinnedTask { - PinnedTaskUploadImage(Image image, std::pair identifier) + PinnedTaskUploadImage(Image image, ASyncId identifier, bool isThumbnail) : enki::IPinnedTask(0) // set pinned thread to 0 , mImage(image) , mIdentifier(identifier) + , mbIsThumbnail(isThumbnail) { } + virtual void Execute() { unsigned int textureId = Evaluation::UploadImage(&mImage); - if (library.mMaterials.size() > mIdentifier.first && library.mMaterials[mIdentifier.first].mRuntimeUniqueId == mIdentifier.second) + if (mbIsThumbnail) { - library.mMaterials[mIdentifier.first].mThumbnailTextureId = textureId; + Material* material = library.Get(mIdentifier); + if (material) + material->mThumbnailTextureId = textureId; } else { - // find identifier - for (auto& material : library.mMaterials) + TileNodeEditGraphDelegate::ImogenNode *node = TileNodeEditGraphDelegate::GetInstance()->Get(mIdentifier); + if (node) { - if (material.mRuntimeUniqueId == mIdentifier.second) - { - material.mThumbnailTextureId = textureId; - break; - } + node->mbProcessing = false; + Evaluation::SetEvaluationImage(int(node->mEvaluationTarget), &mImage); + gEvaluation.SetEvaluationParameters(node->mEvaluationTarget, node->mParameters, node->mParametersSize); } + Evaluation::FreeImage(&mImage); } - } Image mImage; - std::pair mIdentifier; + ASyncId mIdentifier; + bool mbIsThumbnail; }; struct DecodeThumbnailTaskSet : enki::ITaskSet { - DecodeThumbnailTaskSet(std::vector *src, std::pair identifier) : enki::ITaskSet(), mIdentifier(identifier), mSrc(src) + DecodeThumbnailTaskSet(std::vector *src, ASyncId identifier) : enki::ITaskSet(), mIdentifier(identifier), mSrc(src) { } virtual void ExecuteRange(enki::TaskSetPartition range, uint32_t threadnum) { Image image; - unsigned char *data = stbi_load_from_memory(mSrc->data(), mSrc->size(), &image.width, &image.height, &image.components, 0); + unsigned char *data = stbi_load_from_memory(mSrc->data(), int(mSrc->size()), &image.width, &image.height, &image.components, 0); if (data) { image.bits = data; - PinnedTaskUploadImage uploadTexTask(image, mIdentifier); + PinnedTaskUploadImage uploadTexTask(image, mIdentifier, true); g_TS.AddPinnedTask(&uploadTexTask); g_TS.WaitforTask(&uploadTexTask); Evaluation::FreeImage(&image); } delete this; } - std::pair mIdentifier; + ASyncId mIdentifier; + std::vector *mSrc; +}; + +struct EncodeImageTaskSet : enki::ITaskSet +{ + EncodeImageTaskSet(Image image, ASyncId materialIdentifier, ASyncId nodeIdentifier) : enki::ITaskSet(), mMaterialIdentifier(materialIdentifier), mNodeIdentifier(nodeIdentifier), mImage(image) + { + } + virtual void ExecuteRange(enki::TaskSetPartition range, uint32_t threadnum) + { + int outlen; + unsigned char *bits = stbi_write_png_to_mem((unsigned char*)mImage.bits, mImage.width * mImage.components, mImage.width, mImage.height, mImage.components, &outlen); + if (bits) + { + Material *material = library.Get(mMaterialIdentifier); + if (material) + { + MaterialNode *node = material->Get(mNodeIdentifier); + if (node) + { + node->mImage.resize(outlen); + memcpy(node->mImage.data(), bits, outlen); + } + } + } + delete this; + } + ASyncId mMaterialIdentifier; + ASyncId mNodeIdentifier; + Image mImage; +}; + +struct DecodeImageTaskSet : enki::ITaskSet +{ + DecodeImageTaskSet(std::vector *src, ASyncId identifier) : enki::ITaskSet(), mIdentifier(identifier), mSrc(src) + { + } + virtual void ExecuteRange(enki::TaskSetPartition range, uint32_t threadnum) + { + Image image; + unsigned char *data = stbi_load_from_memory(mSrc->data(), int(mSrc->size()), &image.width, &image.height, &image.components, 0); + if (data) + { + image.bits = data; + PinnedTaskUploadImage uploadTexTask(image, mIdentifier, false); + g_TS.AddPinnedTask(&uploadTexTask); + g_TS.WaitforTask(&uploadTexTask); + } + delete this; + } + ASyncId mIdentifier; std::vector *mSrc; }; @@ -304,7 +389,7 @@ template bool TVRes(std::vector& res, const cha SortedResource::ComputeSortedResources(res, sortedResources); unsigned int defaultTextureId = evaluation.GetTexture("Stock/thumbnail-icon.png"); - for (const auto& sortedRes : sortedResources) //unsigned int i = 0; i < res.size(); i++) + for (const auto& sortedRes : sortedResources) { unsigned int indexInRes = sortedRes.mIndex; bool selected = ((selection >> 16) == index) && (selection & 0xFFFF) == (int)indexInRes; @@ -339,7 +424,7 @@ template bool TVRes(std::vector& res, const cha resource.mThumbnailTextureId = defaultTextureId; g_TS.AddTaskSetToPipe(new DecodeThumbnailTaskSet(&resource.mThumbnail, std::make_pair(indexInRes,resource.mRuntimeUniqueId))); } - ImGui::Image(ImTextureID(resource.mThumbnailTextureId), ImVec2(64, 64)); + ImGui::Image((ImTextureID)(int64_t)(resource.mThumbnailTextureId), ImVec2(64, 64), ImVec2(0, 1), ImVec2(1, 0)); bool clicked = ImGui::IsItemClicked(); ImGui::SameLine(); ImGui::TreeNodeEx(GetName(resource.mName).c_str(), node_flags); @@ -361,7 +446,6 @@ template bool TVRes(std::vector& res, const cha inline void GuiString(const char*label, std::string* str, int stringId, bool multi) { - //static int guiStringId = 47414; ImGui::PushID(stringId); char eventStr[2048]; strcpy(eventStr, str->c_str()); @@ -389,10 +473,23 @@ void ValidateMaterial(Library& library, TileNodeEditGraphDelegate &nodeGraphDele return; Material& material = library.mMaterials[materialIndex]; material.mMaterialNodes.resize(nodeGraphDelegate.mNodes.size()); + + int metaNodeCount; + const TileNodeEditGraphDelegate::MetaNode* metaNodes = nodeGraphDelegate.GetMetaNodes(metaNodeCount); + for (size_t i = 0; i < nodeGraphDelegate.mNodes.size(); i++) { TileNodeEditGraphDelegate::ImogenNode srcNode = nodeGraphDelegate.mNodes[i]; MaterialNode &dstNode = material.mMaterialNodes[i]; + dstNode.mRuntimeUniqueId = GetRuntimeId(); + if (metaNodes[srcNode.mType].mbSaveTexture) + { + Image image; + if (Evaluation::GetEvaluationImage(int(i), &image) == EVAL_OK) + { + g_TS.AddTaskSetToPipe(new EncodeImageTaskSet(image, std::make_pair(materialIndex, material.mRuntimeUniqueId), std::make_pair(i, dstNode.mRuntimeUniqueId))); + } + } dstNode.mType = uint32_t(srcNode.mType); dstNode.mParameters.resize(srcNode.mParametersSize); @@ -436,6 +533,24 @@ void LibraryEdit(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, evaluation.Clear(); NodeGraphClear(); } + ImGui::SameLine(); + if (ImGui::Button("Import")) + { + nfdchar_t *outPath = NULL; + nfdresult_t result = NFD_OpenDialog("imogen", NULL, &outPath); + + if (result == NFD_OKAY) + { + Library tempLibrary; + LoadLib(&tempLibrary, outPath); + for (auto& material : tempLibrary.mMaterials) + { + Log("Importing Graph %s\n", material.mName.c_str()); + library.mMaterials.push_back(material); + } + free(outPath); + } + } ImGui::BeginChild("TV", ImVec2(250, -1)); if (TVRes(library.mMaterials, "Materials", selectedMaterial, 0, evaluation)) { @@ -451,11 +566,21 @@ void LibraryEdit(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, nodeGraphDelegate.Clear(); evaluation.Clear(); NodeGraphClear(); + + int metaNodeCount; + const TileNodeEditGraphDelegate::MetaNode* metaNodes = nodeGraphDelegate.GetMetaNodes(metaNodeCount); + Material& material = library.mMaterials[selectedMaterial]; for (size_t i = 0; i < material.mMaterialNodes.size(); i++) { MaterialNode& node = material.mMaterialNodes[i]; NodeGraphAddNode(&nodeGraphDelegate, node.mType, node.mParameters.data(), node.mPosX, node.mPosY); + if (!node.mImage.empty()) + { + TileNodeEditGraphDelegate::ImogenNode& lastNode = nodeGraphDelegate.mNodes.back(); + lastNode.mbProcessing = true; + g_TS.AddTaskSetToPipe(new DecodeImageTaskSet(&node.mImage, std::make_pair(i, lastNode.mRuntimeUniqueId))); + } } for (size_t i = 0; i < material.mMaterialConnections.size(); i++) { @@ -489,7 +614,7 @@ void Imogen::Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate ImGuiIO& io = ImGui::GetIO(); ImGui::SetNextWindowPos(ImVec2(0, 0)); ImGui::SetNextWindowSize(io.DisplaySize); - if (ImGui::Begin("Imogen", 0, ImGuiWindowFlags_NoTitleBar|ImGuiWindowFlags_NoResize|ImGuiWindowFlags_NoMove|ImGuiWindowFlags_NoScrollbar|ImGuiWindowFlags_NoScrollWithMouse|ImGuiWindowFlags_NoCollapse|ImGuiWindowFlags_AlwaysAutoResize)) + if (ImGui::Begin("Imogen", 0, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_AlwaysAutoResize)) { ImGui::BeginDockspace(); @@ -504,6 +629,22 @@ void Imogen::Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate { nodeGraphDelegate.DoForce(); } + ImGui::SameLine(); + if (ImGui::Button("Save Graph")) + { + nfdchar_t *outPath = NULL; + nfdresult_t result = NFD_SaveDialog("imogen", NULL, &outPath); + + if (result == NFD_OKAY) + { + Library tempLibrary; + Material& material = library.mMaterials[selectedMaterial]; + tempLibrary.mMaterials.push_back(material); + SaveLib(&tempLibrary, outPath); + Log("Graph %s saved at path %s\n", material.mName.c_str(), outPath); + free(outPath); + } + } ImGui::PopItemWidth(); NodeGraph(&nodeGraphDelegate, selectedMaterial != -1); } @@ -540,7 +681,10 @@ void Imogen::Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate ImGui::EndDockspace(); ImGui::End(); } +} +void Imogen::ValidateCurrentMaterial(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate) +{ ValidateMaterial(library, nodeGraphDelegate, selectedMaterial); } diff --git a/src/Imogen.h b/src/Imogen.h index d28e7aef..b643de81 100644 --- a/src/Imogen.h +++ b/src/Imogen.h @@ -27,11 +27,15 @@ #include #include +#include "imgui.h" +#include "imgui_internal.h" + struct TileNodeEditGraphDelegate; struct Evaluation; class TextEditor; struct Library; + enum EVALUATOR_TYPE { EVALUATOR_GLSL, @@ -53,6 +57,7 @@ struct Imogen void Finish(); void Show(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate, Evaluation& evaluation); + void ValidateCurrentMaterial(Library& library, TileNodeEditGraphDelegate &nodeGraphDelegate); void DiscoverNodes(const char *extension, const char *directory, EVALUATOR_TYPE evaluatorType, std::vector& files); std::vector mEvaluatorFiles; @@ -63,3 +68,11 @@ struct Imogen }; void DebugLogText(const char *szText); +struct ImogenDrawCallback +{ + ImRect mRect; + size_t mNodeIndex; +}; +extern std::vector mCallbackRects; +void InitCallbackRects(); +size_t AddNodeUICallbackRect(const ImRect& rect, size_t nodeIndex); diff --git a/src/Library.cpp b/src/Library.cpp index 3440eca2..95243f79 100644 --- a/src/Library.cpp +++ b/src/Library.cpp @@ -30,6 +30,7 @@ enum : uint32_t v_initial, v_materialComment, v_thumbnail, + v_nodeImage, v_lastVersion }; #define ADD(_fieldAdded, _fieldName) if (dataVersion >= _fieldAdded){ Ser(_fieldName); } @@ -115,6 +116,7 @@ template struct Serialize ADD(v_initial, materialNode->mPosY); ADD(v_initial, materialNode->mInputSamplers); ADD(v_initial, materialNode->mParameters); + ADD(v_nodeImage, materialNode->mImage); } void Ser(MaterialConnection *materialConnection) { @@ -158,6 +160,10 @@ void LoadLib(Library *library, const char *szFilename) { material.mThumbnailTextureId = 0; material.mRuntimeUniqueId = GetRuntimeId(); + for (auto& node : material.mMaterialNodes) + { + node.mRuntimeUniqueId = GetRuntimeId(); + } } } diff --git a/src/Library.h b/src/Library.h index bee6bcc2..cd6e5511 100644 --- a/src/Library.h +++ b/src/Library.h @@ -29,6 +29,31 @@ #include #include #include + +// used to retrieve structure in library. left is index. right is uniqueId +// if item at index doesn't correspond to uniqueid, then a search is done +// based on the unique id +typedef std::pair ASyncId; +template T* GetByAsyncId(ASyncId id, std::vector& items) +{ + if (items.size() > id.first && items[id.first].mRuntimeUniqueId == id.second) + { + return &items[id.first]; + } + else + { + // find identifier + for (auto& item : items) + { + if (item.mRuntimeUniqueId == id.second) + { + return &item; + } + } + } + return NULL; +} + struct InputSampler { InputSampler() : mWrapU(0), mWrapV(0), mFilterMin(0), mFilterMag(0) @@ -46,6 +71,10 @@ struct MaterialNode uint32_t mPosY; std::vector mInputSamplers; std::vector mParameters; + std::vector mImage; + + // runtime + unsigned int mRuntimeUniqueId; }; struct MaterialConnection { @@ -62,6 +91,8 @@ struct Material std::vector mMaterialConnections; std::vector mThumbnail; + MaterialNode* Get(ASyncId id) { return GetByAsyncId(id, mMaterialNodes); } + //run time unsigned int mThumbnailTextureId; unsigned int mRuntimeUniqueId; @@ -69,10 +100,12 @@ struct Material struct Library { std::vector mMaterials; + Material* Get(ASyncId id) { return GetByAsyncId(id, mMaterials); } }; void LoadLib(Library *library, const char *szFilename); void SaveLib(Library *library, const char *szFilename); unsigned int GetRuntimeId(); -extern Library library; \ No newline at end of file +extern Library library; + diff --git a/src/Nodes.cpp b/src/Nodes.cpp index d0c1ef85..31062609 100644 --- a/src/Nodes.cpp +++ b/src/Nodes.cpp @@ -30,7 +30,7 @@ #include #include #include - +#include "Evaluation.h" UndoRedoHandler undoRedoHandler; int Log(const char *szFormat, ...); @@ -221,6 +221,8 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) int node_hovered_in_scene = -1; bool open_context_menu = false; + static const float factor = 1.0f; + ImGui::BeginGroup(); const float NODE_SLOT_RADIUS = 8.0f; @@ -244,7 +246,7 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) if (show_grid) { ImU32 GRID_COLOR = IM_COL32(100, 100, 100, 40); - float GRID_SZ = 64.0f; + float GRID_SZ = 64.0f * factor; ImVec2 win_pos = ImGui::GetCursorScreenPos(); ImVec2 canvas_sz = ImGui::GetWindowSize(); for (float x = fmodf(scrolling.x, GRID_SZ); x < canvas_sz.x; x += GRID_SZ) @@ -274,7 +276,7 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) Node* node_out = &nodes[link->OutputIdx]; ImVec2 p1 = offset + node_inp->GetOutputSlotPos(link->InputSlot); ImVec2 p2 = offset + node_out->GetInputSlotPos(link->OutputSlot); - draw_list->AddBezierCurve(p1, p1 + ImVec2(+50, 0), p2 + ImVec2(-50, 0), p2, IM_COL32(200, 200, 150, 255), 3.0f); + draw_list->AddBezierCurve(p1 * factor, (p1 + ImVec2(+50, 0)) * factor, (p2 + ImVec2(-50, 0)) * factor, p2 * factor, IM_COL32(200, 200, 150, 255), 3.0f * factor); } // edit node @@ -291,7 +293,7 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) { Node* node = &nodes[node_idx]; ImGui::PushID(node_idx); - ImVec2 node_rect_min = offset + node->Pos; + ImVec2 node_rect_min = (offset + node->Pos) * factor; // Display node contents first draw_list->ChannelsSetCurrent(1); // Foreground @@ -299,7 +301,7 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) ImGui::SetCursorScreenPos(node_rect_min + NODE_WINDOW_PADDING); - ImGui::InvisibleButton("canvas", ImVec2(100, 100)); + ImGui::InvisibleButton("canvas", ImVec2(100, 100) * factor); bool node_moving_active = ImGui::IsItemActive(); // must be called right after creating the control we want to be able to move // Save the size of what we have emitted and whether any of the widgets are being used @@ -344,7 +346,25 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) ImVec2 imgSize = node_rect_max + ImVec2(-5, -5) - imgPos; float imgSizeComp = std::min(imgSize.x, imgSize.y); - draw_list->AddImage(ImTextureID(delegate->GetNodeTexture(size_t(node_idx))), imgPos, imgPos + ImVec2(imgSizeComp, imgSizeComp)); + ImVec2 imgPosMax = imgPos + ImVec2(imgSizeComp, imgSizeComp); + draw_list->AddRectFilled(imgPos, imgPosMax, 0xFF000000); + + ImVec2 imageSize = delegate->GetEvaluationSize(node_idx); + float imageRatio = imageSize.y / imageSize.x; + ImVec2 quadSize = imgPosMax - imgPos; + ImVec2 marge(0.f, 0.f); + if (imageRatio > 1.f) + { + marge.x = (quadSize.x - quadSize.y / imageRatio) * 0.5f; + } + else + { + marge.y = (quadSize.y - quadSize.y * imageRatio) * 0.5f; + } + draw_list->AddImage((ImTextureID)(int64_t)(delegate->GetNodeTexture(size_t(node_idx))), imgPos + marge, imgPosMax - marge, ImVec2(0, 1), ImVec2(1, 0)); + if (delegate->NodeIsProcesing(node_idx)) + draw_list->AddCallback((ImDrawCallback)(Evaluation::NodeUICallBack), (void*)(AddNodeUICallbackRect(ImRect(imgPos + marge, imgPosMax - marge), -1))); + // draw/use inputs/outputs bool hoverSlot = false; for (int i = 0; i < 2; i++) @@ -353,7 +373,8 @@ void NodeGraph(NodeGraphDelegate *delegate, bool enabled) GetConCount(metaNodes, node->mType, slotCount[0], slotCount[1]); for (int slot_idx = 0; slot_idx < slotCount[i]; slot_idx++) { - ImVec2 p = offset + (i? node->GetOutputSlotPos(slot_idx):node->GetInputSlotPos(slot_idx)); + ImVec2 p = offset + (i? node->GetOutputSlotPos(slot_idx):node->GetInputSlotPos(slot_idx)) * factor; + bool overCon = !hoverSlot && (node_hovered_in_scene == -1 || editingNode) && Distance(p, ImGui::GetIO().MousePos) < NODE_SLOT_RADIUS*2.f; const NodeGraphDelegate::Con *con = i ? metaNodes[node->mType].mOutputs:metaNodes[node->mType].mInputs; diff --git a/src/Nodes.h b/src/Nodes.h index a1a54423..6fa1b24a 100644 --- a/src/Nodes.h +++ b/src/Nodes.h @@ -47,10 +47,12 @@ struct NodeGraphDelegate virtual void AddNode(size_t type) = 0; // node deleted virtual void DeleteNode(size_t index) = 0; - + virtual ImVec2 GetEvaluationSize(size_t index) = 0; virtual void DoForce() = 0; virtual unsigned char *GetParamBlock(size_t index, size_t& paramBlockSize) = 0; virtual void SetParamBlock(size_t index, unsigned char* paramBlock) = 0; + virtual bool NodeHasUI(size_t nodeIndex) = 0; + virtual bool NodeIsProcesing(size_t nodeIndex) = 0; static const int MaxCon = 32; struct Con { @@ -59,6 +61,7 @@ struct NodeGraphDelegate float mRangeMinX, mRangeMaxX; float mRangeMinY, mRangeMaxY; bool mbRelative; + bool mbQuadSelect; const char* mEnumList; }; struct MetaNode @@ -69,6 +72,8 @@ struct NodeGraphDelegate Con mInputs[MaxCon]; Con mOutputs[MaxCon]; Con mParams[MaxCon]; + bool mbHasUI; + bool mbSaveTexture; }; virtual const MetaNode* GetMetaNodes(int &metaNodeCount) = 0; }; diff --git a/src/NodesDelegate.h b/src/NodesDelegate.h index d486b124..07c8fcb4 100644 --- a/src/NodesDelegate.h +++ b/src/NodesDelegate.h @@ -33,9 +33,9 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate { - TileNodeEditGraphDelegate(Evaluation& evaluation) : mEvaluation(evaluation) + TileNodeEditGraphDelegate(Evaluation& evaluation) : mEvaluation(evaluation), mbMouseDragging(false) { - mCategoriesCount = 7; + mCategoriesCount = 8; static const char *categories[] = { "Transform", "Generator", @@ -43,9 +43,13 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "Blend", "Filter", "Noise", - "File"}; + "File", + "Paint"}; mCategories = categories; + assert(!mInstance); + mInstance = this; } + Evaluation& mEvaluation; struct ImogenNode @@ -54,7 +58,9 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate size_t mEvaluationTarget; void *mParameters; size_t mParametersSize; + unsigned int mRuntimeUniqueId; std::vector mInputSamplers; + bool mbProcessing; }; std::vector mNodes; @@ -117,7 +123,8 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate size_t index = mNodes.size(); ImogenNode node; node.mEvaluationTarget = mEvaluation.AddEvaluation(type, metaNodes[type].mName); - + node.mRuntimeUniqueId = GetRuntimeId(); + node.mbProcessing = false; node.mType = type; size_t paramsSize = ComputeParamMemSize(type); node.mParameters = malloc(paramsSize); @@ -164,11 +171,12 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate static const uint32_t hcBlend = IM_COL32(200, 150, 150, 255); static const uint32_t hcFilter = IM_COL32(200, 200, 150, 255); static const uint32_t hcNoise = IM_COL32(150, 250, 150, 255); + static const uint32_t hcPaint = IM_COL32(100, 250, 180, 255); - metaNodeCount = 26; + metaNodeCount = 29; - static const MetaNode metaNodes[26] = { + static const MetaNode metaNodes[29] = { { "Circle", hcGenerator, 1 @@ -266,7 +274,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "Blend", hcBlend, 3 ,{ { "", (int)Con_Float4 },{ "", (int)Con_Float4 } } ,{ { "", (int)Con_Float4 } } - ,{ {"A", (int)Con_Float4 },{ "B", (int)Con_Float4 },{ "Operation", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, "Add\0Multiply\0Darken\0Lighten\0Average\0Screen\0Color Burn\0Color Dodge\0Soft Light\0Subtract\0Difference\0Inverse Difference\0Exclusion\0" } } + ,{ {"A", (int)Con_Float4 },{ "B", (int)Con_Float4 },{ "Operation", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, "Add\0Multiply\0Darken\0Lighten\0Average\0Screen\0Color Burn\0Color Dodge\0Soft Light\0Subtract\0Difference\0Inverse Difference\0Exclusion\0" } } } , @@ -315,7 +323,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "NormalMapBlending", hcBlend, 3 ,{ { "", (int)Con_Float4 },{ "", (int)Con_Float4 } } ,{ { "Out", (int)Con_Float4 } } - ,{ { "Technique", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, "RNM\0Partial Derivatives\0Whiteout\0UDN\0Unity\0Linear\0Overlay\0" } } + ,{ { "Technique", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, "RNM\0Partial Derivatives\0Whiteout\0UDN\0Unity\0Linear\0Overlay\0" } } } , @@ -340,7 +348,7 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "PolarCoords", hcTransform, 0 ,{ { "", (int)Con_Float4 } } ,{ { "", (int)Con_Float4 } } - ,{ { "Type", (int)Con_Enum, 0.f,0.f,0.f,0.f,false,"Linear to polar\0Polar to linear\0" } } + ,{ { "Type", (int)Con_Enum, 0.f,0.f,0.f,0.f,false, false, "Linear to polar\0Polar to linear\0" } } } , @@ -364,9 +372,10 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate "ImageWrite", hcFilter, 6 ,{ { "", (int)Con_Float4 } } ,{ } - ,{ { "File name", (int)Con_FilenameWrite },{ "Format", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, "JPEG\0PNG\0TGA\0BMP\0HDR\0"},{ "Quality", (int)Con_Int } - ,{ "Width", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, " 256\0 512\0 1024\0 2048\0 4096\0" } - ,{ "Height", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, " 256\0 512\0 1024\0 2048\0 4096\0" } + ,{ { "File name", (int)Con_FilenameWrite },{ "Format", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, "JPEG\0PNG\0TGA\0BMP\0HDR\0"} + ,{ "Quality", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, " 0 .. Best\0 1\0 2\0 3\0 4\0 5 .. Medium\0 6\0 7\0 8\0 9 .. Lowest\0" } + ,{ "Width", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, " 256\0 512\0 1024\0 2048\0 4096\0" } + ,{ "Height", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, " 256\0 512\0 1024\0 2048\0 4096\0" } ,{ "Export", (int)Con_ForceEvaluate } } } @@ -377,6 +386,31 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate ,{} ,{ { "Make", (int)Con_ForceEvaluate } } } + + , + { + "Paint2D", hcPaint, 7 + ,{ { "Brush", (int)Con_Float4 } } + ,{ { "", (int)Con_Float4 } } + ,{ { "Size", (int)Con_Enum, 0.f,0.f,0.f,0.f, false, false, " 256\0 512\0 1024\0 2048\0 4096\0" } } + , true + , true + } + , + { + "Swirl", hcTransform, 0 + ,{ { "", (int)Con_Float4 } } + ,{ { "", (int)Con_Float4 } } + ,{ { "Angles", (int)Con_Angle2 } } + } + , + { + "Crop", hcTransform, 0 + ,{ { "", (int)Con_Float4 } } + ,{ { "", (int)Con_Float4 } } + ,{ { "Quad", (int)Con_Float4, 0.f,1.f,0.f,1.f, false, true } } + , true + } }; return metaNodes; @@ -532,12 +566,15 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate paramBuffer += ComputeParamMemSize(param->mType); } - //ImGui::End(); if (dirty) mEvaluation.SetEvaluationParameters(node.mEvaluationTarget, node.mParameters, node.mParametersSize); if (forceEval) - mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true); - + { + EvaluationInfo evaluationInfo; + evaluationInfo.forcedDirty = 1; + evaluationInfo.uiPass = 0; + mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true, evaluationInfo); + } } virtual void DoForce() @@ -565,7 +602,12 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate } } if (forceEval) - mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true); + { + EvaluationInfo evaluationInfo; + evaluationInfo.forcedDirty = 1; + evaluationInfo.uiPass = 0; + mEvaluation.PerformEvaluationForNode(node.mEvaluationTarget, 256, 256, true, evaluationInfo); + } } } @@ -576,44 +618,72 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate } template static inline T nmin(T lhs, T rhs) { return lhs >= rhs ? rhs : lhs; } - void SetMouseRatios(float rx, float ry, float dx, float dy) + + bool mbMouseDragging; + void SetMouse(float rx, float ry, float dx, float dy, bool lButDown, bool rButDown) { + if (mSelectedNodeIndex == -1) + return; + + if (!lButDown) + mbMouseDragging = false; int metaNodeCount; const MetaNode* metaNodes = GetMetaNodes(metaNodeCount); size_t res = 0; const NodeGraphDelegate::Con * param = metaNodes[mNodes[mSelectedNodeIndex].mType].mParams; unsigned char *paramBuffer = (unsigned char*)mNodes[mSelectedNodeIndex].mParameters; - for (int i = 0; i < MaxCon; i++, param++) + if (lButDown) { - if (!param->mName) - break; - float *paramFlt = (float*)paramBuffer; - if (param->mRangeMinX != 0.f || param->mRangeMaxX != 0.f) + for (int i = 0; i < MaxCon; i++, param++) { - if (param->mbRelative) - { - paramFlt[0] += ImLerp(param->mRangeMinX, param->mRangeMaxX, dx); - paramFlt[0] = fmodf(paramFlt[0], fabsf(param->mRangeMaxX - param->mRangeMinX)) + nmin(param->mRangeMinX, param->mRangeMaxX); - } - else + if (!param->mName) + break; + float *paramFlt = (float*)paramBuffer; + + if (param->mbQuadSelect && param->mType == Con_Float4) { - paramFlt[0] = ImLerp(param->mRangeMinX, param->mRangeMaxX, rx); + if (!mbMouseDragging) + { + paramFlt[2] = paramFlt[0] = rx; + paramFlt[3] = paramFlt[1] = 1.f - ry; + mbMouseDragging = true; + } + else + { + paramFlt[2] = rx; + paramFlt[3] = 1.f - ry; + } + continue; } - } - if (param->mRangeMinY != 0.f || param->mRangeMaxY != 0.f) - { - if (param->mbRelative) + + if (param->mRangeMinX != 0.f || param->mRangeMaxX != 0.f) { - paramFlt[1] += ImLerp(param->mRangeMinY, param->mRangeMaxY, dy); - paramFlt[1] = fmodf(paramFlt[1], fabsf(param->mRangeMaxY - param->mRangeMinY)) + nmin(param->mRangeMinY, param->mRangeMaxY); + if (param->mbRelative) + { + paramFlt[0] += ImLerp(param->mRangeMinX, param->mRangeMaxX, dx); + paramFlt[0] = fmodf(paramFlt[0], fabsf(param->mRangeMaxX - param->mRangeMinX)) + nmin(param->mRangeMinX, param->mRangeMaxX); + } + else + { + paramFlt[0] = ImLerp(param->mRangeMinX, param->mRangeMaxX, rx); + } } - else + if (param->mRangeMinY != 0.f || param->mRangeMaxY != 0.f) { - paramFlt[1] = ImLerp(param->mRangeMinY, param->mRangeMaxY, ry); + if (param->mbRelative) + { + paramFlt[1] += ImLerp(param->mRangeMinY, param->mRangeMaxY, dy); + paramFlt[1] = fmodf(paramFlt[1], fabsf(param->mRangeMaxY - param->mRangeMinY)) + nmin(param->mRangeMinY, param->mRangeMaxY); + } + else + { + paramFlt[1] = ImLerp(param->mRangeMinY, param->mRangeMaxY, ry); + } } + paramBuffer += ComputeParamMemSize(param->mType); } - paramBuffer += ComputeParamMemSize(param->mType); } + mEvaluation.SetMouse(mSelectedNodeIndex, rx, ry, lButDown, rButDown); mEvaluation.SetEvaluationParameters(mNodes[mSelectedNodeIndex].mEvaluationTarget, mNodes[mSelectedNodeIndex].mParameters, mNodes[mSelectedNodeIndex].mParametersSize); } @@ -631,6 +701,17 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate } return res; } + bool NodeHasUI(size_t nodeIndex) + { + int metaNodeCount; + const MetaNode* metaNodes = GetMetaNodes(metaNodeCount); + return metaNodes[mNodes[nodeIndex].mType].mbHasUI; + } + virtual bool NodeIsProcesing(size_t nodeIndex) + { + return mNodes[nodeIndex].mbProcessing; + } + size_t ComputeParamMemSize(int paramType) { size_t res = 0; @@ -675,5 +756,16 @@ struct TileNodeEditGraphDelegate : public NodeGraphDelegate { mEvaluation.SetEvaluationOrder(nodeOrderList); } + + virtual ImVec2 GetEvaluationSize(size_t nodeIndex) + { + int imageWidth(1), imageHeight(1); + mEvaluation.GetEvaluationSize(int(nodeIndex), &imageWidth, &imageHeight); + return ImVec2(float(imageWidth), float(imageHeight)); + } + static TileNodeEditGraphDelegate *GetInstance() { return mInstance; } + ImogenNode* Get(ASyncId id) { return GetByAsyncId(id, mNodes); } +protected: + static TileNodeEditGraphDelegate *mInstance; }; diff --git a/src/main.cpp b/src/main.cpp index 57ff9ed3..d22e0ac6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -34,6 +34,10 @@ #include "Evaluation.h" #include "Imogen.h" #include "TaskScheduler.h" +#include "stb_image.h" +#include "stb_image_write.h" + +TileNodeEditGraphDelegate *TileNodeEditGraphDelegate::mInstance = NULL; int Log(const char *szFormat, ...) { @@ -54,6 +58,57 @@ int Log(const char *szFormat, ...) return 0; } + +void APIENTRY openglCallbackFunction(GLenum /*source*/, + GLenum type, + GLuint id, + GLenum severity, + GLsizei /*length*/, + const GLchar* message, + const void* /*userParam*/) +{ + const char *typeStr = ""; + const char *severityStr = ""; + + switch (type) + { + case GL_DEBUG_TYPE_ERROR: + typeStr = "ERROR"; + break; + case GL_DEBUG_TYPE_DEPRECATED_BEHAVIOR: + typeStr = "DEPRECATED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_UNDEFINED_BEHAVIOR: + typeStr = "UNDEFINED_BEHAVIOR"; + break; + case GL_DEBUG_TYPE_PORTABILITY: + typeStr = "PORTABILITY"; + break; + case GL_DEBUG_TYPE_PERFORMANCE: + typeStr = "PERFORMANCE"; + break; + case GL_DEBUG_TYPE_OTHER: + typeStr = "OTHER"; + // skip + return; + break; + } + + switch (severity) + { + case GL_DEBUG_SEVERITY_LOW: + severityStr = "LOW"; + break; + case GL_DEBUG_SEVERITY_MEDIUM: + severityStr = "MEDIUM"; + break; + case GL_DEBUG_SEVERITY_HIGH: + severityStr = "HIGH"; + break; + } + Log("GL Debug (%s - %s) %s \n", typeStr, severityStr, message); +} + Evaluation gEvaluation; Library library; Imogen imogen; @@ -62,7 +117,8 @@ enki::TaskScheduler g_TS; int main(int, char**) { g_TS.Initialize(); - + stbi_set_flip_vertically_on_load(1); + stbi_flip_vertically_on_write(1); // Setup SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER) != 0) { @@ -81,7 +137,7 @@ int main(int, char**) #else // GL 3.0 + GLSL 130 const char* glsl_version = "#version 130"; - SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, 0); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); @@ -120,6 +176,18 @@ int main(int, char**) ImGui_ImplSDL2_InitForOpenGL(window, gl_context); ImGui_ImplOpenGL3_Init(glsl_version); + // opengl debug + glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS); + glDebugMessageCallback((GLDEBUGPROCARB)openglCallbackFunction, NULL); + GLuint unusedIds = 0; + glDebugMessageControl(GL_DONT_CARE, + GL_DONT_CARE, + GL_DONT_CARE, + 0, + &unusedIds, + true); + + // Setup style ImGui::StyleColorsDark(); @@ -152,6 +220,7 @@ int main(int, char**) ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(window); ImGui::NewFrame(); + InitCallbackRects(); imogen.Show(library, nodeGraphDelegate, gEvaluation); @@ -169,6 +238,7 @@ int main(int, char**) SDL_GL_SwapWindow(window); } + imogen.ValidateCurrentMaterial(library, nodeGraphDelegate); SaveLib(&library, libraryFilename); gEvaluation.Finish();