[Tutorial] Izrada 3D/scenegraph enginea

poruka: 66
|
čitano: 19.952
|
moderatori: Lazarus Long, XXX-Man, vincimus
+/- sve poruke
ravni prikaz
starije poruke gore
15 godina
neaktivan
offline
Intro

Hello,

opet sam u onom lijepom raspolozenju kad mi se piskara i screenshota i pise kod, pa eto razmislih da napravim jedan neodredjeno dugi 3D engine tutorial.

 

Prije svega, ovaj put za razliku od Jave uzet cu C++, cisto da mi bude zanimljivije i da cujem pokoje konstruktivne komentare od kolega koji vise rade u C++u od mene.

Ciljevi:

- cross platform

- scenegraph

- abstraktni renderer (ja cu zapocet s OpenGL-om, a kasnije netko moze to portati na DirectX)

- shaderi

- post-processing efekti

- arhitektura

 

Podboldao sam ovo na kraju jer zelim, za razliku od vecine tutorijala koje vidjam po netu, dizajnirati framework/library kako spada. Bez imena varijabli x,y,z, bez 'hackish' kodiranja, bez rucno unrollanih loopova i slicno.

Alati:

- gcc > 4.0 (ukljucujuci i gmake)

- cmake >= 2.6

- opengl SDK >= 3.0

- boost

- cppunit

 

 

Kul. Valjda nisam nish zaboravio. Ako jesam, dodat cu u editove.

Cheers!

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 27.9.2010 19:39 (Deus ex machina).
 
22 0 hvala 19
15 godina
neaktivan
offline
math::Vector osnove

3D engine, u osnovi osnova, sastoji se od elementarnog 3D vektora, te transformacije tog vektora u 2D ravninu.

Vecina nasih 'matematickih' podataka biti ce izrazena kroz vektore - pozicija, boja, os rotacije, uv koordinate, skaliranje, bla bla bla... dakle, kao osnovu, bilo bi odlicno napraviti Vector klasu (nemojte ovo, molim vas, pomijesati sa vector kolekcijom iz STL-a).

 

Problemu se moze, kao i uvijek, pristupiti na nekoliko nacina:

1. Vektori koji nama trebaju imaju dvije, tri i cetiri dimenzije. Dakle, mogli bismo napraviti 3 klase: Vector2, Vector3 i ColorRGBA

2. Mogli bismo izvuci superklasu koja objedinjuje matematicke operacije izvedene nad elementima vektora, te subklasirati ovisno o tipu ili broju elemenata

3. Mogli bismo napraviti Vector klasu koja prima dinamicki alociran array i velicinu istog, te vrsi operacije nad arrayem

4. Mozemo se praviti pametni i premature optimizirati, zgroziti se nad dinamicki alociranim arrayem, pa to rijesiti kao svaki pametan covjek preko jedne template klase ;-)

 

O vektoru kao jedinici mozete najvise procitati na Wolfram Math. Vektori podrzavaju vecinu operacija kao i skalari, dakle mogu se zbrajati, oduzimati, multiplicirati (pardon, mnoziti), dijeliti. Za razliku od skalara, vektori podrzavaju nekoliko dodatnih operacija kao sto su dot-produkt i cross-produkt (ispricavam se, ne znam hrvatske nazive za ovo).

 

Bilo bi idealno, dakle, kad bi nasa klasa podrzavala bilo koju velicinu vektora, a jos idealnije ako bi podrzavala bilo koji tip.

 

Zapocnimo sa strukturom naseg project-direktorija. Ja to volim rijesiti "po Mavenu" :-)

project-root
- src
 - main
  - cpp
 - test
  - cpp 
- target

 

Napravimo file 'Vector.hpp' u direktoriju src/main/cpp, i unesimo tipicne header guardove:

#ifndef VECTOR_HPP_
#define VECTOR_HPP_


#endif /* VECTOR_HPP_ */

 

Bilo bi uredno i odijeliti vektor u svoj namespace. S obzirom da cemo vjerojatno s vremenom dodati jos nekoliko matematickih konstrukta (matrica, quaternion), namespace 'math' bi bio dobar izbor. Takodjer, dobra praksa je slijediti namespaceove sa strukturom direktorija, pa dodajte 'math' unutar src/main/cpp/math

project-root
- src
 - main
  - cpp
   - math

 

Kod. Primjetite da su promjenjeni i header guardovi.

#ifndef MATH_VECTOR_HPP_
#define MATH_VECTOR_HPP_

namespace math {

}

#endif /* VECTOR_HPP_ */

 

Nadalje, htjeli smo da vektor bude templatiziran po velicini i po tipu, i idealno bi bilo da drzi svoje podatke na stacku - tako kad se budu vrsile operacije, mozemo hintati compileru da zgura membere u registre. Neki medju vama ce takodjer primjetiti da premature optimiziram pa se idem sad kazniti.

#ifndef MATH_VECTOR_HPP_
#define MATH_VECTOR_HPP_

namespace math {

template<typename Type, int Size>
class Vector {
private:
  Type data[Size];
}

}

#endif /* VECTOR_HPP_ */

 

