summaryrefslogtreecommitdiff
path: root/src/egg/gfx/eggLightTextureMgr.cpp
blob: 8ae966c01baec0260923e29bb852d1425ef02f9c (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
#include "egg/gfx/eggLightTextureMgr.h"

#include "common.h"
#include "egg/gfx/eggDrawGX.h"
#include "egg/gfx/eggG3DUtility.h"
#include "egg/gfx/eggLightObject.h"
#include "egg/gfx/eggLightTexture.h"
#include "egg/gfx/eggStateGX.h"
#include "egg/gfx/eggTextureBuffer.h"
#include "nw4r/g3d/res/g3d_resmat.h"
#include "nw4r/math/math_types.h"
#include "nw4r/ut/ut_Color.h"
#include "rvl/GX/GXTexture.h"
#include "rvl/GX/GXTypes.h"
#include "rvl/MTX/mtx.h"
#include "rvl/MTX/mtx44.h"

#include <cstring>

namespace EGG {

LightTextureManager::LightTextureManager(const LightManager *lightMgr) {
    mFlags = 0x78;
    mTextureCount = 0;
    mpTextures = nullptr;
    mpLightMgr = lightMgr;
    field_0x10 = 0;
    mMaxNumTextures = 0x80;
    field_0x16 = 1;
    mpObjects = nullptr;
    mpTextures = new LightTexture *[0x80];
    for (int i = 0; i < 0x80; i++) {
        mpTextures[i] = nullptr;
    }

    mpObjects = new const LightObject *[mMaxNumTextures];
    for (int i = 0; i < mMaxNumTextures; i++) {
        mpObjects[i] = nullptr;
    }

    if ((StateGX::s_flag & 0x40) != 0) {
        mFlags |= 0x80;
        mFlags &= ~0x40;
        field_0x16 = 3;
    }
}

LightTextureManager::~LightTextureManager() {
    for (int i = 0; i < 0x80; i++) {
        deleteTexture(i);
    }
    delete[] mpTextures;
    delete[] mpObjects;
}

u16 LightTextureManager::createTexture(const char *name) {
    // Check if one already exists with the same name
    for (int i = 0; i < getNumLightTextures(); i++) {
        if (mpTextures[i] != nullptr && !strcmp(name, mpTextures[i]->getName())) {
            return i;
        }
    }

    // Find a hole to fill
    int targetPosition = getNumLightTextures();
    for (int i = 0; i < getNumLightTextures(); i++) {
        if (mpTextures[i] == nullptr) {
            targetPosition = i;
        }
    }
    if (targetPosition == getNumLightTextures()) {
        mTextureCount++;
    }
    LightTexture *tex = new LightTexture(name, this);
    mpTextures[targetPosition] = tex;
    tex->configure();
    return targetPosition;
}

u16 LightTextureManager::createTextureFromBin(const void *bin) {
    const LightTexture::Bin *data = reinterpret_cast<const LightTexture::Bin *>(bin);
    u16 idx = createTexture(data->mData.mName);
    mpTextures[idx]->SetBinary(data);
    return idx;
}

int LightTextureManager::createTexturesFromBin(const void *bin) {
    const LightTextureManager::Bin *data = reinterpret_cast<const LightTextureManager::Bin *>(bin);
    // Moderate inefficiency here - this algorithm has quadratic runtime
    // since we always search from the beginning
    for (int i = 0; i < data->mData.mDataCount; i++) {
        const void *subData = getLtexFromLmap(data, (u16)i);
        createTextureFromBin(subData);
    }
    return data->mData.mDataCount;
}

bool LightTextureManager::deleteTexture(int idx) {
    if (0 <= idx && idx < 0x80) {
        if (mpTextures[idx] != nullptr) {
            delete mpTextures[idx];
            mpTextures[idx] = nullptr;
            return true;
        }
    }
    return false;
}

u16 LightTextureManager::replaceModelTextures(nw4r::g3d::ResMdl mdl) const {
    u16 count = 0;
    for (u16 i = 0; i < mTextureCount; i++) {
        count += replaceModelTexture(i, mdl);
    }
    return count;
}

u16 LightTextureManager::replaceModelTexture(int tex, nw4r::g3d::ResMdl mdl) const {
    if (mpTextures[tex] == nullptr) {
        return 0;
    }

    G3DUtility::SetTextureResult buf[256];
    GXTexObj obj;
    mpTextures[tex]->getTexObj(&obj);
    u16 count = 0;
    for (int i = 0; i < mdl.GetResMatNumEntries(); i++) {
        nw4r::g3d::ResMat mat = mdl.GetResMat(i);

        u16 res = G3DUtility::SetTexture(mat, nullptr, mpTextures[tex]->getName(), &obj, false, buf, 0xFF, 2);
        for (int j = 0; j < res; j++) {
            if (buf[j].texCoordId != -1) {
                setupEnvmap(mat, static_cast<GXTexCoordID>(buf[j].texCoordId));
            }
        }
        count += res;
    }
    return count;
}

int LightTextureManager::getTextureIndex(const char *name) const {
    for (int i = 0; i < mTextureCount; i++) {
        if (!strcmp(name, mpTextures[i]->getName())) {
            return i;
        }
    }

    return -1;
}

bool LightTextureManager::setBinaryToTexture(const void *data) {
    const LightTexture::Bin *bin = reinterpret_cast<const LightTexture::Bin *>(data);
    for (int i = 0; i < getNumLightTextures(); i++) {
        if (!std::strcmp(bin->mData.mName, mpTextures[i]->getName())) {
            mpTextures[i]->SetBinary(bin);
            return true;
        }
    }
    return false;
}

void LightTextureManager::correctLightObject() {
    int j = 0;
    for (int i = 0; i < mpLightMgr->GetNumLightData(); i++) {
        const LightObject *o = mpLightMgr->GetLightObject((u16)i);
        if (o->CheckFlag1() && o->CheckFlag0x20()) {
            mpObjects[j] = o;
            j++;
            if (j >= mMaxNumTextures) {
                break;
            }
        }
    }

    for (int i = j; i < mMaxNumTextures; i++) {
        mpObjects[i] = nullptr;
    }
}

void LightTextureManager::frameReset() {
    correctLightObject();
    field_0x10 = 0;
}

void LightTextureManager::drawAndCaptureTexture(f32 ox, f32 oy, f32 sx, f32 sy) {
    bool b1 = getSomeTfRelatedBool();
    u32 mask1 = 1 << (mpLightMgr->GetField0x1D());
    if ((mFlags & 0x10) == 0 || (field_0x10 & mask1) != 0 || mTextureCount == 0) {
        return;
    }

    StateGX::resetGXCache();
    if ((mFlags & 0x40) != 0) {
        ox = 0.0f;
        oy = StateGX::s_heightEfb;
        sx = StateGX::s_widthEfb;
        sy = 528 - StateGX::s_heightEfb;
    }
    field_0x10 |= mask1;

    StateGX::ScopedColor colorGuard(true);
    StateGX::ScopedAlpha alphaGuard(false);

    LightTexture::initDrawSetting(ox + ((u16)ox & 1), oy + ((u16)oy & 1), sx, sy);

    u16 x1, y1, x2, y2;
    TextureBuffer *buf = nullptr;
    LightTexture::getRequiredPosAndSize(&x1, &y1, &x2, &y2, mTextureCount);
    x2 += x2 & 3;
    y2 += y2 & 3;

    if ((mFlags & 0x40) == 0 && (mFlags & 4) != 0) {
        buf = TextureBuffer::alloc(x2, y2, GX_TF_RGBA8);
        buf->capture(x1, y1, false, -1);
    }

    if (b1) {
        StateGX::setPixelFormatGX(GX_PF_RGB8_Z24, GX_ZC_LINEAR);
    }

    nw4r::ut::Color c(StateGX::s_clearEfb);
    if (b1 && StateGX::s_pixFormat == GX_PF_RGBA6_Z24) {
        // convert GX_PF_RGBA6_Z24 -> GX_PF_RGB8_Z24
        u8 tmp = c.a >> 2;
        c = ((c.r >> 2) << 26) | (c.g & 0x3FFC) << 18 | (c.b & 0xFC) << 12 | (tmp & 0x3F) << 8;
    }

    bool b2 = b1 && (mFlags & 8) != 0;
    for (int i = 0; i < mTextureCount; i++) {
        mpTextures[i]->fn_804AC0E0(i, c, b2);
    }

    for (int i = 0; i < mTextureCount; i++) {
        mpTextures[i]->draw(i);
    }

    if (b1) {
        StateGX::setPixelFormatGX(StateGX::s_pixFormat, StateGX::s_zFmt16);
    }

    if ((mFlags & 0x40) == 0 && buf != nullptr) {
        StateGX::ScopedColor colorGuard(true);
        StateGX::ScopedAlpha alphaGuard(true);
        nw4r::math::MTX44 orthMtx;
        C_MTXOrtho(orthMtx, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, 1.0f);
        StateGX::GXSetProjection_(orthMtx, GX_ORTHOGRAPHIC);
        StateGX::GXSetViewport_(x1, y1, x2, y2, 0.0f, 1.0f);
        StateGX::GXSetScissor_(x1, y1, x2, y2);
        StateGX::GXSetScissorBoxOffset_(0, 0);
        nw4r::math::MTX34 identity;
        MTXIdentity(identity);

        DrawGX::BeginDrawScreen(1, 1, 0);
        DrawGX::SetBlendMode(DrawGX::BLEND_14);
        GXSetBlendMode(GX_BM_NONE, GX_BL_SRCALPHA, GX_BL_INVSRCALPHA, GX_LO_CLEAR);
        buf->load(GX_TEXMAP0);
        DrawGX::DrawDL(DrawGX::DL_16, identity, DrawGX::WHITE);
        buf->free();
    }

    StateGX::invalidateTexAllGX();
    if ((mFlags & 0x80) != 0) {
        LightTexture::beginDebugDraw();
        for (int i = 0; i < mTextureCount; i++) {
            mpTextures[i]->debugDraw(i);
        }
    }
}

void LightTextureManager::SetBinaryInner(const Bin &bin) {
    if (bin.mHeader.mVersion != 0) {
        return;
    }
    const char *buf = reinterpret_cast<const char *>(bin.mData.mSubData);
    for (int i = 0; i < bin.mData.mDataCount; i++) {
        const BinHeader *hdr = reinterpret_cast<const BinHeader *>(buf);
        setBinaryToTexture(buf);
        buf += hdr->mSize;
    }
}

void LightTextureManager::GetBinaryInner(Bin *pOutBin) const {
    int count = 0;
    char *buf = reinterpret_cast<char *>(pOutBin->mData.mSubData);
    for (int i = 0; i < getNumLightTextures(); i++) {
        mpTextures[i]->GetBinary(buf);
        BinHeader *hdr = reinterpret_cast<BinHeader *>(buf);
        // This is where the actually written size is added to the whole object
        pOutBin->mHeader.mSize += hdr->mSize;
        buf += hdr->mSize;
        count++;
    }
    pOutBin->mData.mDataCount = count;
}

// Performs a linear search through the data to find the offset for the given index
const void *LightTextureManager::getLtexFromLmap(const void *lmap, u16 index) {
    // A bit awkward but it matches
    const BinHeader *buf = nullptr;
    const Bin *bin = reinterpret_cast<const Bin *>(lmap);
    if (bin->mHeader.mVersion == 0 && index < bin->mData.mDataCount) {
        buf = bin->mData.mSubData;
        for (int i = 0; i < index; i++) {
            buf = reinterpret_cast<const BinHeader *>(reinterpret_cast<const char *>(buf) + buf->mSize);
        }
    }

    return buf;
}

void LightTextureManager::setupEnvmap(nw4r::g3d::ResMat mat, GXTexCoordID id) {
    nw4r::g3d::ResTexSrt dat = mat.GetResTexSrt();
    dat.SetMapMode(id, 1, -1, -1);
    nw4r::g3d::ResMatTexCoordGen coordGen = mat.GetResMatTexCoordGen();

    GXTexGenType type;
    GXTexGenSrc src;
    GXBool normalize;
    u32 postMtx;
    coordGen.GXGetTexCoordGen2(id, &type, &src, &normalize, &postMtx);
    coordGen.GXSetTexCoordGen2(id, GX_TG_MTX3x4, GX_TG_NRM, true, postMtx);
    coordGen.DCStore(false);
}

size_t LightTextureManager::GetBinarySize() const {
    // Contrary to the other dynamically-sized Bins, this only
    // returns the fixed size. GetBinaryInner will add the dynamic
    // parts when writing.
    return sizeof(Bin) - sizeof(BinHeader);
}

} // namespace EGG