Stratos: Punto de Encuentro de Desarrolladores

¡Bienvenido a Stratos!

Acceder

Foros





Motor3D y dx_lib32: El renacimiento

Iniciado por Manu343726, 24 de Febrero de 2012, 09:29:23 AM

« anterior - próximo »

Manu343726

HOLA!!!
Hacía mucho tiempo que no me pasaba por aquí. No me gusta dar malas noticias XD.
Así que hoy, que tengo unas muy buenas, he decidido soltarlas cuanto antes:
Tras trastear durante mucho tiempo con los archivos de configuración de Visual Studio conseguí arreglar mi problema con dx_lib32. Así que ya la he integrado totalmente en la versión 2.0 de mi Motor3D. Aquí os dejo algunas capturas:

Test de renderizado sobre dx_lib32:


Test de colisión mediante octree:


Test de intersección recta-octree:


La versión 2.0 tiene las siguientes nuevas características:
- Reorganización completa de la biblioteca mediante una ordenada jerarquía de clases y espacios de nombres.
- Álgebra matricial y resolución de sistemas de ecuaciones.
- Objetos matemáticos para tratamiento de geometría en 2D y 3D, incluido cálculo de intersecciones, posiciones relativas distancias, etc.
- Camara3D y 2D para un cómodo tratamiento de la escena (Con proyección cónica, por fin, en el caso de la 3D), incluyendo frustum clipping, viewport clipping y backface culling.
- Características de material para el shader a nivel de polígono (En el caso de la primera versión, dichas características eran comunes a todos los objetos)
- Reorganización del cálculo de transformaciones, incluyendo rotación mediante cuaterniones (Adiós al gimbal lock!).
- Detección de colisiones mediante AABBs y octrees (beta XD).

Como es lógico, el motor gráfico y el render los desarrollo únicamente por entretenerme, pero las estructuras de datos y las herramientas de cálculo si que son de utilidad. Cualquiera que desee utilizar la librería es libre de hacerlo.

Espero que os guste!!!

XÑA

Ostras, yo siempre he echado de menos tener una librería hecha por mi de toda la parte de geometría!!! Siempre he usado sistemas de terceros, y la verdad es que me fastidiaba mucho.

No he visto el link para echarle un vistazo....  ;)

[EX3]

Brutal, VBManu!  :o

Para cuando un video en movimiento? Que tengo que presumir en la pagina de Facebook, Google+ y Twitter de como exprimis mi libreria al 110% :D

Buen trabajo :)

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Hechelion

Qué bueno que lograste arreglar el problema.

Las imágenes molan un montón, tal como dice Ex3, a ver cuando sale un vídeo.

Manu343726

#4
Muchas gracias por los comentarios!
He de admitir que después de tanto trabajo es una gran satisfacción que alguien valore el esfuerzo...

Sobre lo de los videos, estoy trabajando en ello. Tengo el mismo problema de parpadeo que ya comenté en la versión 1.0. Ya le encontraré alguna solución. Ah por cierto, lo del cubo con texturas no se me ha olvidado....XD

Tengo un par de dudillas:
Mi principal problema es que el render actualiza la lista de poliedros a redibujar más rápido que el bucle de renderizado, y si tengo actualizaciones continuadas (Como una animación, por ejemplo) no llega a refrescar la pantalla. Se redibuja al parar la animación.
Alguna idea???

Otra cosa: Predeterminadamente, donde se define el centro de dibujo? Supongo que como siempre en la esquina superior izquierda, pero no estoy seguro porque me ha dado problemas.

Gracias

PD: Si me facilitáis alguna dirección de correo, publico la carpeta del proyecto de mi dropbox, así tenéis acceso al código y los cambios automáticamente. Si no por ahora: http://www.mediafire.com/?fw47qr1y4xgcpvf
Si no me equivoco, todos los proyectos de prueba son sobre GDI+, tuve que reescribir el render de dx_lib32 desde cero. Pero por lo menos sirve para echar un vistazo.

[EX3]

Cita de: VBManu en 25 de Febrero de 2012, 01:14:51 AM
Tengo un par de dudillas:
Mi principal problema es que el render actualiza la lista de poliedros a redibujar más rápido que el bucle de renderizado, y si tengo actualizaciones continuadas (Como una animación, por ejemplo) no llega a refrescar la pantalla. Se redibuja al parar la animación.
Alguna idea???
Si no he entedido mal, dices que solo se renderiza el la animacion o el proceso de actualizacion que sea una vez que ha finalizado. Lo que debes hacer es actualizar cada paso de la actualizacion con un periodo de espera en cada paso del render, algo como esto:

Bucle Render
Si Cronometro >= 1000 Entonces
AnimacionFotograma + 1

Si AnimacionFotograma = UltimoFotograma Entonces
AnimacionFotograma = 0
Fin Si

Cronometro.Reiniciar()
Fin Si

DibujarFotograma(AnimacionFotograma)

DibujarEscena()
Fin Bucle


No se si se entiende el pseudocodigo. La idea es que controles los pasos de la animacion mediante un lapso de tiempo y dibujar continuamente el fotograma que toque. Aqui por ejemplo vas dibujando una animacion actualizando el fotograma a dibujar cada 1000 milisegundos. De esta forma, el render siempre se dibuja y muestra cada paso de la animacion. Esto obviamente lo puedes aplicar a movimientos o cualquier proceso continuo de actualizacion.

Cita de: VBManu en 25 de Febrero de 2012, 01:14:51 AM
Otra cosa: Predeterminadamente, donde se define el centro de dibujo? Supongo que como siempre en la esquina superior izquierda, pero no estoy seguro porque me ha dado problemas.
Si hablamos de primitivas como las cajas o los trapezoides, su centro u origen es el primer vertice que defines. Estos objetos graficos no se ven afectados por DEVICE_SetDrawCenter(). Para los sprites, por defecto es su coordenada 0,0, la esquina superior izquierda.

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

XÑA