Kad promislim bolje, cisti arrayi su zlo. Zlo kakvog nema. Bolje da ja iskoristim nesto sto su pametniji od mene vec napravili, i zavucem ruku u Boost i izvucem njihovu array klasu (primjetite i #include):

#ifndef MATH_VECTOR_HPP_
#define MATH_VECTOR_HPP_

#include <boost/array.hpp>

namespace math {

template<typename Type, int Size>
class Vector {
private:
  boost::array<Type, Size> data;
}

}

#endif /* VECTOR_HPP_ */

 

Tako je vec bolje.

Trebat ce nam i par konstruktora, cisto zbog jednostavnije inicijalizacije.

#ifndef MATH_VECTOR_HPP_
#define MATH_VECTOR_HPP_

#include <boost/array.hpp>

namespace math {

template<typename Type, int Size>
class Vector {
private:
  boost::array<Type, Size> data;

public:
   Vector() {
       for (int i = 0; i < Size; ++i) {
          data[i] = 0;
       }
    }

    Vector(const Type x, const Type y) {
       data[0] = x;
       data[1] = y;
    }

    Vector(const Type x, const Type y, const Type z) {
       data[0] = x;
       data[1] = y;
       data[2] = z;
    }

    Vector(const Type r, const Type g, const Type b, const Type a) {
       data[0] = r;
       data[1] = g;
       data[2] = b;
       data[3] = a;
    }

    Vector(const boost::array<Type, Size>& values) : data(values) {}

    Vector(const Type values[Size]) {
       for (int i = 0; i < Size; ++i) {
          data[i] = values[i];
       }
    }

    ~Vector() {
    }

}
}


#endif /* VECTOR_HPP_ */

 

K. Treba nam nacin za dobiti podatke iz vektora. Iako u poslu ne volim operator overloading, zgodan je to feature za hobi projekte pa cemo ga ovdje iskoristiti.

Overloadat cemo indeks-operator, i to na nacin da ne dopusta mijenjanje vektora. To znaci da vrijednost overload funkcije ne vracamo po referenci.

Od sad nadalje necu kopirati cijeli kod, nego samo dodatke.

Type operator[](const int i) const {
    return data[i];
}

 

Trebat ce nam i neke osnovne operacije. Evo zbrajanja, vama ostavljam da sredite oduzimanje, mnozenje, i dijeljenje:

Vector<Type, Size> operator+(const Vector<Type, Size>& that) {
    Type resultData[Size];
    for (int i = 0; i < Size; ++i)
       resultData[i] = data[i] + that[i];

    Vector<Type, Size> result(resultData);
    return result;
}

 

I jos nekoliko dodataka za kraj.
Prvo, bilo bi odlicno da mozemo ispisati vektor na neki stream, tako da mu se vide vrijednosti. U C++-u se to obicno radi overloadanjem "<<" operatora, sto osobno smatram katastrofom, pa cemo to ostaviti za neku buducnost, i posluziti se C#-ovskim (pokradeno iz Jave ;-)) ToString().

 

Na vrhu filea treba dodati, ofc:

#include <string>
#include <sstream>

 

A negdje unutar Vector klase, recimo na dno:

std::string ToString() const {
    std::ostringstream oss;
    oss << "{";
    for (int i = 0; i < Size - 1; ++i) {
       oss << (data[i]) << ", ";
    }
    oss << (data[Size - 1]);
    oss << "}";
    return oss.str();
}

 

K. To bi bilo to za danas, jer sam lijen tipkati dalje.

Slijedeci put dodat cemo jos nekoliko bitnih operacija u Vector, kao sto su kalkulacija duzine, normalizacija, i jos par bitnih operacija.

Takodjer, ustolicit cemo globalnu "Object" klasu sa par generalnih funkcionalnosti korisnih svagdje.

 

Cheers!

 

 

 

Edit:

Refactorirao sam  kod da ga napravim citljivijim, te da se aritmetika overloadanih operatora drzi iskljucivo na jednom mjestu.

Trik se sastoji u tome da se iskoristi mutabilni operator za zbrajanje i oduzimanje (ie. operatori  +=   i -=) te copy constructor.

Kako to rijesiti?

 

Prvo, C++ automatski generira copy-constructor, no cisto radi sigurnosti, bolje da mi to rijesimo na ruke. Definirajmo copy constructor:

    Vector(const Vector<Type, Size>& that) {
       for (int i = 0; i < Size; ++i) {
          data[i] = that.data[i];
       }
    }

 

Nakon toga, definirajmo operator koji mutira this objekt (na primjer +=):

    Vector<Type, Size>& operator+=(const Vector<Type, Size>& that) {
       for (int i = 0; i < Size; ++i)
          data[i] += that[i];
       return *this;
    }

 

Nakon toga, definirajmo ne-mutirajuci operator zbrajanja (+) koristeci copy constructor i assign-sum operator:

    Vector<Type, Size> operator+(const Vector<Type, Size>& that) const {
       Vector<Type, Size> result(*this);
       result += that;
       return result;
    }

 

Eto, uz malo promjena, postigli smo puno citljiviji kod, i fokusirali pogreske u aritmetici na samo jednom mjestu (operator+=). Takvom nacinu programiranja uvijek treba teziti - pozivati metode sto je cesce moguce, umjesto kopiranja slicnih dijelova koda.

Primjetite da operator+= pod obavezno vraca referencu na this, dok operator+ bez problema moze biti const metoda, jer ne mutira this objekt.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 27.9.2010 19:40 (Deus ex machina).
 
9 0 hvala 11
15 godina
neaktivan
offline
Testiranje Vectora

Hello!

 

Rezime proslog posta: napravili smo osnovnu templateiziranu Vector klasu, koja podrzava bilo koji tip podatka i bilo koji broj komponenti. Sredili smo nekoliko convenience konstruktora, copy konstruktor, te overloadali bazicne operatore: zbrajanje, mnozenje, oduzimanje i dijeljenje, i to u dvije varijante, mutirajucoj i ne-mutirajucoj.

 

Danas cemo upotpuniti Vector klasu implementirajuci Unit Test - nesto sto smo trebali napraviti _prije_ implementacije samog Vectora, no kasno sam se sjetio ukomponirati i taj aspekt.

Prvi post se promijenio da doda dependency na CppUnit, tako da je i to potrebno downloadati, compilirati, i pripremiti za rad. Ne da mi se jos pisati build skripte, pa cu se posluziti IDE-om. U vasem IDE-u potrebno je definirati include path na "src/main/cpp" i na "src/test/cpp".

 

Krenimo od startne klase, koja ce pokretati nas test suite. Unutar "src/test/cpp" prvo napravimo file "AllTestsRunner.cpp", koji ce biti executable za pokretanje svih testova unutar "src/test" direktorija. Takodjer, dodajmo direktorij "math" (dobra praksa je kopirati putanje iz "src/main"), te dodajmo file "VectorTest.hpp". Nas projekt sada izgleda ovako:

project-root
-src
  -main
   -cpp
    -math
      *Vector.hpp
  -test
   -cpp
      *AllTestsRunner.cpp
      -math
      *VectorTest.hpp
-target

 

Kod za AllTestsRunner nije nista posebno:

#include <iostream>

#include <cppunit/CompilerOutputter.h>
#include <cppunit/extensions/TestFactoryRegistry.h>
#include <cppunit/ui/text/TestRunner.h>

#include <math/VectorTest.hpp>

using namespace std;
using namespace CppUnit;
using namespace math;

int main(int argc, char** argv) {
    Test *suite = TestFactoryRegistry::getRegistry().makeTest();
    TextUi::TestRunner runner;
    runner.addTest(suite);
    runner.setOutputter(new CompilerOutputter(&runner.result(), cerr));

    bool wasSuccesful = runner.run();
    return wasSuccesful ? 0 : 1;
}

 

Eto, nekoliko includeova i par linija boilerplate koda za pokretanje testa, i dobra navika na kraju - ako program zavrsi u redu, potrebno je vratiti 0. Ako program zavrsi s greskom, treba vratiti pozitivni broj. Ako se drzimo ove prakse, moguce je jednostavno koristiti nas executable kroz komandnu liniju.

 

Iskoristit cu ovo mjesto da uputim na jos jednu dobru praksu:

"using namespace" direktivu se ne bi smjelo koristiti unutar header fileova - razlog tome je konfuzija oko namespaceova kad se svi headeri includaju naokolo. Sto se tice implementacije, sasvim je OK koristiti using koliko treba, jer se dependencyi ne ostvaruju preko implementacija.

 

VectorTest ce biti klasa koja naslijedjuje CppUnit::TestFixture. S obzirom da testiramo Vector klasu, potrebno je napraviti dependency:

#include <math/Vector.hpp>
#include <cppunit/extensions/HelperMacros.h>

class VectorTest: public CppUnit::TestFixture {
}

Zatim, slijedi inicijalizacija specificna za CppUnit, library se obilno sluzi macroima, koje je potrebno zapamtiti. Usput cemo pripremiti dva trodimenzionalna float Vector membera za koristenje u testovima. Primjetite da nakon definicije testa, potrebno ga je 'registrirati'.

#include <math/Vector.hpp>
#include <cppunit/extensions/HelperMacros.h>

class VectorTest: public CppUnit::TestFixture {
private:
    CPPUNIT_TEST_SUITE(VectorTest);
        // Test defs go here
    CPPUNIT_TEST_SUITE_END();

    math::Vector<float, 3> v1;
    math::Vector<float, 3> v2;

public:
};

CPPUNIT_TEST_SUITE_REGISTRATION(VectorTest);

Prvi test koji cu opisati je test za zbrajanje. Svaku test metodu potrebno je registrirati u "bloku" izmedju CPPUNIT_TEST_SUITE(<klasa>) i CPPUNIT_TEST_SUITE_END(), te naravno dodati metodu u klasu, ovako:

#include <math/Vector.hpp>
#include <cppunit/extensions/HelperMacros.h>

class VectorTest: public CppUnit::TestFixture {
private:
    CPPUNIT_TEST_SUITE(VectorTest);
        CPPUNIT_TEST(TestAddition);
    CPPUNIT_TEST_SUITE_END();

    math::Vector<float, 3> v1;
    math::Vector<float, 3> v2;

public:
    void TestAddition() {
    }
};

CPPUNIT_TEST_SUITE_REGISTRATION(VectorTest);

 

Slijedeci, i zadnji korak u pisanju testa, je naravno testiranje.

Testovi se pisu na slijedeci nacin: potrebno je definirati konstantu koja predstavlja rezultat operacije, te izvrsiti operaciju uz pomoc jednakih operanda, te spremiti rezultat zasebno. Zatim, uz pomoc CPP_ASSERT metoda, usporediti rezultat. CPP_ASSERT metoda prima bilo kakav boolean izraz, te se moze primjeniti generalnije nego CPPUNIT_ASSERT_EQUAL koji cemo mi koristiti. CPP_ASSERT_EQUAL prima dva argumenta umjesto jednog, te ih usporedjuje. Prednost ovog macroa je u ispisu, gdje u slucaju pogreske, test ispisuje obje vrijednosti, ocekivanu i izracunatu, automatski.
TestAddition implementacija:

    void TestAddition() {
       v1.SetAll(0.0F);
       v2.SetAll(1.0F);

       math::Vector<float, 3> v3(v1 + v2);
       CPPUNIT_ASSERT_EQUAL(1.0F, v3[0]);
       CPPUNIT_ASSERT_EQUAL(1.0F, v3[1]);
       CPPUNIT_ASSERT_EQUAL(1.0F, v3[2]);
    }

 

To je to. Ako ste uspjesno namjestili include pathove, moci cete compilirati AllTestsRunner i pokrenuti test, koji ce na cerr konzolu ispisati:

.
OK (1)

 

Evo jos test za multiplikaciju, a za oduzimanje i dijeljenje ostavljam na vjezbu.

Jos jedna napomena - ako budete pisali svoje testove, iako je tehnicki potrebno testirati sve operatore - spasit cemo sebi posla i NECEMO testirati mutirajuce operatore (+=, -=, etc), jer znamo da ne-mutirajuci operatori koriste mutirajuce.

Potvrdu da smo testirali sve te linije koda dobit cemo implementirajuci code-coverage report, koji cemo obraditi u nekom od buducih lekcija.

 

Test za mnozenje. Prije svega, treba ga registrirati u CPPUNIT blok:

    CPPUNIT_TEST_SUITE(VectorTest);
       CPPUNIT_TEST(TestAddition);
       CPPUNIT_TEST(TestMultiplication);
    CPPUNIT_TEST_SUITE_END();

Te onda implementirati metodu:

    void TestMultiplication() {
       v1.SetAll(2.0F);
       v2.SetAll(5.0F);

       math::Vector<float, 3> v3(v1 * v2);
       CPPUNIT_ASSERT_EQUAL(10.0F, v3[0]);
       CPPUNIT_ASSERT_EQUAL(10.0F, v3[1]);
       CPPUNIT_ASSERT_EQUAL(10.0F, v3[2]);
    }

 

 

Eto, to je to za danas.

Za one koji zele vjezbu, implementirajte testove za oduzimanje i dijeljenje te index-operator. Mozete testirati i ispravnost copy konstruktora, no u pravilu, bespotrebno je trositi vrijeme na pisanje testova koji testiraju metode koje u pravilu rade jednostavni assignment.

Opet, teoretski, potrebno je kroz test provuci svaku mogucu liniju koda, ali u praksi - to je trosenje vremena. Assignment primitiva uvijek radi za sigurno, a ako se assignaju custom tipovi podataka, onda ce se taj kod ionako proci u testovima tih tipova.

 

Cheers!

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 27.9.2010 19:40 (Deus ex machina).
 
8 0 hvala 11
15 godina
neaktivan
offline
CMake build skripta + finaliziranje Vectora

Hullo,

dan odsustva, pa eto danas cemo odraditi dvije stvari :-)
Prvo, bilo bi fino da mozemo izbuildati ono sto pisemo. Nekako, to je dosta korisno :-D Kao sto je napomenuto u prvom postu, posluzit cemo se odlicnim cmake. Nema smisla drzati tutorial iz CMakea ovdje, pa cemo odraditi to kroz listu koraka koji se moraju poduzeti.
Nulto, treba napraviti osnovni runnable file, tj. file koji ce biti glavni exe naseg tutoriala. Unutar src/main/cpp napravite file 'Tutorial1.cpp', i unutra napisite obicni hello world:
#include <iostream>

