• Уважаемые гости и новички, приветствуем Вас на нашем форуме
    Здесь вы можете найти ответы практически на все свои вопросы о серии игр «Готика» (в том числе различных модах на нее), «Ведьмак», «Ризен», «Древние свитки», «Эра дракона» и о многих других играх. Можете также узнать свежие новости о разработке новых проектов, восхититься творчеством наших форумчан, либо самим показать, что вы умеете. Ну и наконец, можете обсудить общие увлечения или просто весело пообщаться с посетителями «Таверны».

    Чтобы получить возможность писать на форуме, оставьте сообщение в этой теме.
    Удачи!
  • Друзья, доброго времени суток!
    Стартовал новый литературный конкурс от "Ордена Хранителей" - "Пираты Миртанского моря".
    Каждый может принять в нём участие и снискать славу и уважение, а в случае занятия призового места ещё и получить награду. Дерзайте
  • Дорогие друзья, год подходит к концу, и пришло время подвести его итоги и наградить достойных

    Не ленитесь, голосуйте в этой теме за тех форумчан, которые по вашему мнению больше всех проявили себя в этом году
    По желанию, аргументировать свой выбор можете в теме обсуждения голосования.

Console command with camera manipulation

GreenGo

Участник форума
Регистрация
18 Дек 2022
Сообщения
3
Благодарности
0
Баллы
20
Hello everyone,

for another gothic project, I need to write a plugin that will add a command to the Marvin console, which allow me teleport camera in X declared coordinates (XYZ) and take a screenshot. Could someone give me a hint how to write such a plugin?

Of course, I've already written a basic plugin from one of the union lessons :)
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.909
Баллы
320
add a command to the Marvin console
Example of custom console command handler. Register it once on LoadBegin.
teleport camera in X declared coordinates (XYZ)
Just before oCGame::Render change zCCamera::activeCam->connectedVob->trafoObjToWorld matrix. And restore it after the call.

It doesn't work for DX11 Wrapper
 

GreenGo

Участник форума
Регистрация
18 Дек 2022
Сообщения
3
Благодарности
0
Баллы
20
Thank you for the hint, but after a few hours it turns out that I am too stupid to put it all together...
 

Slavemaster