Esto está mal :(

No tienes que reiniciar el cronómetro. Pq el valor a lo mejor no es 1000, es 1100. Y por tanto, al reiniciar, estás perdiendo estos 100. Lo que tienes que hacer es:

Cronometro=Cronometro-1000

[EX3]

Cita de: XÑA en 25 de Febrero de 2012, 07:17:41 PM
Esto está mal :(

No tienes que reiniciar el cronómetro. Pq el valor a lo mejor no es 1000, es 1100. Y por tanto, al reiniciar, estás perdiendo estos 100. Lo que tienes que hacer es:

Cronometro=Cronometro-1000
Mal por que? Ya se hacia asi en Div Games Studio, los cronometros de dx_lib32 y los que tengo implementados en XNA funcionan asi. Ellos devuelven el lapso de tiempo entre el valor de tiempo inicial que guardaron al crearse o reiniciarse y el valor de tiempo actual. Asi llevo trabajando años sin ningun problema y asi lo he visto hacer en multitud de sitios.

Funciones en dx_lib32:
// Devuelve el valor actual del cronometro:
Public Function TIMER_GetValue(Timer As Long) As Long
    TIMER_GetValue = GetTickCount() - m_Timer(Timer)
End Function

// Reinicia el cronometro:
Public Sub TIMER_Reset(Timer As Long)
    m_Timer(Timer) = GetTickCount()
End Sub


Version .NET:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace TLSA.Engine.Tools
{
    /// <summary>
    /// Cronometro independiente de tiempo.
    /// </summary>
    public class Timer
    {
        private int delta;

        public Timer()
        {
            this.Reset();
        }

        /// <summary>
        /// Devuelve los milisegundos transcurridos desdel ultimo reinicio o desde que se creo el cronometro.
        /// </summary>
        public int Value { get { return ((int)DateTime.Now.Ticks - delta) / 10000; } }

        /// <summary>
        /// Reinicia el cronometro a 0.
        /// </summary>
        public void Reset()
        {
            delta = (int)System.DateTime.Now.Ticks;
        }
    }
}


Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

blau

mmm... yo estoy con xña... ^_^'

lo más correcto para mi es quitarle la duración, y no ponerlo a cero.

[EX3]

#9
No se resetea el cronometro si no se que actualiza al valor actual del reloj de la CPU. La lectura del valor de tiempo del cronometro no es otra que restar el tiempo actual al tiempo de cuando se reseteo.

Si Cronometro >= 1000 Entonces
AnimacionFotograma + 1

Si AnimacionFotograma = UltimoFotograma Entonces
AnimacionFotograma = 0
Fin Si

Cronometro.Reiniciar()
Fin Si

Pensarlo bien. ¿De verdad creeis en esas dos lineas entre que se lee el valor del cronometro y en la que lo reseteo pierdo 100 milisegundos? O tendria muchisimo codigo por medio entre ambas llamadas o es que el lenguaje es verdaderamente lento para ejecutar el codigo por que en 100ms hasta en Visual Basic 6.0 me daba tiempo a realizar como 20 o muchas mas operaciones como esa de actualizar el fotograma de animacion.

A mi me ha funcionado de perlas esta forma comoda de controlar intervalos de tiempo durante 12 años, no veo razon de peso para no seguir usandola y menos cuando la he visto aplicada en cientos de ejemplos y librerias por la red (vamos, que no es invento mio).

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

Vicente

No se refieren a las lineas entre el if y el Reiniciar, si no a que en el if el Cronometro puede valer por ejemplo 1200. Si Reiniciar lo que hace es ponerlo a 0, pues la puedes liar.

[EX3]

#11
Cita de: Vicente en 26 de Febrero de 2012, 03:41:50 AM
No se refieren a las lineas entre el if y el Reiniciar, si no a que en el if el Cronometro puede valer por ejemplo 1200. Si Reiniciar lo que hace es ponerlo a 0, pues la puedes liar.
Vale, ahora entiendo a lo que se refieren exactamente.

Nunca he tenido problemas con ese supuesto lapso de tiempo perdido en el caso de cumplirse y lo peor que te puede pasar, por ejemplo con una animacion, es que si has estado un tiempo inactivo y el cronometro, tal y como esta implementado en el codigo de abajo, te da un lapso muy superior al que esperas cuando no deberia ser asi, es que la animacion adelante un fotograma al instante. En el caso practico sigo sin verle un problema como tal o nunca he tenido problemas al respecto como decia antes. Asi lo he hecho toda la vida con dx_lib32, en Div Game Studio en su dia y asi lo estoy haciendo en XNA y en los proyectos que he realizado en Flash.

Pensar tambien que estamos hablando de intervalos en milisegundos y de periodos entre lectura de tiempo y reinicio muy cortos para lo que dura un milisegundo. Esto que comentais podria pasar por ejemplo si lanzas un menu de pausa y el cronometro sigue contando. Cuando vuelvas a escena obviamente, en tiempo de juego no deberia haber pasado tiempo pero el lapso real de tiempo si ha sucedido, ¿lo peor que te puede pasar en una animacion? Que se adelante al instante un fotograma de la secuencia, pero teniendo en cuenta el tiempo de pausa para el jugador, este no apreciaria ese salto de actualizacion, no se si me explico :)

Salu2...

P.D.: De corregir esto seria facil, agregar un metodo Pause() al cronometro que fuera calculando el lapso de tiempo mientras este pausado y al volver de la pausa que lo sume al delta de tiempo inicial. Con esto deberia estar cubierto el problema que comentais.
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt

XÑA

Esto de los timers tiene su historia. De hecho, hace muy poco, leia un documento sobre el hecho de usar timers en floats o en doubles.  :-X

Resulta que según como trabajes, estás perdiendo un poco de precisión a cada momento, y cuando lleva DIAS encendidos la precisión ya se nota. Evidentemente a casi todos no nos preocupa este problema, pero..¿y si programas algo que se ejecuta constantemente en un servidor?

Pues el que hizo este doc, se ve que tenía es problema y explicaba cómo resolverlo.

A veces el no encontrarnos un problema no significa que no exista. Como yo SIEMPRE trabajo Time-based, tengo mucho cuidado con estas cosas de poner a 0, porqué si tienes eventos asociados a tiempos, puedes tener desfases importantes ( hombre es muy exagerado esto que digo, lo reconozco  :P ). En fin, al menos es saberlo, y así si tienes algún pequeño desfase, saber porqué puede ser.  ;)

No es tran grave como el boxing/unboxing de los structs en C#, que sí te puede volver loco, pero.... :P

Manu343726

Bueno bueno, hay que ver la que he montado....tomo nota: Ojo con los timers.

Sobre la solución propuesta, es más complicado de lo que parece: Uno de los fundamentos que tuve en mente a la hora de reescribir el motor fue que la escena solo se renderice si ocurre algún cambio, de manera que el pipeline de renderizado tiene diferentes puntos de entrada, dependiendo del tipo de modificación (Una transformación sobre un poliedro, un movimiento de la cámara, o un cambio en un foco, por ejemplo).

Para ayudar con la explicación, he hecho un par de diagramas:



