PureGDK - 3D Programming for PureBasic

Developed or developing a new product in PureBasic? Tell the world about it.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

I don't want to create plkugins for DBP :D

I will do this...

1) Create a startup engine (file EXE made with PB) containing OpenDBWnd()
2) Open a DLL create with PB and containing mix code and some PureGDK functions (this DLL will also contain main game loop. It will be the main loop of runtime game engine).
3) This DLL will be used by my scripting engine to get full scripting capabilities, plus a full featured editor.

I will show some posts as soon as I will have a really small demo working.
I'm working to a commercial Game Framework to let the people make games in a different way than everybody proposed until now :wink:

Thank you!
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Ok, I made a small example about this interesting feature (put PureGDK functions inside a DLL).
NOTE: You must initialize the engine in the main executable, but everything else could stay inside a DLL.

NOTE for Mistrel: please do not lock this feature! It is a key feature for my project!!! :-)

Main code:

Code: Select all

OpenLibrary(1, "my_dll.dll")

; Show the PureGDK render window
hDBWnd=OpenDBWnd(0,0,640,480,32,#GDK_Window_SystemMenu|#GDK_Window_ScreenCentered)
Delay(100)

; Set the sync rate
dbSyncRate(0)

; Create a cube
dbMakeObjectCube(1, 1)

; Call DLL function to start multithread engine!
CallFunction(1, "startEngine")

; Wait for 2 seconds
Delay(2000)

; Put thread in pause (cube will stop from rotating)!
CallFunction(1, "pause")
Delay(1000)

; Resume (cube restart rotating)
CallFunction(1, "resume")
Delay(2000)

; Clean exit

CloseLibrary(1)
Delay(100)

End
DLL Code (I called it "my_dll.pb"). Compile this code as a DLL called "my_dll.dll".

Code: Select all


; THREAD: Never ending loop.
; This procedure will run in a thread, and
; will rotate the cube until the thread self is running
; (neither killed nor paused).

Procedure updateAll(Null)
  Repeat
    dbTurnObjectRight(1, 0.1)
    dbSync()
  ForEver
EndProcedure

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; Reference for the thread.
;
; NOTE: Global variables inside a DLL are "global" only
; inside the DLL self, and not in the whole program!

Global t1.l

ProcedureDLL startEngine()
  t1 = CreateThread(@updateAll(), 0)
EndProcedure

ProcedureDLL pause()
  PauseThread(t1)
EndProcedure

ProcedureDLL resume()
  ResumeThread(t1)
EndProcedure
Last edited by ale870 on Tue Nov 11, 2008 11:30 pm, edited 1 time in total.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

I intend to turn this 'bug' into a feature. It's unsupported for now but I won't remove it. :)

Just be careful where you use threads. PureGDK 1.0.3 is not threadsafe with the debugger on. It should be threadsafe with the debugger off (or compiling to a dll).
Last edited by Mistrel on Tue Nov 11, 2008 11:28 pm, edited 2 times in total.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Thank you!
I appreciate your work!

About threads: thank you for your "warning", but I usually work with non-threadsafe to get more performance, and I use mutex to carefully manage them :wink:

Thank you!
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

Make sure that you're compiling with the PureGDKThreadSafe subsystem. This is not checked by the pre-compiler because of your trick.