Модостроитель
Регистрация
10 Июн 2019
Сообщения
1.086
Благодарности
1.909
Баллы
320
Thank you for the hint, but after a few hours it turns out that I am too stupid to put it all together...
The code is created using C++17 template, but it should be easier to adapt it for you anyway.
It adds screen at 0 0 0 console command.
C++:
namespace NAMESPACE
{
    int GetEncoderClsID(const WCHAR* format, CLSID* pClsid) {
        UINT num = 0; // number of image encoders
        UINT size = 0; // size of the image encoder array in bytes

        ImageCodecInfo* pImageCodecInfo = NULL;

        GetImageEncodersSize(&num, &size);
        if (size == 0)
            return -1;  // Failure

        pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
        if (pImageCodecInfo == NULL)
            return -1;  // Failure

        GetImageEncoders(num, size, pImageCodecInfo);

        for (UINT j = 0; j < num; ++j) {
            if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0) {
                *pClsid = pImageCodecInfo[j].Clsid;
                free(pImageCodecInfo);
                return j;  // Success
            }
        }
        free(pImageCodecInfo);
        return -1;  // Failure
    }

    string CheckFileName(string name) {
        if (_access("ScreenShots/" + name + ".jpg", 0) != 0)
            return name;

        for (uint i = 2; true; i++) {
            string nextName = name + "_v" + i;
            if (_access("ScreenShots/" + nextName + ".jpg", 0) != 0)
                return nextName;
        }
    }

    CLSID myClsId;
    void CreateScreenShot(zCTextureConvert* texConv) {
        time_t baseTime = time(Null);
        tm* now = localtime(&baseTime);

        string timeFormat = string::Combine(
            "%u_%u_%u_%u_%u_%u",
            now->tm_year + 1900,
            now->tm_mon + 1,
            now->tm_mday,
            now->tm_hour,
            now->tm_min,
            now->tm_sec
        );

        string fileName = "ScreenShot_" + timeFormat;
        fileName = CheckFileName(fileName) + ".jpg";

        //cmd << "begin" << endl;
        if (_access("ScreenShots", 0) != 0)
            CreateDirectory("ScreenShots", 0);

        zCTextureInfo info = texConv->GetTextureInfo();
        info.texFormat = zRND_TEX_FORMAT_BGRA_8888;
        ((zCTexConGeneric*)texConv)->ConvertTextureFormat(info);
        zCTextureConvert* pNewConv = zrenderer->CreateTextureConvert();
        zCTextureExchange::CopyContents(texConv, pNewConv);
        ((zCTexConGeneric*)texConv)->SetDetailTextureMode(0);
        ((zCTexConGeneric*)texConv)->ConvertTextureFormat(info);
        int nPitch = info.sizeX * 4;
        int nImageSize = nPitch * info.sizeY;
        void* pb = (void*) ::GlobalAlloc(GMEM_FIXED, nImageSize);
        pNewConv->CopyTextureDataTo(0, pb, info.sizeX * 4);
        delete pNewConv;
        GdiplusStartupInput gdiplusStartupInput;
        ULONG_PTR gdiplusToken;
        GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
        HBITMAP hBitmap = CreateBitmap(info.sizeX, info.sizeY, 1, 32, pb);
        ::GlobalFree(pb);
        Bitmap* image = new Bitmap(hBitmap, NULL);
        wstring outName = L"ScreenShots/" + fileName.AToW();

        static bool once = true;
        if (once) {
            once = false;
            int retVal = GetEncoderClsID(L"image/jpeg", &myClsId);
        }

        Gdiplus::Status s = image->Save(outName, &myClsId);
        delete image;
        GdiplusShutdown(gdiplusToken);

        if (s == Gdiplus::Status::Ok) {
            //cmd << "ScreenShot saved" << endl;
            //ScreenShotPush( "ScreenShot saved! " + fileName );
        }
        else {
            cmd << "ScreenShot NOT saved" << endl;
            fileName = "NOT SAVED :(";
            //ScreenShotPush( "ScreenShot NOT saved! " + fileName );
        }
    }

    void CreateScreenShot(const zVEC3& cameraPos, zCTexture*& texture)
    {
        zMAT4& newTrafo = zCCamera::activeCam->connectedVob->trafoObjToWorld;
        zMAT4 oldTrafo = newTrafo;

        newTrafo.SetTranslation(cameraPos);

        {
            auto frameTimeFloatScope = AssignTemp(ztimer->frameTimeFloat, 0.0f);
            auto frameTimeScope = AssignTemp(ztimer->frameTime, 0ul);
            auto aiScope = AssignTemp(oCNpc::ai_baseEnabled, 0);
            auto fileNameScope = AssignTemp(ogame->GetGameWorld()->worldFilename, Z"");

#if ENGINE >= Engine_G2
            auto nameScope = AssignTemp(ogame->GetWorld()->m_strlevelName, Z"");
#endif

            auto activeVobListScope = AssignTemp(ogame->GetWorld()->activeVobList, zCArray<zCVob*>{});

            ogame->GetWorld()->Render(*zCCamera::activeCam);
        }

        zCTexConGeneric* textureConvert = dynamic_cast<zCTexConGeneric*>(zrenderer->CreateTextureConvert());
        zrenderer->Vid_GetFrontBufferCopy(*textureConvert);
        CreateScreenShot(textureConvert);

        newTrafo = oldTrafo;

        if (!texture)
            texture = zrenderer->CreateTexture();

        zCTextureInfo info = textureConvert->GetTextureInfo();
        info.texFormat = zCTexture::CalcNextBestTexFormat(zRND_TEX_FORMAT_RGB_565);
        info.numMipMap = 1;
        info.sizeX = 1024;
        info.sizeY = 1024;

        textureConvert->ConvertTextureFormat(info);
        zCTextureExchange::CopyContents(textureConvert, texture);

        delete textureConvert;
    }

    int (*innerEvalFunc)(const zSTRING&, zSTRING&);

    int ConsoleEvalFunc(const zSTRING& inpstr, zSTRING& msg)
    {
        if (innerEvalFunc && innerEvalFunc(inpstr, msg))
            return true;

        zSTRING input = inpstr;
        input.Upper();

        if (input.StartWith("SCREEN AT"))
        {
            zVEC3 cameraPos;

            for (int i = 0; i < 3; i++)
            {
                zSTRING numString = input.GetWord(" ", 3 + i);
                
                if (numString.IsEmpty())
                {
                    msg = "Not enough parameters";
                    return true;
                }

                cameraPos[i] = numString.ToReal32();
            }

            zCTexture* shot = nullptr;
            CreateScreenShot(cameraPos, shot);

            static zCView* shotView = new zCView{ 1000, 1000, 3000, 3000 };

            if (!shotView->ondesk)
                screen->InsertItem(shotView);
            
            shotView->InsertBack(shot);

            msg = "Ok";
            return true;
        }

        return false;
    }

    void RegisterEvalFunc()
    {
        int evalNum = 0;

        for (int i = 1; i < zCON_MAX_EVAL; i++)
            if (zcon->evalfunc[i])
                evalNum = i;

        innerEvalFunc = zcon->evalfunc[evalNum];
        zcon->evalfunc[evalNum] = &ConsoleEvalFunc;
    }

    extern Sub<void> registerCommand;
    Sub<void> registerCommand(ZSUB(GameEvent::LoadBegin), []
        {
            registerCommand = {};

            RegisterEvalFunc();

            zcon->Register("SCREEN AT", "Creates screen shot at specified position");
        });
}
 

Вложения

  • zTest.vdf
    87 KB · Просмотры: 4
Сверху Снизу