Не применяется динамическое изменение цвета в TESR

Версия Minecraft
1.12.2
API
Forge
Всем привет!
Пытаюсь отрендерить модельку светофора в кубах, где через теср хочу рендерить линзы разных цветов. Светофор имеет конфигурируемый головной блок, поэтому каждой линзе можно задать свой цвет. Оттого сама текстура линз белая и, используя GlStateManager.color, хочу окрашивать линзу соответствующим цветом.

Проблема в том, что изменение цвета абсолютно игнорируется и постоянно рендерится оригинальная текстура в белом цвете:
1764531960451.png1764532008106.png
Есть предположение что так как это obj модель и загружается форжом, то он запекает цвет в вершины и поэтому не работает GlStateManager.color, но разве шейдер не должен перемножать цвета вершин на глобальные?

Думаю это можно решить перебором вершин в самом TESR и изменением их цветов, но возможно есть более оптимальный способ? Хотелось бы использовать GlStateManager.color

Сам TESR для рендера линз
LightSignalSpecialRenderer.java:
@Override
    public void render(SignalTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) {
        BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher();
        IBakedModel model = dispatcher.getModelForState(this.getWorld().getBlockState(te.getPos()));
        if (model instanceof MatoiModel.MatoiBakedModel) {
            IBakedModel base = ((MatoiModel.MatoiBakedModel<?>) model).getBase();
            if (base instanceof ITransformable) {

                GlStateManager.pushAttrib();
                GlStateManager.pushMatrix();

                GlStateManager.translate(x, y, z);
                bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);

                GlStateManager.enableBlend();
                GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
                GlStateManager.disableCull();
                GlStateManager.enableTexture2D();

                // Apply transformations
                GlStateManager.translate(0.5f, 0.0f, 0.5f);
                FloatPoint rotation = te.getRotation();
                rotateAtCenter(rotation.getX(), te.getScale().getX() > 0.0f ? -rotation.getY() : rotation.getY(), rotation.getZ());
                GlStateManager.translate(-0.5f, 0.0f, -0.5f);

                LightSignalConfig cfg = te.getSignalConfig();
                List<Byte> signalData = te.getSignals();

                for (int i = 0; i < cfg.lensCount(); i++ ) {
                    if (signalData != null && signalData.size() >= cfg.lensCount()) {
                        final String lensPart = cfg.getLensPrefix() + (i + 1) + cfg.getLensSuffix();
                        List<BakedQuad> lensQuads = ((ITransformable) base).getGroupQuads(Collections.singleton(lensPart));

                        LightSignalColor color = cfg.getLens()[i];
                        float[] colorMask = getColorMask(color);
                        boolean enabled = signalData.get(i) > 0;
                        float brightnessFactor = enabled ? 1.0f : 0.2f;

                        if (enabled) {
                            GlStateManager.disableLighting();
                            Minecraft.getMinecraft().entityRenderer.disableLightmap();
                        }

                        renderQuads(lensQuads, colorMask[0], colorMask[1], colorMask[2], brightnessFactor,
                                ((ITransformable) base).getFormat());

                        if (enabled) {
                            Minecraft.getMinecraft().entityRenderer.enableLightmap();
                            GlStateManager.enableLighting();
                        }
                    }
                }

                GlStateManager.enableCull();
                GlStateManager.disableBlend();
                GlStateManager.popMatrix();
                GlStateManager.popAttrib();
            }
        }
    }

    private float[] getColorMask(LightSignalColor color) {
        switch (color) {
            case RED:
                return new float[]{1.0f, 0.0f, 0.0f};
            case GREEN:
                return new float[]{0.0f, 1.0f, 0.0f};
            case YELLOW:
                return new float[]{1.0f, 1.0f, 0.0f};
            case BLUE:
                return new float[]{0.0f, 0.0f, 1.0f};
            default:
                return new float[]{1.0f, 1.0f, 1.0f};
        }
    }

    private void renderQuads(List<BakedQuad> quads, float r, float g, float b, float brightnessFactor, VertexFormat format) {
        GlStateManager.color(r * brightnessFactor, g * brightnessFactor, b * brightnessFactor, 1.0f);

        BufferBuilder buf = Tessellator.getInstance().getBuffer();
        buf.begin(GL11.GL_QUADS, format);
        for (BakedQuad quad : quads) {
            buf.addVertexData(quad.getVertexData());
        }
        Tessellator.getInstance().draw();
    }
 