Also be sure to pause all threads in the DLL before you close it. Here it crashes without it.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Here a small upgrade: I know many programming languages but I really love newLisp, a scripting open source version of scheme (it's wonderful and really FAAST!).

newLisp home page: http://www.newlisp.org

Here a possible usage of your great package, using it inside a DLL (using newLisp scripting language and Pure Basic!):

Main code

Code: Select all

; Show the PureGDK render window
hDBWnd=OpenDBWnd(0,0,640,480,32,#GDK_Window_SystemMenu|#GDK_Window_ScreenCentered)
Delay(100)

OpenLibrary(0, "newlisp.dll")

startCode.s = "(import {my_dll.dll} {startEngine})"
startCode + "(import {my_dll.dll} {pause})"
startCode + "(import {my_dll.dll} {resume})"
startCode + "(import {my_dll.dll} {killThisThread})"

CallFunction(0, "newlispEvalStr", @startCode)

; Set the sync rate
dbSyncRate(0)

dbMakeObjectCube(1, 0.5)

CallFunction(0, "newlispEvalStr", @"(startEngine)")
Delay(2000)
CallFunction(0, "newlispEvalStr", @"(pause)")
Delay(2000)
CallFunction(0, "newlispEvalStr", @"(resume)")
Delay(2000)
CallFunction(0, "newlispEvalStr", @"(killThisThread)")

CloseLibrary(0)

Delay(100)
DLL my_dll.dll is similar to my previous post, but I added a kill-thread function :wink:

Code: Select all

ProcedureDLL killThisThread()
  KillThread(t1)
EndProcedure
What is happening here?

Well, I start PB application, and it will open my newLisp interpreter (newlisp.dll).
Then I will push in newLisp instance a small code to open my DLL "my_dll.dll" (see below) and get a reference to the functions inside:

Code: Select all

(import {my_dll.dll} {startEngine})
(import {my_dll.dll} {pause})
(import {my_dll.dll} {resume})
(import {my_dll.dll} {killThisThread})
Then I simply use my new DLL functions! It means I can use a 3D Game engine from my scripting language!
Possibilities are endless!!!

In order to use this program, simply download newLisp engine from the home page (http://www.newlisp.org) then copy newlisp.dll inside the same directory as this program and my_dll.dll is running! That newLisp DLL is wonderful: only a simple dll to get all the power of newLisp in your pocket!

I hope this will help you! :D
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

I'm glad it's working so well for you. If you have any problems or suggestions for the next release please let me know.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

i found a strange behaviour: I added in my DLL the following function:

Code: Select all

ProcedureDLL doCube()
  conta+1
  LockMutex(m)
  dbMakeObjectCube(conta, 0.1)
  dbPositionObject(conta, Random(100)/50-1, Random(100)/50-1, Random(100)/50-1)
  UnlockMutex(m)
EndProcedure
And I call it in the usual way. That function simply create a cube in a random position.
The problem is that function does NOT work unless I create a cube (even of size zero!) in the main program.
In my main application I need to insert the following code:

Code: Select all

dbMakeObjectCube(9999, 0)
If I insert that line I get a blue background screen with all the cubes. If I do not insert that line I get a black window and I see nothing inside.
It seems the engine must be initialized in the main application.
Is there any initialization function that could substitute that bad line (I think creating a cube of size "0" is not good).

Thank you.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

The reason for this is that PureGDK parses your code and resolves dependencies depending on which commands you use.

So, if you don't use any commands from the 3D library in your main source then that library won't be available to you from your DLL.
Mistrel wrote:Just know that there is no dependency checking when compiling to a DLL so if the plugin required by the function is not found it will return harmlessly but may cause your code to do something strange if you expected it to work.

For example, you wouldn't be able to use dbMakeObjectCube without the 3D library being included.
You can get around this by creating a function like this in your main source to force the library to include:

Code: Select all

Procedure ForceIncludes()
	dbMakeObjectCube(0,0) ;/ Basic3D
	dbBackdropOn() ;/ Camera
EndProcedure
You don't have to actually call this function for it to work.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Ok, good workaround.
It means that code is enough to included everything needed to work with all 3D features? Even "optional" ones like Dark Physics, and other separated plugins (I bought some of them)?
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

PureGDK resolves inter-plugin dependencies just like the DBP compiler. So, for example, if you call dbMakeObjectCube it will include dbprocameradebug.dll, dbproimagedebug.dll, dbprovectorsdebug.dll, etc. Dark Physics is not a dependency so that library will not be included with this one function.

To include Dark Physics you could use dbPhyStart() in your ForceIncludes procedure. Check the help file for a list of supported plugins.

If PureGDK were to include all of the plugins you had installer rather than only the ones you needed then your compiled programs would be unnecessarily massive.

If you have a suggestion that would make this easier I'll take it into consideration for the next release. I would like to see this bug become a feature too. :)

Also, I'm pleased to inform you that Newton is working great now. As far as I can tell all of the problems have been ironed out and it works great with the debugger on or off.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Great news for Newton engine! I'm very happy about it!

About a possible suggestion how to convert this "bug" into a feature, I think you could follow one of these ways:

1) Use #INCLUDE statement in order to let the people which functions/libraries include (and not using a call resolved during compilation automatically).

2) Similar to (1) but including a library.

3) Using a definition #DEFINE. If I define a specific constant then it means I'm including a specifi library. E.g.: to include Dark Physics i could define:
#define PUREGDK_INCLUDE_DARKPHYSICS