using namespace std;

int main(int argc, char** argv) {
    cout << "Hello world!" << endl;
    return 0;
}

Ovaj file je potreban tako da ne moramo u buducnosti mijenjati build skriptu, i odmah srediti build za production i za testove.
Nakon toga:

1. Normalno, instalirati cmake
2. Napraviti file CMakeLists.txt u rootu projekta
3. Iskopirati ovo u CMakeLists.txt
## The minimum version of cmake that we support
cmake_minimum_required(VERSION 2.6)

## Identify the project
project(scenegraph-tutorial)

## Make sure that we run tests with each build
## enable_testing()

## Flags that will be passed as arguments to the compiler
add_definitions(-g -O2 -fsigned-char -freg-struct-return -Wall -W -Wpointer-arith -Wcast-qual)

## Some custom variables
set(MAIN_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/src/main/cpp/Tutorial1.cpp")
set(TESTSUITE_EXECUTABLE "${CMAKE_CURRENT_SOURCE_DIR}/src/test/cpp/TestSuite.cpp")
file(GLOB_RECURSE SOURCES src/main/cpp/*.cpp)
file(GLOB_RECURSE TEST_SOURCES src/test/cpp/*.cpp)

## Fix lists to exclude main files
list(REMOVE_ITEM SOURCES ${MAIN_EXECUTABLE})
list(REMOVE_ITEM SOURCES ${TESTSUITE_EXECUTABLE})

## Set libs
set(LIBS GL GLU)
set(TEST_LIBS cppunit ${LIBS})

## Target executables
add_executable(scenegraph-tutorial ${MAIN_EXECUTABLE} ${SOURCES})
add_executable(testsuite ${TEST_EXECUTABLE} ${SOURCES} ${TEST_SOURCES})
add_test(testsuite testsuite)

## Linker data
target_link_libraries(scenegraph-tutorial ${LIBS})
target_link_libraries(testsuite cppunit ${TEST_LIBS})

## We specify the directories where are located the include files (include and lib) with the INCLUDE_DIRECTORIES directive
include_directories(src/main/cpp src/main/resources src/test/cpp)

4. Otvoriti direktorij u konzoli i utipkati
cmake .

Korak 4 dovoljno je napraviti samo jednom, nakon (uspjesnog) generiranja Makefileova, 'make' ce se potruditi da pozove cmake i regenerira sve promjene. Svaki IDE se moze konfigurirati da koristi externi build sistem, te je vas IDE potrebno konfigurirati da za Build poziva 'make', a za clean 'make clean'. Takodjer, moguce je pozivati make sa opcijom -j, koja ce vrtiti make na vise coreova, sto je preporucljivo jer dobrano ubrzava buildanje. Dakle, komande bi bile 'make -j' i 'make -j clean'.

Ako ste ovo uspjesno odradili, u project rootu nalazit ce se dva filea, 'scenegraph-tutorial' i 'testsuite'. Jedan pokrece nas hello world (u buducnosti test-aplikaciju za engine) a drugi pokrece test suite.


Kad je to rijeseno, idemo dopuniti nas vektor test, prije svega. U math::VectorTest klasu dodajmo testove za bitne vektorske operacije: duljinu vektora, normalizaciju, dot produkt i cross produkt. O tome se (opet) moze procitati na Wolfram Math, no ukratko:
- duljina vektora se kalkulira uz pomoc pitagorinog poucka
- normalizacija vektora svodi bilo koji vektor na jedinicni vektor, tj. vektor cija je duzina = 1
- dot produkt je operacija koja vraca skalar (float), te kalkulira kosinu izmedju dva vektora
- cross produkt je 'posebna' operacija u tome sto funkcionira iskljucivo na 3D vektorima, te vraca vektor koji je ortogonalan na dva vektora s kojima izvodimo cross

Sad kad znamo ove informacije, krenimo s testovima. Prvo cemo testirati duljinu. Za primjer, uzet cemo 3D vektor {3, 4, 5}, te uz pomoc kalkulatora izracunati duljinu (7.071067812).
Nakon toga, napisimo test koji provjerava da rezultat kalkulacije duljine iznosi upravo taj broj. Nemojte zaboraviti registrirati test u CPPUNIT bloku:
    void TestLength() {
       float values[] = { 3.0F, 4.0F, 5.0F };
       v1.Set(values);
       CPPUNIT_ASSERT_EQUAL(50.0F, v1.LengthSquared());
       const float squareOf50 = 7.071067812F;
       CPPUNIT_ASSERT_EQUAL(squareOf50, v1.Length());
    }

Sad, pokrenite build i provjerite da compiler puca jer nema metode koju pozivamo. Super, dodajmo metodu unutar math::Vector klase:
    float Length() const {
       return -1.0F;
    }

Pokrenimo build, provjerimo da compilira. Nakon toga pokrenite testsuite i provjerite da test puca, jer vrijednost koju ocekujemo nije vrijednost koju vracamo.
Ovaj korak se vjerojatno cini nepotreban, no bitan je zbog toga jer na ovaj nacin testiramo ispravnost samog testa. Neispravan test nije bas koristan :-D
Sad je vrijeme za implementaciju. Negdje pri vrhu filea dodajte
#include <cmath>
a negdje unutar klase:
    float Length() const {
       float lengthSquared = 0;
       for (int i = 0; i < Size; ++i) {
          lengthSquared += (data[i] * data[i]);
       }
       return sqrt(lengthSquared);
    }

Sad, razmislimo da cemo cesto usporedjivati duljine vektora, i tocna duljina nam u tim slucajevima nece biti bitna. Korijen koji vucemo na kraju je beskoristan u tim slucajevima, pa cemo ovu metodu rastaviti na dvije:
    float LengthSquared() const {
       float lengthSquared = 0;
       for (int i = 0; i < Size; ++i) {
          lengthSquared += (data[i] * data[i]);
       }
       return lengthSquared;
    }

    float Length() const {
       return sqrt(LengthSquared());
    }

Tako je bolje. Sad kad mozemo iskalkulirati duljinu, mozemo napraviti normalizaciju. Opet, kalkulator u ruke i izracunajte da vektor {1.0, 1.0, 0.0} ima duljinu 1.414213562, te onda izracunajte da 1.0 / 1.414213562 = 0.707106781. Sa tim brojevima, jednostavno je napisati prvo test:
    void TestNormalize() {
       float values[] = { 1.0F, 1.0F, 0.0F };
       v1.Set(values);

       v1.NormalizeLocal();
       const float lengthOfComponent = 0.707106792F;
       CPPUNIT_ASSERT_EQUAL(lengthOfComponent, v1[0]);
       CPPUNIT_ASSERT_EQUAL(lengthOfComponent, v1[1]);
       CPPUNIT_ASSERT_EQUAL(0.0F, v1[2]);
    }

Nakon toga ponovite sve one korake sa buildanjem i padanjem testa. Nakon toga, dodajmo kod za normalizaciju:
    void NormalizeLocal() {
       float len = Length();
       for (int i = 0; i < Size; ++i) {
          data[i] /= len;
       }
    }

Nazvali smo metodu NormalizeLocal i nije const jer zelimo kroz ime naznaciti da cemo modificirati 'this'.
Dodajmo dot produkt i cross product testove:
    void TestDot() {
       // Cosine of pi/4 should be 0.707106781
       float values[] = { 1.0F, 0.0F, 0.0F };
       float values2[] = { 1.0F, 1.0F, 0.0F };
       v1.Set(values);
       v2.Set(values2);

       v1.NormalizeLocal();
       v2.NormalizeLocal();

       const float cosineOfQuarterPi = 0.707106781;
       CPPUNIT_ASSERT_EQUAL(cosineOfQuarterPi, v1.Dot(v2));
    }

    void TestCross() {
       // Perpendicular to X and Z is Y
       float values[] = { 1.0F, 0.0F, 0.0F };
       float values2[] = { 0.0F, 0.0F, -1.0F };
       v1.Set(values);
       v2.Set(values2);

       v1.NormalizeLocal();
       v2.NormalizeLocal();

       math::Vector3f v3(v1.Cross(v2));
       CPPUNIT_ASSERT_EQUAL(0.0F, v3[0]);
       CPPUNIT_ASSERT_EQUAL(1.0F, v3[1]);
       CPPUNIT_ASSERT_EQUAL(0.0F, v3[2]);
    }

Te dodajmo implementacije:
    float Dot(const Vector<Type, Size>& that) const {
       float cosine = 0.0F;
       for (int i = 0; i < Size; ++i) {
          cosine += data[i] * that.data[i];
       }
       return cosine;
    }

    Vector<Type, 3> Cross(const Vector<Type, 3>& that) const {
       assert(Size == 3); // Works only for 3D vectors :-)

       float resultData[] = {0.0F, 0.0F, 0.0F};
       resultData[0] = ((data[1] * that[2]) - (data[2] * that[1]));
       resultData[1] = ((data[2] * that[0]) - (data[0] * that[2]));
       resultData[2] = ((data[0] * that[1]) - (data[1] * that[0]));
       Vector<Type, 3> result(resultData);
       return result;
    }


I to je to! Nasa Vector klasa je gotova. Onako kao secer na kraju, van klase, ali unutar namespacea, dodajte nesto sintaktickog secera, cisto da se nadje ;-)
typedef Vector<float, 2> Vector2f;
typedef Vector<float, 3> Vector3f;
typedef Vector<float, 4> ColorRGBA;

Ipak je ljepse i jednostavnije pisati Vector3f nego template ;-)


To je to za danas. Sutra cemo ustoliciti Object klasu sa nesto osnovnih funkcionalnosti, po uzoru na moderne OOP jezike.

Za lijencine koji nisu pratili od pocetka, downloadirajte Vector.hpp i VectorTest.hpp.
Cheers!


P.S. Vidim da se topic cita, ali cini se ko da se svi boje spammati. Slobodno pitajte pitanja ili javljajte ako nesto nije jasno.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 27.9.2010 19:41 (Deus ex machina).
 
7 0 hvala 9
16 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

CPPUNIT_ASSERT_EQUAL(1.0F, v3[0]);

 

Ne bi li floate trebalo uspoređivati dozvolivši neku malu pogrešku (recimo 6-7 redova veličine manju od reda veličine same vrijednosti, za single precision)?

 

Moj PC  
0 0 hvala 0
15 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
DigiMagic kaže...

CPPUNIT_ASSERT_EQUAL(1.0F, v3[0]);

 

Ne bi li floate trebalo uspoređivati dozvolivši neku malu pogrešku (recimo 6-7 redova veličine manju od reda veličine same vrijednosti, za single precision)?

 

Naravno! Macro to radi sam - testira floatove do (ako se dobro sjecam) 4 decimale.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put čet 9.9.2010 19:08 (Deus ex machina).
16 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea

OK... pogledao sam kratko dokumentaciju na webu, spominjao se samo operator ==, pa sam mislio... nema veze.

 

Razmišljao sam inače složiti neki svoj mali raytracing engine s fixed point brojevima, samo nikako naći vremena (a ni za što bih ga uopće korisno koristio Osmijeh), ovaj sad tutorial me podsjetio na to... Ako napišeš nešto o tome, neću se buniti Belji se

 

15 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
DigiMagic kaže...

OK... pogledao sam kratko dokumentaciju na webu, spominjao se samo operator ==, pa sam mislio... nema veze.

 

Razmišljao sam inače složiti neki svoj mali raytracing engine s fixed point brojevima, samo nikako naći vremena (a ni za što bih ga uopće korisno koristio Osmijeh), ovaj sad tutorial me podsjetio na to... Ako napišeš nešto o tome, neću se buniti Belji se

 

Pa, nakon sto uspostavimo scenegraph i neki loader, tako da uopce mozemo dobiti podatke za rendanje, bez beda ;-) Jedna od idejnih znacajki je abstraktni renderer, pa ne vidim razloga zasto ne bi implementirali svoj raytracer umjesto rasterizera :-)

Nego, zbog cega fixed-point brojevi?

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
16 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea

- sa floating pointom nije ništa posebno, već ih puno postoji

- imam ekipu u firmi koja bi mogla "pretočiti" softver u FPGA, pa da sve bude hardverski akcelerirano, uključujući i više paralelnih render coreova dok ima mjesta u čipu

 

Vidio sam diplomski rad jednog Nijemca na tu temu, renderer mu je bio vrlo primitivan, ali dobio je solidne slike na kraju uz performanse oko 1 pixel po clocku. Postoje i nekakvi artefakti, ne sjećam se imena, uglavnom zbog nekog zaokruživanja  zrake završe negdje bezveze umjesto na površini najbližeg objekta - toga ima manje s fixed pointom. Sa floating pointom bi definitivno izgubio i recimo 2 reda veličine na performansama, plus mogao bi staviti manje paralelnih jezgri jer bi svaka trošila više sklopova.

 

15 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
DigiMagic kaže...

Vidio sam diplomski rad jednog Nijemca na tu temu, renderer mu je bio vrlo primitivan, ali dobio je solidne slike na kraju uz performanse oko 1 pixel po clocku. Postoje i nekakvi artefakti, ne sjećam se imena, uglavnom zbog nekog zaokruživanja  zrake završe negdje bezveze umjesto na površini najbližeg objekta - toga ima manje s fixed pointom. Sa floating pointom bi definitivno izgubio i recimo 2 reda veličine na performansama, plus mogao bi staviti manje paralelnih jezgri jer bi svaka trošila više sklopova.

Ma to mi nema nikakvog smisla? Floating point ima gresaka u zaokruzivanju iskljucivo ako je renderer napisan tupasto, pa se ocekuje velika preciznost s obje strane tocke, recimo castas zraku od nule prema objektu koji je smjesten negdje na udaljenosti od 10 000 000 000 jedinica, a zelis preciznost na 0.000000001 decimalu. Prvo, tako nesto ne mozes ni vidjeti jer su pikseli puno puno veci, drugo to se ne radi tako, zato imas scenegraph da bi lokalizirao kalkuliacije u maleni segment i spasio greske u dijeljenjima :-)

Trece, nema nacina da sa fixed point brojevima i ostatcima dijeljenja iskalkuliras dijeljenja brze nego uz moderne reprezentacije floating pointova (http://en.wikipedia.org/wiki/IEEE_754-2008). Cak i da nesto tako uspijes (jer recimo, nisi napravio fp unite na FPGA-u), greske u dijeljenjima bi bile monstrouzne, osim u slucajevima da si koristio ogromne brojeve - sto mozes i sa floating pointovima, uz vecu preciznost.

 

Rauzmijem tvoju zelju da probas nesto originalno, ali floating point se koristi u 3D grafici s razlogom :-) Cheers.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pet 10.9.2010 19:21 (Deus ex machina).
15 godina
neaktivan
offline
Root hijerarhije - Object

Danas cemo, kao za petak, sve jednostavnosti srediti :-)

Uvest cemo genericki Object kao root naseg frameworka.
Nekoliko tocaka koje genericki root mora zadovoljavati:
1. Sposobnost testiranja istovjetnosti s drugim objektima (tj. Equals metoda)
2. Sposobnost ispisivanja na stdout (tj. ToString metoda)
3. Sposobnost da se numericki prikaze identitet (tj. HashCode metoda)
4. Najosnovniji start za rucni RTTI (ie. refleksija)

Poboljsamo to sa nekoliko convenience operator overloada, ToDebugString koji se ne moze overloadati i to je to. RTTI rijesit cemo, za pocetak, preko obicne string metode koja vraca puno ime klase (namespace i ime). Metodu cemo napraviti pure virtual, tako da isforsiramo makar direktne subklase da je implementiraju i ujedno onemogucimo instanciranje same Object klase. Takodjer, includat cemo <cassert> tako da jednostavno omogucimo i ohrabrimo assertanje koda kroz cijelu bazu.
S obzirom da ce se Object nasljedjivati svugdje direktno ili indirektno, rastavit cemo ga u header i source, tako da ubrzamo kompilaciju jer ce se Object rijetko mijenjati.

Dodajmo novi direktorij u nas project root, i nazovimo ga general. Unutar tog direktorija, kreirajmo "Object.hpp" i "Object.cpp". Nas project tree sada izgleda ovako (pogotovu nakon sto sam se opametio i poceo koristiti 'tree'):
|-- CMakeLists.txt
|-- src
    |-- main
    |   |-- cpp
    |   |   |-- Tutorial1.cpp
    |   |   |-- general
    |   |   |   |-- Object.cpp
    |   |   |   |-- Object.hpp
    |   |   `-- math
    |   |       `-- Vector.hpp
    `-- test
        `-- cpp
            |-- TestSuite.cpp
            `-- math
                `-- VectorTest.hpp

Unutar header filea, uspostavimo nas novi namespace (general) i interface za Object:
#ifndef GENERAL_OBJECT_HPP_
#define GENERAL_OBJECT_HPP_

#include <string>
#include <cassert>

namespace general {

class Object {
public:
    virtual bool Equals(const Object& other) const;
    virtual bool operator==(const Object &other) const;
    virtual bool operator==(const Object *other) const;
    virtual std::string ToString() const;
    virtual int HashCode() const;
    std::string ToDebugString() const;

    virtual std::string GetClassName() const = 0;

private:
    friend std::ostream& operator<<(std::ostream& os, const Object& object) {
       os << object.ToString();
       return os;
    }
};
}  // namespace general

#endif /* OBJECT_HPP_ */

Implementacija je vrlo jednostavna:
#include <sstream>
#include <iostream>
#include "Object.hpp"

using namespace general;
using namespace std;

bool Object::Equals(const Object& other) const {
    return this == (&other);
}

bool Object::operator==(const Object &other) const {
    return Equals(other);
}

bool Object::operator==(const Object *other) const {
    return Equals(*other);
}

std::string Object::ToString() const {
    return ToDebugString();
}

int Object::HashCode() const {
    return (int) this;
}

std::string Object::ToDebugString() const {
    std::ostringstream os;
    os << GetClassName() << "@" << this;
    return os.str();
}


Vratimo se nasem Vectoru i nasljedimo Object klasu, te implementirajmo GetClassName() metodu:
    virtual std::string GetClassName() const {
       return "math::Vector";
    }

NEMOJTE overridati operatore jednakosti; operator jednakosti NE SMIJE biti tolerantan prema floatovima (dakle, implementacija 'return (x - y) < TOLERANCE') se NE SMIJE koristiti. S obzirom da je floatove nemoguce deterministicki komparirati za jednakost, override se moze napraviti iskljucivo ukoliko je Type integer ili tip koji wrappa integer na neki nacin - to znaci da ce kompariranje Vektora ovisiti o tipu vektora, sto je losa praksa.

To je to. U ponedjeljak cemo razviti osnovnu Mesh klasu kao uvod u scenegraph.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 27.9.2010 19:41 (Deus ex machina).
 
4 0 hvala 6
14 godina
odjavljen
offline
[Tutorial] Izrada 3D/scenegraph enginea

Deus sam sa sobom pričaSmijehSvaka čast na impresivnom znanju, žalosno je vidjeti da baš nema ozbiljnih korisnika koji kad vide kod nezaboli ih glava.

 

Osobno nisam ni ukupno rečenice pročitao, no čim uhvatim vremena budem provjerio kod, pa cu sebi zadati domaci uradak da napišem isit kod ali na neki drugi način ;)

One death is tragedy, but million deaths is statistics - Joseph Staljin [One to all Units Let's toast some russians - By James Gastovski ] // OFP Fan
Moj PC  
0 0 hvala 0
15 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Koristim inerciju dok me ne prodje volja :-D

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
 
1 0 hvala 1
16 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
Deus ex machina kaže...

Ma to mi nema nikakvog smisla? Floating point ima gresaka u zaokruzivanju iskljucivo ako je renderer napisan tupasto, pa se ocekuje velika preciznost s obje strane tocke, recimo castas zraku od nule prema objektu koji je smjesten negdje na udaljenosti od 10 000 000 000 jedinica, a zelis preciznost na 0.000000001 decimalu. Prvo, tako nesto ne mozes ni vidjeti jer su pikseli puno puno veci, drugo to se ne radi tako, zato imas scenegraph da bi lokalizirao kalkuliacije u maleni segment i spasio greske u dijeljenjima :-)

Trece, nema nacina da sa fixed point brojevima i ostatcima dijeljenja iskalkuliras dijeljenja brze nego uz moderne reprezentacije floating pointova (http://en.wikipedia.org/wiki/IEEE_754-2008). Cak i da nesto tako uspijes (jer recimo, nisi napravio fp unite na FPGA-u), greske u dijeljenjima bi bile monstrouzne, osim u slucajevima da si koristio ogromne brojeve - sto mozes i sa floating pointovima, uz vecu preciznost.

 

Rauzmijem tvoju zelju da probas nesto originalno, ali floating point se koristi u 3D grafici s razlogom :-) Cheers.

Poslat ću link idući tjedan, mislim da ga imam spremljenog na poslu.

 

Nisam ja protiv floata, znam da su univerzalniji i precizniji i sve, da moram napraviti nešto zaozbiljno naravno da bih ih koristio, ali svejedno zanima me vidjeti koja su ograničenja fixed pointa...

 

14 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

A di je ono malo datelja oko arhitekture? Smijeh

Možda malo o tome na koji način renderer zapravo povezuje podatke iz scenegraph-a .

 

Na jednoj strani imamo scene objekte, koji imaju svoju geometriju, pa zatim materijale i na kraju efekte.

To su ključne stvari svih 3D engine-a.

Kako je već u samom efektu definirano što je potrebni minimum za rad efekta kako postići da renderer zna koje parametre efekta treba postaviti, kako znati odakle se ti parametri čitaju, kako generički materijal pridružen nekom scen objektu proslijediti do nekok efekta i slč.

Mislim da sam scengraph nije nešto presudno i bilo kakva lista može poslužiti. Odnos effekt-materijal-geometrija i kako to generički posložiti je kritičan.

 

Možda sam promašio temu?

Ako je cilj demonstrirati samo scenegraph i neki plug-in renderer koji "emulira" fixed function pipeline onda je ovo navedeno naravno nepotrebno.

 

Eto samo par mojih milsi prije jutarnje kave...možda se nakon nje razbistre ...

Poz

 

 

Na svijetu postoje samo 10 vrsti ljudi: oni koji znaju te oni koje ne znaju binarno
 
2 0 hvala 1
16 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Svaka čast i izdrži od kraja (relativnog). Odlično objašnjeno.

Pravi znak inteligencije nije znanje, već mašta - Albert Einstein
Moj PC  
5 0 hvala 1
14 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Neosporna je kvaliteta i kvantiteta znanja autora, ali osobno mislim da je pogrešno fokusirana, naime ono koliko sam skužio je da ovdje autor želi napraviti tj napraviti i objasniti izradu scenegraph-a i to na jako kvalitetan način objašnjava,  programerske tehnike koje primjenjuje u radu, koje su koliko vidim  jako kvalitetno objašnjene i primjenjive na drugim poljima ne vezanim za ovu temu, ali da se vratim na bit ovog posta tj onog što želim reć , a ne da ispadne da pišem hvalospjevove autoru :) , nego moj prijedlog autoru čisto iz respekta prema njegovom znanju i načinu na koji isto može prenjet na druge, a vjerujem da mu je to i cilj jer inaće ne bi ovako nešto i radio , je uvod u korištenje već postojećih sustava ili kolekcija koda, npr. svi znamo da je već duže vremena dostupan kod za Quake 2 te bi po mome mišljenju korisnije bilo objađnjenje funkcioniranja nekog game engine-a kroz već provjereni primjer te kako sam rekao za koji već i postoji kolekcija materijala besplatno dostupnih. Tako recimo objasniti recimo kroz Doom engine, ili Quake 2 ( što je u biti Doom engine u srži samo sa poboljšanjim render funcijama ), kako se upravlja objektima u engineu, što je to render engine i kako se manifestira, kakko je rješena umjetna inteligencija, koji render API koristi i kakko je implementiran, kako učitava mesheve, kako funcionira server-client sistem u game engine-u, kako funkcionira fizika, itd.....

