Russian Qt Forum
Май 21, 2024, 19:57 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: 1 [2] 3 4 ... 7   Вниз
  Печать  
Автор Тема: Создание массива вершин фигуры с помощью редактора  (Прочитано 41409 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #15 : Апрель 01, 2014, 14:55 »

Вот хороший туториал, в котором рассказано, как загрузить данные из obj-файла: http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Load_OBJ
Ну как "хороший". Если с нуля - то да  Улыбающийся

1) Автор ничего не говорит о строках начинающихся с "vt" и "vn" - а это самые важные данные модели

2) Автор получает вертексные нормали осредняя полигонные. Такой метод используется только в крайнем случае, когда ничего лучшего нет. Обычно осредненная нормаль заметно хуже аналитической, поэтому правильно читать нормали из obj файла, если они там есть. Да, и нормальная реализация осреднения гораздо сложнее чем у автора - напр нормали для куба
он вычислит некорректно

Хотя и по коду все видно, напр
Цитировать
   
if (line.substr(0,2) == "v ") {
  istringstream s(line.substr(2));
Во, управился Улыбающийся  А если пробел перед "v"?

В общем Вика она Вика и есть. Да, можно много почерпнуть, спасибо ей. Но это не претендует ни на какое качество/глубину (о чем она сама честно предупреждает)
Записан
8Observer8
Гость
« Ответ #16 : Апрель 01, 2014, 17:52 »

Спасибо! Правда про нормали пока ничего непонятно. Надеюсь, позже разберусь.

Как правильно парсить из ".obj"? Может для этих целей лучше применять регулярные выражения? Или это "выстрел из пушки по воробьям"?
« Последнее редактирование: Апрель 01, 2014, 19:18 от 8Observer8 » Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #17 : Апрель 01, 2014, 19:03 »

Как правильно парсить из ".obj"?
Как и любой текстовый формат: нужно помнить, что там могут быть любое количество пробелов, табуляций и переводов строк, причем в различных комбинациях. Улыбающийся

Может для этих целей лучше применять регулярные выражения? Или это "выстрел из пушки по воробьям"?
Можно парсить все руками (формат очень прост), можно с регуляркой, а можно и boost::spirit прикрутить или lex/bison. Улыбающийся
Но что-то мне подсказывает, что силы начинают растрачиваться во все стороны. Улыбающийся Вы только начинаете, зачем вам идеальный загрузчик?
Вам сейчас нужно, что бы он загрузил вашу первую модель, с которой вы сможете экспериментировать. Когда он перестанет справляться, то вы его расширите/переделаете.
Поверьте, если вы будете делать идеальный контейнер с идеальным загрузчиком, до дело до решения собственно задачи не дойдет никогда. Улыбающийся
Записан
8Observer8
Гость
« Ответ #18 : Апрель 01, 2014, 19:17 »

Спасибо огромное за такие развёрнутые ответы! Буду парсить руками, а потом потихоньку прикручу регулярку Улыбающийся За расширение кругозора отдельное спасибо Улыбающийся
Записан
carrygun
Гость
« Ответ #19 : Апрель 02, 2014, 04:22 »

boost::spirit

Это ему точно не нужно.
Записан
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #20 : Апрель 02, 2014, 06:41 »

Это ему точно не нужно.
А кто решает что нужно ТС, а что нет? Улыбающийся
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #21 : Апрель 02, 2014, 09:24 »

Как правильно парсить из ".obj"?
Да, минимизировать задачу, как правильно сказали выше. Если "по-взрослому" то там не одна неделя работы. И надо четко представлять структуры данных которые хотите получить.

Правда про нормали пока ничего непонятно. Надеюсь, позже разберусь.
В школе учили "производная" (дифференциал) и ее геометрический смысл
Записан
8Observer8
Гость
« Ответ #22 : Апрель 09, 2014, 08:54 »

Я решил отложить в сторону голову обезьяны: http://en.wikibooks.org/wiki/OpenGL_Programming/Modern_OpenGL_Tutorial_Load_OBJ

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

Для начала написал создал класс с методом-заглушкой:
Parser.h
Код
C++ (Qt)
#ifndef PARSER_H
#define PARSER_H
 
#include <QList>
#include <QVector3D>
 
class Parser {
public:
   void parseData( const QString &input, QList<QVector3D> &points, QList<QVector3D> &indexes );
};
 
#endif // PARSER_H
 

Parser.cpp
Код
C++ (Qt)
#include "Parser.h"
 
void Parser::parseData( const QString &input, QList<QVector3D> &points, QList<QVector3D> &indexes ) {
 
}
 

По умолчанию в Blender создаётся куб. Я экспортировал его в файл cube.obj:

cude.obj
Цитировать
# Blender v2.68 (sub 0) OBJ File: ''
# www.blender.org
o Cube
v 1.000000 1.000001 -0.999999
v 1.000000 -0.999999 -1.000001
v -1.000000 -0.999999 -1.000001
v -1.000000 1.000001 -0.999999
v 1.000000 0.999999 1.000001
v 0.999999 -1.000001 0.999999
v -1.000000 -1.000000 0.999999
v -1.000000 0.999999 1.000001
s off
f 1 2 4
f 5 8 6
f 1 5 2
f 2 6 3
f 3 7 4
f 5 1 8
f 2 3 4
f 8 7 6
f 5 6 2
f 6 7 3
f 7 8 4
f 1 4 8

Создал тест:
tst_CubeFromBlenderTests.cpp
Код
C++ (Qt)
#include <QString>
#include <QtTest>
#include "Parser.h"
#include <QVector3D>
 
class CubeFromBlenderTests : public QObject {
   Q_OBJECT
 
public:
   CubeFromBlenderTests( );
 
   static inline bool qFuzzyCompare( double p1, double p2, double delta ) {
       return ( qAbs( p1 - p2 ) <= delta * qMin( qAbs( p1 ), qAbs( p2 ) ));
   }
 
   private
Q_SLOTS:
   void testCase1_data( );
   void testCase1( );
};
 
CubeFromBlenderTests::CubeFromBlenderTests( ) {
}
 
void CubeFromBlenderTests::testCase1_data( ) {
   QTest::addColumn<QString>("input");
   QTest::addColumn<QList<QVector3D> >("expected_points");
   QTest::addColumn<QList<QVector3D> >("expected_indexes");
 
   QString input = QString( "# Blender v2.68 (sub 0) OBJ File: ''\n"
           "# www.blender.org\n"
           "o Cube\n"
           "v 1.000000 1.000001 -0.999999\n"
           "v 1.000000 -0.999999 -1.000001\n"
           "v -1.000000 -0.999999 -1.000001\n"
           "v -1.000000 1.000001 -0.999999\n"
           "v 1.000000 0.999999 1.000001\n"
           "v 0.999999 -1.000001 0.999999\n"
           "v -1.000000 -1.000000 0.999999\n"
           "v -1.000000 0.999999 1.000001\n"
           "s off\n"
           "f 1 2 4\n"
           "f 5 8 6\n"
           "f 1 5 2\n"
           "f 2 6 3\n"
           "f 3 7 4\n"
           "f 5 1 8\n"
           "f 2 3 4\n"
           "f 8 7 6\n"
           "f 5 6 2\n"
           "f 6 7 3\n"
           "f 7 8 4\n"
           "f 1 4 8\n" );
   QList<QVector3D> expected_points;
   QVector3D point;
 
   // 1
   point.setX( 1.000000 );
   point.setY( 1.000001 );
   point.setZ( -0.999999 );
   expected_points.append( point );
 
   // 2
   point.setX( 1.000000 );
   point.setY( -0.999999 );
   point.setZ( -1.000001 );
   expected_points.append( point );
 
   // 3
   point.setX( -1.000000 );
   point.setY( -0.999999 );
   point.setZ( -1.000001 );
   expected_points.append( point );
 
   // 4
   point.setX( -1.000000 );
   point.setY( 1.000001 );
   point.setZ( -0.999999 );
   expected_points.append( point );
 
   // 5
   point.setX( 1.000000 );
   point.setY( 0.999999 );
   point.setZ( 1.000001 );
   expected_points.append( point );
 
   // 6
   point.setX( 0.999999 );
   point.setY( -1.000001 );
   point.setZ( 0.999999 );
   expected_points.append( point );
 
   // 7
   point.setX( -1.000000 );
   point.setY( -1.000000 );
   point.setZ( 0.999999 );
   expected_points.append( point );
 
   // 8
   point.setX( -1.000000 );
   point.setY( 0.999999 );
   point.setZ( 1.000001 );
   expected_points.append( point );
 
   QList<QVector3D> expected_indexes;
   QVector3D indexes;
 
   // 1
   indexes.setX( 1.0 );
   indexes.setY( 2.0 );
   indexes.setZ( 4.0 );
   expected_indexes.append( indexes );
 
   // 2
   indexes.setX( 5.0 );
   indexes.setY( 8.0 );
   indexes.setZ( 6.0 );
   expected_indexes.append( indexes );
 
   // 3
   indexes.setX( 1.0 );
   indexes.setY( 5.0 );
   indexes.setZ( 2.0 );
   expected_indexes.append( indexes );
 
   // 4
   indexes.setX( 2.0 );
   indexes.setY( 6.0 );
   indexes.setZ( 3.0 );
   expected_indexes.append( indexes );
 
   // 5
   indexes.setX( 3.0 );
   indexes.setY( 7.0 );
   indexes.setZ( 4.0 );
   expected_indexes.append( indexes );
 
   // 6
   indexes.setX( 5.0 );
   indexes.setY( 1.0 );
   indexes.setZ( 8.0 );
   expected_indexes.append( indexes );
 
   // 7
   indexes.setX( 2.0 );
   indexes.setY( 3.0 );
   indexes.setZ( 4.0 );
   expected_indexes.append( indexes );
 
   // 8
   indexes.setX( 8.0 );
   indexes.setY( 7.0 );
   indexes.setZ( 6.0 );
   expected_indexes.append( indexes );
 
   // 9
   indexes.setX( 5.0 );
   indexes.setY( 6.0 );
   indexes.setZ( 2.0 );
   expected_indexes.append( indexes );
 
   // 10
   indexes.setX( 6.0 );
   indexes.setY( 7.0 );
   indexes.setZ( 3.0 );
   expected_indexes.append( indexes );
 
   // 11
   indexes.setX( 7.0 );
   indexes.setY( 8.0 );
   indexes.setZ( 4.0 );
   expected_indexes.append( indexes );
 
   // 12
   indexes.setX( 1.0 );
   indexes.setY( 4.0 );
   indexes.setZ( 8.0 );
   expected_indexes.append( indexes );
 
   QTest::newRow( "parseData_01" ) << input << expected_points << expected_indexes;
}
 
void CubeFromBlenderTests::testCase1( ) {
   QFETCH( QString, input );
   QFETCH( QList<QVector3D>, expected_points );
   QFETCH( QList<QVector3D>, expected_indexes );
 
   Parser parser;
   QList<QVector3D> points;
   QList<QVector3D> indexes;
   parser.parseData( input, points, indexes );
 
   double delta = 0.0001;
   bool result = false;
   QString msg;
 
   // Check size of points and indexes
   QCOMPARE( points.size( ), expected_points.size( ) );
   QCOMPARE( indexes.size( ), expected_indexes.size( ) );
 
   // Check points
   for ( std::size_t i = 0; i < points.size( ); ++i ) {
       // Check x
       result = qFuzzyCompare( points[i].x( ), expected_points[i].x( ), delta );
       msg = QString( "\nActual: %1"
               "\nExpected: %2"
               "\nDelta: %3" ).arg( points[i].x( ) ).arg( expected_points[i].x( ) ).arg( delta );
       QVERIFY2( result, msg.toStdString( ).c_str( ) );
 
       // Check y
       result = qFuzzyCompare( points[i].y( ), expected_points[i].y( ), delta );
       msg = QString( "\nActual: %1"
               "\nExpected: %2"
               "\nDelta: %3" ).arg( points[i].y( ) ).arg( expected_points[i].y( ) ).arg( delta );
       QVERIFY2( result, msg.toStdString( ).c_str( ) );
 
       // Check z
       result = qFuzzyCompare( points[i].z( ), expected_points[i].z( ), delta );
       msg = QString( "\nActual: %1"
               "\nExpected: %2"
               "\nDelta: %3" ).arg( points[i].z( ) ).arg( expected_points[i].z( ) ).arg( delta );
       QVERIFY2( result, msg.toStdString( ).c_str( ) );
   }
 
   // Check indexes
   int actualIndex = 0;
   int expectedIndex = 0;
   for ( std::size_t i = 0; i < indexes.size( ); ++i ) {
       // 1
       actualIndex = (int) indexes[i].x( );
       expectedIndex = (int) indexes[i].x( );
       QCOMPARE( actualIndex, expectedIndex );
 
       // 2
       actualIndex = (int) indexes[i].y( );
       expectedIndex = (int) indexes[i].y( );
       QCOMPARE( actualIndex, expectedIndex );
 
       // 3
       actualIndex = (int) indexes[i].z( );
       expectedIndex = (int) indexes[i].z( );
       QCOMPARE( actualIndex, expectedIndex );
   }
}
 
QTEST_APPLESS_MAIN( CubeFromBlenderTests )
 
#include "tst_CubeFromBlenderTests.moc"
 

Приступаю к реализации метода parseData().
« Последнее редактирование: Апрель 09, 2014, 10:34 от 8Observer8 » Записан
8Observer8
Гость
« Ответ #23 : Апрель 09, 2014, 10:02 »

Вот такая получилась функция для парсинга и загрузги данных. Она справляется со своей задачей по-минимому. Возможно надо будет обработку ошибок включить. Буду рад любым замечаниям.

Parser.cpp
Код
C++ (Qt)
#include "Parser.h"
#include <QStringList>
 
void Parser::parseData( const QString &input, QList<QVector3D> &points, QList<QVector3D> &indexes ) {
   QStringList list = input.split( "\n" );
 
   for ( std::size_t i = 0; i < list.size( ); ++i ) {
       // Poitns
       if ( list[i][0] == QChar( 'v' ) ) {
           QStringList listForPoints = list[i].split( " " );
           if ( listForPoints.size( ) == 4 ) {
               bool ok;
               float x = listForPoints[1].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               float y = listForPoints[2].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               float z = listForPoints[3].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               points.append( QVector3D( x, y, z ) );
           }
       }
 
       // Indexes
       if ( list[i][0] == QChar( 'f' ) ) {
           QStringList listForIndexes = list[i].split( " " );
           if ( listForIndexes.size( ) == 4 ) {
               bool ok;
               float x = listForIndexes[1].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               float y = listForIndexes[2].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               float z = listForIndexes[3].toFloat( &ok );
               if ( !ok ) {
                   break;
               }
 
               indexes.append( QVector3D( x, y, z ) );
           }
       }
   }
}
 
« Последнее редактирование: Апрель 09, 2014, 10:32 от 8Observer8 » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #24 : Апрель 09, 2014, 20:52 »

Вот такая получилась функция для парсинга и загрузги данных. Она справляется со своей задачей по-минимому. Возможно надо будет обработку ошибок включить. Буду рад любым замечаниям.
Приведенный код - халтура которая не заслуживает обсуждения/замечаний. Вот Вам простенький obj файл (аттач), тренируйтесь
Записан
Hrundel
Гость
« Ответ #25 : Апрель 09, 2014, 21:47 »

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

Код
C++ (Qt)
#ifndef OBJFORMAT_H
#define OBJFORMAT_H
 
#include <QObject>
#include <QString>
#include <QVector>
#include <QFile>
#include <QStringList>
#include <QTextStream>
#include <QMessageBox>
#include <QInputDialog>
#include <QDir>
 
#include "simdynamic.h"
#include "./dialogs/importprogressdialog.h"
 
class ObjFormat : public QObject
{
   Q_OBJECT
 
public:
   explicit ObjFormat(QObject *parent = 0);
 
   void importModel(QString str, int digit);
   SimulationDynamicMesh* getModel(void){return simDynamicMesh;}
 
private:
   SimulationDynamicMesh* simDynamicMesh;
   QMessageBox msgBox;
   QFile       file;
 
   ImportProgressDialog* importProgressDlg;
 
   void parseLine(QString str);
 
   bool appVertex, appTexture, appFaces, appNormals;
 
signals:
 
public slots:
 
};
 
 

Код
C++ (Qt)
#include "objformat.h"
#include "common.h"
 
ObjFormat::ObjFormat(QObject *parent) :
   QObject(parent)
 , simDynamicMesh(new SimulationDynamicMesh())
 , appVertex(false)
 , appTexture(false)
 , appFaces(false)
 , appNormals(false)
{
}
 
void ObjFormat::importModel(QString str, int digit)
{
   importProgressDlg = new ImportProgressDialog();
   importProgressDlg->show();
 
   file.setFileName(str);
 
   if(!file.open(QIODevice::ReadOnly | QIODevice::Text))
   {
       msgBox.setWindowTitle(SimError::critical);
       msgBox.setText(QString::fromLocal8Bit("File '") + str + QString::fromLocal8Bit("' wurde nicht gefunden!"));
       msgBox.setIcon(QMessageBox::Critical);
       msgBox.exec();
       return;
   }
 
   QTextStream stream(&file);
 
   importProgressDlg->printToLog("Parsing *obj File");
 
   QVector<QString> lines;
 
   while(!stream.atEnd())
   {
       lines.append(stream.readLine());
   }
   file.close();
 
   importProgressDlg->setProgressBarMaxValue(lines.size());
 
   for(int i = 0; i < lines.size(); i++)
   {
       parseLine(lines.at(i));
       importProgressDlg->setProgressBarValue(i);
   }
 
   if(!simDynamicMesh->haveIdenticalVertixes(importProgressDlg) && !simDynamicMesh->haveIdenticalFaces(importProgressDlg))
   {
       simDynamicMesh->sortEdges(importProgressDlg);
   }
 
   QString meshName = QString("Object_%1").arg(QString::number(digit));
   bool ok;
   QString text = QInputDialog::getText(NULL,tr("Simulation Object Name:"),tr("Simulation Object Name:"),QLineEdit::Normal,meshName,&ok);
    if (ok && !text.isEmpty())
        simDynamicMesh->setMeshName(text);
 
   importProgressDlg->close();
   delete importProgressDlg;
}
 
void ObjFormat::parseLine(QString str)
{
   str.remove("\t");
   str.remove("\n");
   str = str.trimmed();
 
   QStringList list = str.split(" ");
 
   if(list.at(0) == "v")  // parser vertex
   {
       simDynamicMesh->appendVertex(list.at(1).toFloat(), list.at(2).toFloat(), list.at(3).toFloat());
       simDynamicMesh->setLastVertexColor(0.75F, 0.75F, 0.75F);
       if(!appVertex)
       {
           importProgressDlg->printToLog("\tAppend Vertex Data");
           appVertex = true;
       }
   }
   else if(list.at(0) == "vn")
   {
       simDynamicMesh->appendNormals(list.at(1).toFloat(), list.at(2).toFloat(), list.at(3).toFloat());
       if(!appNormals)
       {
           importProgressDlg->printToLog("\tAppend Normal Data");
           appNormals = true;
       }
   }
   else if(list.at(0) == "vt")
   {
       simDynamicMesh->appendTexture(list.at(1).toFloat(), list.at(2).toFloat());
 
       if(!appTexture)
       {
           importProgressDlg->printToLog("\tAppend Texture Data");
           appTexture = true;
       }
   }
   else if(list.at(0) == "f")
   {
       QStringList vert1 = list.at(1).split("/");
       QStringList vert2 = list.at(2).split("/");
       QStringList vert3 = list.at(3).split("/");
 
       if(vert1.size() > 1)
       {
           simDynamicMesh->appendFace(vert1.at(0).toInt(), vert2.at(0).toInt(), vert3.at(0).toInt(),
                                      vert1.at(1).toInt(), vert2.at(1).toInt(), vert3.at(1).toInt(),
                                      vert1.at(2).toInt(), vert2.at(2).toInt(), vert3.at(2).toInt());
       }
       else
       {
           simDynamicMesh->appendFace(vert1.at(0).toInt(), vert2.at(0).toInt(), vert3.at(0).toInt());
       }
 
       if(!appFaces)
       {
           importProgressDlg->printToLog("\tAppend Face Data");
           appFaces = true;
       }
   }
}
 
 
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #26 : Апрель 10, 2014, 09:23 »

Вот, пришлось тоже недавно написать. Правда импортирую в свою структуру данных. Но это в коде понять можно, и при необхомости заменить.
Ну вот, другое дело - видно что человек работал, задачу решал (а не прыгал туда-сюда "по всему интересному"). Замечания

- фейсет может иметь ссылаться на любое число вертексов (напр 1, 2 или 1000)

- позиции всегда есть, но нормали, и UV могут быть или нет  (f 33//41 ..) или (f  33/21 ..). Также атрибуты не обязаны сбиваться, напр одна фейсет может иметь UV но другая нет.

- файл может содержать любое число объектов, (но нумерация вертексов одна)

- мелочи: индексы могут быть отрицательными, строки могут склеиваться слешем "\"

Это в игровых моделях всегда треугольники и всегда 3 атрибута, в общем случае это не так. Более сложные вещи (импорт материала, декомпозиция по материалу, разбивка complex полигонов и др) не рассматриваем, речь о простом парсере. Ну и конечно "split" загнется уже на среднем объеме, ну то тоже ладно, то уже профессиональный парсер, за рамками темы
Записан
Hrundel
Гость
« Ответ #27 : Апрель 10, 2014, 12:15 »

Спасибо, Игорь за замечания.
У меня была очень  ограниченная задача. Все модели на входе имели только трианглы, поэтому не стал писать универсальную версию. Но на счет орицательных индексов и обратного слеша - не знал. Спасибо за инфу.
Записан
m_ax
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2094



Просмотр профиля
« Ответ #28 : Апрель 10, 2014, 12:35 »

Ну вот, другое дело - видно что человек работал, задачу решал (а не прыгал туда-сюда "по всему интересному").
А что в итоге получилось? В замешательстве
Какая то невообразимая архитектурная каша.. Один чих и всё развалится(
Записан

Над водой луна двурога. Сяду выпью за Ван Гога. Хорошо, что кот не пьет, Он и так меня поймет..

Arch Linux Plasma 5
Old
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 4349



Просмотр профиля
« Ответ #29 : Апрель 10, 2014, 12:43 »

Ну вот, другое дело - видно что человек работал, задачу решал (а не прыгал туда-сюда "по всему интересному").
Расскажите пожалуйста о киллер фичах второго решения по сравнению с первым.
Чем оно вам так понравилось? Правильно я понимаю, что у вас примерно также, только еще материалы читает?
Записан
Страниц: 1 [2] 3 4 ... 7   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.211 секунд. Запросов: 20.