Решение
Если я правильно тебя понял.
Немного доработал твой первый вариант, немного не так высчитывался индекс цвета и цвет записывался не в том порядке, в итоге все сработало спасибо за совет!

Java:
private void renderQuads(List<BakedQuad> quads, float r, float g, float b, float brightnessFactor, VertexFormat format) {

        r *= brightnessFactor;
        g *= brightnessFactor;
        b *= brightnessFactor;

        BufferBuilder buf = Tessellator.getInstance().getBuffer();
        buf.begin(GL11.GL_QUADS, format);

        // --- 1. Находим смещение цвета (в int-ах) ---
        int colorOffsetInInts = -1;
        for (int i = 0; i < format.getElementCount(); i++) {
            VertexFormatElement element = format.getElement(i)...
Java:
private void renderQuads(List<BakedQuad> quads, float r, float g, float b, float brightnessFactor, VertexFormat format) {
    
    r *= brightnessFactor;
    g *= brightnessFactor;
    b *= brightnessFactor;

    BufferBuilder buf = Tessellator.getInstance().getBuffer();
    buf.begin(GL11.GL_QUADS, format);

    for (BakedQuad quad : quads) {
        int[] vertexData = quad.getVertexData().clone();

      
        int colorOffset = -1;
        int offset = 0;
        for (VertexFormatElement element : format.getElements()) {
            if (element.getUsage() == VertexFormatElement.Usage.COLOR) {
                colorOffset = offset;
                break;
            }
            offset += element.getSize() * 4;
        }

        if (colorOffset == -1) {
            
            GlStateManager.color(r, g, b, 1.0f);
            buf.addVertexData(quad.getVertexData());
            continue;
        }

        
        int verticesPerQuad = 4; // Quad всегда 4 вершины
        int stride = format.getNextOffset() / 4;

        for (int i = 0; i < verticesPerQuad; i++) {
            int vertexStart = i * stride;
            int colorIndex = vertexStart + colorOffset / 4;

          )
            //обычно: [R, G, B, A] в int-ах, каждый компонент 0-255
            int alpha = (vertexData[colorIndex] >> 24) & 0xFF;
            int red   = (int) (r * 255);
            int green = (int) (g * 255);
            int blue  = (int) (b * 255);

            // Собирает цвет в int
            int newColor = (alpha << 24) | (red << 16) | (green << 8) | blue;
            vertexData[colorIndex] = newColor;
        }

        buf.addVertexData(vertexData);
    }

    Tessellator.getInstance().draw();
}
 
Да, что-то такое я и имел ввиду под "перебором" вершин в самом TESR и изменением их цветов, но как я понял ты предлагаешь не пересобирать его заново, а подхачить только цвет? :unsure:
В целом оно и так близко к результату, но есть небольшие артефакты

1764543363428.png
А подскажи еще что за метод у формата такой getNextOffset()? Не нашел у себя, взял как getOffset(0). Там формат всегда будет ITEM, может в этом проблема...
 
Java:
@Override
public void render(SignalTileEntity te, double x, double y, double z, float partialTicks, int destroyStage, float alpha) {
    BlockRendererDispatcher dispatcher = Minecraft.getMinecraft().getBlockRendererDispatcher();
    IBakedModel model = dispatcher.getModelForState(this.getWorld().getBlockState(te.getPos()));
    if (model instanceof MatoiModel.MatoiBakedModel) {
        IBakedModel base = ((MatoiModel.MatoiBakedModel<?>) model).getBase();
        if (base instanceof ITransformable) {

            GlStateManager.pushAttrib();
            GlStateManager.pushMatrix();

            GlStateManager.translate(x, y, z);
            Minecraft.getMinecraft().getTextureManager().bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);

            GlStateManager.enableBlend();
            GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
            GlStateManager.disableCull();
            GlStateManager.enableTexture2D();

          
            GlStateManager.translate(0.5f, 0.0f, 0.5f);
            FloatPoint rotation = te.getRotation();
            rotateAtCenter(rotation.getX(), te.getScale().getX() > 0.0f ? -rotation.getY() : rotation.getY(), rotation.getZ());
            GlStateManager.translate(-0.5f, 0.0f, -0.5f);

            LightSignalConfig cfg = te.getSignalConfig();
            List<Byte> signalData = te.getSignals();

            Tessellator tessellator = Tessellator.getInstance();
            BufferBuilder buffer = tessellator.getBuffer();

            for (int i = 0; i < cfg.lensCount(); i++) {
                if (signalData != null && signalData.size() >= cfg.lensCount()) {
                    final String lensPart = cfg.getLensPrefix() + (i + 1) + cfg.getLensSuffix();
                    List<BakedQuad> lensQuads = ((ITransformable) base).getGroupQuads(Collections.singleton(lensPart));

                    if (lensQuads.isEmpty()) continue;

                    LightSignalColor color = cfg.getLens()[i];
                    float[] colorMask = getColorMask(color);
                    boolean enabled = signalData.get(i) > 0;
                    float brightnessFactor = enabled ? 1.0f : 0.2f;

                    
                    if (enabled) {
                        GlStateManager.disableLighting();
                        Minecraft.getMinecraft().entityRenderer.disableLightmap();
                    }

                  
                    float r = Math.max(0.0f, Math.min(1.0f, colorMask[0] * brightnessFactor));
                    float g = Math.max(0.0f, Math.min(1.0f, colorMask[1] * brightnessFactor));
                    float b = Math.max(0.0f, Math.min(1.0f, colorMask[2] * brightnessFactor));

                    int red   = (int) (r * 255.0f);
                    int green = (int) (g * 255.0f);
                    int blue  = (int) (b * 255.0f);
                    int newColor = (255 << 24) | (red << 16) | (green << 8) | blue;

                    buffer.begin(GL11.GL_QUADS, DefaultVertexFormats.BLOCK);
                    for (BakedQuad quad : lensQuads) {
                        int[] vertexData = quad.getVertexData().clone();

                      
                        for (int v = 0; v < 4; v++) {
                            int colorIndex = v * 7 + 1;
                            vertexData[colorIndex] = newColor;
                        }

                        buffer.addVertexData(vertexData);
                    }
                    tessellator.draw();
                  

                    if (enabled) {
                        Minecraft.getMinecraft().entityRenderer.enableLightmap();
                        GlStateManager.enableLighting();
                    }
                }
            }

            GlStateManager.enableCull();
            GlStateManager.disableBlend();
            GlStateManager.popMatrix();
            GlStateManager.popAttrib();
        }
    }
}

