diff --git a/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java b/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java index be58c7c..1f6bb65 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/ModelRender.java @@ -2,20 +2,20 @@ package com.chuangzhou.vivid2D.render; import com.chuangzhou.vivid2D.render.model.Model2D; import com.chuangzhou.vivid2D.render.model.ModelPart; -import com.chuangzhou.vivid2D.render.model.buffer.BufferBuilder; +import com.chuangzhou.vivid2D.render.systems.buffer.BufferBuilder; +import com.chuangzhou.vivid2D.render.systems.buffer.Tesselator; import com.chuangzhou.vivid2D.render.model.util.LightSource; import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; +import com.chuangzhou.vivid2D.render.systems.ShaderSources; import org.joml.Matrix3f; import org.joml.Vector2f; import org.joml.Vector4f; import org.lwjgl.opengl.*; -import org.lwjgl.system.MemoryUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -import java.nio.ByteBuffer; -import java.nio.FloatBuffer; import java.util.*; import java.util.concurrent.atomic.AtomicInteger; @@ -45,82 +45,152 @@ import static org.lwjgl.opengl.GL20.glGetUniformLocation; * * * @author tzdwindows - * @version 1.0 + * @version 1.2 * @since 2025-10-13 */ public final class ModelRender { + /** + * 渲染系统日志记录器,用于记录渲染过程中的调试信息、错误和性能数据 + */ private static final Logger logger = LoggerFactory.getLogger(ModelRender.class); + + /** + * 私有构造函数,防止外部实例化 - 这是一个工具类,只包含静态方法 + */ private ModelRender() { /* no instances */ } - // ================== 全局状态 ================== +// ================== 全局状态 ================== + + /** + * 渲染系统初始化状态标志,确保系统只初始化一次 + * @see #initialize() + * @see #isInitialized() + */ private static boolean initialized = false; + + /** + * 视口宽度(像素),定义渲染区域的大小 + * 默认值:800像素 + * @see #setViewport(int, int) + */ private static int viewportWidth = 800; + + /** + * 视口高度(像素),定义渲染区域的大小 + * 默认值:600像素 + * @see #setViewport(int, int) + */ private static int viewportHeight = 600; + + /** + * 清除颜色(RGBA),用于在每帧开始时清空颜色缓冲区 + * 默认值:黑色不透明 (0.0f, 0.0f, 0.0f, 1.0f) + * @see RenderSystem#clearColor(float, float, float, float) + */ private static final Vector4f CLEAR_COLOR = new Vector4f(0.0f, 0.0f, 0.0f, 1.0f); + + /** + * 深度测试启用标志,控制是否进行深度缓冲测试 + * 在2D渲染中通常禁用以提高性能 + * 默认值:false(禁用) + * @see RenderSystem#enableDepthTest() + * @see RenderSystem#disableDepthTest() + */ private static final boolean enableDepthTest = false; + + /** + * 混合功能启用标志,控制透明度和颜色混合 + * 默认值:true(启用) + * @see RenderSystem#enableBlend() + * @see RenderSystem#disableBlend() + */ private static final boolean enableBlending = true; - // 着色器与资源 - private static final Map shaderMap = new HashMap<>(); - private static ShaderProgram defaultProgram = null; + private static final int SHADER_MAX_LIGHTS = 8; +// ================== 着色器与资源管理 ================== + + /** + * 着色器程序缓存映射,按名称存储已编译的着色器程序 + * 键:着色器名称(如 "default") + * 值:对应的着色器程序对象 + * @see ShaderSources.ShaderProgram + */ + private static final Map shaderMap = new HashMap<>(); + + /** + * 默认着色器程序,用于大多数模型的渲染 + * 包含基础的光照、纹理和变换功能 + * @see #compileDefaultShader() + */ + private static ShaderSources.ShaderProgram defaultProgram = null; + + /** + * 网格GPU资源缓存,管理已上传到GPU的网格数据 + * 键:Mesh2D对象 + * 值:对应的OpenGL资源(VAO、VBO、EBO) + * @see MeshGLResources + */ private static final Map meshResources = new HashMap<>(); + + /** + * 纹理单元分配器,用于管理多个纹理的绑定 + * 确保不同的纹理绑定到正确的纹理单元 + * 默认从0开始递增分配 + */ private static final AtomicInteger textureUnitAllocator = new AtomicInteger(0); - // 默认白色纹理 + /** + * 默认白色纹理ID,当模型没有指定纹理时使用 + * 这是一个1x1的纯白色纹理,确保模型有基本的颜色显示 + * @see #createDefaultTexture() + */ private static int defaultTextureId = 0; - // ================== 碰撞箱渲染配置 ================== - // 是否在渲染时绘制碰撞箱(线框) - public static boolean renderColliders = false; - // 碰撞箱线宽 - public static float colliderLineWidth = 2.0f; - // 碰撞箱颜色(默认白色) +// ================== 碰撞箱渲染配置 ================== + + /** + * 碰撞箱渲染开关,控制是否在场景中显示物理碰撞体的轮廓 + * 调试时非常有用,可以直观看到碰撞边界 + * 默认值:true(启用) + */ + public static boolean renderColliders = true; + + /** + * 碰撞箱线框宽度,控制碰撞体轮廓线的粗细 + * 单位:像素 + * 默认值:1.0f + */ + public static float colliderLineWidth = 1.0f; + + /** + * 碰撞箱颜色(RGBA),定义碰撞体轮廓的显示颜色 + * 默认值:白色不透明 (1.0f, 1.0f, 1.0f, 1.0f) + */ public static Vector4f colliderColor = new Vector4f(1.0f, 1.0f, 1.0f, 1.0f); - // 圆形碰撞体绘制细分(越高越圆) + + /** + * 圆形碰撞体细分数量,控制圆形碰撞体的平滑度 + * 值越高圆形越平滑,但渲染开销也越大 + * 默认值:32(在性能和视觉效果间取得平衡) + */ private static final int CIRCLE_SEGMENTS = 32; - // 是否在渲染时绘制碰撞箱 + + /** + * 光源位置渲染开关,控制是否在场景中显示光源的位置 + * 用点状标记显示每个启用的光源位置 + * 默认值:true(启用) + */ public static boolean renderLightPositions = true; - // ================== 内部类:ShaderProgram ================== - private static class ShaderProgram { - final int programId; - final Map uniformCache = new HashMap<>(); + // ================== 内部类:ShaderSources.ShaderProgram ================== - ShaderProgram(int programId) { - this.programId = programId; - } - - void use() { - GL20.glUseProgram(programId); - } - - void stop() { - GL20.glUseProgram(0); - } - - int getUniformLocation(String name) { - return uniformCache.computeIfAbsent(name, k -> { - int loc = glGetUniformLocation(programId, k); - if (loc == -1) { - // debug 时可以打开 - logger.warn("Warning: uniform not found: {}", k); - } - return loc; - }); - } - - void delete() { - if (GL20.glIsProgram(programId)) GL20.glDeleteProgram(programId); - } - } // ================== 内部类:MeshGLResources ================== private static class MeshGLResources { int vao = 0; int vbo = 0; int ebo = 0; - int vertexCount = 0; boolean initialized = false; void dispose() { @@ -131,179 +201,19 @@ public final class ModelRender { } } - // ================== 着色器源 ================== - private static final String VERTEX_SHADER_SRC = - """ - #version 330 core - layout(location = 0) in vec2 aPosition; - layout(location = 1) in vec2 aTexCoord; - out vec2 vTexCoord; - out vec2 vWorldPos; - - uniform mat3 uModelMatrix; - uniform mat3 uViewMatrix; - uniform mat3 uProjectionMatrix; - - void main() { - vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0); - gl_Position = vec4(p.xy, 0.0, 1.0); - vTexCoord = aTexCoord; - vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy; - } - """; - - private static final String FRAGMENT_SHADER_SRC = - """ - #version 330 core - in vec2 vTexCoord; - in vec2 vWorldPos; - out vec4 FragColor; - - uniform sampler2D uTexture; - uniform vec4 uColor; - uniform float uOpacity; - uniform int uBlendMode; - uniform int uDebugMode; - - #define MAX_LIGHTS 8 - uniform vec2 uLightsPos[MAX_LIGHTS]; - uniform vec3 uLightsColor[MAX_LIGHTS]; - uniform float uLightsIntensity[MAX_LIGHTS]; - uniform int uLightsIsAmbient[MAX_LIGHTS]; - uniform int uLightCount; - - // 辉光相关 - uniform int uLightsIsGlow[MAX_LIGHTS]; - uniform vec2 uLightsGlowDir[MAX_LIGHTS]; - uniform float uLightsGlowIntensity[MAX_LIGHTS]; - uniform float uLightsGlowRadius[MAX_LIGHTS]; - uniform float uLightsGlowAmount[MAX_LIGHTS]; - - // 常用衰减系数(可在 shader 内微调) - const float ATT_CONST = 1.0; - const float ATT_LINEAR = 0.09; - const float ATT_QUAD = 0.032; - - // 简单 Reinhard tone mapping,避免过曝 - vec3 toneMap(vec3 color) { - return color / (color + vec3(1.0)); - } - - void main() { - // 先采样纹理 - vec4 tex = texture(uTexture, vTexCoord); - float alpha = tex.a * uOpacity; - if (alpha <= 0.001) discard; - - // 基础颜色(纹理 * 部件颜色) - vec3 baseColor = tex.rgb * uColor.rgb; - - // 如果没有光源,仅返回基础颜色(节约性能) - if (uLightCount == 0) { - vec3 outCol = clamp(baseColor, 0.0, 1.0); - if (uBlendMode == 1) outCol = tex.rgb + uColor.rgb; - else if (uBlendMode == 2) outCol = tex.rgb * uColor.rgb; - else if (uBlendMode == 3) outCol = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb); - FragColor = vec4(outCol, alpha); - return; - } - - // 环境光基线 - vec3 ambientBase = vec3(0.06); - vec3 lighting = vec3(0.0); - vec3 glowAccum = vec3(0.0); - vec3 specularAccum = vec3(0.0); - - // 累积显式标记为环境光的光源 - for (int i = 0; i < uLightCount; ++i) { - if (uLightsIsAmbient[i] == 1) { - lighting += uLightsColor[i] * uLightsIntensity[i]; - } - } - lighting += ambientBase; - - // 对每个非环境光源计算物理式衰减 + 漫反射 + 简单高光 + 辉光(若启用) - for (int i = 0; i < uLightCount; ++i) { - if (uLightsIsAmbient[i] == 1) continue; - - vec2 toLight2 = uLightsPos[i] - vWorldPos; - float dist = length(toLight2); - // 物理风格衰减 - float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist); - float radiance = uLightsIntensity[i] * attenuation; - - // 漫反射(在二维中基于距离模拟衰减的明暗) - // 使用更平滑的距离曲线:max(0, 1 - (dist / (radiusApprox))) - float radiusApprox = max(1.0, 1000.0 * attenuation); // 通过衰减估算影响半径 - float diffuseFactor = clamp(1.0 - (dist / (radiusApprox + 0.0001)), 0.0, 1.0); - vec3 diff = uLightsColor[i] * radiance * diffuseFactor; - lighting += diff; - - // 简单高光(在 2D 中模拟亮点) - vec3 viewDir = vec3(0.0, 0.0, 1.0); - vec3 lightDir3 = normalize(vec3(toLight2, 0.0)); - vec3 normal = vec3(0.0, 0.0, 1.0); - vec3 reflectDir = reflect(-lightDir3, normal); - float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 32.0); - float specIntensity = 0.25; - specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity; - - // 若启用了辉光(glow),使用高斯风格衰减,并支持方向性辉光 - if (uLightsIsGlow[i] == 1) { - float glowRadius = max(0.0001, uLightsGlowRadius[i]); - float gdist = dist; - // 高斯分布:exp(-(d^2) / (2 * sigma^2)) - float sigma = glowRadius * 0.5; // sigma = radius * 0.5(经验值) - float gauss = exp(- (gdist * gdist) / (2.0 * sigma * sigma)); - - // 方向性因子:如果给出方向,使用方向与片元向量点积来增强朝向一侧的辉光 - float dirFactor = 1.0; - vec2 glowDir = uLightsGlowDir[i]; - if (length(glowDir) > 0.0001) { - vec2 ndir = normalize(glowDir); - vec2 toFrag = normalize(vWorldPos - uLightsPos[i]); - dirFactor = max(dot(ndir, toFrag), 0.0); // 只在方向半球贡献 - } - float gIntensity = uLightsGlowIntensity[i]; - float gAmount = uLightsGlowAmount[i]; - vec3 glow = uLightsColor[i] * gauss * gIntensity * gAmount * dirFactor; - glowAccum += glow; - } - } - - // 合并直接光照与高光后进行简单的色调映射(避免过曝) - vec3 totalLighting = lighting + specularAccum; - // 防止数值过大,进行 Reinhard tone mapping - vec3 litMapped = toneMap(totalLighting); - vec3 finalColor = baseColor * litMapped; - - // 将辉光作为屏幕加色(加法混合),然后再做一次 tone map 以稳定输出 - finalColor += glowAccum; - finalColor = toneMap(finalColor); - - // 支持简单的 blend 模式(保留已有行为) - if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb; - else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb; - else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb); - - finalColor = clamp(finalColor, 0.0, 1.0); - FragColor = vec4(finalColor, alpha); - } - """; - // ================== 初始化 / 清理 ================== public static synchronized void initialize() { if (initialized) return; logger.info("Initializing ModelRender..."); - // 需要在外部创建 OpenGL 上下文并调用 GL.createCapabilities() - logGLInfo(); + // 初始化渲染系统 + RenderSystem.beginInitialization(); + RenderSystem.initRenderThread(); - // 初始 GL 状态 + logGLInfo(); setupGLState(); - // 创建默认 shader try { compileDefaultShader(); } catch (RuntimeException ex) { @@ -311,24 +221,24 @@ public final class ModelRender { throw ex; } - // 创建默认纹理 createDefaultTexture(); - - // 初始化视口 - GL11.glViewport(0, 0, viewportWidth, viewportHeight); + RenderSystem.viewport(0, 0, viewportWidth, viewportHeight); + RenderSystem.finishInitialization(); initialized = true; logger.info("ModelRender initialized successfully"); } private static void logGLInfo() { - logger.info("OpenGL Vendor: {}", GL11.glGetString(GL11.GL_VENDOR)); - logger.info("OpenGL Renderer: {}", GL11.glGetString(GL11.GL_RENDERER)); - logger.info("OpenGL Version: {}", GL11.glGetString(GL11.GL_VERSION)); - logger.info("GLSL Version: {}", GL20.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION)); + logger.info("OpenGL Vendor: {}", RenderSystem.getVendor()); + logger.info("OpenGL Renderer: {}", RenderSystem.getRenderer()); + logger.info("OpenGL Version: {}", RenderSystem.getOpenGLVersion()); + logger.info("GLSL Version: {}", RenderSystem.getGLSLVersion()); + RenderSystem.logDetailedGLInfo(); } - private static void uploadLightsToShader(ShaderProgram sp, Model2D model) { + + private static void uploadLightsToShader(ShaderSources.ShaderProgram sp, Model2D model) { List lights = model.getLights(); int idx = 0; @@ -372,33 +282,34 @@ public final class ModelRender { } } + private static void setupGLState() { - GL11.glClearColor(CLEAR_COLOR.x, CLEAR_COLOR.y, CLEAR_COLOR.z, CLEAR_COLOR.w); + RenderSystem.clearColor(CLEAR_COLOR.x, CLEAR_COLOR.y, CLEAR_COLOR.z, CLEAR_COLOR.w); if (enableBlending) { - GL11.glEnable(GL11.GL_BLEND); - GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + RenderSystem.enableBlend(); + RenderSystem.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); } else { - GL11.glDisable(GL11.GL_BLEND); + RenderSystem.disableBlend(); } if (enableDepthTest) { - GL11.glEnable(GL11.GL_DEPTH_TEST); - GL11.glDepthFunc(GL11.GL_LEQUAL); + RenderSystem.enableDepthTest(); + RenderSystem.depthFunc(GL11.GL_LEQUAL); + RenderSystem.depthMask(true); + RenderSystem.clearDepth(1.0); } else { - GL11.glDisable(GL11.GL_DEPTH_TEST); + RenderSystem.disableDepthTest(); } - GL11.glDisable(GL11.GL_CULL_FACE); - - checkGLError("setupGLState"); + RenderSystem.checkGLError("setupGLState"); } private static void compileDefaultShader() { - int vs = compileShader(GL20.GL_VERTEX_SHADER, VERTEX_SHADER_SRC); - int fs = compileShader(GL20.GL_FRAGMENT_SHADER, FRAGMENT_SHADER_SRC); + int vs = compileShader(GL20.GL_VERTEX_SHADER, ShaderSources.VERTEX_SHADER_SRC); + int fs = compileShader(GL20.GL_FRAGMENT_SHADER, ShaderSources.FRAGMENT_SHADER_SRC); int prog = linkProgram(vs, fs); - ShaderProgram sp = new ShaderProgram(prog); + ShaderSources.ShaderProgram sp = new ShaderSources.ShaderProgram(prog); shaderMap.put("default", sp); defaultProgram = sp; @@ -413,50 +324,19 @@ public final class ModelRender { } private static int compileShader(int type, String src) { - int shader = GL20.glCreateShader(type); - GL20.glShaderSource(shader, src); - GL20.glCompileShader(shader); - int status = GL20.glGetShaderi(shader, GL20.GL_COMPILE_STATUS); - if (status == GL11.GL_FALSE) { - String log = GL20.glGetShaderInfoLog(shader); - GL20.glDeleteShader(shader); - throw new RuntimeException("Shader compilation failed: " + log); - } - return shader; + RenderSystem.assertOnRenderThread(); + return RenderSystem.compileShader(type, src); } private static int linkProgram(int vs, int fs) { - int prog = GL20.glCreateProgram(); - GL20.glAttachShader(prog, vs); - GL20.glAttachShader(prog, fs); - GL20.glLinkProgram(prog); - int status = GL20.glGetProgrami(prog, GL20.GL_LINK_STATUS); - if (status == GL11.GL_FALSE) { - String log = GL20.glGetProgramInfoLog(prog); - GL20.glDeleteProgram(prog); - throw new RuntimeException("Program link failed: " + log); - } - // shaders can be deleted after linking - GL20.glDetachShader(prog, vs); - GL20.glDetachShader(prog, fs); - GL20.glDeleteShader(vs); - GL20.glDeleteShader(fs); - return prog; + RenderSystem.assertOnRenderThread(); + return RenderSystem.linkProgram(vs, fs); } private static void createDefaultTexture() { - // 使用 GL11.glGenTextures() 获取单个 id(更直观,避免 IntBuffer 问题) - defaultTextureId = GL11.glGenTextures(); - GL11.glBindTexture(GL11.GL_TEXTURE_2D, defaultTextureId); - ByteBuffer white = MemoryUtil.memAlloc(4); - white.put((byte)255).put((byte)255).put((byte)255).put((byte)255).flip(); - GL11.glTexImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 1, 1, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, white); - MemoryUtil.memFree(white); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, GL11.GL_NEAREST); - GL11.glTexParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, GL11.GL_NEAREST); - GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); - - checkGLError("createDefaultTexture"); + RenderSystem.assertOnRenderThread(); + defaultTextureId = RenderSystem.createDefaultTexture(); + RenderSystem.checkGLError("createDefaultTexture"); } public static synchronized void cleanup() { @@ -469,13 +349,13 @@ public final class ModelRender { meshResources.clear(); // shaders - for (ShaderProgram sp : shaderMap.values()) sp.delete(); + for (ShaderSources.ShaderProgram sp : shaderMap.values()) sp.delete(); shaderMap.clear(); defaultProgram = null; // textures if (defaultTextureId != 0) { - GL11.glDeleteTextures(defaultTextureId); + RenderSystem.deleteTextures(defaultTextureId); defaultTextureId = 0; } @@ -488,6 +368,12 @@ public final class ModelRender { if (!initialized) throw new IllegalStateException("ModelRender not initialized"); if (model == null) return; + // 确保在渲染线程 + RenderSystem.assertOnRenderThread(); + + // 添加前置错误检查 + RenderSystem.checkGLError("render_start"); + // 物理系统更新 PhysicsSystem physics = model.getPhysics(); if (physics != null && physics.isEnabled()) { @@ -496,18 +382,34 @@ public final class ModelRender { model.update(deltaTime); - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | (enableDepthTest ? GL11.GL_DEPTH_BUFFER_BIT : 0)); + // 检查清除操作前的状态 + RenderSystem.checkGLError("before_clear"); + RenderSystem.clear(GL11.GL_COLOR_BUFFER_BIT | (enableDepthTest ? GL11.GL_DEPTH_BUFFER_BIT : 0)); + RenderSystem.checkGLError("after_clear"); + + // 检查着色器程序 + if (defaultProgram == null || defaultProgram.programId == 0) { + logger.error("Default shader program is not initialized"); + return; + } defaultProgram.use(); + RenderSystem.checkGLError("after_use_program"); // 设置投影与视图 Matrix3f proj = buildOrthoProjection(viewportWidth, viewportHeight); setUniformMatrix3(defaultProgram, "uProjectionMatrix", proj); setUniformMatrix3(defaultProgram, "uViewMatrix", new Matrix3f().identity()); + RenderSystem.checkGLError("after_set_matrices"); // 添加光源数据上传 uploadLightsToShader(defaultProgram, model); + RenderSystem.checkGLError("after_upload_lights"); + + // 在渲染光源位置前检查 + RenderSystem.checkGLError("before_render_light_positions"); renderLightPositions(model); + RenderSystem.checkGLError("after_render_light_positions"); // 递归渲染所有根部件 Matrix3f identity = new Matrix3f().identity(); @@ -515,36 +417,81 @@ public final class ModelRender { if (p.getParent() != null) continue; renderPartRecursive(p, identity); } + RenderSystem.checkGLError("after_render_parts"); if (renderColliders && physics != null) { renderPhysicsColliders(physics); + RenderSystem.checkGLError("after_render_colliders"); } - - defaultProgram.stop(); - checkGLError("render"); + RenderSystem.checkGLError("render_end"); } private static void renderLightPositions(Model2D model) { if (!renderLightPositions) return; - - GL11.glPointSize(10.0f); - setUniformIntInternal(defaultProgram, "uDebugMode", 1); - + // 设置灯泡颜色为光源的颜色 for (LightSource light : model.getLights()) { if (!light.isEnabled()) continue; - // 绘制光源位置 - BufferBuilder bb = - new BufferBuilder(1 * 4); - bb.begin(GL11.GL_POINTS, 1); - bb.vertex(light.getPosition().x, light.getPosition().y, 0.5f, 0.5f); - bb.end(); - } + // 使用光源的颜色来绘制灯泡 + Vector4f lightColor = new Vector4f(light.getColor().x, light.getColor().y, light.getColor().z, 1.0f); + setUniformVec4Internal(defaultProgram, "uColor", lightColor); - setUniformIntInternal(defaultProgram, "uDebugMode", 0); - GL11.glPointSize(1.0f); + // 绘制灯泡形状 + drawLightBulb(light.getPosition(), light.getIntensity()); + + if (light.isAmbient()) { + drawCrossMark(light.getPosition(), light.getIntensity()); + } + } + // 恢复原始颜色 + setUniformVec4Internal(defaultProgram, "uColor", new Vector4f(1,1,1,1)); + } + + /** + * 绘制简洁的灯泡形状 + * @param position 灯泡位置 + * @param intensity 光源强度,用于控制灯泡大小 + */ + private static void drawLightBulb(Vector2f position, float intensity) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder builder = tesselator.getBuilder(); + float bulbSize = 3.0f + (intensity / 10.0f); + int segments = 16; + builder.begin(RenderSystem.DRAW_TRIANGLE_FAN, segments + 2); + + builder.vertex(position.x, position.y, 0.5f, 0.5f); + + for (int i = 0; i <= segments; i++) { + double angle = 2.0 * Math.PI * i / segments; + float x = position.x + bulbSize * (float) Math.cos(angle); + float y = position.y + bulbSize * (float) Math.sin(angle); + builder.vertex(x, y, 0.5f, 0.5f); + } + tesselator.end(); + } + + /** + * 绘制十字标记(用于环境光) + */ + private static void drawCrossMark(Vector2f position, float size) { + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder builder = tesselator.getBuilder(); + + float crossSize = size * 0.8f; + + // 绘制水平线 + builder.begin(RenderSystem.DRAW_LINES, 2); + builder.vertex(position.x - crossSize, position.y, 0.5f, 0.5f); + builder.vertex(position.x + crossSize, position.y, 0.5f, 0.5f); + tesselator.end(); + + // 绘制垂直线 + builder.begin(RenderSystem.DRAW_LINES, 2); + builder.vertex(position.x, position.y - crossSize, 0.5f, 0.5f); + builder.vertex(position.x, position.y + crossSize, 0.5f, 0.5f); + tesselator.end(); } /** @@ -587,7 +534,7 @@ public final class ModelRender { setUniformIntInternal(defaultProgram, "uTexture", 0); } else { // 使用默认白色纹理 - GL11.glBindTexture(GL11.GL_TEXTURE_2D, defaultTextureId); + RenderSystem.bindTexture(defaultTextureId); setUniformIntInternal(defaultProgram, "uTexture", 0); } @@ -597,43 +544,78 @@ public final class ModelRender { // 调用 Mesh2D 的 draw 方法,传入当前使用的着色器程序和变换矩阵 mesh.draw(defaultProgram.programId, matToUse); - checkGLError("renderMesh"); + RenderSystem.checkGLError("renderMesh"); } // ================== 渲染碰撞箱相关实现 ================== private static void renderPhysicsColliders(PhysicsSystem physics) { - // 设置渲染状态 - GL11.glLineWidth(colliderLineWidth); + if (physics == null) { + logger.warn("renderPhysicsColliders: physics system is null"); + return; + } + + // 设置渲染状态 + RenderSystem.checkGLError("before_set_line_width"); + RenderSystem.lineWidth(colliderLineWidth); + RenderSystem.checkGLError("after_set_line_width"); + + RenderSystem.activeTexture(RenderSystem.GL_TEXTURE0); + RenderSystem.bindTexture(defaultTextureId); + RenderSystem.checkGLError("after_bind_texture"); - // 绑定默认纹理(shader 依赖 uTexture)并设置颜色/opacity - GL13.glActiveTexture(GL13.GL_TEXTURE0); - GL11.glBindTexture(GL11.GL_TEXTURE_2D, defaultTextureId); setUniformIntInternal(defaultProgram, "uTexture", 0); setUniformVec4Internal(defaultProgram, "uColor", colliderColor); setUniformFloatInternal(defaultProgram, "uOpacity", 1.0f); setUniformIntInternal(defaultProgram, "uBlendMode", 0); setUniformIntInternal(defaultProgram, "uDebugMode", 0); + RenderSystem.checkGLError("after_set_uniforms"); // 使用单位矩阵作为 model(碰撞体顶点按世界坐标提供) setUniformMatrix3(defaultProgram, "uModelMatrix", new Matrix3f().identity()); + RenderSystem.checkGLError("after_set_model_matrix"); - for (PhysicsSystem.PhysicsCollider collider : physics.getColliders()) { - if (!collider.isEnabled()) continue; + List colliders = physics.getColliders(); + if (colliders == null || colliders.isEmpty()) { + logger.debug("No colliders to render"); + return; + } + + int enabledColliders = 0; + for (PhysicsSystem.PhysicsCollider collider : colliders) { + if (collider == null || !collider.isEnabled()) continue; + + RenderSystem.checkGLError("before_render_collider_" + enabledColliders); if (collider instanceof PhysicsSystem.CircleCollider) { PhysicsSystem.CircleCollider c = (PhysicsSystem.CircleCollider) collider; - drawCircleColliderWire(c.getCenter(), c.getRadius()); + if (c.getCenter() != null && c.getRadius() > 0) { + drawCircleColliderWire(c.getCenter(), c.getRadius()); + enabledColliders++; + } else { + logger.warn("Invalid CircleCollider: center={}, radius={}", c.getCenter(), c.getRadius()); + } } else if (collider instanceof PhysicsSystem.RectangleCollider) { PhysicsSystem.RectangleCollider r = (PhysicsSystem.RectangleCollider) collider; - drawRectangleColliderWire(r.getCenter(), r.getWidth(), r.getHeight()); + if (r.getCenter() != null && r.getWidth() > 0 && r.getHeight() > 0) { + drawRectangleColliderWire(r.getCenter(), r.getWidth(), r.getHeight()); + enabledColliders++; + } else { + logger.warn("Invalid RectangleCollider: center={}, width={}, height={}", + r.getCenter(), r.getWidth(), r.getHeight()); + } } else { - // 未知类型:尝试调用 collidesWith 以获取位置(跳过) + logger.warn("Unknown collider type: {}", collider.getClass().getSimpleName()); } + + RenderSystem.checkGLError("after_render_collider_" + enabledColliders); } + logger.debug("Rendered {} enabled colliders", enabledColliders); + // 恢复默认线宽 - GL11.glLineWidth(1.0f); + RenderSystem.lineWidth(1.0f); + RenderSystem.checkGLError("after_reset_line_width"); } @@ -643,16 +625,18 @@ public final class ModelRender { */ private static void drawCircleColliderWire(Vector2f center, float radius) { int segments = Math.max(8, CIRCLE_SEGMENTS); - BufferBuilder bb = new BufferBuilder(segments * 4); - bb.begin(GL11.GL_LINE_LOOP, segments); + + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder builder = tesselator.getBuilder(); + + builder.begin(RenderSystem.DRAW_LINE_LOOP, segments); for (int i = 0; i < segments; i++) { double ang = 2.0 * Math.PI * i / segments; float x = center.x + radius * (float) Math.cos(ang); float y = center.y + radius * (float) Math.sin(ang); - // 给常量 texcoord - bb.vertex(x, y, 0.5f, 0.5f); + builder.vertex(x, y, 0.5f, 0.5f); } - bb.end(); + tesselator.end(); } /** @@ -661,117 +645,52 @@ public final class ModelRender { private static void drawRectangleColliderWire(Vector2f center, float width, float height) { float halfW = width / 2.0f; float halfH = height / 2.0f; - BufferBuilder bb = new BufferBuilder(4 * 4); - bb.begin(GL11.GL_LINE_LOOP, 4); - bb.vertex(center.x - halfW, center.y - halfH, 0.5f, 0.5f); - bb.vertex(center.x + halfW, center.y - halfH, 0.5f, 0.5f); - bb.vertex(center.x + halfW, center.y + halfH, 0.5f, 0.5f); - bb.vertex(center.x - halfW, center.y + halfH, 0.5f, 0.5f); - bb.end(); - } - /** - * 从 float[] (x,y,u,v interleaved) 绘制 GL_LINE_LOOP - */ - private static void drawLineLoopFromFloatArray(float[] interleavedXYUV, int vertexCount) { - // 创建 VAO/VBO - int vao = GL30.glGenVertexArrays(); - int vbo = GL15.glGenBuffers(); + Tesselator tesselator = Tesselator.getInstance(); + BufferBuilder builder = tesselator.getBuilder(); - GL30.glBindVertexArray(vao); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - - // 上传数据 - FloatBuffer fb = MemoryUtil.memAllocFloat(interleavedXYUV.length); - fb.put(interleavedXYUV).flip(); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, fb, GL15.GL_DYNAMIC_DRAW); - MemoryUtil.memFree(fb); - - // attrib 0 -> aPosition (vec2) - GL20.glEnableVertexAttribArray(0); - GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, 4 * Float.BYTES, 0); - - // attrib 1 -> aTexCoord (vec2) (提供常量 texcoord) - GL20.glEnableVertexAttribArray(1); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, 4 * Float.BYTES, 2 * Float.BYTES); - - // 绘制线环 - GL11.glDrawArrays(GL11.GL_LINE_LOOP, 0, vertexCount); - - // 清理 - GL20.glDisableVertexAttribArray(0); - GL20.glDisableVertexAttribArray(1); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - GL30.glBindVertexArray(0); - - GL15.glDeleteBuffers(vbo); - GL30.glDeleteVertexArrays(vao); - - checkGLError("drawLineLoopFromFloatArray"); + builder.begin(RenderSystem.DRAW_LINE_LOOP, 4); + builder.vertex(center.x - halfW, center.y - halfH, 0.5f, 0.5f); + builder.vertex(center.x + halfW, center.y - halfH, 0.5f, 0.5f); + builder.vertex(center.x + halfW, center.y + halfH, 0.5f, 0.5f); + builder.vertex(center.x - halfW, center.y + halfH, 0.5f, 0.5f); + tesselator.end(); } // ================== uniform 设置辅助(内部使用,确保 program 已绑定) ================== - private static void setUniformIntInternal(ShaderProgram sp, String name, int value) { + private static void setUniformIntInternal(ShaderSources.ShaderProgram sp, String name, int value) { int loc = sp.getUniformLocation(name); - if (loc != -1) GL20.glUniform1i(loc, value); + if (loc != -1) RenderSystem.uniform1i(loc, value); } - private static void setUniformVec3Internal(ShaderProgram sp, String name, org.joml.Vector3f vec) { + private static void setUniformVec3Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector3f vec) { int loc = sp.getUniformLocation(name); - if (loc != -1) { - GL20.glUniform3f(loc, vec.x, vec.y, vec.z); - } + if (loc != -1) RenderSystem.uniform3f(loc, vec); } - private static void setUniformVec2Internal(ShaderProgram sp, String name, org.joml.Vector2f vec) { + private static void setUniformVec2Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector2f vec) { int loc = sp.getUniformLocation(name); - if (loc != -1) { - GL20.glUniform2f(loc, vec.x, vec.y); - } + if (loc != -1) RenderSystem.uniform2f(loc, vec); } - private static void setUniformFloatInternal(ShaderProgram sp, String name, float value) { + private static void setUniformFloatInternal(ShaderSources.ShaderProgram sp, String name, float value) { int loc = sp.getUniformLocation(name); - if (loc != -1) GL20.glUniform1f(loc, value); + if (loc != -1) RenderSystem.uniform1f(loc, value); } - private static void setUniformVec4Internal(ShaderProgram sp, String name, Vector4f vec) { + private static void setUniformVec4Internal(ShaderSources.ShaderProgram sp, String name, org.joml.Vector4f vec) { int loc = sp.getUniformLocation(name); - if (loc != -1) GL20.glUniform4f(loc, vec.x, vec.y, vec.z, vec.w); + if (loc != -1) RenderSystem.uniform4f(loc, vec); } - private static void setUniformMatrix3(ShaderProgram sp, String name, Matrix3f m) { + private static void setUniformMatrix3(ShaderSources.ShaderProgram sp, String name, org.joml.Matrix3f m) { int loc = sp.getUniformLocation(name); if (loc == -1) return; - FloatBuffer fb = MemoryUtil.memAllocFloat(9); - try { - m.get(fb); - GL20.glUniformMatrix3fv(loc, false, fb); - } finally { - MemoryUtil.memFree(fb); - } - } - - - // 外部可用的统一设置(会自动切换到默认程序) - private static void setUniformInt(String name, int value) { - defaultProgram.use(); - setUniformIntInternal(defaultProgram, name, value); - defaultProgram.stop(); - } - private static void setUniformFloat(String name, float value) { - defaultProgram.use(); - setUniformFloatInternal(defaultProgram, name, value); - defaultProgram.stop(); - } - private static void setUniformVec4(String name, Vector4f v) { - defaultProgram.use(); - setUniformVec4Internal(defaultProgram, name, v); - defaultProgram.stop(); + RenderSystem.uniformMatrix3(loc, m); } // ================== 部件属性 ================== - private static void setPartUniforms(ShaderProgram sp, ModelPart part) { + private static void setPartUniforms(ShaderSources.ShaderProgram sp, ModelPart part) { setUniformFloatInternal(sp, "uOpacity", part.getOpacity()); int blend = 0; ModelPart.BlendMode bm = part.getBlendMode(); @@ -804,28 +723,7 @@ public final class ModelRender { public static void setViewport(int width, int height) { viewportWidth = Math.max(1, width); viewportHeight = Math.max(1, height); - GL11.glViewport(0, 0, viewportWidth, viewportHeight); - } - - public static void setClearColor(float r, float g, float b, float a) { - GL11.glClearColor(r,g,b,a); - } - - private static void checkGLError(String op) { - int e = GL11.glGetError(); - if (e != GL11.GL_NO_ERROR) { - //logger.error("OpenGL error during {}: {}", op, getGLErrorString(e)); - } - } - - private static String getGLErrorString(int err) { - switch (err) { - case GL11.GL_INVALID_ENUM: return "GL_INVALID_ENUM"; - case GL11.GL_INVALID_VALUE: return "GL_INVALID_VALUE"; - case GL11.GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; - case GL11.GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; - default: return "Unknown(0x" + Integer.toHexString(err) + ")"; - } + RenderSystem.viewport(0, 0, viewportWidth, viewportHeight); } // ================== 辅助:外部获取状态 ================== diff --git a/src/main/java/com/chuangzhou/vivid2D/render/ModelGLPanel.java b/src/main/java/com/chuangzhou/vivid2D/render/awt/ModelGLPanel.java similarity index 94% rename from src/main/java/com/chuangzhou/vivid2D/render/ModelGLPanel.java rename to src/main/java/com/chuangzhou/vivid2D/render/awt/ModelGLPanel.java index 5bfd0b7..fcdf305 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/ModelGLPanel.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/awt/ModelGLPanel.java @@ -1,10 +1,11 @@ -package com.chuangzhou.vivid2D.render; +package com.chuangzhou.vivid2D.render.awt; +import com.chuangzhou.vivid2D.render.ModelRender; import com.chuangzhou.vivid2D.render.model.Model2D; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.lwjgl.glfw.*; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL13; import org.lwjgl.system.MemoryUtil; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -127,19 +128,24 @@ public class ModelGLPanel extends JPanel { GLFW.glfwMakeContextCurrent(windowId); GL.createCapabilities(); - GL11.glPixelStorei(GL11.GL_PACK_ALIGNMENT, 1); + // 使用 RenderSystem 初始化 OpenGL 状态 + RenderSystem.beginInitialization(); + RenderSystem.initRenderThread(); + + RenderSystem.pixelStore(RenderSystem.GL_PACK_ALIGNMENT, 1); + // 初始化 OpenGL 状态 - GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderSystem.enableDepthTest(); // 检查是否支持多重采样 - if (GL.getCapabilities().OpenGL13) { - GL11.glEnable(GL13.GL_MULTISAMPLE); + if (RenderSystem.isExtensionSupported("GL_ARB_multisample")) { + RenderSystem.enable(RenderSystem.GL_MULTISAMPLE); logger.info("多重采样已启用"); } else { logger.info("不支持多重采样,跳过启用"); } - GL11.glViewport(0, 0, width, height); + RenderSystem.viewport(0, 0, width, height); // 按当前宽高分配像素读取缓冲 int pixelCount = Math.max(1, width * height); @@ -150,6 +156,8 @@ public class ModelGLPanel extends JPanel { ModelRender.initialize(); + RenderSystem.finishInitialization(); + // 在正确的上下文中加载模型(可能会耗时) loadModelInContext(); @@ -257,9 +265,9 @@ public class ModelGLPanel extends JPanel { Model2D currentModel = modelRef.get(); if (currentModel != null) { try { - // 清除缓冲区 - GL11.glClearColor(0.18f, 0.18f, 0.25f, 1f); - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + // 使用 RenderSystem 清除缓冲区 + RenderSystem.setClearColor(0.18f, 0.18f, 0.25f, 1f); + RenderSystem.clear(RenderSystem.GL_COLOR_BUFFER_BIT | RenderSystem.GL_DEPTH_BUFFER_BIT); // 渲染模型 ModelRender.render(1.0f / 60f, currentModel); @@ -302,8 +310,8 @@ public class ModelGLPanel extends JPanel { * 渲染默认背景 */ private void renderDefaultBackground() { - GL11.glClearColor(0.1f, 0.1f, 0.15f, 1f); - GL11.glClear(GL11.GL_COLOR_BUFFER_BIT | GL11.GL_DEPTH_BUFFER_BIT); + RenderSystem.setClearColor(0.1f, 0.1f, 0.15f, 1f); + RenderSystem.clear(RenderSystem.GL_COLOR_BUFFER_BIT | RenderSystem.GL_DEPTH_BUFFER_BIT); readPixelsToImage(); } @@ -328,8 +336,8 @@ public class ModelGLPanel extends JPanel { } pixelBuffer.clear(); - // 从 GPU 读取 RGBA 字节到本地缓冲 - GL11.glReadPixels(0, 0, w, h, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, pixelBuffer); + // 从 GPU 读取 RGBA 字节到本地缓冲 - 使用 RenderSystem + RenderSystem.readPixels(0, 0, w, h, RenderSystem.GL_RGBA, RenderSystem.GL_UNSIGNED_BYTE, pixelBuffer); // 以 int 批量读取(依赖本机字节序),然后转换为带 alpha 的 ARGB 并垂直翻转 IntBuffer ib = pixelBuffer.asIntBuffer(); @@ -539,7 +547,7 @@ public class ModelGLPanel extends JPanel { GLFW.glfwSetWindowSize(windowId, this.width, this.height); // 更新 OpenGL 视口与 ModelRender 的视口 - GL11.glViewport(0, 0, this.width, this.height); + RenderSystem.viewport(0, 0, this.width, this.height); ModelRender.setViewport(this.width, this.height); // 重新分配像素读取缓冲区(释放旧的) diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/buffer/BufferBuilder.java b/src/main/java/com/chuangzhou/vivid2D/render/model/buffer/BufferBuilder.java deleted file mode 100644 index fbb77e0..0000000 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/buffer/BufferBuilder.java +++ /dev/null @@ -1,119 +0,0 @@ -package com.chuangzhou.vivid2D.render.model.buffer; - -import org.lwjgl.opengl.GL11; -import org.lwjgl.opengl.GL15; -import org.lwjgl.opengl.GL20; -import org.lwjgl.opengl.GL30; -import org.lwjgl.system.MemoryUtil; - -import java.nio.FloatBuffer; - -/** - * 简化版 BufferBuilder,用于按顶点流构建并一次性绘制几何体。 - * 每个顶点格式: float x, float y, float u, float v (共4个 float) - * - * 用法: - * BufferBuilder bb = new BufferBuilder(); - * bb.begin(GL11.GL_LINE_LOOP, 16); - * bb.vertex(x,y,u,v); - * ... - * bb.end(); // 立即绘制并 cleanup - * - * 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。 - * @author tzdwindows 7 - */ -public class BufferBuilder { - private static final int COMPONENTS_PER_VERTEX = 4; // x,y,u,v - private float[] array; - private int size; // float 数量 - private int vertexCount; - private int mode; // GL mode - - public BufferBuilder() { - this(256); // 默认容量:256 floats -> 64 顶点 - } - - public BufferBuilder(int initialFloatCapacity) { - this.array = new float[Math.max(16, initialFloatCapacity)]; - this.size = 0; - this.vertexCount = 0; - this.mode = GL11.GL_TRIANGLES; - } - - private void ensureCapacity(int additionalFloats) { - int need = size + additionalFloats; - if (need > array.length) { - int newCap = array.length; - while (newCap < need) newCap <<= 1; - float[] na = new float[newCap]; - System.arraycopy(array, 0, na, 0, size); - array = na; - } - } - - /** - * 开始构建,传入要绘制的 GL 模式(例如 GL11.GL_LINE_LOOP) - * estimatedVertexCount 可传 0 表示不估计 - */ - public void begin(int glMode, int estimatedVertexCount) { - this.mode = glMode; - this.size = 0; - this.vertexCount = 0; - if (estimatedVertexCount > 0) { - ensureCapacity(estimatedVertexCount * COMPONENTS_PER_VERTEX); - } - } - - /** - * 添加顶点:x,y,u,v - */ - public void vertex(float x, float y, float u, float v) { - ensureCapacity(COMPONENTS_PER_VERTEX); - array[size++] = x; - array[size++] = y; - array[size++] = u; - array[size++] = v; - vertexCount++; - } - - /** - * 立即上传并绘制,然后释放临时 GPU 资源(方便、线程不复杂)。 - * 如果你想缓存 VAO/VBO 以便反复绘制,可以扩展本类。 - */ - public void end() { - if (vertexCount == 0) return; - - // upload buffer - FloatBuffer fb = MemoryUtil.memAllocFloat(size); - fb.put(array, 0, size).flip(); - - int vao = GL30.glGenVertexArrays(); - int vbo = GL15.glGenBuffers(); - - GL30.glBindVertexArray(vao); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vbo); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, fb, GL15.GL_DYNAMIC_DRAW); - - // layout: attrib 0 -> vec2 position (x,y) - GL20.glEnableVertexAttribArray(0); - GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, COMPONENTS_PER_VERTEX * Float.BYTES, 0); - - // layout: attrib 1 -> vec2 texcoord (u,v) - GL20.glEnableVertexAttribArray(1); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, COMPONENTS_PER_VERTEX * Float.BYTES, 2 * Float.BYTES); - - // draw - GL11.glDrawArrays(mode, 0, vertexCount); - - // cleanup - GL20.glDisableVertexAttribArray(0); - GL20.glDisableVertexAttribArray(1); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); - GL30.glBindVertexArray(0); - - GL15.glDeleteBuffers(vbo); - GL30.glDeleteVertexArrays(vao); - - MemoryUtil.memFree(fb); - } -} diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/util/LightSource.java b/src/main/java/com/chuangzhou/vivid2D/render/model/util/LightSource.java index c7b8991..38282e1 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/util/LightSource.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/model/util/LightSource.java @@ -38,17 +38,17 @@ public class LightSource { } // 带辉光参数 - public LightSource(Vector2f pos, Color color, float intensity, - boolean isGlow, Vector2f glowDirection, float glowIntensity, float glowRadius, float glowAmount) { - this.position = pos; - this.color = colorToVector3f(color); - this.intensity = intensity; - this.isGlow = isGlow; - this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f); - this.glowIntensity = glowIntensity; - this.glowRadius = glowRadius; - this.glowAmount = glowAmount; - } + //public LightSource(Vector2f pos, Color color, float intensity, + // boolean isGlow, Vector2f glowDirection, float glowIntensity, float glowRadius, float glowAmount) { + // this.position = pos; + // this.color = colorToVector3f(color); + // this.intensity = intensity; + // this.isGlow = isGlow; + // this.glowDirection = glowDirection != null ? glowDirection : new Vector2f(0f, 0f); + // this.glowIntensity = glowIntensity; + // this.glowRadius = glowRadius; + // this.glowAmount = glowAmount; + //} public static Vector3f colorToVector3f(Color color) { if (color == null) return new Vector3f(1, 1, 1); diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/util/Mesh2D.java b/src/main/java/com/chuangzhou/vivid2D/render/model/util/Mesh2D.java index 2822963..2ccaac5 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/util/Mesh2D.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/model/util/Mesh2D.java @@ -1,5 +1,6 @@ package com.chuangzhou.vivid2D.render.model.util; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.joml.Vector2f; import java.nio.FloatBuffer; @@ -411,6 +412,9 @@ public class Mesh2D { */ public void uploadToGPU() { if (uploaded) return; + + RenderSystem.assertOnRenderThread(); + // 组织 interleaved buffer (x,y,u,v) int vertexCount = getVertexCount(); int floatCount = vertexCount * 4; // x,y,u,v @@ -421,29 +425,32 @@ public class Mesh2D { try { getIndexBuffer(ib); - vaoId = GL30.glGenVertexArrays(); - GL30.glBindVertexArray(vaoId); + // 使用 RenderSystem 生成 VAO + vaoId = RenderSystem.glGenVertexArrays(); + RenderSystem.glBindVertexArray(() -> vaoId); - vboId = GL15.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, vboId); - GL15.glBufferData(GL15.GL_ARRAY_BUFFER, interleaved, GL15.GL_STATIC_DRAW); + // 使用 RenderSystem 生成和绑定 VBO + vboId = RenderSystem.glGenBuffers(); + RenderSystem.glBindBuffer(RenderSystem.GL_ARRAY_BUFFER, () -> vboId); + RenderSystem.glBufferData(RenderSystem.GL_ARRAY_BUFFER, interleaved, RenderSystem.GL_STATIC_DRAW); - eboId = GL15.glGenBuffers(); - GL15.glBindBuffer(GL15.GL_ELEMENT_ARRAY_BUFFER, eboId); - GL15.glBufferData(GL15.GL_ELEMENT_ARRAY_BUFFER, ib, GL15.GL_STATIC_DRAW); + // 使用 RenderSystem 生成和绑定 EBO + eboId = RenderSystem.glGenBuffers(); + RenderSystem.glBindBuffer(RenderSystem.GL_ELEMENT_ARRAY_BUFFER, () -> eboId); + RenderSystem.glBufferData(RenderSystem.GL_ELEMENT_ARRAY_BUFFER, ib, RenderSystem.GL_STATIC_DRAW); int stride = 4 * Float.BYTES; // x,y,u,v // position attrib (location 0) -> vec2 - GL20.glEnableVertexAttribArray(0); - GL20.glVertexAttribPointer(0, 2, GL11.GL_FLOAT, false, stride, 0); + RenderSystem.enableVertexAttribArray(0); + RenderSystem.vertexAttribPointer(0, 2, RenderSystem.GL_FLOAT, false, stride, 0); // uv attrib (location 1) -> vec2 - GL20.glEnableVertexAttribArray(1); - GL20.glVertexAttribPointer(1, 2, GL11.GL_FLOAT, false, stride, 2 * Float.BYTES); + RenderSystem.enableVertexAttribArray(1); + RenderSystem.vertexAttribPointer(1, 2, RenderSystem.GL_FLOAT, false, stride, 2 * Float.BYTES); // unbind VAO (keep EBO bound to VAO on unbind) - GL30.glBindVertexArray(0); + RenderSystem.glBindVertexArray(() -> 0); indexCount = indices.length; uploaded = true; @@ -454,7 +461,7 @@ public class Mesh2D { } finally { MemoryUtil.memFree(interleaved); // unbind array buffer - GL15.glBindBuffer(GL15.GL_ARRAY_BUFFER, 0); + RenderSystem.glBindBuffer(RenderSystem.GL_ARRAY_BUFFER, () -> 0); } } @@ -473,46 +480,33 @@ public class Mesh2D { texture.bind(); } - // 绑定 VAO - GL30.glBindVertexArray(vaoId); + // 绑定 VAO - 使用 RenderSystem + RenderSystem.glBindVertexArray(() -> vaoId); - // 关键修改:使用传入的着色器程序 - GL20.glUseProgram(shaderProgram); + // 使用着色器程序 - 使用 RenderSystem + RenderSystem.useProgram(shaderProgram); - // 将 modelMatrix 上传到 shader 的 uniform "uModelMatrix"(与ModelRender中的命名一致) - int loc = GL20.glGetUniformLocation(shaderProgram, "uModelMatrix"); + // 将 modelMatrix 上传到 shader 的 uniform + int loc = RenderSystem.getUniformLocation(shaderProgram, "uModelMatrix"); if (loc == -1) { - // 如果找不到 uModelMatrix,尝试 uModel - loc = GL20.glGetUniformLocation(shaderProgram, "uModel"); + loc = RenderSystem.getUniformLocation(shaderProgram, "uModel"); } if (loc != -1) { - java.nio.FloatBuffer fb = org.lwjgl.system.MemoryUtil.memAllocFloat(9); - try { - modelMatrix.get(fb); - fb.flip(); - GL20.glUniformMatrix3fv(loc, false, fb); - - // 调试信息 - //System.out.println("Mesh2D: 应用模型矩阵到着色器 - " + name); - //System.out.printf(" [%.2f, %.2f, %.2f]\n", modelMatrix.m00(), modelMatrix.m01(), modelMatrix.m02()); - //System.out.printf(" [%.2f, %.2f, %.2f]\n", modelMatrix.m10(), modelMatrix.m11(), modelMatrix.m12()); - //System.out.printf(" [%.2f, %.2f, %.2f]\n", modelMatrix.m20(), modelMatrix.m21(), modelMatrix.m22()); - } finally { - org.lwjgl.system.MemoryUtil.memFree(fb); - } + RenderSystem.uniformMatrix3(loc, modelMatrix); } else { //logger.warn("警告: 着色器中未找到 uModelMatrix 或 uModel uniform"); } - // 绘制 - GL11.glDrawElements(GL11.GL_TRIANGLES, indexCount, GL11.GL_UNSIGNED_INT, 0); + // 绘制 - 使用 RenderSystem + RenderSystem.drawElements(RenderSystem.DRAW_TRIANGLES, indexCount, + RenderSystem.GL_UNSIGNED_INT, 0); - // 解绑 - GL30.glBindVertexArray(0); + // 解绑 VAO + RenderSystem.glBindVertexArray(() -> 0); if (texture != null) { - texture.unbind(); + texture.unbind(); // 需要检查 texture.unbind() 是否也需要封装 } } diff --git a/src/main/java/com/chuangzhou/vivid2D/render/model/util/Texture.java b/src/main/java/com/chuangzhou/vivid2D/render/model/util/Texture.java index 33a59bb..31593b4 100644 --- a/src/main/java/com/chuangzhou/vivid2D/render/model/util/Texture.java +++ b/src/main/java/com/chuangzhou/vivid2D/render/model/util/Texture.java @@ -1,5 +1,6 @@ package com.chuangzhou.vivid2D.render.model.util; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; import org.lwjgl.opengl.GL12; @@ -127,9 +128,6 @@ public class Texture { public int getGLWrap() { return glWrap; } } - // ==================== 构造器 ==================== - - // ==================== STB 图像加载 ==================== static { @@ -463,25 +461,9 @@ public class Texture { throw new IllegalArgumentException("Invalid texture unit: " + textureUnit); } - // 如果支持 GL13,保存当前活动纹理单元并激活目标单元,绑定纹理后保持可以恢复 - boolean hasGL13 = GL.getCapabilities().OpenGL13; - if (hasGL13) { - try { - // 保存之前的活动纹理单元(返回值是 GL_TEXTUREi 的枚举值) - previousActiveTexture = GL11.glGetInteger(GL13.GL_ACTIVE_TEXTURE); - GL13.glActiveTexture(GL13.GL_TEXTURE0 + textureUnit); - } catch (Exception e) { - // 如果查询/激活失败,重置标志,不影响后续绑定(仍尝试绑定) - previousActiveTexture = -1; - System.err.println("Warning: failed to change active texture unit: " + e.getMessage()); - } - } else { - previousActiveTexture = -1; - } - - // 绑定纹理到当前(已激活的)纹理单元 - GL11.glBindTexture(GL11.GL_TEXTURE_2D, textureId); - checkGLError("glBindTexture"); + RenderSystem.activeTexture(GL13.GL_TEXTURE0 + textureUnit); + RenderSystem.bindTexture(textureId); + RenderSystem.checkGLError("Texture.bind"); } /** @@ -495,18 +477,15 @@ public class Texture { * 解绑纹理 */ public void unbind() { - // 解绑当前纹理目标 - GL11.glBindTexture(GL11.GL_TEXTURE_2D, 0); - - // 如果之前保存了活动纹理单元并且支持 GL13,尝试恢复它 - try { - if (previousActiveTexture != -1 && GL.getCapabilities().OpenGL13) { - GL13.glActiveTexture(previousActiveTexture); + RenderSystem.bindTexture(0); + if (previousActiveTexture != -1) { + try { + RenderSystem.activeTexture(previousActiveTexture); + } catch (Exception e) { + System.err.println("Warning: failed to restore previous active texture unit: " + e.getMessage()); + } finally { + previousActiveTexture = -1; } - } catch (Exception e) { - System.err.println("Warning: failed to restore previous active texture unit: " + e.getMessage()); - } finally { - previousActiveTexture = -1; } } @@ -519,11 +498,7 @@ public class Texture { public void dispose() { if (!disposed) { try { - IntBuffer textures = MemoryUtil.memAllocInt(1); - textures.put(textureId); - textures.flip(); - GL11.glDeleteTextures(textures); - MemoryUtil.memFree(textures); + RenderSystem.deleteTextures(textureId); } catch (Exception e) { System.err.println("Error disposing texture: " + e.getMessage()); } @@ -680,10 +655,10 @@ public class Texture { ByteBuffer pixelData = MemoryUtil.memAlloc(dataSize); // 从GPU读取纹理数据 - GL11.glGetTexImage(GL11.GL_TEXTURE_2D, 0, format.getGLFormat(), type.getGLType(), pixelData); + RenderSystem.getTexImage(GL11.GL_TEXTURE_2D, 0, format.getGLFormat(), type.getGLType(), pixelData); // 检查OpenGL错误 - checkGLError("glGetTexImage"); + RenderSystem.checkGLError("Texture.extractTextureData"); pixelData.flip(); return pixelData; diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/RenderSystem.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/RenderSystem.java new file mode 100644 index 0000000..1cba732 --- /dev/null +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/RenderSystem.java @@ -0,0 +1,1398 @@ +package com.chuangzhou.vivid2D.render.systems; + +import org.lwjgl.opengl.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Queue; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.function.IntSupplier; +import java.util.function.Supplier; + +/** + * vivid2D 渲染系统 + * + *

提供线程安全的 OpenGL 调用抽象,支持渲染命令队列

+ * + * @author tzdwindows 7 + * @version 1.0 + * @since 2025-10-16 + */ +public final class RenderSystem { + private static final Logger logger = LoggerFactory.getLogger(RenderSystem.class); + private RenderSystem() { /* no instances */ } + + // ================== 线程管理 ================== + private static Thread gameThread; + private static Thread renderThread; + private static boolean isInInit; + + // ================== 渲染命令队列 ================== + private static final Queue renderQueue = new ConcurrentLinkedQueue<>(); + private static boolean isReplayingQueue; + + // ================== 状态管理 ================== + private static int viewportWidth = 800; + private static int viewportHeight = 600; + private static final float[] clearColor = new float[]{0.0f, 0.0f, 0.0f, 1.0f}; + public static final int GL_MULTISAMPLE = GL13.GL_MULTISAMPLE; + + // 纹理过滤模式常量 + public static final int FILTER_NEAREST = GL11.GL_NEAREST; + public static final int FILTER_LINEAR = GL11.GL_LINEAR; + public static final int FILTER_NEAREST_MIPMAP_NEAREST = GL11.GL_NEAREST_MIPMAP_NEAREST; + public static final int FILTER_LINEAR_MIPMAP_NEAREST = GL11.GL_LINEAR_MIPMAP_NEAREST; + public static final int FILTER_NEAREST_MIPMAP_LINEAR = GL11.GL_NEAREST_MIPMAP_LINEAR; + public static final int FILTER_LINEAR_MIPMAP_LINEAR = GL11.GL_LINEAR_MIPMAP_LINEAR; + + // 纹理环绕模式常量 + public static final int WRAP_REPEAT = GL11.GL_REPEAT; + public static final int WRAP_CLAMP_TO_EDGE = GL12.GL_CLAMP_TO_EDGE; + public static final int WRAP_CLAMP_TO_BORDER = GL13.GL_CLAMP_TO_BORDER; + public static final int WRAP_MIRRORED_REPEAT = GL14.GL_MIRRORED_REPEAT; + + public static final int GL_ARRAY_BUFFER = GL15.GL_ARRAY_BUFFER; + public static final int GL_ELEMENT_ARRAY_BUFFER = GL15.GL_ELEMENT_ARRAY_BUFFER; + public static final int GL_STATIC_DRAW = GL15.GL_STATIC_DRAW; + public static final int GL_DYNAMIC_DRAW = GL15.GL_DYNAMIC_DRAW; + public static final int GL_STREAM_DRAW = GL15.GL_STREAM_DRAW; + public static final int GL_FLOAT = GL11.GL_FLOAT; + + // ================== 绘制模式常量 ================== + public static final int DRAW_POINTS = GL11.GL_POINTS; + public static final int DRAW_LINES = GL11.GL_LINES; + public static final int DRAW_LINE_LOOP = GL11.GL_LINE_LOOP; + public static final int DRAW_LINE_STRIP = GL11.GL_LINE_STRIP; + public static final int DRAW_TRIANGLES = GL11.GL_TRIANGLES; + public static final int DRAW_TRIANGLE_STRIP = GL11.GL_TRIANGLE_STRIP; + public static final int DRAW_TRIANGLE_FAN = GL11.GL_TRIANGLE_FAN; + public static final int DRAW_QUADS = GL11.GL_QUADS; + + // ================== 索引类型常量 ================== + public static final int GL_UNSIGNED_BYTE = GL11.GL_UNSIGNED_BYTE; + public static final int GL_UNSIGNED_SHORT = GL11.GL_UNSIGNED_SHORT; + public static final int GL_UNSIGNED_INT = GL11.GL_UNSIGNED_INT; + public static final int GL_TEXTURE0 = GL13.GL_TEXTURE0; + + public static final int GL_PACK_ALIGNMENT = GL11.GL_PACK_ALIGNMENT; + public static final int GL_UNPACK_ALIGNMENT = GL11.GL_UNPACK_ALIGNMENT; + + public static final int GL_COLOR_BUFFER_BIT = GL11.GL_COLOR_BUFFER_BIT; + public static final int GL_DEPTH_BUFFER_BIT = GL11.GL_DEPTH_BUFFER_BIT; + public static final int GL_STENCIL_BUFFER_BIT = GL11.GL_STENCIL_BUFFER_BIT; + + public static final int GL_RGBA = GL11.GL_RGBA; + public static final int GL_RGB = GL11.GL_RGB; + public static final int GL_BGRA = GL12.GL_BGRA; + public static final int GL_BGR = GL12.GL_BGR; + public static final int GL_RED = GL11.GL_RED; + public static final int GL_RG = GL30.GL_RG; + + public static final int GL_BYTE = GL11.GL_BYTE; + public static final int GL_SHORT = GL11.GL_SHORT; + public static final int GL_INT = GL11.GL_INT; + + // ================== 初始化方法 ================== + + public static void initRenderThread() { + if (renderThread == null) { + renderThread = Thread.currentThread(); + logger.info("Render thread initialized: {}", Thread.currentThread().getName()); + } else if (renderThread != Thread.currentThread()) { + throw new IllegalStateException("Render thread already initialized by another thread"); + } + } + + public static void beginInitialization() { + isInInit = true; + } + + public static void finishInitialization() { + isInInit = false; + if (!renderQueue.isEmpty()) { + replayQueue(); + } + } + + // ================== 线程断言 ================== + + public static boolean isOnRenderThread() { + return Thread.currentThread() == renderThread; + } + + public static boolean isInInitPhase() { + return isInInit; + } + + public static void assertOnRenderThread() { + if (!isOnRenderThread()) { + throw new IllegalStateException("RenderSystem called from wrong thread: " + + Thread.currentThread().getName() + ", expected: " + + (renderThread != null ? renderThread.getName() : "render thread")); + } + } + + public static void assertOnRenderThreadOrInit() { + if (!isInInit && !isOnRenderThread()) { + throw new IllegalStateException("RenderSystem called from wrong thread"); + } + } + + // ================== 渲染命令队列 ================== + + public static void recordRenderCall(Runnable renderCall) { + renderQueue.add(renderCall); + } + + public static void replayQueue() { + isReplayingQueue = true; + while (!renderQueue.isEmpty()) { + Runnable call = renderQueue.poll(); + if (call != null) { + call.run(); + } + } + isReplayingQueue = false; + } + + // ================== OpenGL 状态管理封装 ================== + + public static void enable(int capability) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _enable(capability)); + } else { + _enable(capability); + } + } + + private static void _enable(int capability) { + assertOnRenderThread(); + GL11.glEnable(capability); + } + + public static void disable(int capability) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _disable(capability)); + } else { + _disable(capability); + } + } + + private static void _disable(int capability) { + assertOnRenderThread(); + GL11.glDisable(capability); + } + + public static void clearColor(float r, float g, float b, float a) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _clearColor(r, g, b, a)); + } else { + _clearColor(r, g, b, a); + } + } + + public static void setClearColor(float r, float g, float b, float a) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _setClearColor(r, g, b, a)); + } else { + _setClearColor(r, g, b, a); + } + } + + private static void _setClearColor(float r, float g, float b, float a) { + assertOnRenderThread(); + GL11.glClearColor(r, g, b, a); + // 可选:更新内部状态记录 + clearColor[0] = r; + clearColor[1] = g; + clearColor[2] = b; + clearColor[3] = a; + } + + private static void _clearColor(float r, float g, float b, float a) { + assertOnRenderThread(); + GL11.glClearColor(r, g, b, a); + clearColor[0] = r; + clearColor[1] = g; + clearColor[2] = b; + clearColor[3] = a; + } + + public static void clear(int mask) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _clear(mask)); + } else { + _clear(mask); + } + } + + private static void _clear(int mask) { + assertOnRenderThread(); + GL11.glClear(mask); + } + + public static void viewport(int x, int y, int width, int height) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _viewport(x, y, width, height)); + } else { + _viewport(x, y, width, height); + } + } + + private static void _viewport(int x, int y, int width, int height) { + assertOnRenderThread(); + GL11.glViewport(x, y, width, height); + viewportWidth = width; + viewportHeight = height; + } + + // ================== VAO 操作 ================== + + public static int glGenVertexArrays() { + if (!isOnRenderThread()) { + throw new IllegalStateException("VAO generation must be on render thread"); + } + return GL30.glGenVertexArrays(); + } + + public static void glDeleteVertexArrays(int vao) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glDeleteVertexArrays(vao)); + } else { + _glDeleteVertexArrays(vao); + } + } + + private static void _glDeleteVertexArrays(int vao) { + assertOnRenderThread(); + GL30.glDeleteVertexArrays(vao); + } + + // ================== Uniform 设置 ================== + + public static void uniform1i(int location, int value) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniform1i(location, value)); + } else { + _uniform1i(location, value); + } + } + + private static void _uniform1i(int location, int value) { + assertOnRenderThread(); + GL20.glUniform1i(location, value); + } + + public static void uniform1f(int location, float value) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniform1f(location, value)); + } else { + _uniform1f(location, value); + } + } + + private static void _uniform1f(int location, float value) { + assertOnRenderThread(); + GL20.glUniform1f(location, value); + } + + public static void uniform2f(int location, float x, float y) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniform2f(location, x, y)); + } else { + _uniform2f(location, x, y); + } + } + + private static void _uniform2f(int location, float x, float y) { + assertOnRenderThread(); + GL20.glUniform2f(location, x, y); + } + + public static void uniform3f(int location, float x, float y, float z) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniform3f(location, x, y, z)); + } else { + _uniform3f(location, x, y, z); + } + } + + private static void _uniform3f(int location, float x, float y, float z) { + assertOnRenderThread(); + GL20.glUniform3f(location, x, y, z); + } + + public static void uniform4f(int location, float x, float y, float z, float w) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniform4f(location, x, y, z, w)); + } else { + _uniform4f(location, x, y, z, w); + } + } + + private static void _uniform4f(int location, float x, float y, float z, float w) { + assertOnRenderThread(); + GL20.glUniform4f(location, x, y, z, w); + } + + public static void uniformMatrix3(int location, boolean transpose, java.nio.FloatBuffer matrix) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniformMatrix3(location, transpose, matrix)); + } else { + _uniformMatrix3(location, transpose, matrix); + } + } + + private static void _uniformMatrix3(int location, boolean transpose, java.nio.FloatBuffer matrix) { + assertOnRenderThread(); + GL20.glUniformMatrix3fv(location, transpose, matrix); + } + + public static void uniformMatrix4(int location, boolean transpose, java.nio.FloatBuffer matrix) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _uniformMatrix4(location, transpose, matrix)); + } else { + _uniformMatrix4(location, transpose, matrix); + } + } + + private static void _uniformMatrix4(int location, boolean transpose, java.nio.FloatBuffer matrix) { + assertOnRenderThread(); + GL20.glUniformMatrix4fv(location, transpose, matrix); + } + + // 便捷方法:Vector2f + public static void uniform2f(int location, org.joml.Vector2f vec) { + uniform2f(location, vec.x, vec.y); + } + + // 便捷方法:Vector3f + public static void uniform3f(int location, org.joml.Vector3f vec) { + uniform3f(location, vec.x, vec.y, vec.z); + } + + // 便捷方法:Vector4f + public static void uniform4f(int location, org.joml.Vector4f vec) { + uniform4f(location, vec.x, vec.y, vec.z, vec.w); + } + + // 便捷方法:Matrix3f + public static void uniformMatrix3(int location, org.joml.Matrix3f matrix) { + java.nio.FloatBuffer fb = org.lwjgl.system.MemoryUtil.memAllocFloat(9); + try { + matrix.get(fb); + uniformMatrix3(location, false, fb); + } finally { + org.lwjgl.system.MemoryUtil.memFree(fb); + } + } + + // 便捷方法:Matrix4f + public static void uniformMatrix4(int location, org.joml.Matrix4f matrix) { + java.nio.FloatBuffer fb = org.lwjgl.system.MemoryUtil.memAllocFloat(16); + try { + matrix.get(fb); + uniformMatrix4(location, false, fb); + } finally { + org.lwjgl.system.MemoryUtil.memFree(fb); + } + } + + // Uniform 位置查询 + public static int getUniformLocation(int program, String name) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Uniform location queries must be on render thread"); + } + return GL20.glGetUniformLocation(program, name); + } + + // ================== 绘制命令 ================== + + public static void lineWidth(float width) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _lineWidth(width)); + } else { + _lineWidth(width); + } + } + + private static void _lineWidth(float width) { + assertOnRenderThread(); + GL11.glLineWidth(width); + } + public static void drawElements(int mode, int count, int type, long indices) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _drawElements(mode, count, type, indices)); + } else { + _drawElements(mode, count, type, indices); + } + } + + private static void _drawElements(int mode, int count, int type, long indices) { + assertOnRenderThread(); + GL11.glDrawElements(mode, count, type, indices); + } + + public static void drawArrays(int mode, int first, int count) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _drawArrays(mode, first, count)); + } else { + _drawArrays(mode, first, count); + } + } + + private static void _drawArrays(int mode, int first, int count) { + assertOnRenderThread(); + GL11.glDrawArrays(mode, first, count); + } + + // VAO 绑定支持 + public static void glBindVertexArray(Supplier vaoSupplier) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glBindVertexArray(vaoSupplier.get())); + } else { + _glBindVertexArray(vaoSupplier.get()); + } + } + + private static void _glBindVertexArray(int vao) { + assertOnRenderThread(); + GL30.glBindVertexArray(vao); + } + + // ================== 混合模式 ================== + + public static void enableBlend() { + if (!isOnRenderThread()) { + recordRenderCall(() -> _enableBlend()); + } else { + _enableBlend(); + } + } + + private static void _enableBlend() { + assertOnRenderThread(); + GL11.glEnable(GL11.GL_BLEND); + } + + public static void disableBlend() { + if (!isOnRenderThread()) { + recordRenderCall(() -> _disableBlend()); + } else { + _disableBlend(); + } + } + + private static void _disableBlend() { + assertOnRenderThread(); + GL11.glDisable(GL11.GL_BLEND); + } + + public static void blendFunc(int sfactor, int dfactor) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _blendFunc(sfactor, dfactor)); + } else { + _blendFunc(sfactor, dfactor); + } + } + + private static void _blendFunc(int sfactor, int dfactor) { + assertOnRenderThread(); + GL11.glBlendFunc(sfactor, dfactor); + } + + public static void defaultBlendFunc() { + blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + } + + // ================== 着色器程序管理 ================== + + public static int createProgram() { + if (!isOnRenderThread()) { + throw new IllegalStateException("Program creation must be on render thread"); + } + return GL20.glCreateProgram(); + } + + /** + * 获取当前激活的着色器程序ID + */ + public static int getCurrentProgram() { + if (!isOnRenderThread()) { + throw new IllegalStateException("getCurrentProgram must be called on render thread"); + } + return GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM); + } + + public static void attachShader(int program, int shader) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _attachShader(program, shader)); + } else { + _attachShader(program, shader); + } + } + + private static void _attachShader(int program, int shader) { + assertOnRenderThread(); + GL20.glAttachShader(program, shader); + } + + public static void linkProgram(int program) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _linkProgram(program)); + } else { + _linkProgram(program); + } + } + + private static void _linkProgram(int program) { + assertOnRenderThread(); + GL20.glLinkProgram(program); + } + + public static int getProgrami(int program, int pname) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Program queries must be on render thread"); + } + return GL20.glGetProgrami(program, pname); + } + + public static String getProgramInfoLog(int program) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Program queries must be on render thread"); + } + return GL20.glGetProgramInfoLog(program); + } + + public static void detachShader(int program, int shader) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _detachShader(program, shader)); + } else { + _detachShader(program, shader); + } + } + + private static void _detachShader(int program, int shader) { + assertOnRenderThread(); + GL20.glDetachShader(program, shader); + } + + public static void deleteProgram(int program) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _deleteProgram(program)); + } else { + _deleteProgram(program); + } + } + + private static void _deleteProgram(int program) { + assertOnRenderThread(); + if (GL20.glIsProgram(program)) { + GL20.glDeleteProgram(program); + } + } + + // 完整的程序链接方法 + public static int linkProgram(int vertexShader, int fragmentShader) { + assertOnRenderThread(); + + int program = createProgram(); + attachShader(program, vertexShader); + attachShader(program, fragmentShader); + linkProgram(program); + + int status = getProgrami(program, GL20.GL_LINK_STATUS); + if (status == GL11.GL_FALSE) { + String log = getProgramInfoLog(program); + deleteProgram(program); + throw new RuntimeException("Program link failed: " + log); + } + + // 着色器可以在链接后删除 + detachShader(program, vertexShader); + detachShader(program, fragmentShader); + deleteShader(vertexShader); + deleteShader(fragmentShader); + + return program; + } + + // 支持几何着色器的链接方法 + public static int linkProgram(int vertexShader, int geometryShader, int fragmentShader) { + assertOnRenderThread(); + + int program = createProgram(); + attachShader(program, vertexShader); + if (geometryShader != 0) { + attachShader(program, geometryShader); + } + attachShader(program, fragmentShader); + linkProgram(program); + + int status = getProgrami(program, GL20.GL_LINK_STATUS); + if (status == GL11.GL_FALSE) { + String log = getProgramInfoLog(program); + deleteProgram(program); + throw new RuntimeException("Program link failed: " + log); + } + + // 清理着色器 + detachShader(program, vertexShader); + if (geometryShader != 0) { + detachShader(program, geometryShader); + } + detachShader(program, fragmentShader); + deleteShader(vertexShader); + if (geometryShader != 0) { + deleteShader(geometryShader); + } + deleteShader(fragmentShader); + + return program; + } + + // ================== 着色器管理 ================== + + public static int createShader(int type) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Shader creation must be on render thread"); + } + return GL20.glCreateShader(type); + } + + public static void shaderSource(int shader, String source) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _shaderSource(shader, source)); + } else { + _shaderSource(shader, source); + } + } + + private static void _shaderSource(int shader, String source) { + assertOnRenderThread(); + GL20.glShaderSource(shader, source); + } + + public static void compileShader(int shader) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _compileShader(shader)); + } else { + _compileShader(shader); + } + } + + private static void _compileShader(int shader) { + assertOnRenderThread(); + GL20.glCompileShader(shader); + } + + public static int getShaderi(int shader, int pname) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Shader queries must be on render thread"); + } + return GL20.glGetShaderi(shader, pname); + } + + public static String getShaderInfoLog(int shader) { + if (!isOnRenderThread()) { + throw new IllegalStateException("Shader queries must be on render thread"); + } + return GL20.glGetShaderInfoLog(shader); + } + + public static void deleteShader(int shader) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _deleteShader(shader)); + } else { + _deleteShader(shader); + } + } + + private static void _deleteShader(int shader) { + assertOnRenderThread(); + GL20.glDeleteShader(shader); + } + + public static int compileShader(int type, String source) { + assertOnRenderThread(); + + int shader = createShader(type); + shaderSource(shader, source); + compileShader(shader); + + int status = getShaderi(shader, GL20.GL_COMPILE_STATUS); + if (status == GL11.GL_FALSE) { + String log = getShaderInfoLog(shader); + deleteShader(shader); + throw new RuntimeException("Shader compilation failed: " + log); + } + return shader; + } + + // ================== 深度测试 ================== + + public static void depthFunc(int func) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _depthFunc(func)); + } else { + _depthFunc(func); + } + } + + private static void _depthFunc(int func) { + assertOnRenderThread(); + GL11.glDepthFunc(func); + } + + public static void depthMask(boolean flag) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _depthMask(flag)); + } else { + _depthMask(flag); + } + } + + private static void _depthMask(boolean flag) { + assertOnRenderThread(); + GL11.glDepthMask(flag); + } + + public static void clearDepth(double depth) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _clearDepth(depth)); + } else { + _clearDepth(depth); + } + } + + private static void _clearDepth(double depth) { + assertOnRenderThread(); + GL11.glClearDepth(depth); + } + + public static void enableDepthTest() { + if (!isOnRenderThread()) { + recordRenderCall(() -> _enableDepthTest()); + } else { + _enableDepthTest(); + } + } + + private static void _enableDepthTest() { + assertOnRenderThread(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + } + + public static void disableDepthTest() { + if (!isOnRenderThread()) { + recordRenderCall(() -> _disableDepthTest()); + } else { + _disableDepthTest(); + } + } + + private static void _disableDepthTest() { + assertOnRenderThread(); + GL11.glDisable(GL11.GL_DEPTH_TEST); + } + + // ================== 纹理管理 ================== + + public static void bindTexture(int texture) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _bindTexture(texture)); + } else { + _bindTexture(texture); + } + } + + public static void getTexImage(int target, int level, int format, int type, java.nio.ByteBuffer pixels) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _getTexImage(target, level, format, type, pixels)); + } else { + _getTexImage(target, level, format, type, pixels); + } + } + + private static void _getTexImage(int target, int level, int format, int type, java.nio.ByteBuffer pixels) { + assertOnRenderThread(); + GL11.glGetTexImage(target, level, format, type, pixels); + } + + private static void _bindTexture(int texture) { + assertOnRenderThread(); + GL11.glBindTexture(GL11.GL_TEXTURE_2D, texture); + } + + public static void activeTexture(int texture) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _activeTexture(texture)); + } else { + _activeTexture(texture); + } + } + + private static void _activeTexture(int texture) { + assertOnRenderThread(); + GL13.glActiveTexture(texture); + } + + public static int genTextures() { + if (!isOnRenderThread()) { + throw new IllegalStateException("Texture generation must be on render thread"); + } + return GL11.glGenTextures(); + } + + public static void deleteTextures(int texture) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _deleteTextures(texture)); + } else { + _deleteTextures(texture); + } + } + + private static void _deleteTextures(int texture) { + assertOnRenderThread(); + GL11.glDeleteTextures(texture); + } + + public static void texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, java.nio.ByteBuffer pixels) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _texImage2D(target, level, internalFormat, width, height, border, format, type, pixels)); + } else { + _texImage2D(target, level, internalFormat, width, height, border, format, type, pixels); + } + } + + private static void _texImage2D(int target, int level, int internalFormat, int width, int height, int border, int format, int type, java.nio.ByteBuffer pixels) { + assertOnRenderThread(); + GL11.glTexImage2D(target, level, internalFormat, width, height, border, format, type, pixels); + } + + public static void texParameteri(int target, int pname, int param) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _texParameteri(target, pname, param)); + } else { + _texParameteri(target, pname, param); + } + } + + private static void _texParameteri(int target, int pname, int param) { + assertOnRenderThread(); + GL11.glTexParameteri(target, pname, param); + } + + public static void setTextureMinFilter(int filter) { + texParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MIN_FILTER, filter); + } + + public static void setTextureMagFilter(int filter) { + texParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_MAG_FILTER, filter); + } + + public static void setTextureWrapS(int wrap) { + texParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_S, wrap); + } + + public static void setTextureWrapT(int wrap) { + texParameteri(GL11.GL_TEXTURE_2D, GL11.GL_TEXTURE_WRAP_T, wrap); + } + + // 创建默认白色纹理的便捷方法 + public static int createDefaultTexture() { + assertOnRenderThread(); + + int textureId = genTextures(); + bindTexture(textureId); + + // 创建 1x1 白色纹理 + java.nio.ByteBuffer buffer = org.lwjgl.system.MemoryUtil.memAlloc(4); + try { + buffer.put((byte) 255) + .put((byte) 255) + .put((byte) 255) + .put((byte) 255) + .flip(); + + texImage2D(GL11.GL_TEXTURE_2D, 0, GL11.GL_RGBA8, 1, 1, 0, GL11.GL_RGBA, GL11.GL_UNSIGNED_BYTE, buffer); + } finally { + org.lwjgl.system.MemoryUtil.memFree(buffer); + } + + // 设置纹理参数 + setTextureMinFilter(GL11.GL_NEAREST); + setTextureMagFilter(GL11.GL_NEAREST); + setTextureWrapS(GL11.GL_REPEAT); + setTextureWrapT(GL11.GL_REPEAT); + + bindTexture(0); // 解绑 + return textureId; + } + + // ================== 纹理管理扩展 ================== + + /** + * 设置着色器纹理 + * @param textureUnit 纹理单元 (0, 1, 2, ...) + * @param texture 纹理对象 + */ + public static void setShaderTexture(int textureUnit, com.chuangzhou.vivid2D.render.model.util.Texture texture) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _setShaderTexture(textureUnit, texture)); + } else { + _setShaderTexture(textureUnit, texture); + } + } + + private static void _setShaderTexture(int textureUnit, com.chuangzhou.vivid2D.render.model.util.Texture texture) { + assertOnRenderThread(); + + if (texture == null) { + logger.warn("setShaderTexture: texture is null for unit {}", textureUnit); + return; + } + + if (texture.isDisposed()) { + logger.warn("setShaderTexture: texture is disposed for unit {}", textureUnit); + return; + } + + try { + // 激活纹理单元 + activeTexture(GL13.GL_TEXTURE0 + textureUnit); + + // 绑定纹理 + bindTexture(texture.getTextureId()); + + // 检查错误 + checkGLError("setShaderTexture"); + + } catch (Exception e) { + logger.error("setShaderTexture failed for unit {}: {}", textureUnit, e.getMessage()); + } + } + + /** + * 设置着色器纹理 - 接受纹理ID的版本 + * @param textureUnit 纹理单元 + * @param textureId 纹理ID + */ + public static void setShaderTexture(int textureUnit, int textureId) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _setShaderTexture(textureUnit, textureId)); + } else { + _setShaderTexture(textureUnit, textureId); + } + } + + private static void _setShaderTexture(int textureUnit, int textureId) { + assertOnRenderThread(); + + if (textureId == 0) { + logger.warn("setShaderTexture: textureId is 0 for unit {}", textureUnit); + return; + } + + try { + // 激活纹理单元 + activeTexture(GL13.GL_TEXTURE0 + textureUnit); + + // 绑定纹理 + bindTexture(textureId); + + // 检查错误 + checkGLError("setShaderTexture"); + + } catch (Exception e) { + logger.error("setShaderTexture failed for unit {}: {}", textureUnit, e.getMessage()); + } + } + + // ================== 着色器程序 ================== + + public static void useProgram(int program) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _useProgram(program)); + } else { + _useProgram(program); + } + } + + private static void _useProgram(int program) { + assertOnRenderThread(); + GL20.glUseProgram(program); + } + + // ================== 缓冲区操作 ================== + + public static int glGenBuffers() { + if (!isOnRenderThread()) { + throw new IllegalStateException("Buffer generation must be on render thread"); + } + return GL15.glGenBuffers(); + } + + public static void glBindBuffer(int target, IntSupplier buffer) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glBindBuffer(target, buffer.getAsInt())); + } else { + _glBindBuffer(target, buffer.getAsInt()); + } + } + + private static void _glBindBuffer(int target, int buffer) { + assertOnRenderThread(); + GL15.glBindBuffer(target, buffer); + } + + public static void glBufferData(int target, java.nio.IntBuffer data, int usage) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glBufferData(target, data, usage)); + } else { + _glBufferData(target, data, usage); + } + } + + private static void _glBufferData(int target, java.nio.IntBuffer data, int usage) { + assertOnRenderThread(); + GL15.glBufferData(target, data, usage); + } + + public static void glBufferData(int target, java.nio.FloatBuffer data, int usage) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glBufferData(target, data, usage)); + } else { + _glBufferData(target, data, usage); + } + } + + private static void _glBufferData(int target, java.nio.FloatBuffer data, int usage) { + assertOnRenderThread(); + GL15.glBufferData(target, data, usage); + } + + public static void glDeleteBuffers(int buffer) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _glDeleteBuffers(buffer)); + } else { + _glDeleteBuffers(buffer); + } + } + + private static void _glDeleteBuffers(int buffer) { + assertOnRenderThread(); + GL15.glDeleteBuffers(buffer); + } + +// ================== 顶点属性 ================== + + public static void enableVertexAttribArray(int index) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _enableVertexAttribArray(index)); + } else { + _enableVertexAttribArray(index); + } + } + + private static void _enableVertexAttribArray(int index) { + assertOnRenderThread(); + GL20.glEnableVertexAttribArray(index); + } + + public static void disableVertexAttribArray(int index) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _disableVertexAttribArray(index)); + } else { + _disableVertexAttribArray(index); + } + } + + private static void _disableVertexAttribArray(int index) { + assertOnRenderThread(); + GL20.glDisableVertexAttribArray(index); + } + + public static void vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _vertexAttribPointer(index, size, type, normalized, stride, pointer)); + } else { + _vertexAttribPointer(index, size, type, normalized, stride, pointer); + } + } + + private static void _vertexAttribPointer(int index, int size, int type, boolean normalized, int stride, long pointer) { + assertOnRenderThread(); + GL20.glVertexAttribPointer(index, size, type, normalized, stride, pointer); + } + + // ================== 工具方法 ================== + + public static void readPixels(int x, int y, int width, int height, int format, int type, java.nio.ByteBuffer pixels) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _readPixels(x, y, width, height, format, type, pixels)); + } else { + _readPixels(x, y, width, height, format, type, pixels); + } + } + + private static void _readPixels(int x, int y, int width, int height, int format, int type, java.nio.ByteBuffer pixels) { + assertOnRenderThread(); + GL11.glReadPixels(x, y, width, height, format, type, pixels); + } + + /** + * 检查特定扩展是否支持 + */ + public static boolean isExtensionSupported(String extension) { + assertOnRenderThread(); + + String extensionsString = GL11.glGetString(GL11.GL_EXTENSIONS); + if (extensionsString == null) { + return false; + } + + String[] extensions = extensionsString.split("\\s+"); + for (String ext : extensions) { + if (ext.equals(extension)) { + return true; + } + } + + return false; + } + public static void pixelStore(int pname, int param) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _pixelStore(pname, param)); + } else { + _pixelStore(pname, param); + } + } + + private static void _pixelStore(int pname, int param) { + assertOnRenderThread(); + GL11.glPixelStorei(pname, param); + } + + + /** + * 获取 OpenGL 厂商信息 + */ + public static String getVendor() { + if (!isOnRenderThread()) { + throw new IllegalStateException("OpenGL info queries must be on render thread"); + } + return GL11.glGetString(GL11.GL_VENDOR); + } + + /** + * 获取 OpenGL 渲染器信息 + */ + public static String getRenderer() { + if (!isOnRenderThread()) { + throw new IllegalStateException("OpenGL info queries must be on render thread"); + } + return GL11.glGetString(GL11.GL_RENDERER); + } + + /** + * 获取 OpenGL 版本信息 + */ + public static String getOpenGLVersion() { + if (!isOnRenderThread()) { + throw new IllegalStateException("OpenGL info queries must be on render thread"); + } + return GL11.glGetString(GL11.GL_VERSION); + } + + /** + * 获取 GLSL 版本信息 + */ + public static String getGLSLVersion() { + if (!isOnRenderThread()) { + throw new IllegalStateException("OpenGL info queries must be on render thread"); + } + return GL20.glGetString(GL20.GL_SHADING_LANGUAGE_VERSION); + } + + /** + * 记录详细的 OpenGL 信息 + */ + public static void logDetailedGLInfo() { + assertOnRenderThread(); + + Logger logger = LoggerFactory.getLogger(RenderSystem.class); + + logger.info("=== OpenGL System Information ==="); + logger.info("Vendor: {}", getVendor()); + logger.info("Renderer: {}", getRenderer()); + logger.info("OpenGL Version: {}", getOpenGLVersion()); + logger.info("GLSL Version: {}", getGLSLVersion()); + + // 添加扩展数量信息 + int numExtensions = GL11.glGetInteger(GL30.GL_NUM_EXTENSIONS); + logger.info("Number of Extensions: {}", numExtensions); + + // 添加重要特性支持检查 + logger.info("Max Texture Size: {}x{}", + GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE), + GL11.glGetInteger(GL11.GL_MAX_TEXTURE_SIZE)); + logger.info("Max Vertex Attributes: {}", + GL11.glGetInteger(GL20.GL_MAX_VERTEX_ATTRIBS)); + logger.info("Max Texture Units: {}", + GL11.glGetInteger(GL20.GL_MAX_TEXTURE_IMAGE_UNITS)); + } + public static void setupDefaultState() { + beginInitialization(); + + // 设置默认 OpenGL 状态 + clearColor(0.0f, 0.0f, 0.0f, 1.0f); + viewport(0, 0, viewportWidth, viewportHeight); + enableBlend(); + defaultBlendFunc(); + disableDepthTest(); + + finishInitialization(); + } + + public static void checkGLError(String operation) { + if (!isOnRenderThread()) { + recordRenderCall(() -> _checkGLError(operation)); + } else { + _checkGLError(operation); + } + } + + private static void _checkGLError(String operation) { + assertOnRenderThread(); + int error = GL11.glGetError(); + if (error != GL11.GL_NO_ERROR) { + Exception stackTraceException = new Exception("OpenGL error stack trace"); + StackTraceElement[] stackTrace = stackTraceException.getStackTrace(); + + StringBuilder stackTraceBuilder = new StringBuilder(); + stackTraceBuilder.append("Call stack:\n"); + + for (int i = 2; i < Math.min(stackTrace.length, 10); i++) { + StackTraceElement element = stackTrace[i]; + stackTraceBuilder.append(" at ") + .append(element.getClassName()) + .append(".") + .append(element.getMethodName()) + .append("(") + .append(element.getFileName()) + .append(":") + .append(element.getLineNumber()) + .append(")\n"); + } + + logger.error("OpenGL error during {}: {}\n{}", + operation, getGLErrorString(error), stackTraceBuilder.toString()); + } + } + + private static String getGLErrorString(int error) { + switch (error) { + case GL11.GL_INVALID_ENUM: return "GL_INVALID_ENUM"; + case GL11.GL_INVALID_VALUE: return "GL_INVALID_VALUE"; + case GL11.GL_INVALID_OPERATION: return "GL_INVALID_OPERATION"; + case GL11.GL_OUT_OF_MEMORY: return "GL_OUT_OF_MEMORY"; + default: return "Unknown (0x" + Integer.toHexString(error) + ")"; + } + } + + // ================== 获取状态 ================== + + public static int getViewportWidth() { + return viewportWidth; + } + + public static int getViewportHeight() { + return viewportHeight; + } + + public static float[] getClearColor() { + return clearColor.clone(); + } + + public static int getQueueSize() { + return renderQueue.size(); + } + + + /** + * 顶点格式模式枚举 + */ + public enum VertexFormat { + POINTS(DRAW_POINTS), + LINES(DRAW_LINES), + LINE_LOOP(DRAW_LINE_LOOP), + LINE_STRIP(DRAW_LINE_STRIP), + TRIANGLES(DRAW_TRIANGLES), + TRIANGLE_STRIP(DRAW_TRIANGLE_STRIP), + TRIANGLE_FAN(DRAW_TRIANGLE_FAN), + QUADS(DRAW_QUADS); + + private final int glMode; + + VertexFormat(int glMode) { + this.glMode = glMode; + } + + public int asGLMode() { + return glMode; + } + + public static VertexFormat fromGLMode(int glMode) { + for (VertexFormat format : values()) { + if (format.glMode == glMode) { + return format; + } + } + return TRIANGLES; // 默认回退 + } + } + + /** + * 索引类型枚举 + */ + public enum IndexType { + BYTE(GL_UNSIGNED_BYTE, 1), + SHORT(GL_UNSIGNED_SHORT, 2), + INT(GL_UNSIGNED_INT, 4); + + private final int glType; + private final int bytes; + + IndexType(int glType, int bytes) { + this.glType = glType; + this.bytes = bytes; + } + + public int asGLType() { + return glType; + } + + public int bytes() { + return bytes; + } + + public static IndexType fromGLType(int glType) { + for (IndexType type : values()) { + if (type.glType == glType) { + return type; + } + } + return SHORT; // 默认回退 + } + } + + /** + * 模式类(用于向后兼容) + */ + public static class Mode { + public static VertexFormat fromGLMode(int glMode) { + return VertexFormat.fromGLMode(glMode); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/ShaderSources.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/ShaderSources.java new file mode 100644 index 0000000..d196255 --- /dev/null +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/ShaderSources.java @@ -0,0 +1,168 @@ +package com.chuangzhou.vivid2D.render.systems; + +import org.lwjgl.opengl.GL20; + +import java.util.HashMap; +import java.util.Map; + +import static org.lwjgl.opengl.GL20.glGetUniformLocation; + +/** + * 着色器源代码 + * + * @author tzdwindows 7 + * @version 1.0 + * @since 2025-10-16 + */ +public class ShaderSources { + public static final String VERTEX_SHADER_SRC = + """ + #version 330 core + layout(location = 0) in vec2 aPosition; + layout(location = 1) in vec2 aTexCoord; + out vec2 vTexCoord; + out vec2 vWorldPos; + + uniform mat3 uModelMatrix; + uniform mat3 uViewMatrix; + uniform mat3 uProjectionMatrix; + + void main() { + // 使用 3x3 矩阵链计算屏幕位置(假设矩阵是二维仿射) + vec3 p = uProjectionMatrix * uViewMatrix * uModelMatrix * vec3(aPosition, 1.0); + gl_Position = vec4(p.xy, 0.0, 1.0); + vTexCoord = aTexCoord; + // 输出 world-space 位置供 fragment shader 使用(仅 xy) + vWorldPos = (uModelMatrix * vec3(aPosition, 1.0)).xy; + } + """; + + public static final String FRAGMENT_SHADER_SRC = + """ + #version 330 core + in vec2 vTexCoord; + in vec2 vWorldPos; + out vec4 FragColor; + + uniform sampler2D uTexture; + uniform vec4 uColor; + uniform float uOpacity; + uniform int uBlendMode; + uniform int uDebugMode; + + #define MAX_LIGHTS 8 + uniform vec2 uLightsPos[MAX_LIGHTS]; + uniform vec3 uLightsColor[MAX_LIGHTS]; + uniform float uLightsIntensity[MAX_LIGHTS]; + uniform int uLightsIsAmbient[MAX_LIGHTS]; + uniform int uLightCount; + + // 常用衰减系数(可在 shader 内微调) + const float ATT_CONST = 1.0; + const float ATT_LINEAR = 0.09; + const float ATT_QUAD = 0.032; + + void main() { + // 先采样纹理 + vec4 tex = texture(uTexture, vTexCoord); + float alpha = tex.a * uOpacity; + if (alpha <= 0.001) discard; + + // 如果没有光源,跳过光照计算(性能更好并且保持原始贴图色) + if (uLightCount == 0) { + vec3 base = tex.rgb * uColor.rgb; + // 简单的色调映射(防止数值过大) + base = clamp(base, 0.0, 1.0); + FragColor = vec4(base, alpha); + return; + } + + // 基础颜色(纹理 * 部件颜色) + vec3 baseColor = tex.rgb * uColor.rgb; + + // 全局环境光基线(可以适度提高以避免全黑) + vec3 ambient = vec3(0.06); // 小环境光补偿 + vec3 lighting = vec3(0.0); + vec3 specularAccum = vec3(0.0); + + // 累积环境光(来自被标记为环境光的光源) + for (int i = 0; i < uLightCount; ++i) { + if (uLightsIsAmbient[i] == 1) { + lighting += uLightsColor[i] * uLightsIntensity[i]; + } + } + // 加上基线环境光 + lighting += ambient; + + // 对每个非环境光计算基于距离的衰减与简单高光 + for (int i = 0; i < uLightCount; ++i) { + if (uLightsIsAmbient[i] == 1) continue; + + vec2 toLight = uLightsPos[i] - vWorldPos; + float dist = length(toLight); + // 标准物理式衰减 + float attenuation = ATT_CONST / (ATT_CONST + ATT_LINEAR * dist + ATT_QUAD * dist * dist); + + // 强度受光源强度和衰减影响 + float radiance = uLightsIntensity[i] * attenuation; + + // 漫反射:在纯2D情景下,法线与视线近似固定(Z向), + // 所以漫反射对所有片元是恒定的。我们用一个基于距离的柔和因子来模拟明暗变化。 + float diffuseFactor = clamp(1.0 - (dist * 0.0015), 0.0, 1.0); // 通过调节常数控制半径感觉 + vec3 diff = uLightsColor[i] * radiance * diffuseFactor; + lighting += diff; + + // 简单高光(基于视向与反射的大致模拟,产生亮点) + vec3 lightDir3 = normalize(vec3(toLight, 0.0)); + vec3 viewDir = vec3(0.0, 0.0, 1.0); + vec3 normal = vec3(0.0, 0.0, 1.0); + vec3 reflectDir = reflect(-lightDir3, normal); + float specFactor = pow(max(dot(viewDir, reflectDir), 0.0), 16.0); // 16 为高光粗糙度,可调 + float specIntensity = 0.2; // 高光强度系数 + specularAccum += uLightsColor[i] * radiance * specFactor * specIntensity; + } + + // 限制光照的最大值以避免过曝(可根据场景调整) + vec3 totalLighting = min(lighting + specularAccum, vec3(2.0)); + + // 将光照应用到基础颜色 + vec3 finalColor = baseColor * totalLighting; + + // 支持简单混合模式(保留原有行为) + if (uBlendMode == 1) finalColor = tex.rgb + uColor.rgb; + else if (uBlendMode == 2) finalColor = tex.rgb * uColor.rgb; + else if (uBlendMode == 3) finalColor = 1.0 - (1.0 - tex.rgb) * (1.0 - uColor.rgb); + + finalColor = clamp(finalColor, 0.0, 1.0); + FragColor = vec4(finalColor, alpha); + } + """; + + + public static class ShaderProgram { + public final int programId; + public final Map uniformCache = new HashMap<>(); + + public ShaderProgram(int programId) { + this.programId = programId; + } + + public void use() { + GL20.glUseProgram(programId); + } + + public void stop() { + GL20.glUseProgram(0); + } + + public int getUniformLocation(String name) { + return uniformCache.computeIfAbsent(name, k -> { + return glGetUniformLocation(programId, k); + }); + } + + public void delete() { + if (GL20.glIsProgram(programId)) GL20.glDeleteProgram(programId); + } + } +} diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferBuilder.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferBuilder.java new file mode 100644 index 0000000..d8b40e8 --- /dev/null +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferBuilder.java @@ -0,0 +1,268 @@ +package com.chuangzhou.vivid2D.render.systems.buffer; + +import com.chuangzhou.vivid2D.render.systems.RenderSystem; +import org.joml.Vector4f; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL20; +import org.lwjgl.system.MemoryUtil; + +import java.nio.FloatBuffer; + +/** + * 简化版 BufferBuilder,用于按顶点流构建并一次性绘制几何体。 + * 每个顶点格式: float x, float y, float u, float v (共4个 float) + * + * 用法: + * BufferBuilder bb = new BufferBuilder(); + * bb.begin(GL11.GL_LINE_LOOP, 16); + * bb.vertex(x,y,u,v); + * ... + * bb.end(); // 立即绘制并 cleanup + * + * 设计原则:简单、可靠、方便把临时多顶点数据提交到 GPU。 + * + * + * @version 1.2 + * @since 2025-10-16 + * @author tzdwindows + */ +public class BufferBuilder { + private static final org.slf4j.Logger logger = org.slf4j.LoggerFactory.getLogger(BufferBuilder.class); + private static final int COMPONENTS_PER_VERTEX = 4; // x,y,u,v + private float[] array; + private int size; // float 数量 + private int vertexCount; + private int mode; // GL mode + private RenderState renderState = new RenderState(); + private boolean stateSaved = false; + // 内置状态,用于构建完成的缓冲区 + public static class BuiltBuffer { + private final int vao; + private final int vbo; + private final int vertexCount; + private final int mode; + private final RenderState renderState; + + public BuiltBuffer(int vao, int vbo, int vertexCount, int mode, RenderState renderState) { + this.vao = vao; + this.vbo = vbo; + this.vertexCount = vertexCount; + this.mode = mode; + this.renderState = renderState; + } + + public RenderState renderState() { return renderState; } + + public int vao() { return vao; } + public int vbo() { return vbo; } + public int vertexCount() { return vertexCount; } + public int mode() { return mode; } + } + + /** + * 渲染状态容器类 + */ + public static class RenderState { + public int textureId = 0; + public int textureUnit = 0; + public int shaderProgram = 0; + public Vector4f color = new Vector4f(1, 1, 1, 1); + public float opacity = 1.0f; + public int blendMode = 0; + public boolean depthTest = false; + public boolean blending = true; + + // 保存当前状态 + public void saveCurrentState() { + this.textureId = GL11.glGetInteger(GL11.GL_TEXTURE_BINDING_2D); + this.shaderProgram = GL11.glGetInteger(GL20.GL_CURRENT_PROGRAM); + } + + // 应用保存的状态 + public void applyState() { + if (textureId != 0) { + RenderSystem.bindTexture(textureId); + } + if (shaderProgram != 0) { + RenderSystem.useProgram(shaderProgram); + } + } + + // 复制状态 + public RenderState copy() { + RenderState copy = new RenderState(); + copy.textureId = this.textureId; + copy.textureUnit = this.textureUnit; + copy.shaderProgram = this.shaderProgram; + copy.color = new Vector4f(this.color); + copy.opacity = this.opacity; + copy.blendMode = this.blendMode; + copy.depthTest = this.depthTest; + copy.blending = this.blending; + return copy; + } + } + + + public BufferBuilder() { + this(256); // 默认容量:256 floats -> 64 顶点 + } + + public BufferBuilder(int initialFloatCapacity) { + this.array = new float[Math.max(16, initialFloatCapacity)]; + this.size = 0; + this.vertexCount = 0; + this.mode = RenderSystem.DRAW_TRIANGLES; + } + + private void ensureCapacity(int additionalFloats) { + int need = size + additionalFloats; + if (need > array.length) { + int newCap = array.length; + while (newCap < need) newCap <<= 1; + float[] na = new float[newCap]; + System.arraycopy(array, 0, na, 0, size); + array = na; + } + } + + /** + * 开始构建,传入要绘制的 GL 模式(例如 GL11.GL_LINE_LOOP) + * estimatedVertexCount 可传 0 表示不估计 + */ + public void begin(int glMode, int estimatedVertexCount) { + this.mode = glMode; + this.size = 0; + this.vertexCount = 0; + + // 保存当前渲染状态 + if (!stateSaved) { + renderState.saveCurrentState(); + stateSaved = true; + } + + if (estimatedVertexCount > 0) { + ensureCapacity(estimatedVertexCount * COMPONENTS_PER_VERTEX); + } + } + + /** + * 设置纹理状态 + */ + public void setTexture(int textureId, int textureUnit) { + this.renderState.textureId = textureId; + this.renderState.textureUnit = textureUnit; + } + + /** + * 设置着色器程序 + */ + public void setShader(int programId) { + this.renderState.shaderProgram = programId; + } + + /** + * 设置颜色 + */ + public void setColor(Vector4f color) { + this.renderState.color = new Vector4f(color); + } + + /** + * 设置透明度 + */ + public void setOpacity(float opacity) { + this.renderState.opacity = opacity; + } + + /** + * 添加顶点:x,y,u,v + */ + public void vertex(float x, float y, float u, float v) { + ensureCapacity(COMPONENTS_PER_VERTEX); + array[size++] = x; + array[size++] = y; + array[size++] = u; + array[size++] = v; + vertexCount++; + } + + /** + * 结束构建并返回 BuiltBuffer(不立即绘制) + */ + public BuiltBuffer end() { + if (vertexCount == 0) return null; + + RenderSystem.assertOnRenderThread(); + + // 上传缓冲区数据 + FloatBuffer fb = MemoryUtil.memAllocFloat(size); + fb.put(array, 0, size).flip(); + + // 创建 GPU 资源 + int vao = RenderSystem.glGenVertexArrays(); + int vbo = RenderSystem.glGenBuffers(); + + RenderSystem.glBindVertexArray(() -> vao); + RenderSystem.glBindBuffer(RenderSystem.GL_ARRAY_BUFFER, () -> vbo); + RenderSystem.glBufferData(RenderSystem.GL_ARRAY_BUFFER, fb, RenderSystem.GL_DYNAMIC_DRAW); + + // 设置顶点属性 + RenderSystem.enableVertexAttribArray(0); + RenderSystem.vertexAttribPointer(0, 2, RenderSystem.GL_FLOAT, false, + COMPONENTS_PER_VERTEX * Float.BYTES, 0); + + RenderSystem.enableVertexAttribArray(1); + RenderSystem.vertexAttribPointer(1, 2, RenderSystem.GL_FLOAT, false, + COMPONENTS_PER_VERTEX * Float.BYTES, 2 * Float.BYTES); + + MemoryUtil.memFree(fb); + RenderSystem.glBindBuffer(RenderSystem.GL_ARRAY_BUFFER, () -> 0); + RenderSystem.glBindVertexArray(() -> 0); + + // 创建状态副本 + RenderState stateCopy = renderState.copy(); + + // 重置状态保存标志 + stateSaved = false; + + return new BuiltBuffer(vao, vbo, vertexCount, mode, stateCopy); + } + + /** + * 立即上传并绘制(传统方式,向后兼容) + */ + public void endImmediate() { + BuiltBuffer buffer = end(); + if (buffer != null) { + BufferUploader.drawWithShader(buffer); + } + } + + /** + * 清理构建器创建的所有 GPU 资源 + * 应该在确定不再需要这些资源时调用 + */ + public static void dispose(BuiltBuffer buffer) { + if (buffer != null) { + RenderSystem.glDeleteVertexArrays(buffer.vao()); + RenderSystem.glDeleteBuffers(buffer.vbo()); + } + } + + /** + * 清除构建器状态以便重用 + */ + public void clear() { + this.size = 0; + this.vertexCount = 0; + } + + public int getVertexCount() { + return vertexCount; + } + + public boolean isEmpty() { + return vertexCount == 0; + } +} \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferUploader.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferUploader.java new file mode 100644 index 0000000..5edf0dc --- /dev/null +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/BufferUploader.java @@ -0,0 +1,87 @@ +package com.chuangzhou.vivid2D.render.systems.buffer; + +import com.chuangzhou.vivid2D.render.systems.RenderSystem; + +/** + * 缓冲区上传器 + * + * @version 1.0 + * @author tzdwindows + */ +public class BufferUploader { + + public static void drawWithShader(BufferBuilder.BuiltBuffer buffer) { + if (buffer == null) return; + + RenderSystem.assertOnRenderThread(); + + // 保存当前状态 + BufferBuilder.RenderState currentState = new BufferBuilder.RenderState(); + currentState.saveCurrentState(); + + try { + // 应用缓冲区指定的渲染状态 + BufferBuilder.RenderState bufferState = buffer.renderState(); + applyRenderState(bufferState); + + // 绑定 VAO 和绘制 + if (buffer.vao() != 0) { + RenderSystem.glBindVertexArray(() -> buffer.vao()); + } + + if (buffer.vertexCount() > 0) { + RenderSystem.drawArrays(buffer.mode(), 0, buffer.vertexCount()); + } + + if (buffer.vao() != 0) { + RenderSystem.glBindVertexArray(() -> 0); + } + + } finally { + // 恢复之前的状态 + restoreRenderState(currentState); + } + } + + /** + * 应用指定的渲染状态 + */ + private static void applyRenderState(BufferBuilder.RenderState state) { + // 应用纹理 + if (state.textureId != 0) { + RenderSystem.activeTexture(RenderSystem.GL_TEXTURE0 + state.textureUnit); + RenderSystem.bindTexture(state.textureId); + } + + // 应用着色器 + if (state.shaderProgram != 0) { + RenderSystem.useProgram(state.shaderProgram); + } + + // 应用混合模式 + if (state.blending) { + RenderSystem.enableBlend(); + RenderSystem.defaultBlendFunc(); + } else { + RenderSystem.disableBlend(); + } + + // 应用深度测试 + if (state.depthTest) { + RenderSystem.enableDepthTest(); + } else { + RenderSystem.disableDepthTest(); + } + } + + /** + * 恢复之前的渲染状态 + */ + private static void restoreRenderState(BufferBuilder.RenderState previousState) { + previousState.applyState(); + } + + public static void draw(BufferBuilder.BuiltBuffer buffer) { + drawWithShader(buffer); + } +} \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/Tesselator.java b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/Tesselator.java new file mode 100644 index 0000000..dd501aa --- /dev/null +++ b/src/main/java/com/chuangzhou/vivid2D/render/systems/buffer/Tesselator.java @@ -0,0 +1,38 @@ +package com.chuangzhou.vivid2D.render.systems.buffer; + +import com.chuangzhou.vivid2D.render.systems.RenderSystem; + +/** + * 管理缓存 + * + * @version 1.0 + * @author tzdwindows + */ +public class Tesselator { + private static final int DEFAULT_BUFFER_SIZE = 2097152; // 2MB + private static final Tesselator INSTANCE = new Tesselator(); + + private final BufferBuilder builder; + + public static Tesselator getInstance() { + RenderSystem.assertOnRenderThreadOrInit(); + return INSTANCE; + } + + public Tesselator(int bufferSize) { + this.builder = new BufferBuilder(bufferSize); + } + + public Tesselator() { + this(DEFAULT_BUFFER_SIZE); + } + + public void end() { + RenderSystem.assertOnRenderThread(); + BufferUploader.drawWithShader(this.builder.end()); + } + + public BufferBuilder getBuilder() { + return this.builder; + } +} \ No newline at end of file diff --git a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderLightingTest.java b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderLightingTest.java index b8be4bf..46e856a 100644 --- a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderLightingTest.java +++ b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderLightingTest.java @@ -6,6 +6,7 @@ import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.util.LightSource; import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.Texture; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.joml.Vector2f; import org.joml.Vector3f; import org.lwjgl.glfw.GLFW; @@ -136,15 +137,15 @@ public class ModelRenderLightingTest { body.addChild(rightLeg); LightSource ambientLight = new LightSource( - Color.GRAY, + Color.lightGray, 0.3f ); ambientLight.setAmbient(true); model.addLight(ambientLight); // 添加光源 - model.addLight(new LightSource(new Vector2f(-100, -100), Color.ORANGE, 100f)); - //model.addLight(new LightSource(new Vector2f(150, 150), new Color(1f, 1f, 1f), 200f)); + model.addLight(new LightSource(new Vector2f(-100, -100), Color.lightGray, 200f)); + model.addLight(new LightSource(new Vector2f(150, 150), new Color(1f, 1f, 1f), 200f)); } private Texture createSolidTexture(int w, int h, int rgba) { @@ -221,7 +222,7 @@ public class ModelRenderLightingTest { } private void render() { - ModelRender.setClearColor(0.18f,0.18f,0.25f,1.0f); + RenderSystem.setClearColor(0.18f,0.18f,0.25f,1.0f); ModelRender.render(1f/60f, model); } diff --git a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest.java b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest.java index a5e9e73..2abcc19 100644 --- a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest.java +++ b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest.java @@ -6,6 +6,7 @@ import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem; import com.chuangzhou.vivid2D.render.model.util.Texture; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.joml.Vector2f; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; @@ -261,7 +262,7 @@ public class ModelRenderTest { } private void render() { - ModelRender.setClearColor(0.18f, 0.18f, 0.25f, 1.0f); + RenderSystem.setClearColor(0.18f, 0.18f, 0.25f, 1.0f); ModelRender.render(1.0f / 60.0f, testModel); // 每 5 秒输出一次统计 diff --git a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest2.java b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest2.java index c9fbf33..1ca944a 100644 --- a/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest2.java +++ b/src/main/java/com/chuangzhou/vivid2D/test/ModelRenderTest2.java @@ -5,6 +5,7 @@ import com.chuangzhou.vivid2D.render.model.Model2D; import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.Texture; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.joml.Matrix3f; import org.joml.Vector2f; import org.lwjgl.glfw.GLFW; @@ -214,7 +215,7 @@ public class ModelRenderTest2 { } private void render() { - ModelRender.setClearColor(0.2f, 0.2f, 0.3f, 1.0f); + RenderSystem.setClearColor(0.2f, 0.2f, 0.3f, 1.0f); ModelRender.render(1.0f / 60.0f, testModel); } diff --git a/src/main/java/com/chuangzhou/vivid2D/test/ModelTest2.java b/src/main/java/com/chuangzhou/vivid2D/test/ModelTest2.java index 781c77b..341edd1 100644 --- a/src/main/java/com/chuangzhou/vivid2D/test/ModelTest2.java +++ b/src/main/java/com/chuangzhou/vivid2D/test/ModelTest2.java @@ -6,6 +6,7 @@ import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.util.Mesh2D; import com.chuangzhou.vivid2D.render.model.util.Texture; import com.chuangzhou.vivid2D.render.model.util.PhysicsSystem; +import com.chuangzhou.vivid2D.render.systems.RenderSystem; import org.joml.Vector2f; import org.lwjgl.glfw.GLFW; import org.lwjgl.glfw.GLFWErrorCallback; @@ -666,7 +667,7 @@ public class ModelTest2 { } private void render(long last) { - ModelRender.setClearColor(0.1f, 0.1f, 0.15f, 1.0f); + RenderSystem.setClearColor(0.1f, 0.1f, 0.15f, 1.0f); ModelRender.render(last, physicsModel); } diff --git a/src/main/java/com/chuangzhou/vivid2D/test/TestModelGLPanel.java b/src/main/java/com/chuangzhou/vivid2D/test/TestModelGLPanel.java index 1d92e31..341a5c8 100644 --- a/src/main/java/com/chuangzhou/vivid2D/test/TestModelGLPanel.java +++ b/src/main/java/com/chuangzhou/vivid2D/test/TestModelGLPanel.java @@ -1,6 +1,6 @@ package com.chuangzhou.vivid2D.test; -import com.chuangzhou.vivid2D.render.ModelGLPanel; +import com.chuangzhou.vivid2D.render.awt.ModelGLPanel; import com.chuangzhou.vivid2D.render.model.Model2D; import com.chuangzhou.vivid2D.render.model.ModelPart; import com.chuangzhou.vivid2D.render.model.util.Mesh2D;