Eto ovo je moja sugestija autoru.

 

 

http://graffiti-jam.blogspot.com/
 
0 0 hvala 1
15 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Svaka čast autore! Svjetski post! Nisan shvatio ništa! Smijeh

 
6 0 hvala 0
14 godina
odjavljen
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
pod009 kaže...

Nisan shvatio ništa!  Smijeh

  Heheheheheh

 

A kladim se da nitko ziv nije shvatio kompletno (osim Deus-a). Dobro možda tu i tamo koji programer na forumu, al kad mlad čovjek vidi ovaj kod padne mu mrak na oči.

One death is tragedy, but million deaths is statistics - Joseph Staljin [One to all Units Let's toast some russians - By James Gastovski ] // OFP Fan
Poruka je uređivana zadnji put pon 13.9.2010 6:54 (Weky).
16 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
enriko.riba kaže...

A di je ono malo datelja oko arhitekture? Smijeh

Možda malo o tome na koji način renderer zapravo povezuje podatke iz scenegraph-a .

 

Na jednoj strani imamo scene objekte, koji imaju svoju geometriju, pa zatim materijale i na kraju efekte.

To su ključne stvari svih 3D engine-a.

Kako je već u samom efektu definirano što je potrebni minimum za rad efekta kako postići da renderer zna koje parametre efekta treba postaviti, kako znati odakle se ti parametri čitaju, kako generički materijal pridružen nekom scen objektu proslijediti do nekok efekta i slč.

Mislim da sam scengraph nije nešto presudno i bilo kakva lista može poslužiti. Odnos effekt-materijal-geometrija i kako to generički posložiti je kritičan.

 

Ovo bi bilo korisno...

woodgamesfx kaže...

Neosporna je kvaliteta i kvantiteta znanja autora, ali osobno mislim da je pogrešno fokusirana, naime ono koliko sam skužio je da ovdje autor želi napraviti tj napraviti i objasniti izradu scenegraph-a i to na jako kvalitetan način objašnjava,  programerske tehnike koje primjenjuje u radu, koje su koliko vidim  jako kvalitetno objašnjene i primjenjive na drugim poljima ne vezanim za ovu temu, ali da se vratim na bit ovog posta tj onog što želim reć , a ne da ispadne da pišem hvalospjevove autoru :) , nego moj prijedlog autoru čisto iz respekta prema njegovom znanju i načinu na koji isto može prenjet na druge, a vjerujem da mu je to i cilj jer inaće ne bi ovako nešto i radio , je uvod u korištenje već postojećih sustava ili kolekcija koda, npr. svi znamo da je već duže vremena dostupan kod za Quake 2 te bi po mome mišljenju korisnije bilo objađnjenje funkcioniranja nekog game engine-a kroz već provjereni primjer te kako sam rekao za koji već i postoji kolekcija materijala besplatno dostupnih. Tako recimo objasniti recimo kroz Doom engine, ili Quake 2 ( što je u biti Doom engine u srži samo sa poboljšanjim render funcijama ), kako se upravlja objektima u engineu, što je to render engine i kako se manifestira, kakko je rješena umjetna inteligencija, koji render API koristi i kakko je implementiran, kako učitava mesheve, kako funcionira server-client sistem u game engine-u, kako funkcionira fizika, itd.....