private float[] getColorMask(LightSignalColor color) {
    switch (color) {
        case RED:
            return new float[]{1.0f, 0.0f, 0.0f};
        case GREEN:
            return new float[]{0.0f, 1.0f, 0.0f};
        case YELLOW:
            return new float[]{1.0f, 1.0f, 0.0f};
        case BLUE:
            return new float[]{0.0f, 0.0f, 1.0f};
        default:
            return new float[]{1.0f, 1.0f, 1.0f};
    }
}
 
Ммм к сожалению это похоже совсем мимо, сами линзы перестали рендерится вовсе
1764610299555.png

Если это поможет, то вот как запаковываются квады (Тоже самое что делает фордж с obj моделями, но с небольшими изменениями):
Java:
private static void putVertexData(UnpackedBakedQuad.Builder builder, OBJModel.Vertex v, OBJModel.Normal faceNormal, OBJModel.TextureCoordinate defUV, TextureAtlasSprite sprite, VertexFormat format, boolean flipV)
    {
        for (int e = 0; e < format.getElementCount(); e++)
        {
            switch (format.getElement(e).getUsage())
            {
                case POSITION: {
                    builder.put(e, v.getPos().x, v.getPos().y, v.getPos().z, v.getPos().w);
                    break;
                }
                case COLOR: {
                    if (v.getMaterial() != null)
                        builder.put(e,
                                v.getMaterial().getColor().x,
                                v.getMaterial().getColor().y,
                                v.getMaterial().getColor().z,
                                v.getMaterial().getColor().w);
                    else
                        builder.put(e, 1, 1, 1, 1);
                    break;
                }
                case UV: {
                    if (format.getElement(e).getIndex() == 0) {
                        // Texture coordinates (u,v)
                        if (!v.hasTextureCoordinate())
                            builder.put(e,
                                    sprite.getInterpolatedU(defUV.u * 16),
                                    sprite.getInterpolatedV((flipV ? 1 - defUV.v : defUV.v) * 16),
                                    0, 1);
                        else
                            builder.put(e,
                                    sprite.getInterpolatedU(v.getTextureCoordinate().u * 16),
                                    sprite.getInterpolatedV((flipV ? 1 - v.getTextureCoordinate().v : v.getTextureCoordinate().v) * 16),
                                    0, 1);
                    } else if (format.getElement(e).getIndex() == 2) {
                        // Lightmap UVs (required by shaders!)
                        int light = 0x00F000F0;
                        int lx = (light >> 16) & 0xFFFF;
                        int ly = light & 0xFFFF;
                        builder.put(e, (float) lx, (float) ly, 0f, 1f);
                    }
                    break;
                }
                case NORMAL: {
                    float nx = v.hasNormal() ? v.getNormal().x : faceNormal.x;
                    float ny = v.hasNormal() ? v.getNormal().y : faceNormal.y;
                    float nz = v.hasNormal() ? v.getNormal().z : faceNormal.z;
                    builder.put(e, nx, ny, nz, 0f);
                    break;
                }
                default: {
                    builder.put(e, 0f, 0f, 0f, 1f);
                }
            }
        }
    }
 
