Added Post Processing, Improved UI a lot, Made File Explorer look nicer, Fixed up Raycast Selection, Added Placeholder Playmode Button, Added cameras, Organized Create menu in Inspector, Added outlines to selected objects, added view output for viewing cameras while in Playmode area.
This commit is contained in:
31
Resources/Shaders/postfx_blur_frag.glsl
Normal file
31
Resources/Shaders/postfx_blur_frag.glsl
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
#version 330 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
uniform sampler2D image;
|
||||||
|
uniform vec2 texelSize;
|
||||||
|
uniform bool horizontal = true;
|
||||||
|
uniform float sigma = 3.0;
|
||||||
|
uniform int radius = 5;
|
||||||
|
|
||||||
|
const float PI = 3.14159265359;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float twoSigma2 = 2.0 * sigma * sigma;
|
||||||
|
vec2 dir = horizontal ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||||
|
|
||||||
|
vec3 result = texture(image, TexCoord).rgb;
|
||||||
|
float weightSum = 1.0;
|
||||||
|
|
||||||
|
for (int i = 1; i <= radius; ++i) {
|
||||||
|
float w = exp(-(float(i * i)) / twoSigma2);
|
||||||
|
vec2 offset = dir * texelSize * float(i);
|
||||||
|
result += texture(image, TexCoord + offset).rgb * w;
|
||||||
|
result += texture(image, TexCoord - offset).rgb * w;
|
||||||
|
weightSum += 2.0 * w;
|
||||||
|
}
|
||||||
|
|
||||||
|
result /= weightSum;
|
||||||
|
FragColor = vec4(result, 1.0);
|
||||||
|
}
|
||||||
15
Resources/Shaders/postfx_bright_frag.glsl
Normal file
15
Resources/Shaders/postfx_bright_frag.glsl
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
#version 330 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
uniform sampler2D sceneTex;
|
||||||
|
uniform float threshold = 1.0;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 c = texture(sceneTex, TexCoord).rgb;
|
||||||
|
float luma = dot(c, vec3(0.2125, 0.7154, 0.0721));
|
||||||
|
float bright = max(luma - threshold, 0.0);
|
||||||
|
vec3 masked = c * step(0.0, bright);
|
||||||
|
FragColor = vec4(masked, 1.0);
|
||||||
|
}
|
||||||
49
Resources/Shaders/postfx_frag.glsl
Normal file
49
Resources/Shaders/postfx_frag.glsl
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
#version 330 core
|
||||||
|
out vec4 FragColor;
|
||||||
|
|
||||||
|
in vec2 TexCoord;
|
||||||
|
|
||||||
|
uniform sampler2D sceneTex;
|
||||||
|
uniform sampler2D bloomTex;
|
||||||
|
uniform sampler2D historyTex;
|
||||||
|
|
||||||
|
uniform bool enableBloom = false;
|
||||||
|
uniform float bloomIntensity = 0.8;
|
||||||
|
|
||||||
|
uniform bool enableColorAdjust = false;
|
||||||
|
uniform float exposure = 0.0; // EV stops
|
||||||
|
uniform float contrast = 1.0;
|
||||||
|
uniform float saturation = 1.0;
|
||||||
|
uniform vec3 colorFilter = vec3(1.0);
|
||||||
|
|
||||||
|
uniform bool enableMotionBlur = false;
|
||||||
|
uniform bool hasHistory = false;
|
||||||
|
uniform float motionBlurStrength = 0.15;
|
||||||
|
|
||||||
|
vec3 applyColorAdjust(vec3 color) {
|
||||||
|
if (enableColorAdjust) {
|
||||||
|
color *= exp2(exposure);
|
||||||
|
color = (color - 0.5) * contrast + 0.5;
|
||||||
|
float luma = dot(color, vec3(0.299, 0.587, 0.114));
|
||||||
|
color = mix(vec3(luma), color, saturation);
|
||||||
|
color *= colorFilter;
|
||||||
|
}
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 color = texture(sceneTex, TexCoord).rgb;
|
||||||
|
if (enableBloom) {
|
||||||
|
vec3 glow = texture(bloomTex, TexCoord).rgb;
|
||||||
|
color += glow * bloomIntensity;
|
||||||
|
}
|
||||||
|
|
||||||
|
color = applyColorAdjust(color);
|
||||||
|
|
||||||
|
if (enableMotionBlur && hasHistory) {
|
||||||
|
vec3 history = texture(historyTex, TexCoord).rgb;
|
||||||
|
color = mix(color, history, clamp(motionBlurStrength, 0.0, 0.98));
|
||||||
|
}
|
||||||
|
|
||||||
|
FragColor = vec4(color, 1.0);
|
||||||
|
}
|
||||||
10
Resources/Shaders/postfx_vert.glsl
Normal file
10
Resources/Shaders/postfx_vert.glsl
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
#version 330 core
|
||||||
|
layout (location = 0) in vec2 aPos;
|
||||||
|
layout (location = 1) in vec2 aTexCoord;
|
||||||
|
|
||||||
|
out vec2 TexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TexCoord = aTexCoord;
|
||||||
|
gl_Position = vec4(aPos, 0.0, 1.0);
|
||||||
|
}
|
||||||
144
Resources/ThirdParty/BloomFilter/BloomFilter.pde
vendored
Normal file
144
Resources/ThirdParty/BloomFilter/BloomFilter.pde
vendored
Normal file
@@ -0,0 +1,144 @@
|
|||||||
|
import controlP5.*;
|
||||||
|
|
||||||
|
ControlP5 cp5;
|
||||||
|
|
||||||
|
PGraphics canvas;
|
||||||
|
|
||||||
|
PGraphics brightPass;
|
||||||
|
PGraphics horizontalBlurPass;
|
||||||
|
PGraphics verticalBlurPass;
|
||||||
|
|
||||||
|
PShader bloomFilter;
|
||||||
|
PShader blurFilter;
|
||||||
|
|
||||||
|
int angle = 0;
|
||||||
|
|
||||||
|
final int surfaceWidth = 250;
|
||||||
|
final int surfaceHeight = 250;
|
||||||
|
|
||||||
|
float luminanceFilter = 0.8;
|
||||||
|
float blurSize = 20;
|
||||||
|
float sigma = 12;
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
size(1000, 250, P3D);
|
||||||
|
|
||||||
|
addUI();
|
||||||
|
|
||||||
|
canvas = createGraphics(surfaceWidth, surfaceHeight, P3D);
|
||||||
|
|
||||||
|
brightPass = createGraphics(surfaceWidth, surfaceHeight, P2D);
|
||||||
|
brightPass.noSmooth();
|
||||||
|
|
||||||
|
horizontalBlurPass = createGraphics(surfaceWidth, surfaceHeight, P2D);
|
||||||
|
horizontalBlurPass.noSmooth();
|
||||||
|
|
||||||
|
verticalBlurPass = createGraphics(surfaceWidth, surfaceHeight, P2D);
|
||||||
|
verticalBlurPass.noSmooth();
|
||||||
|
|
||||||
|
bloomFilter = loadShader("bloomFrag.glsl");
|
||||||
|
blurFilter = loadShader("blurFrag.glsl");
|
||||||
|
}
|
||||||
|
|
||||||
|
void draw()
|
||||||
|
{
|
||||||
|
background(0);
|
||||||
|
|
||||||
|
bloomFilter.set("brightPassThreshold", luminanceFilter);
|
||||||
|
|
||||||
|
blurFilter.set("blurSize", (int)blurSize);
|
||||||
|
blurFilter.set("sigma", sigma);
|
||||||
|
|
||||||
|
canvas.beginDraw();
|
||||||
|
render(canvas);
|
||||||
|
canvas.endDraw();
|
||||||
|
|
||||||
|
// bright pass
|
||||||
|
brightPass.beginDraw();
|
||||||
|
brightPass.shader(bloomFilter);
|
||||||
|
brightPass.image(canvas, 0, 0);
|
||||||
|
brightPass.endDraw();
|
||||||
|
|
||||||
|
// blur horizontal pass
|
||||||
|
horizontalBlurPass.beginDraw();
|
||||||
|
blurFilter.set("horizontalPass", 1);
|
||||||
|
horizontalBlurPass.shader(blurFilter);
|
||||||
|
horizontalBlurPass.image(brightPass, 0, 0);
|
||||||
|
horizontalBlurPass.endDraw();
|
||||||
|
|
||||||
|
// blur vertical pass
|
||||||
|
verticalBlurPass.beginDraw();
|
||||||
|
blurFilter.set("horizontalPass", 0);
|
||||||
|
verticalBlurPass.shader(blurFilter);
|
||||||
|
verticalBlurPass.image(horizontalBlurPass, 0, 0);
|
||||||
|
verticalBlurPass.endDraw();
|
||||||
|
|
||||||
|
// draw original
|
||||||
|
image(canvas.copy(), 0, 0);
|
||||||
|
text("Original", 20, height - 20);
|
||||||
|
|
||||||
|
// draw bright pass
|
||||||
|
image(brightPass, surfaceWidth, 0);
|
||||||
|
text("Bright Pass", surfaceWidth + 20, height - 20);
|
||||||
|
|
||||||
|
image(verticalBlurPass, (surfaceWidth * 2), 0);
|
||||||
|
text("Blur", (surfaceWidth * 2) + 20, height - 20);
|
||||||
|
|
||||||
|
// draw
|
||||||
|
image(canvas, (surfaceWidth * 3), 0);
|
||||||
|
blendMode(SCREEN);
|
||||||
|
image(verticalBlurPass, (surfaceWidth * 3), 0);
|
||||||
|
blendMode(BLEND);
|
||||||
|
text("Combined", (surfaceWidth * 3) + 20, height - 20);
|
||||||
|
|
||||||
|
// fps
|
||||||
|
fill(0, 255, 0);
|
||||||
|
text("FPS: " + frameRate, 20, 20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void render(PGraphics pg)
|
||||||
|
{
|
||||||
|
pg.background(0, 0);
|
||||||
|
pg.stroke(255, 0, 0);
|
||||||
|
|
||||||
|
for (int i = -1; i < 2; i++)
|
||||||
|
{
|
||||||
|
if (i == -1)
|
||||||
|
pg.fill(0, 255, 0);
|
||||||
|
else if (i == 0)
|
||||||
|
pg.fill(255);
|
||||||
|
else
|
||||||
|
pg.fill(0, 200, 200);
|
||||||
|
|
||||||
|
pg.pushMatrix();
|
||||||
|
// left-right, up-down, near-far
|
||||||
|
pg.translate(surfaceWidth / 2 + (i * 50), surfaceHeight / 2, 0);
|
||||||
|
pg.rotateX(radians(angle));
|
||||||
|
pg.rotateZ(radians(angle));
|
||||||
|
pg.box(30);
|
||||||
|
pg.popMatrix();
|
||||||
|
}
|
||||||
|
|
||||||
|
angle = ++angle % 360;
|
||||||
|
}
|
||||||
|
|
||||||
|
void addUI()
|
||||||
|
{
|
||||||
|
cp5 = new ControlP5(this);
|
||||||
|
|
||||||
|
cp5.addSlider("luminanceFilter")
|
||||||
|
.setPosition(200, 5)
|
||||||
|
.setRange(0, 1)
|
||||||
|
;
|
||||||
|
|
||||||
|
cp5.addSlider("blurSize")
|
||||||
|
.setPosition(400, 5)
|
||||||
|
.setRange(0, 100)
|
||||||
|
;
|
||||||
|
|
||||||
|
cp5.addSlider("sigma")
|
||||||
|
.setPosition(600, 5)
|
||||||
|
.setRange(1, 100)
|
||||||
|
;
|
||||||
|
}
|
||||||
23
Resources/ThirdParty/BloomFilter/bloomFrag.glsl
vendored
Normal file
23
Resources/ThirdParty/BloomFilter/bloomFrag.glsl
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
uniform float brightPassThreshold;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 luminanceVector = vec3(0.2125, 0.7154, 0.0721);
|
||||||
|
vec4 c = texture2D(texture, vertTexCoord.st) * vertColor;
|
||||||
|
|
||||||
|
float luminance = dot(luminanceVector, c.xyz);
|
||||||
|
luminance = max(0.0, luminance - brightPassThreshold);
|
||||||
|
c.xyz *= sign(luminance);
|
||||||
|
c.a = 1.0;
|
||||||
|
|
||||||
|
gl_FragColor = c;
|
||||||
|
}
|
||||||
18
Resources/ThirdParty/BloomFilter/bloomVert.glsl
vendored
Normal file
18
Resources/ThirdParty/BloomFilter/bloomVert.glsl
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#define PROCESSING_TEXTURE_SHADER
|
||||||
|
|
||||||
|
uniform mat4 transform;
|
||||||
|
uniform mat4 texMatrix;
|
||||||
|
|
||||||
|
attribute vec4 vertex;
|
||||||
|
attribute vec4 color;
|
||||||
|
attribute vec2 texCoord;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = transform * vertex;
|
||||||
|
|
||||||
|
vertColor = color;
|
||||||
|
vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
|
||||||
|
}
|
||||||
59
Resources/ThirdParty/BloomFilter/blurFrag.glsl
vendored
Normal file
59
Resources/ThirdParty/BloomFilter/blurFrag.glsl
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Adapted from:
|
||||||
|
// <a href="http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html" target="_blank" rel="nofollow">http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html</a>
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PROCESSING_TEXTURE_SHADER
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
// The inverse of the texture dimensions along X and Y
|
||||||
|
uniform vec2 texOffset;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
uniform int blurSize;
|
||||||
|
uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
|
||||||
|
uniform float sigma; // The sigma value for the gaussian function: higher value means more blur
|
||||||
|
// A good value for 9x9 is around 3 to 5
|
||||||
|
// A good value for 7x7 is around 2.5 to 4
|
||||||
|
// A good value for 5x5 is around 2 to 3.5
|
||||||
|
// ... play around with this based on what you need <span class="Emoticon Emoticon1"><span>:)</span></span>
|
||||||
|
|
||||||
|
const float pi = 3.14159265;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float numBlurPixelsPerSide = float(blurSize / 2);
|
||||||
|
|
||||||
|
vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||||
|
|
||||||
|
// Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
|
||||||
|
vec3 incrementalGaussian;
|
||||||
|
incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
|
||||||
|
incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
|
||||||
|
incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
|
||||||
|
|
||||||
|
vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
float coefficientSum = 0.0;
|
||||||
|
|
||||||
|
// Take the central sample first...
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
|
||||||
|
coefficientSum += incrementalGaussian.x;
|
||||||
|
incrementalGaussian.xy *= incrementalGaussian.yz;
|
||||||
|
|
||||||
|
// Go through the remaining 8 vertical samples (4 on each side of the center)
|
||||||
|
for (float i = 1.0; i <= numBlurPixelsPerSide; i++) {
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st - i * texOffset *
|
||||||
|
blurMultiplyVec) * incrementalGaussian.x;
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st + i * texOffset *
|
||||||
|
blurMultiplyVec) * incrementalGaussian.x;
|
||||||
|
coefficientSum += 2.0 * incrementalGaussian.x;
|
||||||
|
incrementalGaussian.xy *= incrementalGaussian.yz;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = avgValue / coefficientSum;
|
||||||
|
}
|
||||||
18
Resources/ThirdParty/BloomFilter/blurVert.glsl
vendored
Normal file
18
Resources/ThirdParty/BloomFilter/blurVert.glsl
vendored
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
#define PROCESSING_TEXTURE_SHADER
|
||||||
|
|
||||||
|
uniform mat4 transform;
|
||||||
|
uniform mat4 texMatrix;
|
||||||
|
|
||||||
|
attribute vec4 vertex;
|
||||||
|
attribute vec4 color;
|
||||||
|
attribute vec2 texCoord;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = transform * vertex;
|
||||||
|
|
||||||
|
vertColor = color;
|
||||||
|
vertTexCoord = texMatrix * vec4(texCoord, 1.0, 1.0);
|
||||||
|
}
|
||||||
48
Resources/ThirdParty/MotionBlur/motionBlur_f.glsl
vendored
Normal file
48
Resources/ThirdParty/MotionBlur/motionBlur_f.glsl
vendored
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
#version 330 core
|
||||||
|
//Per-Object Motion Blur
|
||||||
|
|
||||||
|
uniform sampler2D uTexInput; // texture we're blurring
|
||||||
|
uniform sampler2D uTexVelocity; // velocity buffer
|
||||||
|
|
||||||
|
in vec2 texture_coordinate;
|
||||||
|
|
||||||
|
uniform float uVelocityScale; //currentFps / targetFps
|
||||||
|
/*
|
||||||
|
What's uVelocityScale? It's used to address the following problem:
|
||||||
|
if the framerate is very high, velocity will be very small as the
|
||||||
|
amount of motion in between frames will be low. Correspondingly, if
|
||||||
|
the framerate is very low the motion between frames will be high
|
||||||
|
and velocity will be much larger. This ties the blur size to the
|
||||||
|
framerate, which is technically correct if you equate framrate with
|
||||||
|
shutter speed, however is undesirable for realtime rendering where
|
||||||
|
the framerate can vary. To fix it we need to cancel out the framerate
|
||||||
|
*/
|
||||||
|
|
||||||
|
int MAX_SAMPLES = 16; //32
|
||||||
|
|
||||||
|
layout (location=0) out vec4 result;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
|
||||||
|
vec2 velocity = texture(uTexVelocity, texture_coordinate).rg * 2.0 - 1.0;
|
||||||
|
velocity *= uVelocityScale;
|
||||||
|
|
||||||
|
//get the size of on pixel (texel)
|
||||||
|
vec2 texelSize = 1.0 / vec2(textureSize(uTexInput, 0));
|
||||||
|
//mprove performance by adapting the number of samples according to the velocity
|
||||||
|
float speed = length(velocity / texelSize);
|
||||||
|
int nSamples = clamp(int(speed), 1, MAX_SAMPLES);
|
||||||
|
result = vec4(0.0);
|
||||||
|
|
||||||
|
velocity = normalize(velocity) * texelSize;
|
||||||
|
float hlim = float(-nSamples) * 0.5 + 0.5;
|
||||||
|
//the actual blurring of the current pixel
|
||||||
|
vec2 offset;
|
||||||
|
for (int i = 0; i < nSamples; ++i)
|
||||||
|
{
|
||||||
|
offset = velocity * (hlim + float(i));
|
||||||
|
result += texture(uTexInput, texture_coordinate + offset);
|
||||||
|
}
|
||||||
|
//average the result
|
||||||
|
result /= float(nSamples);
|
||||||
|
}
|
||||||
12
Resources/ThirdParty/MotionBlur/motionBlur_v.glsl
vendored
Normal file
12
Resources/ThirdParty/MotionBlur/motionBlur_v.glsl
vendored
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
|
||||||
|
#version 330 compatibility
|
||||||
|
|
||||||
|
out vec2 texture_coordinate;
|
||||||
|
|
||||||
|
void main()
|
||||||
|
{
|
||||||
|
//set the position of the current vertex
|
||||||
|
gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
|
||||||
|
|
||||||
|
texture_coordinate = vec2(gl_MultiTexCoord0);
|
||||||
|
}
|
||||||
59
Resources/ThirdParty/ProcessingPostFX/shader/blurFrag.glsl
vendored
Normal file
59
Resources/ThirdParty/ProcessingPostFX/shader/blurFrag.glsl
vendored
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
// Adapted from:
|
||||||
|
// <a href="http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html" target="_blank" rel="nofollow">http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html</a>
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PROCESSING_TEXTURE_SHADER
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
// The inverse of the texture dimensions along X and Y
|
||||||
|
uniform vec2 texOffset;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
uniform int blurSize;
|
||||||
|
uniform int horizontalPass; // 0 or 1 to indicate vertical or horizontal pass
|
||||||
|
uniform float sigma; // The sigma value for the gaussian function: higher value means more blur
|
||||||
|
// A good value for 9x9 is around 3 to 5
|
||||||
|
// A good value for 7x7 is around 2.5 to 4
|
||||||
|
// A good value for 5x5 is around 2 to 3.5
|
||||||
|
// ... play around with this based on what you need <span class="Emoticon Emoticon1"><span>:)</span></span>
|
||||||
|
|
||||||
|
const float pi = 3.14159265;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
float numBlurPixelsPerSide = float(blurSize / 2);
|
||||||
|
|
||||||
|
vec2 blurMultiplyVec = 0 < horizontalPass ? vec2(1.0, 0.0) : vec2(0.0, 1.0);
|
||||||
|
|
||||||
|
// Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889)
|
||||||
|
vec3 incrementalGaussian;
|
||||||
|
incrementalGaussian.x = 1.0 / (sqrt(2.0 * pi) * sigma);
|
||||||
|
incrementalGaussian.y = exp(-0.5 / (sigma * sigma));
|
||||||
|
incrementalGaussian.z = incrementalGaussian.y * incrementalGaussian.y;
|
||||||
|
|
||||||
|
vec4 avgValue = vec4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
float coefficientSum = 0.0;
|
||||||
|
|
||||||
|
// Take the central sample first...
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st) * incrementalGaussian.x;
|
||||||
|
coefficientSum += incrementalGaussian.x;
|
||||||
|
incrementalGaussian.xy *= incrementalGaussian.yz;
|
||||||
|
|
||||||
|
// Go through the remaining 8 vertical samples (4 on each side of the center)
|
||||||
|
for (float i = 1.0; i <= numBlurPixelsPerSide; i++) {
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st - i * texOffset *
|
||||||
|
blurMultiplyVec) * incrementalGaussian.x;
|
||||||
|
avgValue += texture2D(texture, vertTexCoord.st + i * texOffset *
|
||||||
|
blurMultiplyVec) * incrementalGaussian.x;
|
||||||
|
coefficientSum += 2.0 * incrementalGaussian.x;
|
||||||
|
incrementalGaussian.xy *= incrementalGaussian.yz;
|
||||||
|
}
|
||||||
|
|
||||||
|
gl_FragColor = avgValue / coefficientSum;
|
||||||
|
}
|
||||||
23
Resources/ThirdParty/ProcessingPostFX/shader/brightPassFrag.glsl
vendored
Normal file
23
Resources/ThirdParty/ProcessingPostFX/shader/brightPassFrag.glsl
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
uniform float brightPassThreshold;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec3 luminanceVector = vec3(0.2125, 0.7154, 0.0721);
|
||||||
|
vec4 c = texture2D(texture, vertTexCoord.st) * vertColor;
|
||||||
|
|
||||||
|
float luminance = dot(luminanceVector, c.xyz);
|
||||||
|
luminance = max(0.0, luminance - brightPassThreshold);
|
||||||
|
c.xyz *= sign(luminance);
|
||||||
|
c.a = 1.0;
|
||||||
|
|
||||||
|
gl_FragColor = c;
|
||||||
|
}
|
||||||
38
Resources/ThirdParty/ProcessingPostFX/shader/sobelFrag.glsl
vendored
Executable file
38
Resources/ThirdParty/ProcessingPostFX/shader/sobelFrag.glsl
vendored
Executable file
@@ -0,0 +1,38 @@
|
|||||||
|
// Adapted from:
|
||||||
|
// <a href="http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html" target="_blank" rel="nofollow">http://callumhay.blogspot.com/2010/09/gaussian-blur-shader-glsl.html</a>
|
||||||
|
|
||||||
|
#ifdef GL_ES
|
||||||
|
precision mediump float;
|
||||||
|
precision mediump int;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define PROCESSING_TEXTURE_SHADER
|
||||||
|
|
||||||
|
uniform sampler2D texture;
|
||||||
|
|
||||||
|
varying vec4 vertColor;
|
||||||
|
varying vec4 vertTexCoord;
|
||||||
|
|
||||||
|
uniform vec2 resolution;
|
||||||
|
|
||||||
|
void main(void) {
|
||||||
|
float x = 1.0 / resolution.x;
|
||||||
|
float y = 1.0 / resolution.y;
|
||||||
|
vec4 horizEdge = vec4( 0.0 );
|
||||||
|
horizEdge -= texture2D( texture, vec2( vertTexCoord.x - x, vertTexCoord.y - y ) ) * 1.0;
|
||||||
|
horizEdge -= texture2D( texture, vec2( vertTexCoord.x - x, vertTexCoord.y ) ) * 2.0;
|
||||||
|
horizEdge -= texture2D( texture, vec2( vertTexCoord.x - x, vertTexCoord.y + y ) ) * 1.0;
|
||||||
|
horizEdge += texture2D( texture, vec2( vertTexCoord.x + x, vertTexCoord.y - y ) ) * 1.0;
|
||||||
|
horizEdge += texture2D( texture, vec2( vertTexCoord.x + x, vertTexCoord.y ) ) * 2.0;
|
||||||
|
horizEdge += texture2D( texture, vec2( vertTexCoord.x + x, vertTexCoord.y + y ) ) * 1.0;
|
||||||
|
vec4 vertEdge = vec4( 0.0 );
|
||||||
|
vertEdge -= texture2D( texture, vec2( vertTexCoord.x - x, vertTexCoord.y - y ) ) * 1.0;
|
||||||
|
vertEdge -= texture2D( texture, vec2( vertTexCoord.x , vertTexCoord.y - y ) ) * 2.0;
|
||||||
|
vertEdge -= texture2D( texture, vec2( vertTexCoord.x + x, vertTexCoord.y - y ) ) * 1.0;
|
||||||
|
vertEdge += texture2D( texture, vec2( vertTexCoord.x - x, vertTexCoord.y + y ) ) * 1.0;
|
||||||
|
vertEdge += texture2D( texture, vec2( vertTexCoord.x , vertTexCoord.y + y ) ) * 2.0;
|
||||||
|
vertEdge += texture2D( texture, vec2( vertTexCoord.x + x, vertTexCoord.y + y ) ) * 1.0;
|
||||||
|
vec3 edge = sqrt((horizEdge.rgb * horizEdge.rgb) + (vertEdge.rgb * vertEdge.rgb));
|
||||||
|
|
||||||
|
gl_FragColor = vec4(edge, texture2D(texture, vertTexCoord.xy).a);
|
||||||
|
}
|
||||||
@@ -14,25 +14,25 @@ Size=500,250
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][DockSpace]
|
[Window][DockSpace]
|
||||||
Pos=0,21
|
Pos=0,23
|
||||||
Size=1920,985
|
Size=1920,983
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Viewport]
|
[Window][Viewport]
|
||||||
Pos=306,42
|
Pos=306,46
|
||||||
Size=1265,741
|
Size=1265,737
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000002,0
|
DockId=0x00000002,0
|
||||||
|
|
||||||
[Window][Hierarchy]
|
[Window][Hierarchy]
|
||||||
Pos=0,42
|
Pos=0,46
|
||||||
Size=304,741
|
Size=304,737
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,0
|
DockId=0x00000001,0
|
||||||
|
|
||||||
[Window][Inspector]
|
[Window][Inspector]
|
||||||
Pos=1573,42
|
Pos=1573,46
|
||||||
Size=347,964
|
Size=347,960
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000008,0
|
DockId=0x00000008,0
|
||||||
|
|
||||||
@@ -60,25 +60,31 @@ Size=1000,800
|
|||||||
Collapsed=0
|
Collapsed=0
|
||||||
|
|
||||||
[Window][Camera]
|
[Window][Camera]
|
||||||
Pos=0,42
|
Pos=0,46
|
||||||
Size=304,741
|
Size=304,737
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000001,1
|
DockId=0x00000001,1
|
||||||
|
|
||||||
[Window][Environment]
|
[Window][Environment]
|
||||||
Pos=1573,42
|
Pos=1573,46
|
||||||
Size=347,964
|
Size=347,960
|
||||||
Collapsed=0
|
Collapsed=0
|
||||||
DockId=0x00000008,1
|
DockId=0x00000008,1
|
||||||
|
|
||||||
|
[Window][Project Manager]
|
||||||
|
Pos=787,785
|
||||||
|
Size=784,221
|
||||||
|
Collapsed=0
|
||||||
|
DockId=0x00000006,1
|
||||||
|
|
||||||
[Docking][Data]
|
[Docking][Data]
|
||||||
DockSpace ID=0xD71539A0 Window=0x3DA2F1DE Pos=0,42 Size=1920,964 Split=X
|
DockSpace ID=0xD71539A0 Window=0x3DA2F1DE Pos=0,46 Size=1920,960 Split=X
|
||||||
DockNode ID=0x00000007 Parent=0xD71539A0 SizeRef=1509,1015 Split=Y
|
DockNode ID=0x00000007 Parent=0xD71539A0 SizeRef=1509,1015 Split=Y
|
||||||
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=1858,792 Split=X
|
DockNode ID=0x00000003 Parent=0x00000007 SizeRef=1858,792 Split=X
|
||||||
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=304,758 Selected=0xBABDAE5E
|
DockNode ID=0x00000001 Parent=0x00000003 SizeRef=304,758 Selected=0xBABDAE5E
|
||||||
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=694,758 CentralNode=1 Selected=0xC450F867
|
DockNode ID=0x00000002 Parent=0x00000003 SizeRef=694,758 CentralNode=1 Selected=0xC450F867
|
||||||
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1858,221 Split=X Selected=0xEA83D666
|
DockNode ID=0x00000004 Parent=0x00000007 SizeRef=1858,221 Split=X Selected=0xEA83D666
|
||||||
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=929,221 Selected=0xEA83D666
|
DockNode ID=0x00000005 Parent=0x00000004 SizeRef=929,221 Selected=0xEA83D666
|
||||||
DockNode ID=0x00000006 Parent=0x00000004 SizeRef=927,221 Selected=0x9C21DE82
|
DockNode ID=0x00000006 Parent=0x00000004 SizeRef=927,221 Selected=0xDA0DCE3C
|
||||||
DockNode ID=0x00000008 Parent=0xD71539A0 SizeRef=347,1015 Selected=0x36DC96AB
|
DockNode ID=0x00000008 Parent=0xD71539A0 SizeRef=347,1015 Selected=0x36DC96AB
|
||||||
|
|
||||||
|
|||||||
114
src/EditorUI.cpp
114
src/EditorUI.cpp
@@ -188,71 +188,87 @@ void applyModernTheme() {
|
|||||||
ImGuiStyle& style = ImGui::GetStyle();
|
ImGuiStyle& style = ImGui::GetStyle();
|
||||||
ImVec4* colors = style.Colors;
|
ImVec4* colors = style.Colors;
|
||||||
|
|
||||||
colors[ImGuiCol_WindowBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
ImVec4 slate = ImVec4(0.09f, 0.10f, 0.12f, 1.00f);
|
||||||
colors[ImGuiCol_ChildBg] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
ImVec4 panel = ImVec4(0.11f, 0.12f, 0.14f, 1.00f);
|
||||||
colors[ImGuiCol_PopupBg] = ImVec4(0.12f, 0.12f, 0.14f, 0.98f);
|
ImVec4 overlay = ImVec4(0.07f, 0.08f, 0.10f, 0.98f);
|
||||||
|
ImVec4 accent = ImVec4(0.33f, 0.63f, 0.98f, 1.00f);
|
||||||
|
ImVec4 accentMuted = ImVec4(0.25f, 0.46f, 0.78f, 1.00f);
|
||||||
|
ImVec4 highlight = ImVec4(0.18f, 0.23f, 0.30f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_Header] = ImVec4(0.20f, 0.20f, 0.24f, 1.00f);
|
colors[ImGuiCol_Text] = ImVec4(0.87f, 0.89f, 0.92f, 1.00f);
|
||||||
colors[ImGuiCol_HeaderHovered] = ImVec4(0.28f, 0.28f, 0.32f, 1.00f);
|
colors[ImGuiCol_TextDisabled] = ImVec4(0.52f, 0.56f, 0.62f, 1.00f);
|
||||||
colors[ImGuiCol_HeaderActive] = ImVec4(0.24f, 0.24f, 0.28f, 1.00f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Button] = ImVec4(0.22f, 0.22f, 0.26f, 1.00f);
|
colors[ImGuiCol_WindowBg] = panel;
|
||||||
colors[ImGuiCol_ButtonHovered] = ImVec4(0.30f, 0.30f, 0.36f, 1.00f);
|
colors[ImGuiCol_ChildBg] = ImVec4(0.10f, 0.11f, 0.13f, 1.00f);
|
||||||
colors[ImGuiCol_ButtonActive] = ImVec4(0.26f, 0.26f, 0.30f, 1.00f);
|
colors[ImGuiCol_PopupBg] = overlay;
|
||||||
|
colors[ImGuiCol_Border] = ImVec4(0.21f, 0.24f, 0.28f, 0.60f);
|
||||||
|
colors[ImGuiCol_BorderShadow] = ImVec4(0, 0, 0, 0);
|
||||||
|
colors[ImGuiCol_MenuBarBg] = ImVec4(0.06f, 0.07f, 0.08f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_FrameBg] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f);
|
colors[ImGuiCol_Header] = highlight;
|
||||||
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.18f, 0.18f, 0.22f, 1.00f);
|
colors[ImGuiCol_HeaderHovered] = ImVec4(0.22f, 0.28f, 0.36f, 1.00f);
|
||||||
colors[ImGuiCol_FrameBgActive] = ImVec4(0.22f, 0.22f, 0.26f, 1.00f);
|
colors[ImGuiCol_HeaderActive] = ImVec4(0.24f, 0.44f, 0.72f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_TitleBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
|
colors[ImGuiCol_Button] = ImVec4(0.17f, 0.19f, 0.22f, 1.00f);
|
||||||
colors[ImGuiCol_TitleBgActive] = ImVec4(0.12f, 0.12f, 0.14f, 1.00f);
|
colors[ImGuiCol_ButtonHovered] = ImVec4(0.23f, 0.27f, 0.33f, 1.00f);
|
||||||
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.06f, 0.06f, 0.08f, 1.00f);
|
colors[ImGuiCol_ButtonActive] = ImVec4(0.26f, 0.36f, 0.46f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_Tab] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f);
|
colors[ImGuiCol_FrameBg] = ImVec4(0.14f, 0.15f, 0.18f, 1.00f);
|
||||||
colors[ImGuiCol_TabHovered] = ImVec4(0.24f, 0.24f, 0.28f, 1.00f);
|
colors[ImGuiCol_FrameBgHovered] = ImVec4(0.20f, 0.22f, 0.27f, 1.00f);
|
||||||
colors[ImGuiCol_TabActive] = ImVec4(0.20f, 0.20f, 0.24f, 1.00f);
|
colors[ImGuiCol_FrameBgActive] = ImVec4(0.26f, 0.32f, 0.41f, 1.00f);
|
||||||
colors[ImGuiCol_TabUnfocused] = ImVec4(0.10f, 0.10f, 0.12f, 1.00f);
|
|
||||||
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.14f, 0.14f, 0.16f, 1.00f);
|
|
||||||
|
|
||||||
colors[ImGuiCol_Separator] = ImVec4(0.20f, 0.20f, 0.24f, 1.00f);
|
colors[ImGuiCol_TitleBg] = ImVec4(0.06f, 0.07f, 0.08f, 1.00f);
|
||||||
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.30f, 0.30f, 0.36f, 1.00f);
|
colors[ImGuiCol_TitleBgActive] = ImVec4(0.10f, 0.12f, 0.14f, 1.00f);
|
||||||
colors[ImGuiCol_SeparatorActive] = ImVec4(0.40f, 0.40f, 0.48f, 1.00f);
|
colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.04f, 0.05f, 0.06f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
|
colors[ImGuiCol_Tab] = ImVec4(0.12f, 0.13f, 0.15f, 1.00f);
|
||||||
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.24f, 0.24f, 0.28f, 1.00f);
|
colors[ImGuiCol_TabHovered] = ImVec4(0.20f, 0.28f, 0.38f, 1.00f);
|
||||||
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.30f, 0.30f, 0.36f, 1.00f);
|
colors[ImGuiCol_TabActive] = ImVec4(0.16f, 0.19f, 0.23f, 1.00f);
|
||||||
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.36f, 0.36f, 0.42f, 1.00f);
|
colors[ImGuiCol_TabUnfocused] = ImVec4(0.09f, 0.10f, 0.12f, 1.00f);
|
||||||
|
colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.13f, 0.15f, 0.18f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_CheckMark] = ImVec4(0.45f, 0.72f, 0.95f, 1.00f);
|
colors[ImGuiCol_Separator] = ImVec4(0.18f, 0.20f, 0.24f, 1.00f);
|
||||||
colors[ImGuiCol_SliderGrab] = ImVec4(0.45f, 0.72f, 0.95f, 1.00f);
|
colors[ImGuiCol_SeparatorHovered] = ImVec4(0.24f, 0.32f, 0.42f, 1.00f);
|
||||||
colors[ImGuiCol_SliderGrabActive] = ImVec4(0.55f, 0.78f, 1.00f, 1.00f);
|
colors[ImGuiCol_SeparatorActive] = ImVec4(0.30f, 0.44f, 0.60f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.26f, 0.30f, 1.00f);
|
colors[ImGuiCol_ScrollbarBg] = ImVec4(0.07f, 0.08f, 0.10f, 1.00f);
|
||||||
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.45f, 0.72f, 0.95f, 0.67f);
|
colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.21f, 0.23f, 0.27f, 1.00f);
|
||||||
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.45f, 0.72f, 0.95f, 0.95f);
|
colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.26f, 0.30f, 0.36f, 1.00f);
|
||||||
|
colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.30f, 0.36f, 0.44f, 1.00f);
|
||||||
|
|
||||||
colors[ImGuiCol_DockingPreview] = ImVec4(0.45f, 0.72f, 0.95f, 0.70f);
|
colors[ImGuiCol_CheckMark] = accent;
|
||||||
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.08f, 0.08f, 0.10f, 1.00f);
|
colors[ImGuiCol_SliderGrab] = accent;
|
||||||
|
colors[ImGuiCol_SliderGrabActive] = accentMuted;
|
||||||
|
|
||||||
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.45f, 0.72f, 0.95f, 0.35f);
|
colors[ImGuiCol_ResizeGrip] = ImVec4(0.22f, 0.26f, 0.33f, 1.00f);
|
||||||
colors[ImGuiCol_NavHighlight] = ImVec4(0.45f, 0.72f, 0.95f, 1.00f);
|
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.32f, 0.46f, 0.62f, 0.80f);
|
||||||
|
colors[ImGuiCol_ResizeGripActive] = accent;
|
||||||
|
|
||||||
style.WindowRounding = 4.0f;
|
colors[ImGuiCol_DockingPreview] = ImVec4(0.33f, 0.63f, 0.98f, 0.60f);
|
||||||
style.ChildRounding = 4.0f;
|
colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.05f, 0.06f, 0.07f, 1.00f);
|
||||||
style.FrameRounding = 4.0f;
|
|
||||||
style.PopupRounding = 4.0f;
|
|
||||||
style.ScrollbarRounding = 4.0f;
|
|
||||||
style.GrabRounding = 4.0f;
|
|
||||||
style.TabRounding = 4.0f;
|
|
||||||
|
|
||||||
style.WindowPadding = ImVec2(8.0f, 8.0f);
|
colors[ImGuiCol_TextSelectedBg] = ImVec4(0.33f, 0.63f, 0.98f, 0.25f);
|
||||||
style.FramePadding = ImVec2(6.0f, 4.0f);
|
colors[ImGuiCol_NavHighlight] = accent;
|
||||||
style.ItemSpacing = ImVec2(8.0f, 4.0f);
|
colors[ImGuiCol_TableHeaderBg] = ImVec4(0.15f, 0.17f, 0.20f, 1.00f);
|
||||||
style.ItemInnerSpacing = ImVec2(4.0f, 4.0f);
|
|
||||||
|
style.WindowRounding = 5.0f;
|
||||||
|
style.ChildRounding = 5.0f;
|
||||||
|
style.FrameRounding = 6.0f;
|
||||||
|
style.PopupRounding = 6.0f;
|
||||||
|
style.ScrollbarRounding = 10.0f;
|
||||||
|
style.GrabRounding = 6.0f;
|
||||||
|
style.TabRounding = 6.0f;
|
||||||
|
|
||||||
|
style.WindowPadding = ImVec2(10.0f, 10.0f);
|
||||||
|
style.FramePadding = ImVec2(9.0f, 5.0f);
|
||||||
|
style.ItemSpacing = ImVec2(10.0f, 6.0f);
|
||||||
|
style.ItemInnerSpacing = ImVec2(6.0f, 4.0f);
|
||||||
|
style.IndentSpacing = 18.0f;
|
||||||
|
|
||||||
style.WindowBorderSize = 1.0f;
|
style.WindowBorderSize = 1.0f;
|
||||||
style.FrameBorderSize = 0.0f;
|
style.FrameBorderSize = 1.0f;
|
||||||
style.PopupBorderSize = 1.0f;
|
style.PopupBorderSize = 1.0f;
|
||||||
|
style.TabBorderSize = 1.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setupDockspace() {
|
void setupDockspace() {
|
||||||
|
|||||||
116
src/Engine.cpp
116
src/Engine.cpp
@@ -87,6 +87,22 @@ SceneObject* Engine::getSelectedObject() {
|
|||||||
return (it != sceneObjects.end()) ? &(*it) : nullptr;
|
return (it != sceneObjects.end()) ? &(*it) : nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Camera Engine::makeCameraFromObject(const SceneObject& obj) const {
|
||||||
|
Camera cam;
|
||||||
|
cam.position = obj.position;
|
||||||
|
glm::quat q = glm::quat(glm::radians(obj.rotation));
|
||||||
|
glm::mat3 rot = glm::mat3_cast(q);
|
||||||
|
cam.front = glm::normalize(rot * glm::vec3(0.0f, 0.0f, -1.0f));
|
||||||
|
cam.up = glm::normalize(rot * glm::vec3(0.0f, 1.0f, 0.0f));
|
||||||
|
if (!std::isfinite(cam.front.x) || glm::length(cam.front) < 1e-3f) {
|
||||||
|
cam.front = glm::vec3(0.0f, 0.0f, -1.0f);
|
||||||
|
}
|
||||||
|
if (!std::isfinite(cam.up.x) || glm::length(cam.up) < 1e-3f) {
|
||||||
|
cam.up = glm::vec3(0.0f, 1.0f, 0.0f);
|
||||||
|
}
|
||||||
|
return cam;
|
||||||
|
}
|
||||||
|
|
||||||
void Engine::DecomposeMatrix(const glm::mat4& matrix, glm::vec3& pos, glm::vec3& rot, glm::vec3& scale) {
|
void Engine::DecomposeMatrix(const glm::mat4& matrix, glm::vec3& pos, glm::vec3& rot, glm::vec3& scale) {
|
||||||
pos = glm::vec3(matrix[3]);
|
pos = glm::vec3(matrix[3]);
|
||||||
scale.x = glm::length(glm::vec3(matrix[0]));
|
scale.x = glm::length(glm::vec3(matrix[0]));
|
||||||
@@ -204,6 +220,17 @@ void Engine::run() {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Enforce cursor lock state every frame to avoid backends restoring it.
|
||||||
|
int desiredMode = cursorLocked ? GLFW_CURSOR_DISABLED : GLFW_CURSOR_NORMAL;
|
||||||
|
if (glfwGetInputMode(editorWindow, GLFW_CURSOR) != desiredMode) {
|
||||||
|
glfwSetInputMode(editorWindow, GLFW_CURSOR, desiredMode);
|
||||||
|
if (cursorLocked && glfwRawMouseMotionSupported()) {
|
||||||
|
glfwSetInputMode(editorWindow, GLFW_RAW_MOUSE_MOTION, GLFW_TRUE);
|
||||||
|
} else if (!cursorLocked && glfwRawMouseMotionSupported()) {
|
||||||
|
glfwSetInputMode(editorWindow, GLFW_RAW_MOUSE_MOTION, GLFW_FALSE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
float currentFrame = glfwGetTime();
|
float currentFrame = glfwGetTime();
|
||||||
deltaTime = currentFrame - lastFrame;
|
deltaTime = currentFrame - lastFrame;
|
||||||
lastFrame = currentFrame;
|
lastFrame = currentFrame;
|
||||||
@@ -223,6 +250,17 @@ void Engine::run() {
|
|||||||
camera.firstMouse = true;
|
camera.firstMouse = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Scroll-wheel speed adjustment while freelook is active
|
||||||
|
if (viewportController.isViewportFocused() && cursorLocked) {
|
||||||
|
float wheel = ImGui::GetIO().MouseWheel;
|
||||||
|
if (std::abs(wheel) > 0.0001f) {
|
||||||
|
float factor = std::pow(1.12f, wheel);
|
||||||
|
float ratio = (camera.moveSpeed > 0.001f) ? (camera.sprintSpeed / camera.moveSpeed) : 2.0f;
|
||||||
|
camera.moveSpeed = std::clamp(camera.moveSpeed * factor, 0.5f, 100.0f);
|
||||||
|
camera.sprintSpeed = std::clamp(camera.moveSpeed * ratio, 0.5f, 200.0f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (viewportController.isViewportFocused() && cursorLocked) {
|
if (viewportController.isViewportFocused() && cursorLocked) {
|
||||||
camera.processKeyboard(deltaTime, editorWindow);
|
camera.processKeyboard(deltaTime, editorWindow);
|
||||||
}
|
}
|
||||||
@@ -234,7 +272,7 @@ void Engine::run() {
|
|||||||
glm::mat4 proj = glm::perspective(glm::radians(FOV), aspect, NEAR_PLANE, FAR_PLANE);
|
glm::mat4 proj = glm::perspective(glm::radians(FOV), aspect, NEAR_PLANE, FAR_PLANE);
|
||||||
|
|
||||||
renderer.beginRender(view, proj, camera.position);
|
renderer.beginRender(view, proj, camera.position);
|
||||||
renderer.renderScene(camera, sceneObjects);
|
renderer.renderScene(camera, sceneObjects, selectedObjectId);
|
||||||
renderer.endRender();
|
renderer.endRender();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -504,23 +542,39 @@ void Engine::handleKeyboardShortcuts() {
|
|||||||
ctrlNPressed = false;
|
ctrlNPressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!cursorLocked) {
|
bool cameraActive = cursorLocked || viewportController.isViewportFocused() && cursorLocked;
|
||||||
|
if (!cameraActive) {
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Q)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
if (ImGui::IsKeyPressed(ImGuiKey_Q)) mCurrentGizmoOperation = ImGuizmo::TRANSLATE;
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_W)) mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
if (ImGui::IsKeyPressed(ImGuiKey_W)) mCurrentGizmoOperation = ImGuizmo::ROTATE;
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_E)) mCurrentGizmoOperation = ImGuizmo::SCALE;
|
if (ImGui::IsKeyPressed(ImGuiKey_E)) mCurrentGizmoOperation = ImGuizmo::SCALE;
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_R)) mCurrentGizmoOperation = ImGuizmo::UNIVERSAL;
|
if (ImGui::IsKeyPressed(ImGuiKey_R)) mCurrentGizmoOperation = ImGuizmo::BOUNDS;
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_T)) mCurrentGizmoOperation = ImGuizmo::UNIVERSAL;
|
||||||
|
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_Z)) {
|
if (ImGui::IsKeyPressed(ImGuiKey_U)) {
|
||||||
mCurrentGizmoMode = (mCurrentGizmoMode == ImGuizmo::LOCAL) ? ImGuizmo::WORLD : ImGuizmo::LOCAL;
|
mCurrentGizmoMode = (mCurrentGizmoMode == ImGuizmo::LOCAL) ? ImGuizmo::WORLD : ImGuizmo::LOCAL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool snapPressed = false;
|
static bool snapPressed = false;
|
||||||
if (ImGui::IsKeyPressed(ImGuiKey_X) && !snapPressed) {
|
static bool snapHeldByCtrl = false;
|
||||||
useSnap = !useSnap;
|
static bool snapStateBeforeCtrl = false;
|
||||||
snapPressed = true;
|
|
||||||
|
if (!snapHeldByCtrl && ctrlDown) {
|
||||||
|
snapStateBeforeCtrl = useSnap;
|
||||||
|
snapHeldByCtrl = true;
|
||||||
|
useSnap = true;
|
||||||
|
} else if (snapHeldByCtrl && !ctrlDown) {
|
||||||
|
useSnap = snapStateBeforeCtrl;
|
||||||
|
snapHeldByCtrl = false;
|
||||||
}
|
}
|
||||||
if (ImGui::IsKeyReleased(ImGuiKey_X)) {
|
|
||||||
|
if (!cameraActive) {
|
||||||
|
if (ImGui::IsKeyPressed(ImGuiKey_Y) && !snapPressed) {
|
||||||
|
useSnap = !useSnap;
|
||||||
|
snapPressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ImGui::IsKeyReleased(ImGuiKey_Y)) {
|
||||||
snapPressed = false;
|
snapPressed = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -544,16 +598,37 @@ void Engine::handleKeyboardShortcuts() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void Engine::OpenProjectPath(const std::string& path) {
|
void Engine::OpenProjectPath(const std::string& path) {
|
||||||
if (projectManager.loadProject(path)) {
|
try {
|
||||||
if (!initRenderer()) {
|
if (projectManager.loadProject(path)) {
|
||||||
addConsoleMessage("Error: Failed to initialize renderer!", ConsoleMessageType::Error);
|
// Make sure project folders exist even for older/minimal projects
|
||||||
} else {
|
if (!fs::exists(projectManager.currentProject.assetsPath)) {
|
||||||
showLauncher = false;
|
fs::create_directories(projectManager.currentProject.assetsPath);
|
||||||
|
}
|
||||||
|
if (!fs::exists(projectManager.currentProject.scenesPath)) {
|
||||||
|
fs::create_directories(projectManager.currentProject.scenesPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!initRenderer()) {
|
||||||
|
addConsoleMessage("Error: Failed to initialize renderer!", ConsoleMessageType::Error);
|
||||||
|
showLauncher = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
loadRecentScenes();
|
loadRecentScenes();
|
||||||
|
fileBrowser.setProjectRoot(projectManager.currentProject.assetsPath);
|
||||||
|
fileBrowser.currentPath = projectManager.currentProject.assetsPath;
|
||||||
|
fileBrowser.needsRefresh = true;
|
||||||
|
showLauncher = false;
|
||||||
addConsoleMessage("Opened project: " + projectManager.currentProject.name, ConsoleMessageType::Info);
|
addConsoleMessage("Opened project: " + projectManager.currentProject.name, ConsoleMessageType::Info);
|
||||||
|
} else {
|
||||||
|
addConsoleMessage("Error opening project: " + projectManager.errorMessage, ConsoleMessageType::Error);
|
||||||
}
|
}
|
||||||
} else {
|
} catch (const std::exception& e) {
|
||||||
addConsoleMessage("Error opening project: " + projectManager.errorMessage, ConsoleMessageType::Error);
|
addConsoleMessage(std::string("Exception opening project: ") + e.what(), ConsoleMessageType::Error);
|
||||||
|
showLauncher = true;
|
||||||
|
} catch (...) {
|
||||||
|
addConsoleMessage("Unknown exception opening project", ConsoleMessageType::Error);
|
||||||
|
showLauncher = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -578,6 +653,7 @@ void Engine::createNewProject(const char* name, const char* location) {
|
|||||||
|
|
||||||
addObject(ObjectType::Cube, "Cube");
|
addObject(ObjectType::Cube, "Cube");
|
||||||
|
|
||||||
|
fileBrowser.setProjectRoot(projectManager.currentProject.assetsPath);
|
||||||
fileBrowser.currentPath = projectManager.currentProject.assetsPath;
|
fileBrowser.currentPath = projectManager.currentProject.assetsPath;
|
||||||
fileBrowser.needsRefresh = true;
|
fileBrowser.needsRefresh = true;
|
||||||
|
|
||||||
@@ -614,6 +690,7 @@ void Engine::loadRecentScenes() {
|
|||||||
}
|
}
|
||||||
recordState("sceneLoaded");
|
recordState("sceneLoaded");
|
||||||
|
|
||||||
|
fileBrowser.setProjectRoot(projectManager.currentProject.assetsPath);
|
||||||
fileBrowser.currentPath = projectManager.currentProject.assetsPath;
|
fileBrowser.currentPath = projectManager.currentProject.assetsPath;
|
||||||
fileBrowser.needsRefresh = true;
|
fileBrowser.needsRefresh = true;
|
||||||
}
|
}
|
||||||
@@ -702,6 +779,13 @@ void Engine::addObject(ObjectType type, const std::string& baseName) {
|
|||||||
sceneObjects.back().light.range = 10.0f;
|
sceneObjects.back().light.range = 10.0f;
|
||||||
sceneObjects.back().light.intensity = 3.0f;
|
sceneObjects.back().light.intensity = 3.0f;
|
||||||
sceneObjects.back().light.size = glm::vec2(2.0f, 2.0f);
|
sceneObjects.back().light.size = glm::vec2(2.0f, 2.0f);
|
||||||
|
} else if (type == ObjectType::PostFXNode) {
|
||||||
|
sceneObjects.back().postFx.enabled = true;
|
||||||
|
sceneObjects.back().postFx.bloomEnabled = true;
|
||||||
|
sceneObjects.back().postFx.colorAdjustEnabled = true;
|
||||||
|
} else if (type == ObjectType::Camera) {
|
||||||
|
sceneObjects.back().camera.type = SceneCameraType::Player;
|
||||||
|
sceneObjects.back().camera.fov = 60.0f;
|
||||||
}
|
}
|
||||||
selectedObjectId = id;
|
selectedObjectId = id;
|
||||||
if (projectManager.currentProject.isLoaded) {
|
if (projectManager.currentProject.isLoaded) {
|
||||||
@@ -732,6 +816,8 @@ void Engine::duplicateSelected() {
|
|||||||
newObj.fragmentShaderPath = it->fragmentShaderPath;
|
newObj.fragmentShaderPath = it->fragmentShaderPath;
|
||||||
newObj.useOverlay = it->useOverlay;
|
newObj.useOverlay = it->useOverlay;
|
||||||
newObj.light = it->light;
|
newObj.light = it->light;
|
||||||
|
newObj.camera = it->camera;
|
||||||
|
newObj.postFx = it->postFx;
|
||||||
|
|
||||||
sceneObjects.push_back(newObj);
|
sceneObjects.push_back(newObj);
|
||||||
selectedObjectId = id;
|
selectedObjectId = id;
|
||||||
|
|||||||
@@ -82,6 +82,10 @@ private:
|
|||||||
float fileBrowserIconScale = 1.0f; // 0.5 to 2.0 range
|
float fileBrowserIconScale = 1.0f; // 0.5 to 2.0 range
|
||||||
bool showEnvironmentWindow = true;
|
bool showEnvironmentWindow = true;
|
||||||
bool showCameraWindow = true;
|
bool showCameraWindow = true;
|
||||||
|
bool isPlaying = false;
|
||||||
|
bool isPaused = false;
|
||||||
|
bool showViewOutput = true;
|
||||||
|
int previewCameraId = -1;
|
||||||
|
|
||||||
// Private methods
|
// Private methods
|
||||||
SceneObject* getSelectedObject();
|
SceneObject* getSelectedObject();
|
||||||
@@ -107,6 +111,7 @@ private:
|
|||||||
void renderViewport();
|
void renderViewport();
|
||||||
void renderDialogs();
|
void renderDialogs();
|
||||||
void renderProjectBrowserPanel();
|
void renderProjectBrowserPanel();
|
||||||
|
Camera makeCameraFromObject(const SceneObject& obj) const;
|
||||||
|
|
||||||
void renderFileBrowserToolbar();
|
void renderFileBrowserToolbar();
|
||||||
void renderFileBrowserBreadcrumb();
|
void renderFileBrowserBreadcrumb();
|
||||||
|
|||||||
1030
src/EnginePanels.cpp
1030
src/EnginePanels.cpp
File diff suppressed because it is too large
Load Diff
@@ -119,6 +119,7 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
|
|
||||||
glm::vec3 boundsMin(FLT_MAX);
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
glm::vec3 boundsMax(-FLT_MAX);
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
|
std::vector<glm::vec3> triPositions;
|
||||||
|
|
||||||
// Process all meshes in the scene
|
// Process all meshes in the scene
|
||||||
std::vector<float> vertices;
|
std::vector<float> vertices;
|
||||||
@@ -128,7 +129,7 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
result.hasTangents = false;
|
result.hasTangents = false;
|
||||||
|
|
||||||
// Process the root node recursively
|
// Process the root node recursively
|
||||||
processNode(scene->mRootNode, scene, vertices, boundsMin, boundsMax);
|
processNode(scene->mRootNode, scene, aiMatrix4x4(), vertices, triPositions, boundsMin, boundsMax);
|
||||||
|
|
||||||
// Check mesh properties
|
// Check mesh properties
|
||||||
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
|
for (unsigned int i = 0; i < scene->mNumMeshes; i++) {
|
||||||
@@ -157,6 +158,7 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
|
|
||||||
loaded.boundsMin = boundsMin;
|
loaded.boundsMin = boundsMin;
|
||||||
loaded.boundsMax = boundsMax;
|
loaded.boundsMax = boundsMax;
|
||||||
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
|
||||||
loadedMeshes.push_back(std::move(loaded));
|
loadedMeshes.push_back(std::move(loaded));
|
||||||
|
|
||||||
@@ -171,20 +173,33 @@ ModelLoadResult ModelLoader::loadModel(const std::string& filepath) {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelLoader::processNode(aiNode* node, const aiScene* scene, std::vector<float>& vertices, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
static glm::mat4 aiToGlm(const aiMatrix4x4& m) {
|
||||||
|
return glm::mat4(
|
||||||
|
m.a1, m.b1, m.c1, m.d1,
|
||||||
|
m.a2, m.b2, m.c2, m.d2,
|
||||||
|
m.a3, m.b3, m.c3, m.d3,
|
||||||
|
m.a4, m.b4, m.c4, m.d4
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ModelLoader::processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
||||||
|
aiMatrix4x4 currentTransform = parentTransform * node->mTransformation;
|
||||||
// Process all meshes in this node
|
// Process all meshes in this node
|
||||||
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
for (unsigned int i = 0; i < node->mNumMeshes; i++) {
|
||||||
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
aiMesh* mesh = scene->mMeshes[node->mMeshes[i]];
|
||||||
processMesh(mesh, scene, vertices, boundsMin, boundsMax);
|
processMesh(mesh, currentTransform, vertices, triPositions, boundsMin, boundsMax);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process children nodes
|
// Process children nodes
|
||||||
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
for (unsigned int i = 0; i < node->mNumChildren; i++) {
|
||||||
processNode(node->mChildren[i], scene, vertices, boundsMin, boundsMax);
|
processNode(node->mChildren[i], scene, currentTransform, vertices, triPositions, boundsMin, boundsMax);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ModelLoader::processMesh(aiMesh* mesh, const aiScene* scene, std::vector<float>& vertices, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
void ModelLoader::processMesh(aiMesh* mesh, const aiMatrix4x4& transform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax) {
|
||||||
|
glm::mat4 gTransform = aiToGlm(transform);
|
||||||
|
glm::mat3 normalMat = glm::transpose(glm::inverse(glm::mat3(gTransform)));
|
||||||
|
|
||||||
// Process each face
|
// Process each face
|
||||||
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
for (unsigned int i = 0; i < mesh->mNumFaces; i++) {
|
||||||
aiFace face = mesh->mFaces[i];
|
aiFace face = mesh->mFaces[i];
|
||||||
@@ -193,23 +208,34 @@ void ModelLoader::processMesh(aiMesh* mesh, const aiScene* scene, std::vector<fl
|
|||||||
for (unsigned int j = 0; j < face.mNumIndices; j++) {
|
for (unsigned int j = 0; j < face.mNumIndices; j++) {
|
||||||
unsigned int index = face.mIndices[j];
|
unsigned int index = face.mIndices[j];
|
||||||
|
|
||||||
// Position
|
glm::vec3 pos(mesh->mVertices[index].x,
|
||||||
vertices.push_back(mesh->mVertices[index].x);
|
mesh->mVertices[index].y,
|
||||||
vertices.push_back(mesh->mVertices[index].y);
|
mesh->mVertices[index].z);
|
||||||
vertices.push_back(mesh->mVertices[index].z);
|
glm::vec4 transformed = gTransform * glm::vec4(pos, 1.0f);
|
||||||
|
glm::vec3 finalPos = glm::vec3(transformed) / (transformed.w == 0.0f ? 1.0f : transformed.w);
|
||||||
|
|
||||||
boundsMin.x = std::min(boundsMin.x, mesh->mVertices[index].x);
|
vertices.push_back(finalPos.x);
|
||||||
boundsMin.y = std::min(boundsMin.y, mesh->mVertices[index].y);
|
vertices.push_back(finalPos.y);
|
||||||
boundsMin.z = std::min(boundsMin.z, mesh->mVertices[index].z);
|
vertices.push_back(finalPos.z);
|
||||||
boundsMax.x = std::max(boundsMax.x, mesh->mVertices[index].x);
|
|
||||||
boundsMax.y = std::max(boundsMax.y, mesh->mVertices[index].y);
|
triPositions.push_back(finalPos);
|
||||||
boundsMax.z = std::max(boundsMax.z, mesh->mVertices[index].z);
|
|
||||||
|
boundsMin.x = std::min(boundsMin.x, finalPos.x);
|
||||||
|
boundsMin.y = std::min(boundsMin.y, finalPos.y);
|
||||||
|
boundsMin.z = std::min(boundsMin.z, finalPos.z);
|
||||||
|
boundsMax.x = std::max(boundsMax.x, finalPos.x);
|
||||||
|
boundsMax.y = std::max(boundsMax.y, finalPos.y);
|
||||||
|
boundsMax.z = std::max(boundsMax.z, finalPos.z);
|
||||||
|
|
||||||
// Normal
|
// Normal
|
||||||
if (mesh->mNormals) {
|
if (mesh->mNormals) {
|
||||||
vertices.push_back(mesh->mNormals[index].x);
|
glm::vec3 n(mesh->mNormals[index].x,
|
||||||
vertices.push_back(mesh->mNormals[index].y);
|
mesh->mNormals[index].y,
|
||||||
vertices.push_back(mesh->mNormals[index].z);
|
mesh->mNormals[index].z);
|
||||||
|
n = glm::normalize(normalMat * n);
|
||||||
|
vertices.push_back(n.x);
|
||||||
|
vertices.push_back(n.y);
|
||||||
|
vertices.push_back(n.z);
|
||||||
} else {
|
} else {
|
||||||
vertices.push_back(0.0f);
|
vertices.push_back(0.0f);
|
||||||
vertices.push_back(1.0f);
|
vertices.push_back(1.0f);
|
||||||
|
|||||||
@@ -64,8 +64,8 @@ private:
|
|||||||
ModelLoader& operator=(const ModelLoader&) = delete;
|
ModelLoader& operator=(const ModelLoader&) = delete;
|
||||||
|
|
||||||
// Process Assimp scene
|
// Process Assimp scene
|
||||||
void processNode(aiNode* node, const aiScene* scene, std::vector<float>& vertices, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
void processNode(aiNode* node, const aiScene* scene, const aiMatrix4x4& parentTransform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
||||||
void processMesh(aiMesh* mesh, const aiScene* scene, std::vector<float>& vertices, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
void processMesh(aiMesh* mesh, const aiMatrix4x4& transform, std::vector<float>& vertices, std::vector<glm::vec3>& triPositions, glm::vec3& boundsMin, glm::vec3& boundsMax);
|
||||||
|
|
||||||
// Storage for loaded meshes (reusing OBJLoader::LoadedMesh structure)
|
// Storage for loaded meshes (reusing OBJLoader::LoadedMesh structure)
|
||||||
std::vector<OBJLoader::LoadedMesh> loadedMeshes;
|
std::vector<OBJLoader::LoadedMesh> loadedMeshes;
|
||||||
|
|||||||
@@ -236,7 +236,7 @@ bool SceneSerializer::saveScene(const fs::path& filePath,
|
|||||||
if (!file.is_open()) return false;
|
if (!file.is_open()) return false;
|
||||||
|
|
||||||
file << "# Scene File\n";
|
file << "# Scene File\n";
|
||||||
file << "version=2\n";
|
file << "version=4\n";
|
||||||
file << "nextId=" << nextId << "\n";
|
file << "nextId=" << nextId << "\n";
|
||||||
file << "objectCount=" << objects.size() << "\n";
|
file << "objectCount=" << objects.size() << "\n";
|
||||||
file << "\n";
|
file << "\n";
|
||||||
@@ -269,6 +269,24 @@ bool SceneSerializer::saveScene(const fs::path& filePath,
|
|||||||
file << "lightOuter=" << obj.light.outerAngle << "\n";
|
file << "lightOuter=" << obj.light.outerAngle << "\n";
|
||||||
file << "lightSize=" << obj.light.size.x << "," << obj.light.size.y << "\n";
|
file << "lightSize=" << obj.light.size.x << "," << obj.light.size.y << "\n";
|
||||||
file << "lightEnabled=" << (obj.light.enabled ? 1 : 0) << "\n";
|
file << "lightEnabled=" << (obj.light.enabled ? 1 : 0) << "\n";
|
||||||
|
file << "cameraType=" << static_cast<int>(obj.camera.type) << "\n";
|
||||||
|
file << "cameraFov=" << obj.camera.fov << "\n";
|
||||||
|
file << "cameraNear=" << obj.camera.nearClip << "\n";
|
||||||
|
file << "cameraFar=" << obj.camera.farClip << "\n";
|
||||||
|
if (obj.type == ObjectType::PostFXNode) {
|
||||||
|
file << "postEnabled=" << (obj.postFx.enabled ? 1 : 0) << "\n";
|
||||||
|
file << "postBloomEnabled=" << (obj.postFx.bloomEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postBloomThreshold=" << obj.postFx.bloomThreshold << "\n";
|
||||||
|
file << "postBloomIntensity=" << obj.postFx.bloomIntensity << "\n";
|
||||||
|
file << "postBloomRadius=" << obj.postFx.bloomRadius << "\n";
|
||||||
|
file << "postColorAdjustEnabled=" << (obj.postFx.colorAdjustEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postExposure=" << obj.postFx.exposure << "\n";
|
||||||
|
file << "postContrast=" << obj.postFx.contrast << "\n";
|
||||||
|
file << "postSaturation=" << obj.postFx.saturation << "\n";
|
||||||
|
file << "postColorFilter=" << obj.postFx.colorFilter.r << "," << obj.postFx.colorFilter.g << "," << obj.postFx.colorFilter.b << "\n";
|
||||||
|
file << "postMotionBlurEnabled=" << (obj.postFx.motionBlurEnabled ? 1 : 0) << "\n";
|
||||||
|
file << "postMotionBlurStrength=" << obj.postFx.motionBlurStrength << "\n";
|
||||||
|
}
|
||||||
|
|
||||||
if ((obj.type == ObjectType::OBJMesh || obj.type == ObjectType::Model) && !obj.meshPath.empty()) {
|
if ((obj.type == ObjectType::OBJMesh || obj.type == ObjectType::Model) && !obj.meshPath.empty()) {
|
||||||
file << "meshPath=" << obj.meshPath << "\n";
|
file << "meshPath=" << obj.meshPath << "\n";
|
||||||
@@ -332,6 +350,9 @@ bool SceneSerializer::loadScene(const fs::path& filePath,
|
|||||||
else if (currentObj->type == ObjectType::PointLight) currentObj->light.type = LightType::Point;
|
else if (currentObj->type == ObjectType::PointLight) currentObj->light.type = LightType::Point;
|
||||||
else if (currentObj->type == ObjectType::SpotLight) currentObj->light.type = LightType::Spot;
|
else if (currentObj->type == ObjectType::SpotLight) currentObj->light.type = LightType::Spot;
|
||||||
else if (currentObj->type == ObjectType::AreaLight) currentObj->light.type = LightType::Area;
|
else if (currentObj->type == ObjectType::AreaLight) currentObj->light.type = LightType::Area;
|
||||||
|
else if (currentObj->type == ObjectType::Camera) {
|
||||||
|
currentObj->camera.type = SceneCameraType::Scene;
|
||||||
|
}
|
||||||
} else if (key == "parentId") {
|
} else if (key == "parentId") {
|
||||||
currentObj->parentId = std::stoi(value);
|
currentObj->parentId = std::stoi(value);
|
||||||
} else if (key == "position") {
|
} else if (key == "position") {
|
||||||
@@ -395,6 +416,41 @@ bool SceneSerializer::loadScene(const fs::path& filePath,
|
|||||||
¤tObj->light.size.y);
|
¤tObj->light.size.y);
|
||||||
} else if (key == "lightEnabled") {
|
} else if (key == "lightEnabled") {
|
||||||
currentObj->light.enabled = (std::stoi(value) != 0);
|
currentObj->light.enabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "cameraType") {
|
||||||
|
currentObj->camera.type = static_cast<SceneCameraType>(std::stoi(value));
|
||||||
|
} else if (key == "cameraFov") {
|
||||||
|
currentObj->camera.fov = std::stof(value);
|
||||||
|
} else if (key == "cameraNear") {
|
||||||
|
currentObj->camera.nearClip = std::stof(value);
|
||||||
|
} else if (key == "cameraFar") {
|
||||||
|
currentObj->camera.farClip = std::stof(value);
|
||||||
|
} else if (key == "postEnabled") {
|
||||||
|
currentObj->postFx.enabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postBloomEnabled") {
|
||||||
|
currentObj->postFx.bloomEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postBloomThreshold") {
|
||||||
|
currentObj->postFx.bloomThreshold = std::stof(value);
|
||||||
|
} else if (key == "postBloomIntensity") {
|
||||||
|
currentObj->postFx.bloomIntensity = std::stof(value);
|
||||||
|
} else if (key == "postBloomRadius") {
|
||||||
|
currentObj->postFx.bloomRadius = std::stof(value);
|
||||||
|
} else if (key == "postColorAdjustEnabled") {
|
||||||
|
currentObj->postFx.colorAdjustEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postExposure") {
|
||||||
|
currentObj->postFx.exposure = std::stof(value);
|
||||||
|
} else if (key == "postContrast") {
|
||||||
|
currentObj->postFx.contrast = std::stof(value);
|
||||||
|
} else if (key == "postSaturation") {
|
||||||
|
currentObj->postFx.saturation = std::stof(value);
|
||||||
|
} else if (key == "postColorFilter") {
|
||||||
|
sscanf(value.c_str(), "%f,%f,%f",
|
||||||
|
¤tObj->postFx.colorFilter.r,
|
||||||
|
¤tObj->postFx.colorFilter.g,
|
||||||
|
¤tObj->postFx.colorFilter.b);
|
||||||
|
} else if (key == "postMotionBlurEnabled") {
|
||||||
|
currentObj->postFx.motionBlurEnabled = (std::stoi(value) != 0);
|
||||||
|
} else if (key == "postMotionBlurStrength") {
|
||||||
|
currentObj->postFx.motionBlurStrength = std::stof(value);
|
||||||
} else if (key == "meshPath") {
|
} else if (key == "meshPath") {
|
||||||
currentObj->meshPath = value;
|
currentObj->meshPath = value;
|
||||||
if (!value.empty() && currentObj->type == ObjectType::OBJMesh) {
|
if (!value.empty() && currentObj->type == ObjectType::OBJMesh) {
|
||||||
|
|||||||
@@ -298,6 +298,7 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
|
|
||||||
glm::vec3 boundsMin(FLT_MAX);
|
glm::vec3 boundsMin(FLT_MAX);
|
||||||
glm::vec3 boundsMax(-FLT_MAX);
|
glm::vec3 boundsMax(-FLT_MAX);
|
||||||
|
std::vector<glm::vec3> triPositions;
|
||||||
|
|
||||||
for (const auto& shape : shapes) {
|
for (const auto& shape : shapes) {
|
||||||
size_t indexOffset = 0;
|
size_t indexOffset = 0;
|
||||||
@@ -360,6 +361,7 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
const TempVertex* tri[3] = { &faceVerts[0], &faceVerts[v], &faceVerts[v+1] };
|
const TempVertex* tri[3] = { &faceVerts[0], &faceVerts[v], &faceVerts[v+1] };
|
||||||
|
|
||||||
for (int i = 0; i < 3; i++) {
|
for (int i = 0; i < 3; i++) {
|
||||||
|
triPositions.push_back(tri[i]->pos);
|
||||||
vertices.push_back(tri[i]->pos.x);
|
vertices.push_back(tri[i]->pos.x);
|
||||||
vertices.push_back(tri[i]->pos.y);
|
vertices.push_back(tri[i]->pos.y);
|
||||||
vertices.push_back(tri[i]->pos.z);
|
vertices.push_back(tri[i]->pos.z);
|
||||||
@@ -390,6 +392,7 @@ int OBJLoader::loadOBJ(const std::string& filepath, std::string& errorMsg) {
|
|||||||
loaded.hasTexCoords = !attrib.texcoords.empty();
|
loaded.hasTexCoords = !attrib.texcoords.empty();
|
||||||
loaded.boundsMin = boundsMin;
|
loaded.boundsMin = boundsMin;
|
||||||
loaded.boundsMax = boundsMax;
|
loaded.boundsMax = boundsMax;
|
||||||
|
loaded.triangleVertices = std::move(triPositions);
|
||||||
|
|
||||||
loadedMeshes.push_back(std::move(loaded));
|
loadedMeshes.push_back(std::move(loaded));
|
||||||
return static_cast<int>(loadedMeshes.size() - 1);
|
return static_cast<int>(loadedMeshes.size() - 1);
|
||||||
@@ -420,9 +423,29 @@ Renderer::~Renderer() {
|
|||||||
delete sphereMesh;
|
delete sphereMesh;
|
||||||
delete capsuleMesh;
|
delete capsuleMesh;
|
||||||
delete skybox;
|
delete skybox;
|
||||||
|
delete postShader;
|
||||||
|
delete brightShader;
|
||||||
|
delete blurShader;
|
||||||
|
if (previewTarget.fbo) glDeleteFramebuffers(1, &previewTarget.fbo);
|
||||||
|
if (previewTarget.texture) glDeleteTextures(1, &previewTarget.texture);
|
||||||
|
if (previewTarget.rbo) glDeleteRenderbuffers(1, &previewTarget.rbo);
|
||||||
|
if (postTarget.fbo) glDeleteFramebuffers(1, &postTarget.fbo);
|
||||||
|
if (postTarget.texture) glDeleteTextures(1, &postTarget.texture);
|
||||||
|
if (postTarget.rbo) glDeleteRenderbuffers(1, &postTarget.rbo);
|
||||||
|
if (historyTarget.fbo) glDeleteFramebuffers(1, &historyTarget.fbo);
|
||||||
|
if (historyTarget.texture) glDeleteTextures(1, &historyTarget.texture);
|
||||||
|
if (historyTarget.rbo) glDeleteRenderbuffers(1, &historyTarget.rbo);
|
||||||
|
if (bloomTargetA.fbo) glDeleteFramebuffers(1, &bloomTargetA.fbo);
|
||||||
|
if (bloomTargetA.texture) glDeleteTextures(1, &bloomTargetA.texture);
|
||||||
|
if (bloomTargetA.rbo) glDeleteRenderbuffers(1, &bloomTargetA.rbo);
|
||||||
|
if (bloomTargetB.fbo) glDeleteFramebuffers(1, &bloomTargetB.fbo);
|
||||||
|
if (bloomTargetB.texture) glDeleteTextures(1, &bloomTargetB.texture);
|
||||||
|
if (bloomTargetB.rbo) glDeleteRenderbuffers(1, &bloomTargetB.rbo);
|
||||||
if (framebuffer) glDeleteFramebuffers(1, &framebuffer);
|
if (framebuffer) glDeleteFramebuffers(1, &framebuffer);
|
||||||
if (viewportTexture) glDeleteTextures(1, &viewportTexture);
|
if (viewportTexture) glDeleteTextures(1, &viewportTexture);
|
||||||
if (rbo) glDeleteRenderbuffers(1, &rbo);
|
if (rbo) glDeleteRenderbuffers(1, &rbo);
|
||||||
|
if (quadVBO) glDeleteBuffers(1, &quadVBO);
|
||||||
|
if (quadVAO) glDeleteVertexArrays(1, &quadVAO);
|
||||||
}
|
}
|
||||||
|
|
||||||
Texture* Renderer::getTexture(const std::string& path) {
|
Texture* Renderer::getTexture(const std::string& path) {
|
||||||
@@ -448,6 +471,36 @@ void Renderer::initialize() {
|
|||||||
shader = nullptr;
|
shader = nullptr;
|
||||||
throw std::runtime_error("Shader error");
|
throw std::runtime_error("Shader error");
|
||||||
}
|
}
|
||||||
|
postShader = new Shader(postVertPath.c_str(), postFragPath.c_str());
|
||||||
|
if (!postShader || postShader->ID == 0) {
|
||||||
|
std::cerr << "PostFX shader compilation failed!\n";
|
||||||
|
delete postShader;
|
||||||
|
postShader = nullptr;
|
||||||
|
} else {
|
||||||
|
postShader->use();
|
||||||
|
postShader->setInt("sceneTex", 0);
|
||||||
|
postShader->setInt("bloomTex", 1);
|
||||||
|
postShader->setInt("historyTex", 2);
|
||||||
|
}
|
||||||
|
brightShader = new Shader(postVertPath.c_str(), postBrightFragPath.c_str());
|
||||||
|
if (!brightShader || brightShader->ID == 0) {
|
||||||
|
std::cerr << "Bright-pass shader compilation failed!\n";
|
||||||
|
delete brightShader;
|
||||||
|
brightShader = nullptr;
|
||||||
|
} else {
|
||||||
|
brightShader->use();
|
||||||
|
brightShader->setInt("sceneTex", 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
blurShader = new Shader(postVertPath.c_str(), postBlurFragPath.c_str());
|
||||||
|
if (!blurShader || blurShader->ID == 0) {
|
||||||
|
std::cerr << "Blur shader compilation failed!\n";
|
||||||
|
delete blurShader;
|
||||||
|
blurShader = nullptr;
|
||||||
|
} else {
|
||||||
|
blurShader->use();
|
||||||
|
blurShader->setInt("image", 0);
|
||||||
|
}
|
||||||
ShaderEntry entry;
|
ShaderEntry entry;
|
||||||
entry.shader.reset(defaultShader);
|
entry.shader.reset(defaultShader);
|
||||||
entry.vertPath = defaultVertPath;
|
entry.vertPath = defaultVertPath;
|
||||||
@@ -470,6 +523,12 @@ void Renderer::initialize() {
|
|||||||
skybox = new Skybox();
|
skybox = new Skybox();
|
||||||
|
|
||||||
setupFBO();
|
setupFBO();
|
||||||
|
ensureRenderTarget(postTarget, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(historyTarget, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(bloomTargetA, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(bloomTargetB, currentWidth, currentHeight);
|
||||||
|
ensureQuad();
|
||||||
|
clearHistory();
|
||||||
glEnable(GL_DEPTH_TEST);
|
glEnable(GL_DEPTH_TEST);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -573,6 +632,93 @@ void Renderer::setupFBO() {
|
|||||||
std::cerr << "Framebuffer setup failed!\n";
|
std::cerr << "Framebuffer setup failed!\n";
|
||||||
}
|
}
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
displayTexture = viewportTexture;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::ensureRenderTarget(RenderTarget& target, int w, int h) {
|
||||||
|
if (w <= 0 || h <= 0) return;
|
||||||
|
|
||||||
|
if (target.fbo == 0) {
|
||||||
|
glGenFramebuffers(1, &target.fbo);
|
||||||
|
glGenTextures(1, &target.texture);
|
||||||
|
glGenRenderbuffers(1, &target.rbo);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (target.width == w && target.height == h) return;
|
||||||
|
|
||||||
|
target.width = w;
|
||||||
|
target.height = h;
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, target.fbo);
|
||||||
|
|
||||||
|
glBindTexture(GL_TEXTURE_2D, target.texture);
|
||||||
|
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, target.width, target.height, 0, GL_RGB, GL_UNSIGNED_BYTE, NULL);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
|
||||||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
|
||||||
|
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, target.texture, 0);
|
||||||
|
|
||||||
|
glBindRenderbuffer(GL_RENDERBUFFER, target.rbo);
|
||||||
|
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, target.width, target.height);
|
||||||
|
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, target.rbo);
|
||||||
|
|
||||||
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
|
std::cerr << "Preview framebuffer setup failed!\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::ensureQuad() {
|
||||||
|
if (quadVAO != 0) return;
|
||||||
|
|
||||||
|
float quadVertices[] = {
|
||||||
|
// positions // texcoords
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
-1.0f, -1.0f, 0.0f, 0.0f,
|
||||||
|
1.0f, -1.0f, 1.0f, 0.0f,
|
||||||
|
|
||||||
|
-1.0f, 1.0f, 0.0f, 1.0f,
|
||||||
|
1.0f, -1.0f, 1.0f, 0.0f,
|
||||||
|
1.0f, 1.0f, 1.0f, 1.0f
|
||||||
|
};
|
||||||
|
|
||||||
|
glGenVertexArrays(1, &quadVAO);
|
||||||
|
glGenBuffers(1, &quadVBO);
|
||||||
|
glBindVertexArray(quadVAO);
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), quadVertices, GL_STATIC_DRAW);
|
||||||
|
glEnableVertexAttribArray(0);
|
||||||
|
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)0);
|
||||||
|
glEnableVertexAttribArray(1);
|
||||||
|
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float), (void*)(2 * sizeof(float)));
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::drawFullscreenQuad() {
|
||||||
|
if (quadVAO == 0) ensureQuad();
|
||||||
|
glBindVertexArray(quadVAO);
|
||||||
|
glDrawArrays(GL_TRIANGLES, 0, 6);
|
||||||
|
glBindVertexArray(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::clearHistory() {
|
||||||
|
historyValid = false;
|
||||||
|
if (historyTarget.fbo != 0 && historyTarget.width > 0 && historyTarget.height > 0) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, historyTarget.fbo);
|
||||||
|
glViewport(0, 0, historyTarget.width, historyTarget.height);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::clearTarget(RenderTarget& target) {
|
||||||
|
if (target.fbo == 0 || target.width <= 0 || target.height <= 0) return;
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, target.fbo);
|
||||||
|
glViewport(0, 0, target.width, target.height);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::resize(int w, int h) {
|
void Renderer::resize(int w, int h) {
|
||||||
@@ -591,6 +737,13 @@ void Renderer::resize(int w, int h) {
|
|||||||
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
|
||||||
std::cerr << "Framebuffer incomplete after resize!\n";
|
std::cerr << "Framebuffer incomplete after resize!\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ensureRenderTarget(postTarget, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(historyTarget, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(bloomTargetA, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(bloomTargetB, currentWidth, currentHeight);
|
||||||
|
clearHistory();
|
||||||
|
displayTexture = viewportTexture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::beginRender(const glm::mat4& view, const glm::mat4& proj, const glm::vec3& cameraPos) {
|
void Renderer::beginRender(const glm::mat4& view, const glm::mat4& proj, const glm::vec3& cameraPos) {
|
||||||
@@ -598,6 +751,7 @@ void Renderer::beginRender(const glm::mat4& view, const glm::mat4& proj, const g
|
|||||||
glViewport(0, 0, currentWidth, currentHeight);
|
glViewport(0, 0, currentWidth, currentHeight);
|
||||||
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||||
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
displayTexture = viewportTexture;
|
||||||
|
|
||||||
shader->use();
|
shader->use();
|
||||||
shader->setMat4("view", view);
|
shader->setMat4("view", view);
|
||||||
@@ -698,11 +852,16 @@ void Renderer::renderObject(const SceneObject& obj) {
|
|||||||
case ObjectType::DirectionalLight:
|
case ObjectType::DirectionalLight:
|
||||||
// Not rendered as geometry
|
// Not rendered as geometry
|
||||||
break;
|
break;
|
||||||
|
case ObjectType::Camera:
|
||||||
|
// Cameras are editor helpers only
|
||||||
|
break;
|
||||||
|
case ObjectType::PostFXNode:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::renderScene(const Camera& camera, const std::vector<SceneObject>& sceneObjects) {
|
void Renderer::renderSceneInternal(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int width, int height, bool unbindFramebuffer, float fovDeg, float nearPlane, float farPlane) {
|
||||||
if (!defaultShader) return;
|
if (!defaultShader || width <= 0 || height <= 0) return;
|
||||||
|
|
||||||
struct LightUniform {
|
struct LightUniform {
|
||||||
int type = 0; // 0 dir,1 point,2 spot
|
int type = 0; // 0 dir,1 point,2 spot
|
||||||
@@ -774,8 +933,8 @@ void Renderer::renderScene(const Camera& camera, const std::vector<SceneObject>&
|
|||||||
}
|
}
|
||||||
|
|
||||||
for (const auto& obj : sceneObjects) {
|
for (const auto& obj : sceneObjects) {
|
||||||
// Skip light gizmo-only types
|
// Skip light gizmo-only types and camera helpers
|
||||||
if (obj.type == ObjectType::PointLight || obj.type == ObjectType::SpotLight || obj.type == ObjectType::AreaLight) {
|
if (obj.type == ObjectType::PointLight || obj.type == ObjectType::SpotLight || obj.type == ObjectType::AreaLight || obj.type == ObjectType::Camera || obj.type == ObjectType::PostFXNode) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -785,7 +944,7 @@ void Renderer::renderScene(const Camera& camera, const std::vector<SceneObject>&
|
|||||||
shader->use();
|
shader->use();
|
||||||
|
|
||||||
shader->setMat4("view", camera.getViewMatrix());
|
shader->setMat4("view", camera.getViewMatrix());
|
||||||
shader->setMat4("projection", glm::perspective(glm::radians(FOV), (float)currentWidth / (float)currentHeight, NEAR_PLANE, FAR_PLANE));
|
shader->setMat4("projection", glm::perspective(glm::radians(fovDeg), (float)width / (float)height, nearPlane, farPlane));
|
||||||
shader->setVec3("viewPos", camera.position);
|
shader->setVec3("viewPos", camera.position);
|
||||||
shader->setVec3("ambientColor", ambientColor);
|
shader->setVec3("ambientColor", ambientColor);
|
||||||
shader->setVec3("ambientColor", ambientColor);
|
shader->setVec3("ambientColor", ambientColor);
|
||||||
@@ -862,14 +1021,153 @@ void Renderer::renderScene(const Camera& camera, const std::vector<SceneObject>&
|
|||||||
|
|
||||||
if (skybox) {
|
if (skybox) {
|
||||||
glm::mat4 view = camera.getViewMatrix();
|
glm::mat4 view = camera.getViewMatrix();
|
||||||
glm::mat4 proj = glm::perspective(glm::radians(FOV),
|
glm::mat4 proj = glm::perspective(glm::radians(fovDeg),
|
||||||
(float)currentWidth / currentHeight,
|
(float)width / height,
|
||||||
NEAR_PLANE, FAR_PLANE);
|
nearPlane, farPlane);
|
||||||
|
|
||||||
skybox->draw(glm::value_ptr(view), glm::value_ptr(proj));
|
skybox->draw(glm::value_ptr(view), glm::value_ptr(proj));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (unbindFramebuffer) {
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PostFXSettings Renderer::gatherPostFX(const std::vector<SceneObject>& sceneObjects) const {
|
||||||
|
PostFXSettings combined;
|
||||||
|
combined.enabled = false;
|
||||||
|
for (const auto& obj : sceneObjects) {
|
||||||
|
if (obj.type != ObjectType::PostFXNode) continue;
|
||||||
|
if (!obj.postFx.enabled) continue;
|
||||||
|
combined = obj.postFx; // Last enabled node wins for now
|
||||||
|
combined.enabled = true;
|
||||||
|
}
|
||||||
|
return combined;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::applyPostProcessing(const std::vector<SceneObject>& sceneObjects) {
|
||||||
|
PostFXSettings settings = gatherPostFX(sceneObjects);
|
||||||
|
bool wantsEffects = settings.enabled && (settings.bloomEnabled || settings.colorAdjustEnabled || settings.motionBlurEnabled);
|
||||||
|
|
||||||
|
if (!wantsEffects || !postShader || currentWidth <= 0 || currentHeight <= 0) {
|
||||||
|
displayTexture = viewportTexture;
|
||||||
|
clearHistory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
ensureRenderTarget(postTarget, currentWidth, currentHeight);
|
||||||
|
ensureRenderTarget(historyTarget, currentWidth, currentHeight);
|
||||||
|
if (postTarget.fbo == 0 || postTarget.texture == 0) {
|
||||||
|
displayTexture = viewportTexture;
|
||||||
|
clearHistory();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Bloom using bright pass + separable blur (inspired by ProcessingPostFX) ---
|
||||||
|
unsigned int bloomTex = 0;
|
||||||
|
if (settings.bloomEnabled && brightShader && blurShader) {
|
||||||
|
// Bright pass
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
brightShader->use();
|
||||||
|
brightShader->setFloat("threshold", settings.bloomThreshold);
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, viewportTexture);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, bloomTargetA.fbo);
|
||||||
|
glViewport(0, 0, currentWidth, currentHeight);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
drawFullscreenQuad();
|
||||||
|
|
||||||
|
// Blur ping-pong
|
||||||
|
blurShader->use();
|
||||||
|
float sigma = glm::max(settings.bloomRadius * 2.5f, 0.1f);
|
||||||
|
int radius = static_cast<int>(glm::clamp(settings.bloomRadius * 4.0f, 2.0f, 12.0f));
|
||||||
|
blurShader->setFloat("sigma", sigma);
|
||||||
|
blurShader->setInt("radius", radius);
|
||||||
|
|
||||||
|
bool horizontal = true;
|
||||||
|
unsigned int pingTex = bloomTargetA.texture;
|
||||||
|
RenderTarget* writeTarget = &bloomTargetB;
|
||||||
|
for (int i = 0; i < 4; ++i) {
|
||||||
|
blurShader->setBool("horizontal", horizontal);
|
||||||
|
blurShader->setVec2("texelSize", glm::vec2(1.0f / currentWidth, 1.0f / currentHeight));
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, pingTex);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, writeTarget->fbo);
|
||||||
|
glViewport(0, 0, currentWidth, currentHeight);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
drawFullscreenQuad();
|
||||||
|
|
||||||
|
// swap
|
||||||
|
pingTex = writeTarget->texture;
|
||||||
|
writeTarget = (writeTarget == &bloomTargetA) ? &bloomTargetB : &bloomTargetA;
|
||||||
|
horizontal = !horizontal;
|
||||||
|
}
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
bloomTex = pingTex;
|
||||||
|
} else {
|
||||||
|
bloomTex = 0;
|
||||||
|
clearTarget(bloomTargetA);
|
||||||
|
clearTarget(bloomTargetB);
|
||||||
|
}
|
||||||
|
|
||||||
|
glDisable(GL_DEPTH_TEST);
|
||||||
|
postShader->use();
|
||||||
|
postShader->setBool("enableBloom", settings.bloomEnabled && bloomTex != 0);
|
||||||
|
postShader->setFloat("bloomIntensity", settings.bloomIntensity);
|
||||||
|
postShader->setBool("enableColorAdjust", settings.colorAdjustEnabled);
|
||||||
|
postShader->setFloat("exposure", settings.exposure);
|
||||||
|
postShader->setFloat("contrast", settings.contrast);
|
||||||
|
postShader->setFloat("saturation", settings.saturation);
|
||||||
|
postShader->setVec3("colorFilter", settings.colorFilter);
|
||||||
|
postShader->setBool("enableMotionBlur", settings.motionBlurEnabled);
|
||||||
|
postShader->setFloat("motionBlurStrength", settings.motionBlurStrength);
|
||||||
|
postShader->setBool("hasHistory", historyValid);
|
||||||
|
|
||||||
|
glActiveTexture(GL_TEXTURE0);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, viewportTexture);
|
||||||
|
glActiveTexture(GL_TEXTURE1);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, bloomTex ? bloomTex : viewportTexture);
|
||||||
|
glActiveTexture(GL_TEXTURE2);
|
||||||
|
glBindTexture(GL_TEXTURE_2D, historyTarget.texture);
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, postTarget.fbo);
|
||||||
|
glViewport(0, 0, currentWidth, currentHeight);
|
||||||
|
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT);
|
||||||
|
drawFullscreenQuad();
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
glEnable(GL_DEPTH_TEST);
|
||||||
|
|
||||||
|
displayTexture = postTarget.texture;
|
||||||
|
|
||||||
|
if (settings.motionBlurEnabled && historyTarget.fbo != 0) {
|
||||||
|
glBindFramebuffer(GL_READ_FRAMEBUFFER, postTarget.fbo);
|
||||||
|
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, historyTarget.fbo);
|
||||||
|
glBlitFramebuffer(0, 0, currentWidth, currentHeight, 0, 0, currentWidth, currentHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
||||||
|
historyValid = true;
|
||||||
|
} else {
|
||||||
|
clearHistory();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Renderer::renderScene(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int /*selectedId*/, float fovDeg, float nearPlane, float farPlane) {
|
||||||
|
renderSceneInternal(camera, sceneObjects, currentWidth, currentHeight, true, fovDeg, nearPlane, farPlane);
|
||||||
|
applyPostProcessing(sceneObjects);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int Renderer::renderScenePreview(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int width, int height, float fovDeg, float nearPlane, float farPlane) {
|
||||||
|
ensureRenderTarget(previewTarget, width, height);
|
||||||
|
if (previewTarget.fbo == 0) return 0;
|
||||||
|
|
||||||
|
glBindFramebuffer(GL_FRAMEBUFFER, previewTarget.fbo);
|
||||||
|
glViewport(0, 0, width, height);
|
||||||
|
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||||
|
|
||||||
|
renderSceneInternal(camera, sceneObjects, width, height, true, fovDeg, nearPlane, farPlane);
|
||||||
|
return previewTarget.texture;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Renderer::endRender() {
|
void Renderer::endRender() {
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public:
|
|||||||
bool hasTexCoords = false;
|
bool hasTexCoords = false;
|
||||||
glm::vec3 boundsMin = glm::vec3(FLT_MAX);
|
glm::vec3 boundsMin = glm::vec3(FLT_MAX);
|
||||||
glm::vec3 boundsMax = glm::vec3(-FLT_MAX);
|
glm::vec3 boundsMax = glm::vec3(-FLT_MAX);
|
||||||
|
std::vector<glm::vec3> triangleVertices; // positions duplicated per-triangle for picking
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -59,8 +60,23 @@ class Renderer {
|
|||||||
private:
|
private:
|
||||||
unsigned int framebuffer = 0, viewportTexture = 0, rbo = 0;
|
unsigned int framebuffer = 0, viewportTexture = 0, rbo = 0;
|
||||||
int currentWidth = 800, currentHeight = 600;
|
int currentWidth = 800, currentHeight = 600;
|
||||||
|
struct RenderTarget {
|
||||||
|
unsigned int fbo = 0;
|
||||||
|
unsigned int texture = 0;
|
||||||
|
unsigned int rbo = 0;
|
||||||
|
int width = 0;
|
||||||
|
int height = 0;
|
||||||
|
};
|
||||||
|
RenderTarget previewTarget;
|
||||||
|
RenderTarget postTarget;
|
||||||
|
RenderTarget historyTarget;
|
||||||
|
RenderTarget bloomTargetA;
|
||||||
|
RenderTarget bloomTargetB;
|
||||||
Shader* shader = nullptr;
|
Shader* shader = nullptr;
|
||||||
Shader* defaultShader = nullptr;
|
Shader* defaultShader = nullptr;
|
||||||
|
Shader* postShader = nullptr;
|
||||||
|
Shader* brightShader = nullptr;
|
||||||
|
Shader* blurShader = nullptr;
|
||||||
Texture* texture1 = nullptr;
|
Texture* texture1 = nullptr;
|
||||||
Texture* texture2 = nullptr;
|
Texture* texture2 = nullptr;
|
||||||
std::unordered_map<std::string, std::unique_ptr<Texture>> textureCache;
|
std::unordered_map<std::string, std::unique_ptr<Texture>> textureCache;
|
||||||
@@ -74,14 +90,30 @@ private:
|
|||||||
std::unordered_map<std::string, ShaderEntry> shaderCache;
|
std::unordered_map<std::string, ShaderEntry> shaderCache;
|
||||||
std::string defaultVertPath = "Resources/Shaders/vert.glsl";
|
std::string defaultVertPath = "Resources/Shaders/vert.glsl";
|
||||||
std::string defaultFragPath = "Resources/Shaders/frag.glsl";
|
std::string defaultFragPath = "Resources/Shaders/frag.glsl";
|
||||||
|
std::string postVertPath = "Resources/Shaders/postfx_vert.glsl";
|
||||||
|
std::string postFragPath = "Resources/Shaders/postfx_frag.glsl";
|
||||||
|
std::string postBrightFragPath = "Resources/Shaders/postfx_bright_frag.glsl";
|
||||||
|
std::string postBlurFragPath = "Resources/Shaders/postfx_blur_frag.glsl";
|
||||||
bool autoReloadShaders = true;
|
bool autoReloadShaders = true;
|
||||||
glm::vec3 ambientColor = glm::vec3(0.2f, 0.2f, 0.2f);
|
glm::vec3 ambientColor = glm::vec3(0.2f, 0.2f, 0.2f);
|
||||||
Mesh* cubeMesh = nullptr;
|
Mesh* cubeMesh = nullptr;
|
||||||
Mesh* sphereMesh = nullptr;
|
Mesh* sphereMesh = nullptr;
|
||||||
Mesh* capsuleMesh = nullptr;
|
Mesh* capsuleMesh = nullptr;
|
||||||
Skybox* skybox = nullptr;
|
Skybox* skybox = nullptr;
|
||||||
|
unsigned int quadVAO = 0;
|
||||||
|
unsigned int quadVBO = 0;
|
||||||
|
unsigned int displayTexture = 0;
|
||||||
|
bool historyValid = false;
|
||||||
|
|
||||||
void setupFBO();
|
void setupFBO();
|
||||||
|
void ensureRenderTarget(RenderTarget& target, int w, int h);
|
||||||
|
void ensureQuad();
|
||||||
|
void drawFullscreenQuad();
|
||||||
|
void clearHistory();
|
||||||
|
void clearTarget(RenderTarget& target);
|
||||||
|
void renderSceneInternal(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int width, int height, bool unbindFramebuffer, float fovDeg, float nearPlane, float farPlane);
|
||||||
|
void applyPostProcessing(const std::vector<SceneObject>& sceneObjects);
|
||||||
|
PostFXSettings gatherPostFX(const std::vector<SceneObject>& sceneObjects) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Renderer() = default;
|
Renderer() = default;
|
||||||
@@ -100,9 +132,10 @@ public:
|
|||||||
void beginRender(const glm::mat4& view, const glm::mat4& proj, const glm::vec3& cameraPos);
|
void beginRender(const glm::mat4& view, const glm::mat4& proj, const glm::vec3& cameraPos);
|
||||||
void renderSkybox(const glm::mat4& view, const glm::mat4& proj);
|
void renderSkybox(const glm::mat4& view, const glm::mat4& proj);
|
||||||
void renderObject(const SceneObject& obj);
|
void renderObject(const SceneObject& obj);
|
||||||
void renderScene(const Camera& camera, const std::vector<SceneObject>& sceneObjects);
|
void renderScene(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int selectedId = -1, float fovDeg = FOV, float nearPlane = NEAR_PLANE, float farPlane = FAR_PLANE);
|
||||||
|
unsigned int renderScenePreview(const Camera& camera, const std::vector<SceneObject>& sceneObjects, int width, int height, float fovDeg, float nearPlane, float farPlane);
|
||||||
void endRender();
|
void endRender();
|
||||||
|
|
||||||
Skybox* getSkybox() { return skybox; }
|
Skybox* getSkybox() { return skybox; }
|
||||||
unsigned int getViewportTexture() const { return viewportTexture; }
|
unsigned int getViewportTexture() const { return displayTexture ? displayTexture : viewportTexture; }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,7 +11,9 @@ enum class ObjectType {
|
|||||||
DirectionalLight,
|
DirectionalLight,
|
||||||
PointLight,
|
PointLight,
|
||||||
SpotLight,
|
SpotLight,
|
||||||
AreaLight
|
AreaLight,
|
||||||
|
Camera,
|
||||||
|
PostFXNode
|
||||||
};
|
};
|
||||||
|
|
||||||
struct MaterialProperties {
|
struct MaterialProperties {
|
||||||
@@ -42,6 +44,33 @@ struct LightComponent {
|
|||||||
bool enabled = true;
|
bool enabled = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum class SceneCameraType {
|
||||||
|
Scene = 0,
|
||||||
|
Player = 1
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CameraComponent {
|
||||||
|
SceneCameraType type = SceneCameraType::Scene;
|
||||||
|
float fov = FOV;
|
||||||
|
float nearClip = NEAR_PLANE;
|
||||||
|
float farClip = FAR_PLANE;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct PostFXSettings {
|
||||||
|
bool enabled = true;
|
||||||
|
bool bloomEnabled = true;
|
||||||
|
float bloomThreshold = 1.1f;
|
||||||
|
float bloomIntensity = 0.8f;
|
||||||
|
float bloomRadius = 1.5f;
|
||||||
|
bool colorAdjustEnabled = false;
|
||||||
|
float exposure = 0.0f; // in EV stops
|
||||||
|
float contrast = 1.0f;
|
||||||
|
float saturation = 1.0f;
|
||||||
|
glm::vec3 colorFilter = glm::vec3(1.0f);
|
||||||
|
bool motionBlurEnabled = false;
|
||||||
|
float motionBlurStrength = 0.15f; // 0..1 blend with previous frame
|
||||||
|
};
|
||||||
|
|
||||||
enum class ConsoleMessageType {
|
enum class ConsoleMessageType {
|
||||||
Info,
|
Info,
|
||||||
Warning,
|
Warning,
|
||||||
@@ -71,6 +100,8 @@ public:
|
|||||||
std::string fragmentShaderPath;
|
std::string fragmentShaderPath;
|
||||||
bool useOverlay = false;
|
bool useOverlay = false;
|
||||||
LightComponent light; // Only used when type is a light
|
LightComponent light; // Only used when type is a light
|
||||||
|
CameraComponent camera; // Only used when type is camera
|
||||||
|
PostFXSettings postFx; // Only used when type is PostFXNode
|
||||||
|
|
||||||
SceneObject(const std::string& name, ObjectType type, int id)
|
SceneObject(const std::string& name, ObjectType type, int id)
|
||||||
: name(name), type(type), position(0.0f), rotation(0.0f), scale(1.0f), id(id) {}
|
: name(name), type(type), position(0.0f), rotation(0.0f), scale(1.0f), id(id) {}
|
||||||
|
|||||||
Reference in New Issue
Block a user