Eto ovo je moja sugestija autoru.

 

 

Ne. Nikako.

 

Možemo s dvije strane krenuti.

Ako krenemo s te o Q2 engineu, onda je važno napomenuti da je to prastara stvar. Q2 (ma i Q3) enginei su prastari vrlo specijalizirani BSP enginei, puni hackova, optimizacija i cca. 10x više nerazumljivi od bilo čega modernijeg, a pogotovo nečega što se piše u edukacijske svrhe.

S druge strane, Deus već uvodi ono što je potrebno - dakle, ne izmišlja toplu vodu, već koristi ono što se može i što ima smisla.

 

Svakako ne, a pogotovo ne Tech.

 

Pravi znak inteligencije nije znanje, već mašta - Albert Einstein
16 godina
protjeran
offline
[Tutorial] Izrada 3D/scenegraph enginea

Bili bi lijepo kada bi se kod mogao izvući iz  subverziona i da je checkinan  onako po fazama. Mislim da bi blo lakše pratiti tutorija. I jedva čekam abstraktni renderer :-). BTW. mislim da je ovo prvi tutorijal na forumu kojem jedva čekam nastavak. Deus nemoj sada odustati Namigiva.

Programko http://programko.bloger.hr
 
4 0 hvala 1
14 godina
odjavljen
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
woodgamesfx kaže...