Если я правильно тебя понял.
Немного доработал твой первый вариант, немного не так высчитывался индекс цвета и цвет записывался не в том порядке, в итоге все сработало спасибо за совет!

Java:
private void renderQuads(List<BakedQuad> quads, float r, float g, float b, float brightnessFactor, VertexFormat format) {

        r *= brightnessFactor;
        g *= brightnessFactor;
        b *= brightnessFactor;

        BufferBuilder buf = Tessellator.getInstance().getBuffer();
        buf.begin(GL11.GL_QUADS, format);

        // --- 1. Находим смещение цвета (в int-ах) ---
        int colorOffsetInInts = -1;
        for (int i = 0; i < format.getElementCount(); i++) {
            VertexFormatElement element = format.getElement(i);
            if (element.getUsage() == VertexFormatElement.EnumUsage.COLOR) {
                // format.getOffset(i) возвращает смещение в БАЙТАХ. Делим на 4.
                colorOffsetInInts = format.getOffset(i) / 4;
                break;
            }
        }

        // --- 2. Получаем шаг (stride) ---
        // getIntegerSize() возвращает размер вершины в int-ах (для ITEM это 7)
        final int stride = format.getIntegerSize();

        // Если формат не имеет цвета (например, LENS_FORMAT), используем GlStateManager
        if (colorOffsetInInts == -1) {
            GlStateManager.color(r, g, b, 1.0f);
            for (BakedQuad quad : quads) {
                buf.addVertexData(quad.getVertexData());
            }
            Tessellator.getInstance().draw();
            GlStateManager.color(1.0f, 1.0f, 1.0f, 1.0f); // Сброс
            return;
        }

        // --- 3. Основной цикл (Если формат ИМЕЕТ цвет) ---
        for (BakedQuad quad : quads) {
            // Клонируем, чтобы не испортить кеш модели
            int[] vertexData = quad.getVertexData().clone();

            // Квад = 4 вершины
            for (int i = 0; i < 4; i++) {
                // Находим индекс, где начинается цвет для этой вершины
                int colorIndex = (i * stride) + colorOffsetInInts;

                // Получаем исходную альфу (A из AABBGGRR)
                int alpha = (vertexData[colorIndex] >> 24) & 0xFF;

                // Новые компоненты RGB
                int red   = (int) (r * 255.0F);
                int green = (int) (g * 255.0F);
                int blue  = (int) (b * 255.0F);

                // --- 4. Упаковываем в правильном порядке (AABBGGRR) ---
                int newColor = (alpha << 24) | (blue << 16) | (green << 8) | red;

                // Перезаписываем цвет в клонированном массиве
                vertexData[colorIndex] = newColor;
            }

            // Добавляем измененные данные в буфер
            buf.addVertexData(vertexData);
        }

        Tessellator.getInstance().draw();
    }
 
Немного доработал твой первый вариант, немного не так высчитывался индекс цвета и цвет записывался не в том порядке, в итоге все сработало спасибо за совет!
Нужно было мой ответ лайкнуть))). Ну ладно. Удачи. Рад был помочь.
 
Назад
Сверху