El problema de éste método está en que no es compatible completamente con un renderizado contínuo, como es el caso de dx_lib32.
En el caso del dx_lib32Renderer, éste contiene una lista de primitivas obtenidas a partir del ZBuffer (Que no son más que grupos de triángulos, quads con el último vértice repetido para su uso en DRAW_Trapezoid [En un futuro DRAW_VertexMap, para el cubito de Hechelion,jeje])

Es ésta lista la que es dibujada en el bucle de renderizado de dx_lib32, pero si se hacen modificaciones contínuas de dicha lista (como en el caso de una animación), no se llega a redibujar. No es tanto un problema de tiempo entre cada iteración del bucle, sino más del tiempo entre transformación y transformación. Y claro, eso si que no puedo cambiarlo, ya que es totalmente independiente del motor.

[EX3]

Cita de: VBManu en 26 de Febrero de 2012, 09:35:38 PM
El problema de éste método está en que no es compatible completamente con un renderizado contínuo, como es el caso de dx_lib32.
Hombre, lo de renderizado continuo te lo vas a encontrar con DirectX, XNA, OpenGL y cualquier API de dibujo en tiempo real por norma. El GDI por ejemplo, que veo que lo tienes en el esquema como posible render,  no funciona de esa manera ya que guarda digamos el estado de la ultima operacion de dibujo y solo actualiza donde pinta, que sera en el sistema que te basaste para implementar tu motor.

Cita de: VBManu en 26 de Febrero de 2012, 09:35:38 PM
No es tanto un problema de tiempo entre cada iteración del bucle, sino más del tiempo entre transformación y transformación.
Me sigue costando entender como esta implementado tu motor en el esquema que has subido arriba pero supongo que deberia cumplir esta linea de ejecucion:

Calcular transformaciones
Dibujar objetos
Renderizar la escena (llamada a Frame())

Haciendolo asi, da igual lo que tarden en calcularse las tranformaciones, siempre verias el resultado de estas al dibujar la escena. Si es asi como lo estas haciendo sigo sin entender del todo el por que no se dibuja continuamente los cambios que realizas. ¿Quizas es que solo pintas cuando hay modificaciones de estado en algun objeto? Si es asi esta mal el planteamiento, siempre deberias dibujar los objetos hayan sufrido o no modificaciones, lo que es correcto es no calcularlas de continuo si no hay cambios.

No se como has estructurado los objetos pero estos deberian tener, como resultado final para dibujarse con dx_lib32, la lista de vertices o datos que uses para dibujar, color, etc..., un metodo de actualizacion de logica donde realizases las llamadas pertinentes para actualizar los estados y transformaciones del objeto, y un metodo de dibujo que simplemente haga la llamada al render de dx_lib32 con los parametros ya mencionados. El flujo general del programa seria ejecutar todos los metodos de actualizacion de la lista de objetos de la escena y despues ejecutar todas las llamadas de dibujo de la lista de objetos de la escena.

Bucle Principal

        // Actualizas los estados de todos los objetos de la escena
        Recorrer lista de objetos
            Objeto[x].Actualizar
        Fin recorrer lista de objetos

        // Dibujas todos los objetos de la escena
        Recorrer lista de objetos
            Objeto[x].Dibujar
        Fin recorrer lista de objetos

        // Envias la escena a pantalla
        Renderizar escena

Fin bucle principal


Asi suelo yo estructurar mis objetos al programar mis motores, por ejemplo, con el objeto Sprite, que realiza multitud de tareas desde simplemente calcular posiciones o parametros de la animacion a ejecutar o incluso un mapa de puntos de control (un sistema para definir coordenadas que se transformen junto al sprite para luego usar de referencia y poder ubicar otros graficos por ejemplo) que se calcula unicamente cuando se aplican transformaciones al sprite (rotacion, escala, traslacion, espejado) y el metodo de dibujo que simplemente hace siempre un Draw al render con los parametros definidos o calculados en la actualizacion.

Quizas sigas esta estructura en tu motor y el problema sea otra cosa que se me sigue escapando con lo que has explicado, ya te digo que no me ha quedado muy claro del todo tu esquema de arriba pero visto asi eso que defines ahi seria todo lo que yo meteria como logica separado de las llamadas de dibujo :)

Salu2...
José Miguel Sánchez Fernández
.NET Developer | Game Programmer | Unity Developer

Blog | Game Portfolio | LinkedIn | Twitter | Itch.io | Gamejolt






Stratos es un servicio gratuito, cuyos costes se cubren en parte con la publicidad.
Por favor, desactiva el bloqueador de anuncios en esta web para ayudar a que siga adelante.
Muchísimas gracias.