Neosporna je kvaliteta i kvantiteta znanja autora, ali osobno mislim da je pogrešno fokusirana, naime ono koliko sam skužio je da ovdje autor želi napraviti tj napraviti i objasniti izradu scenegraph-a i to na jako kvalitetan način objašnjava,   programerske tehnike koje primjenjuje u radu, koje su koliko vidim   jako kvalitetno objašnjene i primjenjive na drugim poljima ne vezanim za ovu temu, ali da se vratim na bit ovog posta tj onog što želim reć , a ne da ispadne da pišem hvalospjevove autoru :) , nego moj prijedlog autoru čisto iz respekta prema njegovom znanju i načinu na koji isto može prenjet na druge, a vjerujem da mu je to i cilj jer inaće ne bi ovako nešto i radio , je uvod u korištenje već postojećih sustava ili kolekcija koda, npr. svi znamo da je već duže vremena dostupan kod za Quake 2 te bi po mome mišljenju korisnije bilo objađnjenje funkcioniranja nekog game engine-a kroz već provjereni primjer te kako sam rekao za koji već i postoji kolekcija materijala besplatno dostupnih. Tako recimo objasniti recimo kroz Doom engine, ili Quake 2 ( što je u biti Doom engine u srži samo sa poboljšanjim render funcijama ), kako se upravlja objektima u engineu, što je to render engine i kako se manifestira, kakko je rješena umjetna inteligencija, koji render API koristi i kakko je implementiran, kako učitava mesheve, kako funcionira server-client sistem u game engine-u, kako funkcionira fizika, itd.....

