3D Morphing

Discussions related to graphics (2D and 3D), animation and games programming
Richard Russell
Posts: 457
Joined: Tue 18 Jun 2024, 09:32

3D Morphing

Post by Richard Russell »

This was an experiment in morphing between two objects (here a rabbit and a dragon) and back again. In part it was to discover how quickly 3D objects can be manipulated in real-time - and the answer is not very, because of the work involved in converting to 32-bit floats (which isn't a native BBC BASIC data type).

It's even slower when running in a browser, as you can see by clicking here.

https://youtu.be/B97fedW9h2I

Code: Select all

      INSTALL @lib$ + "webgllib"

      ON CLOSE PROCcleanup : QUIT
      ON ERROR PROCcleanup : MODE 3 : PRINT REPORT$ : END
      ON MOVE IF @msg% <> 5 RETURN ELSE PROCcleanup : CLEAR
      VDU 20,26,12

      DIM pVB%(0), nv%(0), vf%(0), vl%(0), l%(0), m%(0), t%(0), y(0), p(0), r(0)
      DIM X(0), Y(0), Z(0), eye(2), at(2), n(2)

      REM. Initialise OpenGL:
      pDevice% = FN_initgl(@hwnd%, 1, 1)
      IF pDevice% = 0 ERROR 100, "Couldn't initialise OpenGL"

      REM. Load 3D objects:
      Rabbit%% = FNload3d(pDevice%, @dir$+"rabbit_new.fvf", nvr%, vfr%, vlr%)
      Dragon%% = FNload3d(pDevice%, @dir$+"dragon_new.fvf", nvd%, vfd%, vld%)
      IF Rabbit%% = 0 OR Dragon%% = 0 ERROR 101, "Couldn't load 3D objects"
      IF nvr% <> nvd% ERROR 102, "3D object vertex counts differ"

      REM Get vertex coordinates to arrays:
      DIM rabbit(nvr%*6-1), dragon(nvd%*6-1), morph(nvd%*6-1)
      PROCvertices(rabbit(), Rabbit%%, nvr%)
      PROCvertices(dragon(), Dragon%%, nvd%)

      REM Create buffer object:
      SYS `glGenBuffers`, 1, ^pVB%(0), @memhdc%
      nv%(0) = nvr% : vf%(0) = vfr% : vl%(0) = vlr%

      REM. Point-source light:
      DIM light{Type%, Diffuse{r%,g%,b%,a%}, Specular{r%,g%,b%,a%}, \
      \ Ambient{r%,g%,b%,a%}, Position{x%,y%,z%}, Direction{x%,y%,z%}, \
      \ Range%, Falloff%, Attenuation0%, Attenuation1%, Attenuation2%, \
      \ Theta%, Phi%}

      light.Type% = 1              : REM. point source
      light.Diffuse.r% = FN_f4(1)  : REM. diffuse colour RGB
      light.Diffuse.g% = FN_f4(1)
      light.Diffuse.b% = FN_f4(1)
      light.Specular.r% = FN_f4(1) : REM. specular colour RGB
      light.Specular.g% = FN_f4(1)
      light.Specular.b% = FN_f4(1)
      light.Position.x% = FN_f4(0) : REM. position XYZ
      light.Position.y% = FN_f4(0)
      light.Position.z% = FN_f4(-100)
      light.Range% = FN_f4(10)     : REM. range
      light.Attenuation0% = FN_f4(1) : REM. attenuation (constant)
      l%(0) = light{} - PAGE + !340

      REM. Material:
      DIM material{Diffuse{r%,g%,b%,a%}, Ambient{r%,g%,b%,a%}, \
      \     Specular{r%,g%,b%,a%}, Emissive{r%,g%,b%,a%}, Power%}

      material.Diffuse.r% = FN_f4(121/160)  : REM. diffuse colour RGB
      material.Diffuse.g% = FN_f4( 83/160)
      material.Diffuse.b% = FN_f4( 71/160)
      material.Specular.r% = FN_f4(0) : REM. specular colour RGB
      material.Specular.g% = FN_f4(0)
      material.Specular.b% = FN_f4(0)
      material.Power% = FN_f4(20): REM. specular 'power'
      m%(0) = material{} - PAGE + !340

      REM. Render the tumbling object:
      eye() = 0, 0, -4
      at() = 0, 0.1, 0
      TIME = 300 * PI/2
      REPEAT
        mix = (1.0 + ATN(SIN(TIME/300)*8)/ATN(8)) / 2.0
        morph() = rabbit() * mix : morph() += dragon() * (1.0 - mix)

        p%% = Rabbit%% : P% = 0
        FOR I% = 0 TO nvr% - 1
          PROCf4(morph(), P%, p%%) : P% += 6 : p%% += 36
        NEXT
        SYS `glBindBuffer`, GL_ARRAY_BUFFER, pVB%(0), @memhdc%
        SYS `glBufferData`, GL_ARRAY_BUFFER, 36 * nvr%, Rabbit%%, GL_STATIC_DRAW, @memhdc%

        y() = -TIME/200
        PROC_render(pDevice%, &00C060, 1, l%(), 1, m%(), t%(), pVB%(), nv%(), vf%(), vl%(), \
        \ y(), p(), r(), X(), Y(), Z(), eye(), at(), PI/6, @vdu%!208/@vdu%!212, 1, 100, 0)
      UNTIL INKEY(1)=0
      END

      DEF PROCvertices(array(), p%%, N%) : LOCAL A%,I%,P%
      FOR I% = 0 TO N%-1
        A% = p%%!0 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        A% = p%%!4 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        A% = p%%!8 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        A% = p%%!12 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        A% = p%%!16 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        A% = p%%!20 : array(P%)=SGNA%*(A%AND&7FFFFFOR&800000)*2^((A%>>23AND&FF)-150) : P% += 1
        p%% += 36
      NEXT
      ENDPROC

      DEF PROCf4(m(),P%,v%%)LOCALA%,B%,I%,a#,p%%:p%%=^a#:A%=NOT&7FFFFFFF:B%=&38000000
      FORI%=P%TOP%+5:a#=m(I%):!v%%=p%%!4ANDA%OR(p%%!4-B%<<3OR!p%%>>29AND7)+(!p%%>>28AND1)
        v%%+=4:NEXT
      ENDPROC

      DEF FNload3d(D%,f$,RETURN N%,RETURN V%,RETURN S%)
      LOCAL F%,I%,L%,o%%,p%% : F%=OPENIN(f$):IF F%=0 THEN=0
      N%=FN_4#(F%) : V%=BGET#F%+(BGET#F%<<8) : S%=BGET#F%+(BGET#F%<<8)
      SYS "SDL_malloc",N%*36 TO o%% : IF @platform% AND &40 ELSE o%%=!^o%%
      SYS "SDL_memset",o%%,0,N%*36 : p%%=o%%
      FOR I%=0 TO N% - 1 : L%=0
        IF V% AND &002 p%%!0 =FN_4#(F%) : p%%!4 =FN_4#(F%) : p%%!8 =FN_4#(F%) : L% += 12
        IF V% AND &010 p%%!12=FN_4#(F%) : p%%!16=FN_4#(F%) : p%%!20=FN_4#(F%) : L% += 12
        IF V% AND &040 p%%!24=FN_4#(F%) : SWAP p%%?24,p%%?26 : L% += 4 ELSE p%%!24=TRUE
        IF V% AND &100 p%%!28=FN_4#(F%) : p%%!32=FN_4#(F%)                    : L% += 8
        IF L% < S% THEN PTR#F%=PTR#F%+S%-L%
        p%% += 36
      NEXT : CLOSE #F%
      = o%%

      DEF PROCcleanup
      pVB%(0) += 0  : IF pVB%(0)  PROC_release(pVB%(0))
      pDevice% += 0 : IF pDevice% PROC_release(pDevice%)
      Rabbit%% += 0 : IF Rabbit%% SYS "SDL_free", Rabbit%%
      Dragon%% += 0 : IF Dragon%% SYS "SDL_free", Dragon%%
      *REFRESH ON
      ENDPROC