Вторник, 12.12.2017, 09:21
Поиск
Никнэйм
Сертификат на никнейм Olelucoye, зарегистрирован на Тимофеев Константин Михайлович
Зарегистрируй свой никнейм
Обратная связь
olelucoye.tk@yandex.ru
Реклама AdSense
Реклама
Друзья сайта
  • Лига медицинского права
  • Гостиница "Зай"
  • FAQ по системе
  • Инструкции для uCoz
  • Главная » Статьи » libGDX

    Строим модель с помощью ModelBuilder

    Строим модель с помощью ModelBuilder.

    Модель представляет собой 3D актив, состоящий из иерархии узлов (Node), где каждый узел представляет собой сочетание геометрии (Mesh или Сетка) и материала. По сути модель это сложная фигура, состоящая из множества простых. Для построения моделей в коде используется ModelBuilder. Построим несколько моделей и отобразим их в трехмерном пространстве.

    Используем код, который создали в предыдущей статье. Удалим оттуда только код создания и отрисовки куба.

    Создавать будем лес. Сначала объявим несколько моделей, экземпляр модели для поверхности земли, массив экземпляров моделей для деревьев и несколько материалов, которые будем использовать для частей моделей.

        public Model pineTree, LeafTree, ground;
        public ModelInstance groundInstance;
        public Array<ModelInstance> forest = new Array<ModelInstance>();
        public Material pine, trunk, land, crown;

    В create() определим материалы. Три из них будут инициализированы диффузным цветом, один - текстурой (вот этой).

        pine = new Material(ColorAttribute.createDiffuse(Color.GREEN));
        crown = new Material(ColorAttribute.createDiffuse(Color.OLIVE));
        trunk = new Material(ColorAttribute.createDiffuse(Color.GRAY));
        TextureAttribute land_attr = TextureAttribute.createDiffuse(new Texture("land.jpg"));
        land = new Material(land_attr);

    В create() объявляем ModelBuilder. И добавим следующую пару команд:

        ModelBuilder modelBuilder = new ModelBuilder();
        modelBuilder.begin();
        //здесь будем строить модель
        pineTree = modelBuilder.end();

    Строительство модели происходит именно между вызовом begin() и end(). При вызове end() ModelBuilder возвращает готовую модель. Модель состоит из набора узлов, в которых содержится информация о расположении узла в пространстве. Узлы в свою очередь состоят из сеток (Mesh). Сетки состоят из геометрических примитивов и материалов.

    Итак, строим первый узел.

        modelBuilder.begin();
        Node node1 = modelBuilder.node();
        node1.id = "node1";
        node1.translation.set(0f, 2.8f, 0f);

    Узел должен иметь уникальный id в пределах модели. Смещаем узел на 2.8 единиц вверх. Теперь обозначим геометрию узла.

        MeshPartBuilder meshBuilder;
        meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
        meshBuilder.cone(1f, 1f, 1f, 20);

    Для построения геометрии используется MeshPartBuilder. Инициализация происходит с помощью метода modelBuilder.part(). Указываем имя сетки, примитив для построения сетки (треугольники), атрибуты позиции и освещения и материал. Затем строим конус с размерами 1х1х1. Последний атрибут указывает степень детализации сетки. В случае конуса мы указываем сколько граней будет иметь основание конуса. Если, например, указать не 20, а 3, мы получим треугольную пирамиду (тетраэдр).

    По аналогии строим еще несколько узлов и получаем готовую модель.

            ModelBuilder modelBuilder = new ModelBuilder();
            modelBuilder.begin();
            Node node1 = modelBuilder.node();
            node1.id = "node1";
            node1.translation.set(0f, 2.8f, 0f);
            MeshPartBuilder meshBuilder;
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(1f, 1f, 1f, 20);
            Node node2 = modelBuilder.node();
            node2.id = "node2";
            node2.translation.set(0f, 2f, 0f);
            meshBuilder = modelBuilder.part("part2", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(2f, 1.5f, 2f, 20);
            Node node3 = modelBuilder.node();
            node3.id = "node3";
            node3.translation.set(0f, 1f, 0f);
            meshBuilder = modelBuilder.part("part3", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(3f, 2f, 3f, 20);
            Node node4 = modelBuilder.node();
            node4.id = "node4";
            meshBuilder = modelBuilder.part("part4", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, trunk);
            meshBuilder.cylinder(1f, 1f, 1f, 20);
            pineTree = modelBuilder.end();

    Заметьте, что для построения сеток в узлах, не объявляется каждый раз новый объект MeshPartBuilder, а используем созданный в первый раз.

    Теперь создадим несколько экземпляров моделей, соберем их в массив, расставим их случайным образом на территории 50х50.

            Random rnd = new Random();
            for(float x = -25f; x < 25f; x +=5f){
                for(float z = -25f; z < 25f; z +=5f){
                    if(rnd.nextInt(10) > 2){
                        float x_offset = 4*(0.5f - rnd.nextFloat());
                        float z_offset = 4*(0.5f - rnd.nextFloat());
                        ModelInstance tree;
                        tree = new ModelInstance(pineTree);
                        tree.transform.setToTranslation(x + x_offset, 0, z + z_offset );
                        forest.add(tree);
                    }
                }
            }

    С помощью генератора случайных чисел (rnd.nextInt(10) > 2)определяем будет ли на текущем месте (заданном x и z) находиться дерево или нет - в двух случаях из десяти место останется пустым. Затем генерируем небольшое случайное смещение, чтобы деревья не были посажены строго квадратно-гнездовым методом. Строим экземпляр модели, смещаем её в пространстве на заданное место и добавляем созданный экземпляр в массив.

    Теперь передадим полученный массив в ModelBatch (не нужно передавать каждый элемент массива, передаем весь массив сразу):

    modelBatch.render(forest, environment);

    Запустим и посмотрим что получилось.

    Создадим еще одну модель.

            modelBuilder.begin();
            Node lnode1 = modelBuilder.node();
            lnode1.id = "lnode1";
            lnode1.translation.set(0f, 1f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode2 = modelBuilder.node();
            lnode2.id = "lnode2";
            lnode2.translation.set(0.3f, 1.7f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode3 = modelBuilder.node();
            lnode3.id = "lnode3";
            lnode3.translation.set(-0.3f, 1.7f, 0.3f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode4 = modelBuilder.node();
            lnode4.id = "lnode4";
            lnode4.translation.set(-0.3f, 1.7f, -0.3f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode5 = modelBuilder.node();
            lnode5.id = "lnode5";
            lnode5.translation.set(0f, 2.2f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode6 = modelBuilder.node();
            lnode6.id = "lnode6";
            meshBuilder = modelBuilder.part("part4", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, trunk);
            meshBuilder.cylinder(0.7f, 1f, 0.7f, 20);
            LeafTree = modelBuilder.end();

    Для создания второй модели не создаем новые объекты ModelBuilder и MeshPartBuilder, а используем созданные при строительстве первой модели.

    Далее немного изменим заполнение массива экземпляров моделей:

            for(float x = -25f; x < 25f; x +=5f){
                for(float z = -25f; z < 25f; z +=5f){
                    if(rnd.nextInt(10) > 2){
                        float x_offset = 4*(0.5f - rnd.nextFloat());
                        float z_offset = 4*(0.5f - rnd.nextFloat());
                        ModelInstance tree;
                        if(rnd.nextInt(2) == 0){
                            tree = new ModelInstance(LeafTree);
                        }else{
                            tree = new ModelInstance(pineTree);
                        }
                        tree.transform.setToTranslation(x + x_offset, 0, z + z_offset );
                        forest.add(tree);
                    }
                }
            }

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

    Ну и для полноты картины создадим поверхность, на которой будут стоять деревья.

     ground = modelBuilder.createBox(54f, 0.1f, 54f, land, Usage.Position|Usage.Normal|Usage.TextureCoordinates);
     groundInstance = new ModelInstance(ground);
     groundInstance.transform.setTranslation(0f, -0.5f, 0f);

    И добавим в render() её отрисовку:

    modelBatch.render(groundInstance, environment);

    Готово. Запускаем.

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

    Полный листинг главного класса:

    import java.util.Random;
    import com.badlogic.gdx.ApplicationAdapter;
    import com.badlogic.gdx.Gdx;
    import com.badlogic.gdx.graphics.Color;
    import com.badlogic.gdx.graphics.GL20;
    import com.badlogic.gdx.graphics.PerspectiveCamera;
    import com.badlogic.gdx.graphics.Texture;
    import com.badlogic.gdx.graphics.VertexAttributes.Usage;
    import com.badlogic.gdx.graphics.g3d.Environment;
    import com.badlogic.gdx.graphics.g3d.Material;
    import com.badlogic.gdx.graphics.g3d.Model;
    import com.badlogic.gdx.graphics.g3d.ModelBatch;
    import com.badlogic.gdx.graphics.g3d.ModelInstance;
    import com.badlogic.gdx.graphics.g3d.attributes.ColorAttribute;
    import com.badlogic.gdx.graphics.g3d.attributes.TextureAttribute;
    import com.badlogic.gdx.graphics.g3d.environment.DirectionalLight;
    import com.badlogic.gdx.graphics.g3d.model.Node;
    import com.badlogic.gdx.graphics.g3d.utils.CameraInputController;
    import com.badlogic.gdx.graphics.g3d.utils.MeshPartBuilder;
    import com.badlogic.gdx.graphics.g3d.utils.ModelBuilder;
    import com.badlogic.gdx.utils.Array;
    public class Proba3D extends ApplicationAdapter {
        public PerspectiveCamera cam;
        public Model pineTree, LeafTree, ground;
        public ModelInstance groundInstance;
        public Array<ModelInstance> forest = new Array<ModelInstance>();
        public ModelBatch modelBatch;
        public Environment environment;
        public CameraInputController camController;
        public Material pine, trunk, land, crown;
                    
        @Override
        public void create () {
            modelBatch = new ModelBatch();
            environment = new Environment();
            environment.set(new ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f));
            environment.add(new DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f));
                            
            cam = new PerspectiveCamera(67, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
            cam.position.set(10f, 10f, 10f);
            cam.lookAt(0f, 0f, 0f);
            cam.near = 1f;
            cam.far = 300f;
            cam.update();
            
            camController = new CameraInputController(cam);
            Gdx.input.setInputProcessor(camController);
            
            pine = new Material(ColorAttribute.createDiffuse(Color.GREEN));
            crown = new Material(ColorAttribute.createDiffuse(Color.OLIVE));
            trunk = new Material(ColorAttribute.createDiffuse(Color.GRAY));
            TextureAttribute land_attr = TextureAttribute.createDiffuse(new Texture("land.jpg"));
            land = new Material(land_attr);
            
            ModelBuilder modelBuilder = new ModelBuilder();
            modelBuilder.begin();
            Node node1 = modelBuilder.node();
            node1.id = "node1";
            node1.translation.set(0f, 2.8f, 0f);
            MeshPartBuilder meshBuilder;
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(1f, 1f, 1f, 20);
            Node node2 = modelBuilder.node();
            node2.id = "node2";
            node2.translation.set(0f, 2f, 0f);
            meshBuilder = modelBuilder.part("part2", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(2f, 1.5f, 2f, 20);
            Node node3 = modelBuilder.node();
            node3.id = "node3";
            node3.translation.set(0f, 1f, 0f);
            meshBuilder = modelBuilder.part("part3", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, pine);
            meshBuilder.cone(3f, 2f, 3f, 20);
            Node node4 = modelBuilder.node();
            node4.id = "node4";
            meshBuilder = modelBuilder.part("part4", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, trunk);
            meshBuilder.cylinder(1f, 1f, 1f, 20);
            pineTree = modelBuilder.end();
            
            modelBuilder.begin();
            Node lnode1 = modelBuilder.node();
            lnode1.id = "lnode1";
            lnode1.translation.set(0f, 1f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode2 = modelBuilder.node();
            lnode2.id = "lnode2";
            lnode2.translation.set(0.3f, 1.7f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode3 = modelBuilder.node();
            lnode3.id = "lnode3";
            lnode3.translation.set(-0.3f, 1.7f, 0.3f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode4 = modelBuilder.node();
            lnode4.id = "lnode4";
            lnode4.translation.set(-0.3f, 1.7f, -0.3f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode5 = modelBuilder.node();
            lnode5.id = "lnode5";
            lnode5.translation.set(0f, 2.2f, 0f);
            meshBuilder = modelBuilder.part("part1", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, crown);
            meshBuilder.sphere(2f, 2f, 2f, 20, 20);
            Node lnode6 = modelBuilder.node();
            lnode6.id = "lnode6";
            meshBuilder = modelBuilder.part("part4", GL20.GL_TRIANGLES, Usage.Position | Usage.Normal, trunk);
            meshBuilder.cylinder(0.7f, 1f, 0.7f, 20);
            LeafTree = modelBuilder.end();
            
            Random rnd = new Random();
            for(float x = -25f; x < 25f; x +=5f){
                for(float z = -25f; z < 25f; z +=5f){
                    if(rnd.nextInt(10) > 2){
                        float x_offset = 4*(0.5f - rnd.nextFloat());
                        float z_offset = 4*(0.5f - rnd.nextFloat());
                        ModelInstance tree;
                        if(rnd.nextInt(2) == 0){
                            tree = new ModelInstance(LeafTree);
                        }else{
                            tree = new ModelInstance(pineTree);
                        }
                        tree.transform.setToTranslation(x + x_offset, 0, z + z_offset );
                        forest.add(tree);
                    }
                }
            }
                        
            ground = modelBuilder.createBox(54f, 0.1f, 54f, land, Usage.Position|Usage.Normal|Usage.TextureCoordinates);
            groundInstance = new ModelInstance(ground);
            groundInstance.transform.setTranslation(0f, -0.5f, 0f);
        }
        @Override
        public void render () {
            camController.update();
            
            Gdx.gl.glViewport ( 0 , 0 , Gdx.graphics.getWidth (), Gdx.graphics.getHeight ());
            Gdx.gl.glClearColor(1, 0, 0, 1);
            Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT|GL20.GL_DEPTH_BUFFER_BIT);
            
            modelBatch.begin(cam);
            modelBatch.render(groundInstance, environment);
            modelBatch.render(forest, environment);
            modelBatch.end();
            
        }
        
        @Override
        public void dispose(){
            modelBatch.dispose();
            pineTree.dispose();
            LeafTree.dispose();
            
        }
    }

    libGDX. Основы 3D программирования.
    Игра Flower. Ловим капли.
    TexturePacker.Создаем атлас текстур.
    Создаем проект на движке libGDX
    Кастомизация EditText
    Кастомизация SeekBar'а
    9-patch изображения для Андроид
    Кастомный ползунок в виде дуги (аналог SeekBar)
    Анимация в Андроид
    Кастомизация элементов управления в Android
    Смартфон DEXP Ixion ML 5, обзор.
    Создание кастомного View-элемента интерфейса.
    Создание виджета - электронные часы с кастомным шрифтом
    Будильник для Андроид "Разбуди меня"

    Категория: libGDX | Добавил: Olelucoye (03.04.2015)
    Просмотров: 1805 | Комментарии: 1
    | Теги: меш, MeshPartBuilder, массив моделей, libGDX, узел, ModelBuilder, mesh, модель, 3D | Рейтинг: 0.0/0
    Всего комментариев: 1
    1 dmnew   (07.03.2016 12:23)
    //заполнение повторяющейся текстурой (здесь 5x5)
    Texture texture = new Texture(Gdx.files.internal("img/land.jpg"));

    texture.setWrap(Texture.TextureWrap.Repeat, Texture.TextureWrap.Repeat);
    TextureRegion textureRegion = new TextureRegion(texture);
    textureRegion.setRegion(0, 0, texture.getWidth() * 5, texture.getHeight() * 5);
    TextureAttribute land_attr = TextureAttribute.createDiffuse(textureRegion);

    Добавлять комментарии могут только зарегистрированные пользователи.
    [ Регистрация | Вход ]
    Меню сайта
    Категории раздела
    Андроид разработка [23]
    libGDX [24]
    Мои андроид проекты [6]
    Excel [7]
    Железяки [5]
    Скрипты в блокноте [4]
    Разное [1]
    Форма входа
    Статистика

    Онлайн всего: 1
    Гостей: 1
    Пользователей: 0
    Яндекс Метрика
    Яндекс.Метрика