Eto ovo je moja sugestija autoru.

 

 

  Kao što naxeem reče, taj code iz Quake Engine-a (kompletnog, sve do Q4 koji je baziran na DooM 3 Tehnologij) je;

 

  a) Prestar. Fixni Pipeline + GL, ako ćeš danas učiti svakako se kreće odmah od programbilnih shadera, jer čak ni DX10 više nepodrzava FF Pipeline. Osim toga, tona drugih stvari u tom engineu su stari pola tisućljećja.

  b) Užasno je sam code neuredan; Sve je zbrda zdola, ali na kraju čudno brzo i efikasno na ekranu.

 

Definitivno što dalje od Quake Engine-a, daleko daleko lakši primjera ima na tržištu. Taj engine je bio namjenjen samo id softwareu i njegovim igrama, a takve igre su koncept soba-tunel-soba-pokupi health, dakle užasno zatvoreni prostori - Korištena BSP tehnologija za optimizaciju takvih slučajeva.

 

Isto tako i Q1, Q2, Q3, sve je to samo nadograđivani engine tehnologijama koje su u to vrijeme bile aktualne, Q4 je nešto sasvim drugo iako i on vjerovatno daleko kompleksnij nego bilo kakav primjer za početnike.

 

Taj engine je namjenjen samo id softwareu, i eto tko ima želju da kupi pa ga licencira za svoju igru.

 

 

 

One death is tragedy, but million deaths is statistics - Joseph Staljin [One to all Units Let's toast some russians - By James Gastovski ] // OFP Fan
Poruka je uređivana zadnji put pon 13.9.2010 14:04 (Weky).
14 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Sve štima kaj pišete, ok povlačim sugestiju iz razloga što je prema vama Q2 = sranje + zastario + ( smrdi ) :)
OK šalu na stranu mislim da je svima jasno da sam Q2 dao samo za primjer što je jedan od engine-a za koji je dostupan source i materijali na web-u,isto tako mogao sam spomenuti Irrlicht , što je "moderan" engine, naravno sam autor če odlučiti o čem i što če pisati

http://graffiti-jam.blogspot.com/
Poruka je uređivana zadnji put pon 13.9.2010 14:41 (woodgamesfx).
 
0 0 hvala 0
14 godina
odjavljen
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
woodgamesfx kaže...

