{"id":50508486,"url":"https://github.com/nmkazantsev/seal_engine","last_synced_at":"2026-06-02T18:01:14.749Z","repository":{"id":238024479,"uuid":"604308550","full_name":"nmkazantsev/seal_engine","owner":"nmkazantsev","description":"Light and easy android OpenGL game engine","archived":false,"fork":false,"pushed_at":"2025-12-29T21:40:27.000Z","size":12210,"stargazers_count":4,"open_issues_count":8,"forks_count":0,"subscribers_count":1,"default_branch":"master","last_synced_at":"2026-01-02T04:26:58.958Z","etag":null,"topics":["android","engine","game-development","java","opengl","opengl-es"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/nmkazantsev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2023-02-20T19:31:31.000Z","updated_at":"2025-12-31T17:04:32.000Z","dependencies_parsed_at":"2024-05-10T19:46:39.640Z","dependency_job_id":"0d409883-827e-4a95-a388-c8bb4bce59e4","html_url":"https://github.com/nmkazantsev/seal_engine","commit_stats":null,"previous_names":["nmkazantsev/glengine_3_1","nmkazantsev/seal_engine"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/nmkazantsev/seal_engine","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmkazantsev%2Fseal_engine","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmkazantsev%2Fseal_engine/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmkazantsev%2Fseal_engine/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmkazantsev%2Fseal_engine/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/nmkazantsev","download_url":"https://codeload.github.com/nmkazantsev/seal_engine/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/nmkazantsev%2Fseal_engine/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33833277,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["android","engine","game-development","java","opengl","opengl-es"],"created_at":"2026-06-02T18:01:13.773Z","updated_at":"2026-06-02T18:01:14.740Z","avatar_url":"https://github.com/nmkazantsev.png","language":"Java","funding_links":[],"categories":[],"sub_categories":[],"readme":"# SealEngine\n\n## Что это? Зачем оно надо?\n\nДанный движок призван:\n\n1. Там, где это возможно, избавить пользователя от вызовов низкоуровневых и неинтуитивных методов  \n   API OpenGL.\n2. Взять на себя всю работу с видеопамятью.\n3. Предосавить набор обьектов движка, сделать api обьектно-ориентированным.\n4. Предоставить абстракицию над классом OpenGLRenderer, сделав возможным создание аналогов activity.\n5. Обеспечить быстрый старт и легкую разработку прототипа, за счет большого количества дефолтных  \n   функций.\n\nОн может работать с любым glSurfaceView, ниже рассмотрено его использование в полноэкранном режиме.\n\n## Импорт библиотеки в проект\n\n1. Создаем пустой проект в Android studio (c пустой дефолтной MainActivity) и скачиваем последнюю  \n   версию с репозитория (см релизы).\n2. Впроекте создаем в папку `app/libs`, копируем файл, пкм, add as android library.\n3. Ждем.\n4. Ещё ждем.\n5. Ура.\n6. Теперь открываем MainActivity. Нужно из нее открыть главный реднерер, который позже передаст  \n   управление движку и вашему коду. Чтобы это сделать, отредактируем activity:  \n   `Engine engine = new Engine(); //we are unable to make it static because it is impossible to use android context in static way`\n\nПерегрузим методы:\n\n  \tpublic class MainActivity extends Activity implements View.OnTouchListener {\n    \tEngine engine = new Engine(); //we are unable to make it static because \n        //it is impossible to use\n    \t//android context in static way\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    protected void onCreate(Bundle savedInstanceState) {\n        super.onCreate(savedInstanceState);\n        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);\n        requestWindowFeature(Window.FEATURE_NO_TITLE);\n        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);\n        GLSurfaceView v = engine.onCreate(this, unused -\u003e new LorRenderer(), true, true, true);\n        setContentView(v);\n        assert v != null;\n        v.setOnTouchListener(this);\n    }\n\n    @Override\n    protected void onPause() {\n        super.onPause();\n        engine.onPause();\n    }\n\n    @Override\n    protected void onResume() {\n        super.onResume();\n        engine.onResume();\n    }\n\n\n    @SuppressLint(\"ClickableViewAccessibility\")\n    @Override\n    public boolean onTouch(View v, MotionEvent event) {\n        return TouchProcessor.onTouch(v, event);\n    }\n\n    @Override\n    public void onPointerCaptureChanged(boolean hasCapture) {\n        super.onPointerCaptureChanged(hasCapture);\n    }}\n    \n\n\n7. Теперь создаим класс MainRenderer. Это будет входная точка в наш проект. Навзние класса можно  \n   изменить в\n   строке `GLSurfaceView v = engine.onCreate(this, unused -\u003e new MainRenderer(), false);`  \n   Данный класс является классом страницы, он обязательно должен implement `GamePageInterface`.  \n   Конструтор странц может быть любым, но для входной точки обязательно наличие пустого (в процем,  \n   мы сами вызываем его из MainActivity). После переопределения всех функций движок готов к  \n   использованию.\n\n## Устройство движка (коротко)\n\nВ основе всего лежит идея многостраничного приложения, в котором каждой страницы есть свои\nлокальные  \nпеременные, удаляемые при ее закрытии. Такой подход позволит абстрагироваться от других страниц,  \nэкономить RAM и VRAM, не засорять пространство имён.  \nGamePageInterface является центральной сущностью движка и устроен следующим образом:\n\n    package com.seal.gl_engine;        \n    public interface GamePageInterface {  \n    public abstract void draw();        \n    public abstract void onResume();\n    public abstract void onPause();\n    }  \n\nНе спрашивайте почему тюлень. Мем.  \nМы видим, что получив досутп к нашем листенерам, движок отслеживает открытие страницы, вызыввет\ndraw  \nВ моменты открытия/закрытия активити вызываются соответсвующие методы. Они вызываются напрямую из методов активити, откуда запущен движок.\nТак же движок отслеживает использование видеопамяти и автоматически удаляет оттуда объекты, когда  \nони не нужны.  \nДля этого у каждого объекта есть ссылка на страницу создателя (поэтому, при создании объектов  \nпередается this), и при каждом запуске startNewPage происходит проверка, в случае несовпадения  \nназвания класса открытой страницы и класса создателя объекта, все используемые видеоресурсы  \nудаляются, а сам объект становится null, чтобы не было соблазна его использовать дальше.\n\n**Объект движка не будет удален после закрытия вашей страницы сборщиком мусора сразу, так как\nдвижок  \nещё какое-то время будет хранить на него ссылку.**\n\nЕсли нужно, чтобы объект не удалялся каждый раз (напрмер, тяжелый меш), то его нужно объявить  \nstatic, а в вместо this предать null.\n\n**Если не static объекту передать null, это приведет к утечке видеопамяти и непредсказуемым  \nпоследствиям.**\n\n## Создание и использование шейдера\n\nДвижок поддерживает как создание классической пары vertex+fragment:\n\n    shader = new Shader(com.example.gl_engine.R.raw.vertex_shader,com.example.gl_engine.R.raw.fragment_shader, this, new MainShaderAdaptor());//create default example shader  \n\nтак и использование геометрического шейдера:\n\n    shader = new Shader(vertex_shader, geom_shader, fragment_shader, this, new MainShaderAdaptor());  \n\nШейдеры рекомендуется объявлять final:\n\n    private final Shader shader;  \n\nЧтобы применить шейдер:\n\n    applyShader(shader);//static method of class ShaderUtils  \n\nЛоги о компиляции будут выводиться с тегом Info. Отсутсвие логов - признак успешной компиляции.  \nПри примении шейдера все объекты, унаследованные от ShaderData автоматически прогрузят свои\nзначения  \nтуда (исключение - Material).  \nВ движке в стандартной папке ресурсов есть дефолтные шейдеры для рендеринга, для света и для\nработы  \nсо skyBox.\n\n**Остальные объекты, такие как: CameraSettings, ProectionMatrixSettings, матрица преобразования,  \nMaterial требуют ручного вызова функции отправки данных в шейдер.**\n\n## Передача данных в шейдер\n\nДля этих целей введены адапторы.  \nВ движке есть набор стандартных адапторов с интуитивными названиями, которые рекомендуется  \nиспользовать, но при желании можно разработать свой.  \nДля этой цели нужно унаследоваться от абстрактного класса **Adaptor**.  \nВнутри данного класса хранится список всех ShaderData и id шейдера (выдается API при компиляции).  \nСам абстрактный класс отвечает за работу с ShaderData, её перегружать не нужно.  \nНужно перегрузить следующие методы:\n\n    public abstract int bindData(Face faces[]);//отправить массив вершин без сохранения в буфер     public abstract int bindData(Face faces[], VertexBuffer vertexBuffer);//отправка в vbo с разметкой в vao  \n  \n    public abstract void updateLocations();//обновить все используемые id шейдерных переменных  \n    //геттеры соответствующих значений      public abstract int getTransformMatrixLocation();  \n  \n    public abstract int getCameraLocation();  \n    public abstract int getProjectionLocation();  \n    public abstract int getTextureLocation();  \n    public abstract int getNormalTextureLocation();  \n    public abstract int getNormalMapEnableLocation();  \n    public abstract int getCameraPosLlocation();  \n\nПример реализации см в MainShaderAdator, LightShaderAdaptor, SkyboxAdaptor.\n\n## Класс ShaderData\n\nЭто абстрактный класс, при реализации значения, находящегося в видеопамяти (uniform переменная)  \nнужно от него унаследоваться и переопределить\n\n`void getLocations(int programId)` - загрузка id переменной из шейдера (на вход поступает id  \nскомпилированной связки шейдеров)\n\n`void forwardData()` - отправка данных в шейдер\n\n`void delete()` - очистка видеопамяти  \nФункции вызываются движком при применении шейдера или смене страницы.\n\nПри применении вызываются getLocstions, при смене страницы - delete.  \nСмысла удалять uniform переменную нет, но есть смысл удалять ссылки на объект из массивов,\nнапример,  \nудалить объект света из массива источников для данной страницы.\n\nЕсли ссылка на переменную 0, то ничего не произойдёт. Если на переменную ссылаются 2 ссылки, то  \nбудет записано второе значение, первое перезапишется.\n\n# Преобразования вершин и инструменты работы с ними\n\n## Camera\n\nКласс игровой камеры. Объединяет в себе CameraSettings и ProjectionMatrixSettings (которые\nвяляются  \nего полями).\n\nКонструктор сразу настраивает камеру для работы с 3д, вызывая соответствующие методы у обоих полей  \nкласса.\n\n`void apply()` - применние настроек камеры, вызывать перед рисованием объектов. Если перед этим\nбыла  \nвызвана resetFor3d(), то перспектива будет включена, если перед этим была вызвана restFor2d() -  \nвыключена.\n\n`apply(boolean perspectiveEnabled)` - ручное управление перспективой.\n\n**настройками полей класса можно управлять по отдельности, но в большинстве случаев так делать не  \nрекомендуется**\n\nТакже доступны методы для быстрой передачи данных (копирование) в камеру:\n\n```\n     void setPos(PVector pos)\n     void SetUpVector(PVector up) \n     void setCenter(PVector center) \n```\n\n## CameraSettings\n\nКласс настроек камеры.  \nСодежрит в себе набор переменных, настройки камеры происходят с помощью\n\n    Matrix.setLookAtM(mViewMatrix, 0, cam.eyeX, cam.eyeY, cam.eyeZ, cam.centerX, cam.centerY, cam.centerZ, cam.upX, cam.upY, cam.upZ);  \n\nСоответствующие переменные отвечают за положение, направление камеры и направление UP-вектора.  \nАвтоматически настройки камеры не применяются, нужно вызвать\n\n    applyCameraSettings(cameraSettings);  \n\nФункции resetFor3d(), resetFor2d() сбрасывают значения переменных на дефолтные для рисования в\nодном  \nиз режимов с учётом ориентации устройства.\n\n## ProjectionMatrixSettings\n\nКласс настроек матрицы проекци.  \nАвтоматически настройки не применяются, нужно вызвать\n\n    applyProjectionMatrix(projectionMatrixSettings, boolean perspectiveEnabled);  \n\nили\n\n    applyProjectionMatrix(projectionMatrixSettings); // perspectiveEnabled = true;  \n\nКласс поддерживает настройку границ основания призмы проекции, а также положения ближнего и\nдальнего  \nсечений.\n\nФункции resetFor3d(), resetFor2d() сбрасывают значения переменных на дефолтные для рисования в\nодном  \nиз режимов с учётом ориентации устройства.\n\n## Матрица преобразований\n\nКласс матрицы преобразований не реализован. Рекомендуется использовать матрицу \u003ccode\u003e  \nfloat[16]\u003c/code\u003e и функции из пакета \u003ccode\u003eAndroid.OpenGL.Matrix.\u003c/code\u003e  \nДля создания единичной матрицы используется \u003ccode\u003eresetTranslateMatrix(float[16])\u003c/code\u003e (можно  \nпередать существующий массив чтобы не выделять новую память), для применения - \u003ccode\u003eapplyMatrix(  \nfloat[16]);\u003c/code\u003e\n\n## Polygon\n\nКласс для отображения прямоугольной текстуры в любом месте и положении 3д пространства (можно  \nиспользовать в 2д режиме, но для этого есть класс SimplePolygon).  \nКонструктор на вход принимает:\n\n+ `Function\u003cList\u003cObject\u003e, PImage\u003e redrawFunction` - функция, которая будет вызвана автоматически  \n  каждый раз когда будет требоваться перерисовка изображения полигона;\n+ `bool saveMemory` - не держать изображение текстуры в памяти android;\n+ `int paramSize` - длина списка параметров в функции перерисовки (можно изменить с помощью  \n  newParamsSize);\n+ класс создателя;\n\n`void prepareAndDraw(Point a, Point b, Point c)` - отрисовка по 3м точкам.\n\n`prepareAndDraw(Point a, Point b, float texx, float texy, float teexa, float texb)`\n\n` public void prepareAndDraw(PVector a, PVector b, PVector c, float texx, float texy, float teexa, float texb)` -\nотрисовка с учетом текстурных координат\n\n`void setRedrawNeeded` - перед началом следующего цикла отрисовки вызвать (или нет) функцию  \nперерисовки (по умолчанию вызывается при перезаходе в приложение и после создания объекта полигона)\n\n`void delete()` - освобождает память от Bitmap текстуры и отложенно удаляет текстуру из видео  \nпамяти (её удалит движок при переключении страницы)\n\n`void redrawNow()` - вызов перерисовки в ручную\n\n## SimplePolygon\n\nНаследуется от класса Polygon, предназначен для 2д рисования.  \nКонструктор аналогичный.\n\n`void prepareAndDraw(float rot, float x, float y, float a, float b, float z)` - отрисовывает на  \nпрямоугольинке, левый верхний угол в положении x,y, размеры a,b, потом поворачивает прямоугольник\nна  \nr радиан по часовой. z - высота над плоскостью z=0. **Идеально для рисования танчика в 2д.**\nРаньше  \nэто был метод класса танка.\n\n`void prepareAndDraw(float rot, float x, float y, float a, float b, float z)` - то же самое, но экономит на повороте, потому что он не обсчитывается вообще. Самый ходовой метод.\n\n`void prepareAndDraw(float x, float y, float b, float z)` - квадрат стороной b c левым верхниим  \nуглом в x,y\n\n## Shape\n\nКласс для работы с 3д мешами. Позволяет добавлять меш, карту нормалей и текстуру. Меш хранится в  \nвиде массива чисел (массива объектов Face) и в виде vbo+vao в видеопамяти. **Вся работа с видео  \nпамятью автоматизирована**, в том числе подгрузка карты нормалей и вершин.\n\n`Shape(String fileName, String textureFileName, GamePageInterface page)` - конструктор с\nасинхронной  \nподгрузкой файла вершин и синхронной (в основном потоке) загрузкой текстуры  \nВ основе загрузчика вершин лежит сторонняя библиотека, а текстура загружается методом  \nпереопределения redrawFunction.  \nПринимаются только триангулированные .obj файлы. При экспорте из blender внимательно следите за  \nположиенем осей и совпадением их с игровым пространством.  \nПока вершины не загружены, вызовы рисования shape не дадут никакого резуьтата.\n\n`void addNormalMap(String normalMapFileName)` - загрузка карты нормалей. При этом соответствующая  \nшейдерная переменная (вызов `getNormalMapEnableLocation()` у адаптора) будет установлена в\nединицу (  \nиначе 0).  \n**В дефолтном lighting шейдере не происходит отключения перехода в касательное пространство при  \nотключении карты нормалей.**\n\n`void prepareAndDraw()` - отрисовать шейп с учетом текущих настроек камеры, шейдеров и матрицы  \nпреобразования. В случае необходимости вызывает функции перерисовки и перезагрузки вершин.\n\n**Настоятельно не рекомендуется в ручную вызывать функции перерисовки у данного класса** (кроме  \nслучаев крайней необходимости).\n\n## ассинхронность\nПри использовании обычного конструктора меш грузится ассинхронно, текстура и нормаль - в основном потоке. Пока не будут загружены все ресурсы - вызов отрисовки не даст эффекта.\n\nНе вижу смысла выносить загрузку картинок в свой поток, потому что ее все равно придется проводить по новой при выходе приложения из спящего режима (там происходит очистка вдеопамяти)\n\nНо может быть полезным загрузить меш в другой странице, например, на экране загрузки. Тогда текстура шейпа не будет висеть в оперативной памяти (а она висит там с момента вызова конструктора до первой отрисвоки), а при открытии страницы, где шейп нужен, не надо будет ждать, пока загрузится меш.\n\nДля этого есть особый метод, особый конструкор и класс, коорый играет роль struct.\n### класс PreLoadedMesh\n```\n    public static class PreLoadedMesh {\n        private Face[] facesArr;\n        private Object object; //object form library of loading 3d\n    }\n```\nобъект object - это объект с загруженным мешем, а в facesArr он уже распаршен. Движок использует оба поля для загрузки меша в память видеокарты. Однако, доступ к этим полям не нужен обывателю.\n### загрузка меша\nДля этого есть\n`  public static void loadFacesAsync(String fileName, Function\u003cPreLoadedMesh, Void\u003e callback) ` - запуск потока загрузки. Вторым параметром принимается колбек.\n### создание Shape на основе предзагруженного меша\nДля этого есть конструктор \n` public Shape(PreLoadedMesh preLoadedMesh, String textureFileName, GamePageClass page)`\nТакой шейп ни чем не отличается от стадартного, кроме того, что он не будет запускать загрузку меша при запуске конструктора.\n\n# Анимации и SealObject\n\nДля работы со встроенными анимациями нужно испольозовать класс SelObject. Это аналоги игрового  \nобъекта в юнити - обертка над мешем, от которого нужно наследовать все игровые объекты. Хотя, для  \nотрисвоки просто мешей его использование не обязательно.\n\n## класс Animantor\n\n### добавление анимации\n\n`addAnimation(sealObject target, Function\u003cAnimation, float[]\u003e tf, float[] args, Function\u003cfloat[], Float\u003e vf, float duration, float vfa, long st, boolean recurring)`\n\nПервый аргумент — это экземпляр EnObject, который является объектом анимации. Второй аргумент —\nэто  \nфункция, которая принимает экземпляр Animation и возвращает массив из 6 чисел с плавающей точкой,  \nПервые 3 - это положение, вторые 3 определяют вращение (Заметьте, что это не дельты, не разность  \nположений, это новые координаты). Третий аргумент - это функция, которая определяет скорость  \nвоздействия на атрибуты (закон их изменеия), она принимает массив содержащее значение от 0 до 1 (\n0 —  \nначало анимации, 1 — самый последний момент) и некоторого аргумента, функция также должна\nвозвращать  \nзначение от 0 до 1, как было упомянуто ранее 0 - это первая позиция анимации, 1 - самая последняя.  \nЗатем идет длительность, функция скорости и начальное время. Последний аргумент позволяет\nзакциклить  \nанимацию (объект будет стартовать из начальных координат).  \nМожно добавлять несколько анимаций, они будут выполняться параллельно, их эффекты будут  \nнакладываться.  \nАнмации буду оставнавливаться при вызове freezeMillis() так как используют pageMillis() в качестве  \nисточника времени.\n\nПример вызова:\n\n```  \nAnimator.addAnimation( this, (Animator.Animation animation) -\u003e {\n  float[] attrs = animation.getAttrs();            \n  float[] args = animation.getArgs(); \n  return attrs;        \n},       \nnew float[3],        \n(float[] f) -\u003e { \n  float k = f[0]; \n  float a = f[1];\n  return f[0];\n},1000,1.0f,5000);\n   ```  \n  \nВ движке есть встроенные функции анимаций, чтобы страдания не были слишком сильными.  \n  \n*Разработчик данного кода оставил лишь намеки на то, как работает его код, так что приведенная ниже  \nдокументация - это скорее теория, предположения о том, как оно должно работать*  \n  \n### остановка анимаций  \n  \n`freezeAnimations(sealObject target)` ставит на паузу все анимации у данного тюленя.  \n`unfreezeAnimations(sealObject target)` продолжает исполнение анимации.  \n  \n## SealObject  \n  \n`sealObject(Shape shape)` создание нового тюленя на основе существующего меша.  \n  \n`float[] getSpaceAttrs()` получить массив координат. Первые 3 числа - координаты, вторые - углы  \nповорота вокруг этих осей.  \n  \n`setSpaceAttrs(float [6])` установить координаты объекта  \n  \n*все анимации при включении зацикливания будут возвращаться в исходную точку при начале новой  \nитерации*  \n  \n`animMotion(float x, float y, float z, float duration, long startTiming, boolean recurring)`  \nдобавить движение из текущей точки вдоль вектора x y z.  \n  \n`animRotation(float x, float y, float z, float duration, long startTiming, boolean recurring)`  \nповорот к на x, y, z вокруг одноимённых осей.  \n  \n`animPivotRotation(float x, float y, float z, float vx, float vy, float vz, float duration, long startTiming, boolean recurring)`  \nповорот вокруг осей относительно пивота.  \n  \n`stopAnimations()`  заморозка всех анимаций.  \n  \n`continueAnimations()` отморозка всех анимаций.  \n  \n`setObjScale(float n)` увеличение объекта в n раз.  \n  \n`prepareAndDraw()` отрисовка объекта и применение анимации.  \n  \n## Face  \n  \nСлужебный класс. Когда я его писал, как он работает понимали только я и Господь. Теперь это понимает  \nтолько Господь.  \nЗанимается хранением данных в формате треугольного полигона и выдачей их по требованию в  \nсоответствующих форматах для простоты использования в адапторах.  \nНаписание документации на методы данного класса оставляется читателю в качестве самостоятельного  \nнесложного упражнения и не несёт практической пользы, так как они нужны исключительно для работы  \nдефолтных шейдеров.  \nВ списке Face Shape хранит свои данные о вершинах (а также текстурных координатах, нормалях и тп).  \n  \n  \n# MipMaps  \n\n **Opengl поддерживает только квадратные mipMaps**  \n \n Инструмент фильтрации отнесенных на большое от камеры расстояние изображений.  \n Досутпен в класса Polygon и Simplepolygon.  \n Для его использования нужно после объекта GamePageClass в параметрах конструктора дописать  true (тогда при каждой перезагрузке текстуры будут генерироваться mipMaps).  \n \n\n# SectionPolygon  \n  \nКласс рисовалка линии. С помощью 1 экземпляра можно рисовать сколько угодно линий.  \n`SectionPolygon(GamePageClass)` - конструктор. Класс владелец не null  \n`void setColor(PVector color)` - задает цвет по rgb от 0 до 1  \n  \n**Рисовать только с применением шейдера линии!**  \n`draw(Sectionsection)` - рисует линию (поддерживается 3д)  \n  \n### как создать сам шейдер линии:  \n  \n`new Shader(R.raw.Section_vertex, R.raw.Section_fragmant, gamePageClass, new SectionShaderAdaptor());`  \n  \n# Axes  \n  \nкласс-рисовальщик осей для отладки. Внутри себя **уже содержит шейдер линии**.  \n` void drawAxes(float limit, float step, float tickSize, float[] matrix, Camera camera)` -  \nотрисовать систему координат с преобразованием matrix (matrix = null равносильно единичному  \nпреобразованию) на камеру camera. Оси рисовать от -limit до limit, на них будут отметки с шагом step  \nдлиной tickSize * 2. Отметки будут в обоих перепендикулярных каждой оси направлениях.  \nКаждая ось обозначена цветом в положительном направлении и тем же, только более темным цветом в  \nотрицательном направлении.  \n  \n| ось | цвет в положительном нарпавлении | цвет в отрицательном нарпалвении |  \n|-----|----------------------------------|----------------------------------|  \n| x   | красный                          | темно-красный                    |  \n| y   | зеленый                          | темно-зеленый                    |  \n| z   | синий                            | темно-синий                      |  \n  \n  \n## SkyBox  \n  \nНаследуется от Shape (Добработанный класс). **В версии 3.0.x** Для своей работы требует наличия в  \nassets cube.obj.  \n  \n`SkyBox(String textureFileName, String res, GamePageInterface page)` - первым параметром указывается  \nпапка, в которой лежат соответсвующие фрагменты кубической карты. Например, для случая:  \n  \n    skybox-\n      -left.jpg \n      -right.jpg\n      -top.jpg\n      -bottom.jpg\n      -front.jpg\n      -back.jpg  \nвызвать `ShyBox(\"skybox/\",\"jpg\",this);`  \n  \n`void prepareAndDraw()` - вызввать только после применения шейдеров и повторного примения после  \nэтого матриц камеры и проекции  (и, при необходимости, обновления других переменных). Рисует sky  \nbox.  \nВ качестве дефолтного шейдера можно использовать skybox_fragment и skybox_vertex.  \n  \n# Буферы и хранение данных в видео памяти  \n  \nДля всех рассмотренных ниже объектов работа с памятью автоматизирована, но следует учитывать, что  \nнекоторые объекты (напрмер, FrameBuffer) не могут хранить информацию дольше 1 кадра из-за специфики  \nиспользуеомого API.  \n  \n## FrameBuffer  \n  \nНазвание говорит само за себя. Предназначен для внеэкранного реднеринга в текстуру.  \n  \n`FrameBuffer(int width, int height, GamePageInterface page)` - Конструктор. Создает и настраивает  \nобъект. Принимает на вход размеры текстуры.  \n  \n`frameBuffer.apply()` - подключает FB. При повторном подключении буфера можно мотерять предыдущие  \nрезультаты рендеринга. Аналогично с экранным буфером.  \n  \n`static void FrameBuffer.connectDefaultFrameBuffer()` - отключает внеэкранный рендеринг.  \n  \nизменение или получение текущих размеров буфера (не будет применено до следующего вызова onRedraw(),  \nавтоматизированного или ручного. Вообще, проще удалить и создать новый буфер):  \n  \n    public void setH(int h)    public void setW(int w)   \n    public int getWidth()   \n    public int getHeight()  \n  \n`public int getFrameBuffer()` - получить id FrameBuffer  \n  \n`public int getDepth()` - получить id буфера глубины  \n  \n`public int getTexture()` - получить id текстуры.  \n  \n`delete()` - очистка видеопамяти.  \n  \n`drawTexture(Point a, Point b, Point d)` - рисует текстуру на полигоне по 3 точкам (см класс  \nPolygon).  \n  \n# Обработка изображений  \n  \n## PImage  \n  \nВо многом повторяет процессинг, реализует множество функций обработки bitmap. Рендеринг производит  \nна процессоре, сильно бъет по памяти и производительности, но в большинстве случаев это единственный  \nвыход.  \n  \n`PImage(float x, float y)` - принимает размеры картинки в пикселях, создает объект PImage и битмап в  \nнем  \n  \n`void delete()` - удаление битмап, но не самого объекта картинки. **Обязательно вызывать перед тем,  \nкак планируется удалить картинку.**  \n  \nПодробно с функциями рисования можно ознакомиться в исходном коде, там нет каких-либо особенностей.  \nПо умолчанию битмап с поддержкой альфа канала.  \n  \n`Utils.LoadImage(String path)` - возвращает загруженный из assets редактируемый PImage. Загрузка в  \nпотоке, из которого вызвана функция  \n\n` public static void loadImageAsync(String name, Function\u003cPImage,?\u003e callback)` - возвращает загруженный из assets редактируемый PImage в коллбэк. Загрузка ассинхронно.\n  \n# Свет  \n  \nВ основе лежит реализация с learn opengl. Созданы классы источников света, по набору переменных  \nдублирующие структуры этих источников в дефолтном шейдере освещения.  \nЛогично, что все источники света наследуются от ShaderData.  \nКогда нужно, объекты света используют вызовы записи в uniform переменную,адреса которой они получают  \nнапрямую из шейдера. Вызов getLocations() инициируется классом ShaderData.  \n  \nВсе источники имеют конструктор, принимающий GamePageInterface, причем смысла делать объекты  \nстатическими я не вижу. Настройки параметров источников происходят путем сохранения нужных значений  \nв переменные и затем вызова forwardData() ** строго перед отрисовкой и строго после применения  \nшейдера. **  \n  \n**для использования света нужно использовать fragment_shader_light и vertex_shader_light**  \n  \nВсе значиения цвета - от 0 до 1, все координаты - в игровом пространстве.  \n  \n## AmbientLight - класс фонового света  \n  \n* `PVector color` - цвет рассеянного света.  \n  \n## DirectedLight - класс направленного света от плоского или бесконечно удаленного источника.  \n  \n* `PVector color` - цвет источника  \n  \n* `PVector direction` - направление источника.  \n  \n* `float diffuse` - компонента рассечнного света от источника (яркость)  \n  \n* `float specular` - компонента блика от источника (интенсивность)  \n  \n## PointLight - класс точечного источника  \n  \nСветит во все стороны, интенсивоность быстро падает с расстоянием  \n  \n* `PVector color` - цвет источника  \n  \n* `PVector position` - положение в игровом пространстве  \n  \n* `float diffuse` - вклад компоненты рассеянного освещения от этого источника (яркость).  \n  \n* `float specular` - вклад компоненты с бликом.  \n  \n* `float constant, Sectionar, quadratic` - коэффициенты убывания вклада с расстоянием. Вклад считается  \n  по формуле 1.0 / (light.constant + light.Sectionar * distance + light.quadratic * (distance *  \n  distance))  \n  \n## SourceLight - класс фонаря.  \n  \nФактически это точечный источник, но светящий в указанном направлении внутри конуса с указанным  \nуглом раствора.  \nОбладает теми дже полями, что и PointLight, а также:  \n  \n* ` cutOff, outerCutOff` - телесные углы в градусах начала и конца дияракционного затухания света на  \n  границе пучка. Ругелирует плавность границы пучка.  \n  \n## Material  \n  \nНа данном этапе поддержки карты материалов нет. Свойства матриала постоянны в рамках меша.  \n  \nВсе PVector компоненты повзоляют добиваться отттенков цветов матриала при рендеринге света путем  \nскалярного произведения на соответствующие компоненты света.  \n  \n```  \n\n    public PVector ambient;  - компонента рассеянного света\n    public PVector diffuse;  - компонента диффузии падающго света\n    public PVector specular;  - компонента блика\n    public float shininess;  - компонента яркости блика\n    \n```  \n\n## Экспозиция\n\nДостигается методом пост-обработки. Для этого есть встроенный шейдер exposition fragment, который  \nиспользуется вместе с vertex_shader:\n\n```  \n    expositonShader = new Shader(com.example.gl_engine.R.raw.vertex_shader, \n      com.example.gl_engine.R.raw.exposition_fragment,\n      this, new MainShaderAdaptor());\n```  \n  \nСодержимое сцены реднерится во фрейм буфер, далее подключется шейдер экспозиции, с помощью  \nэкземпеляра класса `ExpouseSettings` в него передаются значения и вызывается отрисовка.  \n  \n```  \nexpouseSettings.expouse = expouse.value;\nexpouseSettings.gamma = gamma.value;\n```  \n\n# Обработка касаний\n\n**до версии 3.1.0 использовались вызовы touchStarted, touchMoved и touchEnded, которые были\nудалены  \nв связи со сложностями работы в режиме мультитчача**\n\nС версии 3.1.0 основным инструментом является TouchProcessor.\n\n## класс TouchProcessor\n\nДанный класс является конструктором, содержащим в себе\n\n* обработчик хитбокса\n* вызов touchStarted\n* вызов touchMoved\n* вызов TouchEnded\n\n  **не нужно хранить экземпляры этих классов в своих переменных, если их можно конвертировать в  \n  локальные значения. Экземпляры и так хранятся в памяти движка**  \n  Ниже приведен пример использования TouchProcessor:\n\n\n```  \n  //*************************************************//  \n  new TouchProcessor(this::touchProcHitbox, this::touchStartedCallback,  \t\t\t\t\n  \tthis::touchMovedCallback, this::touchEndCallback, this);  \n  //*************************************************//      \n  private Boolean touchProcHitbox(TouchPoint event) {  \n            return true если так попал в нужных хитбокс, иначе false;\n  }  \n  private Void touchStartedCallback(TouchPoint p) {  \n       //выполнить действия            \n       return null;     \n  }        \n  private Void touchMovedCallback(TouchPoint p) {  \n       //выполнить действия            \n       return null;     \n  }         \n  private Void touchEndCallback(TouchPoint t) {  \n            //выполнить действия            \n            return null;     \n  }  \n  ```  \n\nФункции можно объявить и лямбда функциями или с другими именами.  \n**Все, кроме первого параметра, могут быть null**  \nПоследний параметр - страница-родитель, **не рекомендуется ставить null.**  \nКласс TouchPoint имеет следующие публичные поля и содержит информацию о последне обработанном мете  \nнахождения касания:  \n`public float touchX, touchY` - координаты в пикселях\n\n## правила обработки касаний\n* При появлении нового касания, происходит проверка всех хитбоксов в порядке приоритета\n* При срабатывании одного из них, вызывается touchStartedCallback (второй параметр), происходит  \n  захват тача\n* Дальше конкретный палец привязывается к конкретному объекту. Он будет отслеживаться пока не  \n  оторвется от экрана или не вызовут terminate() у данного объекта\n* В любой момент вызовом terminate() можно незамедлительно прервать отслеживание и инициировать  \n  вызов touchEnded()\n* **настоятельно рекомендуется сразу после terminate() вставить return при вызове из коллбека во  \n  избежание сложностей, так как вызов terminate() не завершает выполнение коллбека, но под капотом  \n  является вызовом touchEndedCallback)**\n* При прекращении отслеживаия пальца через terminate() вызывается touchEndedCallback(null), при  \n  исчезновении пальца на экране - от последнего известного TouchPoint\n* Последний известный TouchPoint является публичным полем экземпляра touchProcesor,может быть null\n* Хитбокс не может сработать, если объект уже отслеживает палец. Если нужно отследить несколько  \n  касаний, начавшихся в одном хитбоксе - нужно создать несколько разных объектов с одинаковыми  \n  параметрами конструктора\n* TouchProcessor с не null последниим параметром удаляются при уходе со страницы\n* TouchProcessor может быть деактивирован методом block() и зново активирован методом unblock().  \n  Рекомендуется вызывать после terminate или вне обработчиков.\n* long getDuration() - возвращает время удержания пальца на экране в миллисекундах или -1 (если в  \n  данный момент палец не отслеживается)\n* boolean getTouchAlive() - true если объект отслеживает палец, иначе false\n\n## приоритеты\nприоритет тача - целое число int. Дефолтный приоритет - 0. \n\nС версии 3.1.7 приоритеты задаются числом, ранее выше был приоритет у того тача, который был создан ранее.\n\nПри каждом создании нового тача или изменении приоритетов происзодит установка флага, если флаг стоит, то перед обработкой касаний он опускается тачи пересотрировываются.\nУстановка и чтение приоритета возможны через `getPriority` и `setPriority` .\n\n## удаление\nможно удалить обрабочик из очереди обработки методом delete. После этого обьект больше ни как не будет связан с движком.\n\n# Пакет maths\n\n**с версии 3.1.5 все классы сериализуемы**\n\n## Vec3 и PVector\n\n**два класса решают одну и ту же задачу преобразования над векторами. PVector ведет себя как в\nпроцесинге, если метод не статик, то копирование вектора не происходит и изменяются его данные.\nСтатические методы делают копирование. У Vec3 почти нет статических методов, все операции над\nвекторами по умолчанию выполняют копирование.**\n\nДоступны конструкторы:\n\n```  \npublic Vec3(float x, float y, float z) {    \n    this.x = x;    \n    this.y = y;    \n    this.z = z;  \n}    \n    \npublic Vec3() {    \n    this.x = 0;    \n    this.y = 0;    \n    this.z = 0;  \n}    \n    \n// Creates vector with values taken from give array. Reads 3 values, starting from i index. \npublic Vec3(float[] arr, int i) {    \n    x = arr[i];    \n    y = arr[i + 1];    \n    z = arr[i + 2];  \n}    \n    \npublic Vec3(Vec3 v) {    \n    this.x = v.x;    \n    this.y = v.y;    \n    this.z = v.z;  \n}    \n    \npublic Vec3(float v) {    \n    this.x = v;    \n    this.y = v;    \n    this.z = v;  \n}    \n    \npublic Vec3(float x, float y) {    \n    this.x = x;    \n    this.y = y;  \n}  \n```  \n\nТакже через конструкторы можно конвертировать PVector в Vec3 и назад.\n**Все эти конструкторы и методы доступны и для PVector.**\n\n### выгрузка данных\n\nПолучить вектор в виде массива длины 3:  \n` public float[] getArray() `\n\n### преобразования над векторами\n\n`public void normalize()` - нормировать\n\n`public float length()` - длина по теореме Пифагора\n\n`public Vec3 add(Vec3 v)` - прибавить 2 вектора\n\n`public Vec3 sub(Vec3 v)` - вычесть из 1го второй (из this в случае метода )\n\n`public Vec3 mul(float i)` - скалярное умножение\n\n`public void cross(Vec3 u)` - векторное произведение\n\n`public Vec3 div(float i)` - скалярное деление\n\n`public static float getAngle(Vec3 v, Vec3 u)` - угол в радианах между векторами  \n` public static float dot(Vec3 a, Vec3 b)` - скалярное произведение\n\n```  \n/**    \n     * rotates vector around axis for a specified angle     *       \n     * @param axis axis, around which to rotate    \n     * @param a    angle in degrees    \n     */    public void rotateVec3(Vec3 axis, float a) {    \n        //create empty translate matrix    \n        float[] matrix;    \n        matrix = resetTranslateMatrix(new float[16]);    \n        Matrix.rotateM(matrix, 0, a, axis.x, axis.y, axis.z);    \n        float[] resultVec = new float[4];    \n        Matrix.multiplyMV(resultVec, 0, matrix, 0, new float[]{this.x, this.y, this.z, 0}, 0);    \n        //return new Vec3(resultVec[0], resultVec[1], resultVec[2]);    \n        this.x = resultVec[0];    \n        this.y = resultVec[1];    \n        this.z = resultVec[2];    \n}      \n}  \n  ```\n## Section \n  \n`Section(Vec3 A, Vec3 B)` - поздание отрезка из точки А и точку Б  \n`public Vec3 getDirectionVector() ` - возвращает вектор из точки Б а точку А  \n`public Vec3 getBaseVector()` - возвращает точку А  \n`public Vec3 findCross(Sectionn)` - вернет точку пересечения 2х отрезков или null, если они не  \nпересекаются  \n  \n# Utils  \n  \nСодежрит множество постоянно дорабатываемх функций, решаюших простейшие задачи. Самые полезные:  \n  \n## адаптация  \n  \n`kx, ky` - поля, инициализуемые движком при запуске. kx = размер экрана по горизонтали в  \nпикселях/720; ky = размер экрана по вертикали в пикселях/1280. Коэффициенты для атаптации  \nинтерфейса.  \n  \n`x, y` - размер экрана по горизонтали и вертикали в пикселях.  \n  \n## работа со времененм  \n  \n`millis()` - время работы приложения с момента последнего запуска в миллисекундах с учетом  \nостановок.  \n  \n`freezeMillis()` - останавливает ход millis и pageMillis. При этом частота вызовов отрисовки не  \nменяется. Останавливет работу всех объектов анимаций.  \n  \n`unfreezeMillis()` - millis и pageMillis продолжают идти с того же места, на котором были  \nостановлены. Продолжает работу всех объектов анимаций.  \n  \n`absoluteMillis()` - время работы приложения с момента последнего запуска в миллисекундах без учета  \nостановок.  \n  \n`getMillisFrozen()` - true елси ход millis остановлен, иначе false.  \n  \nДля любой ориентации устройсвта:  \n  \n`float getTimeK()` - 120/текущие фпс - коэффициент для адаптации физики к реальному времени. **Равен  \n0 при заморозке millis**.  \n  \n## Также полезные поля других классов  \n  \n`float OpenGLRenderer.fps` - текущее значение фпс  \n  \n`OpenGLRenderer.pageMillis()` - время в миллисекундах с момента открытия текущей страницы. При  \nзаморозке millis также замораживается.  \n  \n# Debugger  \n  \nКласс позволяет на лету, без останоки исполнения с помощью графического интерфейса просмаотривать и  \nизменять значения переменных  \n**отрисовка интерфейса не оптимизирована, не используйте этот механимз в продакшене и не пугайтесь  \nпросадкам фпс**  \nДля использования дебагера измеените третий параметр строки  \n`GLSurfaceView v = engine.onCreate(this, unused -\u003e new MainRenderer(), false,false); //второй параметр - ориентация LandScape, третий - использовать ли встроенный дебаггер.`  \nв MainActivity на true.  \nВ левом верхнем углу появится табличка с фпс.  \nДля добавления переменных нужно создать несколько (сколько угодно) экземпляров переменных для  \nотладки  \n  \n## Отладка  \n  \nКаждой переменной соотносится объект класса DebugValueFloat. Другие типы данных пока не  \nподдерживаются.  \nУ класса есть публичное поле value, с которым следует обращаться как с обычной переменной. Его можно  \nсвободно читать и свободно туда писать, все изменнеия мометально отобрзятся в пользовательском  \nинтерфейсе. Все вносимые пользователем изменения в переменную моментально попадают в value.  \n  \nДля создания переменной используйте метод класса Debugger  \n`DebugValueFloat Debugger.addDebugValueFloat(float min, float max, @NotNull String name)`, где min и  \nmax - границы, в которых пользователь может изменять значения переменной, name - отображаемое в  \nинтерфейсе имя переменной. **нельзя создавать 2 переменные с одинаковым именем, попытка сделать это  \nне даст никакого эффекта и будет проигнорирвана**.  \n  \n## использование  \n  \nОкно отладчика открывается нажатием на поле с fps в левом верхнем углу, закрывается либо повтороным  \nнажатием в то же место, либо с помощью креста внизу по центру.  \nСтрелки --\u003e и \u003c-- позволяют перемещаться по списку переменных, если они не поместились на 1 экран.  \n**При выключенном режиме отладки отладчик не инициализован и не занимает память. При вклюении он  \nзанимает некоторую доп. память для отрисовки интерфейса, а также имеет наивысший приоритет обработки  \nкасаний. Если окно свернуто, то наивысший приоритет только у таблички с фпс.**  \n  \nПри развернутом окне отладчика в правом верхнем углу отображаетс версия движка, в левом верхнем -  \nтекущие фпс.  \n  \nОкно отладчика намеренно сделано полупрозрачным, *это не баг, а фича.*  \n  \nПри нажатии на переменную появится возможность отрегулировать ее значение с помощью слайдера,  \nвернуться в главное меняю можно с помощью кнопки под слайдером.  \n  \n\n  \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmkazantsev%2Fseal_engine","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fnmkazantsev%2Fseal_engine","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fnmkazantsev%2Fseal_engine/lists"}