4) Using a #PRAGMA directive. This directive is usually used to "instruct" the compiler about specific configurations.

items 3 and 4 are good, since both of them are resolved at compilation time, it means the compiler could "read" and make some actions based on their definitions.
I think this is a more elegant solution than using a "ForceInclude()" function.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

Is there any way to open two rendering windows?
I'm creating a tool to develop games so it could be very useful getting a main form with the rendering environment (the world) then opening another form, for example, to see a model preview before loading it (or to make other operations).

I think that opening two rendering windows at the same time (two are enough) could be for the programmer (me!) a big time saving (else I need to use the only window and realize something like a "context switch" / rendering switch to see main world, then see my model (or something else) then switch the main world again).

NOTE: in order to avoid to touch existing code/functions you could make a function that allow me the handle of the rendering window.
E.g:

Code: Select all

renderOutput(hWnd)
Thank you.
Mistrel
Addict
Addict
Posts: 3415
Joined: Sat Jun 30, 2007 8:04 pm

Post by Mistrel »

http://www.purebasic.fr/english/viewtop ... 906#242906
Mistrel wrote:It's not possible to open more than one 3D window natively but I think this can be accomplished with API calls.

Don't quote me one this, as I've never tried it before.

http://www.mvps.org/directx/articles/re ... indows.htm

Another method would be to grab the frame from a particular camera without syncing it to the main window. You could then project it to any number of different sources.

You wouldn't want to have multiple engines running in parallel because they would each compete for cpu time. What we need here is a new feature for syncing across multiple windows.
You can get the pointer to the Direct3D interfaces by calling ?GetDirect3D@@YAPAUIDirect3D9@@XZ and ?GetDirect3DDevice@@YAPAUIDirect3DDevice9@@XZ in DBProSetupDebug.dll.

There is no compatibility layer for calling DBP functions directly in debug mode so to do this you will need to compile with the debugger off.
ale870 wrote:Is there any way to use something like a special buffer in order to draw something, then render the buffer to a camera (not the main camera) then use "final texture" to export the frame and put it in another window?
You can capture the camera easily with dbSetImageToCamera. But if you want something faster try the dbGetBackbufferPtr commands. The backbuffer commands are very slow in debug mode because of an internal compatibility layer used for these commands (see the 2D drawing example on the PureGDK website).

Try dbSetImageToCamera, dbMakeMemblockFromImage, and dbGetMemblockPtr to access the camera image. There's probably a faster way to do it I think using dbSyncMasc and dbGetBackbufferPtr. Try asking on the DBP forum.
ale870
Enthusiast
Enthusiast
Posts: 180
Joined: Wed Jul 09, 2008 7:02 am
Contact:

Post by ale870 »

I will try both ways as soon as possible!

HINT FOR YOU: maybe this could be an "official" improvement/new feature for the next version... :wink:


Thank you!
Post Reply