Sve štima kaj pišete, ok povlačim sugestiju iz razloga što je prema vama Q2 = sranje + zastario + ( smrdi ) :)

  Pa dobro nemoze se reci da je bio sranje. Bio je vrhunski u svoje vrijeme. Ali to vrijeme je bilo prije 12 godina. Tehnologija se strašno mjenja, a pogotovo kroz 12 godina.

 

Staro, teško, danas neupotrebljivo.

 

U svoje vrijeme vrhunski tech.

One death is tragedy, but million deaths is statistics - Joseph Staljin [One to all Units Let's toast some russians - By James Gastovski ] // OFP Fan
14 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
Weky kaže...
woodgamesfx kaže...

Sve štima kaj pišete, ok povlačim sugestiju iz razloga što je prema vama Q2 = sranje + zastario + ( smrdi ) :)

  Pa dobro nemoze se reci da je bio sranje. Bio je vrhunski u svoje vrijeme. Ali to vrijeme je bilo prije 12 godina. Tehnologija se strašno mjenja, a pogotovo kroz 12 godina.

 

Staro, teško, danas neupotrebljivo.

 

U svoje vrijeme vrhunski tech.

 

Da ali ne znam jel znaš da imaš par projekata izrađenih za Android temeljenih na Q2 kodu :), Q2 je mila majka za ovakove projekte trenutno , naravno za one koji imaju živaca i volje portat isto :)

 

Nego da se vratim OnTopic interesira me dal če se za unos objekata koristiti neki "otvoreni" format ( tipa obj ) ili planiraš raditi svoj mesh format. Koliko sam skužio render API će bit OpenGL, ako neće bit problem dal češ imat volje modulirat isti za OpenGL ES pošto bi onda kod bio prenosiv i na mobilne platforme ?

 

 

 

 

http://graffiti-jam.blogspot.com/
16 godina
protjeran
offline
[Tutorial] Izrada 3D/scenegraph enginea

1) Quake 2 je pisam u c-u 2) nema abstraktin render layer 3) ne koristi shadere 4) ne postoje post processing efekti 5) kod je jako nečtitljiv 6) netko tko može taj kod čitati sigurno mu ne treba tutorijal 7) sve migracije quake 2 svode se na pisanje wraper klasa 8) molim vas nemojte trovati ovaj tutorijal nepotrebnim komentarima

Programko http://programko.bloger.hr
 
1 0 hvala 0
15 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Mama mia, hvala svima na komentarima i pohvalama i najvise na sugestijama i konstruktivnim kritikama.

Vecinu spomenutih stvari koje ljude interesira obradit cemo s vremenom, ja namjerno idem jako polako i pokusavam objasniti postupke zato jer po puno tutoriala vidim da autor objasnjava kako je nesto napravio, ali ne i zasto. Po meni, "kako" nije tako bitno jer kod prica sam za sebe, i kao sto vecina vas vidi ne trudim se previse komentirati jednostavne linije koda - ali se potrudim objasniti zasto bi nesto trebalo funkcionirati kako funkcionira. Korisno je vidjeti da razvoj cak i bazicnih stvari trazi vremena i da dok covjek dodje do nivoa da ima stabilan engine na kojem moze isprobati neki image-space effect (recimo Bloom ili HDR), treba prvo proci osnove.

 

Da odgovorim na par pitanja, format za unos koji sam planirao koristiti je Collada 1.4, iz vise razloga:

- format je XML, dakle ASCII, dakle citljiv ljudskom oku i moze se napisati parser bez da trazim neku specifikaciju, a postoje vrlo brzi XML parseri za C++

- Blender dolazi po defaultu sa Collada 1.4 exporterom

- Collada je vrlo fleksibilni format, i ima odlicno (iako kompleksno) dizajniran model, sto nam omogucava da u pocetku parsiramo samo osnovno (pozicija vertexa) i provjerimo da li radi, pa onda prosirujemo parser i dodajemo mogucnosti

 

Sto se tice samog scenegrapha, kako je enriko.riba tocno uocio, trenutno nisam izgradio niti jednu klasu - osim Vectora ;-) Iako se Vector cini kao obicna matematicka klasa, on je baza za sve ostale scenegraph objekte, jer uz pomoc vektora definiramo skoro svaki podatkovni element jednog 3D objekta, poziciju, boju, UV, skaliranje, pa cak i rotaciju. Plan je uvesti Quaternione kasnije i u pocetku koristiti axis/angle par.

 

Sto se tice Wekyevog komentara, svatko je slobodan upitati sto god zeli i probat cu odgovoriti ili makar uputiti na neki materijal. Ovaj tutorial je beskoristan ako ne razumijete _zasto_ nesto funkcionira, i ako samo kopirate kod lijevo-desno u nadi da ce raditi. Probat cu objasniti cak i matematiku uz par slika ako je potrebno.

 

Programko, imam trenutno lokalni SVN na kojem vrtim projekt, odlicna je ideja da verzioniram sve na nekom javnom SVN-u. Nisam siguran da bi google code podrzao ovakav projekt, imas li neku ideju za javni source control?

 

Woodgamesfx, dobar je prijedlog. Po mojem iskustvu, vecina 'stabilnih' 3D enginea (Ogre, Irrlicht, do neke mjere cak i Sauerbraten) imaju vrlo slicne koncepte. Pretpostavljam da ce netko tko prodje ovaj tutorial razumjeti osnove 3D scenegraph enginea, te onda naravno skinuti Ogre ili Irrlicht i krenuti prckati po njemu, i iz iskustva shvatiti kako cijela stvar 'stima'. Ono sto pokusavam objasniti je zbog cega recimo postoji koncept 'scenegrapha' i zasto je scenegraph najbolje prikazati kao stablo. Takodjer, zelim pokusati razbiti neke 'ideje' o tome kako je OOP spor i dokazati da bi uvijek i prije svega, citljiv, razumljiv kod trebao biti bitniji od mikro-optimizacija.

Engine koji razvijemo kroz ovaj tutorial kasnije moze napredovati, biti obogacen featurima, ali u globalu nije mi cilj napraviti nesto sto ce imati ikakvu ozbiljnu primjenu u nekoj produkciji - takva vrsta tutoriala bi, naravno, bila odlican doprinos, pogotovu jer moderni enginei dolaze sa velikim skupom alata. No imam osjecaj da su te teme vec puno bolje obradjene okolo po internetu (recimo za UDK, Crytek).

Sto se tice renderera, nisam bas radio s OpenGL ES, ali renderer je po arhitekturi abstraktan, dakle idealno ce se scenegraph moci renderirati preko bilo kakvog backend-a.

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
Poruka je uređivana zadnji put pon 13.9.2010 17:22 (Deus ex machina).
 
3 0 hvala 2
16 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea

github!

Pravi znak inteligencije nije znanje, već mašta - Albert Einstein
15 godina
neaktivan
offline
RE: [Tutorial] Izrada 3D/scenegraph enginea
naxeem kaže...

github!

Vidis, vidis, a pred nepunih 3 tjedna sam si kontao kako bi probao taj git malo istraziti :-D

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
15 godina
neaktivan
offline
[Tutorial] Izrada 3D/scenegraph enginea

Eh da, skoro sam zaboravio, pa cu zaspamati s doublepostom zbog ovoga:

 

 

S R E T A N   N A M   S V I M A   D A N   P R O G R A M E R A !!!!

 

Hugs & kisses and succesfull compiles, everyone :-)

http://en.wikipedia.org/wiki/Programmers%27_Day

I have got no money, I have got no power, I have got no fame... I have my strong beliefs...
 
6 0 hvala 5
Nova poruka
E-mail:
Lozinka:
 
vrh stranice