Monday, March 14

Bricolage engine starts rendering


My experimental bricolage lash-up has started rendering (actually, two weeks ago) .. here's a screenshot of it rendering the videogame testcard..next step is to get a nice font rendering module and debugging console going..before delving into 3d..


Friday, February 4

Bricolage progress.

The initial version of the Bricolage engine is complete. It uses nedmalloc for memory allocation, EASTL for generic containers and GLEW and GLFW for OpenGL and platform abstraction layers, although at the moment the only platform I'm supporting is Windows. It should be more than enough to get an image on the screen, run a shader, and build some basic component - entity architecture. So, here we go...

Tuesday, January 25

A New Engine

I am between projects at the moment, in a phase of experimentation. One thing I've wanted to do is construct a good testbed engine for experiments. However an entire engine is a major undertaking - three months to get the basics down pat. A third party engine always takes time to learn, though. There's nothing to replace the familiarity of something you coded yourself.

It occured to me that there is a third alternative. Bricolage - to assemble an engine or as much as possible of an engine from the multitude of FOSS libraries that are available. Such would be in accord with the UNIX philosophy: to have small programs or tools that do one thing well, and arrange it so that the sum of the parts are greater than the whole. This should give me a platform for experimentation in a much faster time than full self-assembly, but have the advantage of familiarity that comes with self-designed API's.

With no further ado, I introduce the Bricolage engine. I will blog about each component as I add it. Eventually it should be a reasonably featured engine that works with mingw on Windows and is ready for porting to other platforms. To quote Wikipedia:


Bricolage is a term used in several disciplines, among them the visual arts
and literature, to refer to the construction or creation of a work from a
diverse range of things that happen to be available, or a work created by
such a process. The term is borrowed from the French word bricolage, from
the verb bricoler, the core meaning in French being, "fiddle, tinker" and,
by extension, "to make creative and resourceful use of whatever materials
are at hand (regardless of their original purpose)"

My main goal is portability. I need to be able to support mutiple renderers and platforms if at all possible. This pretty much limits me to gcc as a compiler and OpenGL (and it's junior cousin, OpenGLES) as a rendering API. I chose SCons as a build tool, mainly for reasons of tast. It's as portable as Python is and I know it and Python reasonably well. CMake or autotools are equally valid choices.

Tuesday, December 14

How network games work...

Highly handy comment from PlayerController.uc in the Unreal Development Kit: Multiplayer net games 101..



/*
========================================================================
Here's how player movement prediction, replication and correction works in network games:

Every tick, the PlayerTick() function is called. It calls the PlayerMove() function (which is implemented
in various states). PlayerMove() figures out the acceleration and rotation, and then calls ProcessMove()
(for single player or listen servers), or ReplicateMove() (if its a network client).

ReplicateMove() saves the move (in the PendingMove list), calls ProcessMove(), and then replicates the move
to the server by calling the replicated function ServerMove() - passing the movement parameters, the client's
resultant position, and a timestamp.

ServerMove() is executed on the server. It decodes the movement parameters and causes the appropriate movement
to occur. It then looks at the resulting position and if enough time has passed since the last response, or the
position error is significant enough, the server calls ClientAdjustPosition(), a replicated function.

ClientAdjustPosition() is executed on the client. The client sets its position to the servers version of position,
and sets the bUpdatePosition flag to true.

When PlayerTick() is called on the client again, if bUpdatePosition is true, the client will call
ClientUpdatePosition() before calling PlayerMove(). ClientUpdatePosition() replays all the moves in the pending
move list which occured after the timestamp of the move the server was adjusting.
*/

Wednesday, December 1

Unrealscript Mode for Emacs Reloaded

I actually started picking up Unreal again, and Unrealscript. My previous attempt at an Unrealscript mode was badly flawed. It was based on CC-mode, which I did not fully pursuade to understand the syntax of Unrealscript. This time, I've gone back to first principles and created simple progmode not derived from cc-mode. This means a loss of functionality, but it does mean correct indenting, and true case insensitivity. I found CC mode would have to be patched to handle a case insensitive language like UnrealScript. Anyway, without further ado, here it is. I reccomend it over the previous ones.



(defvar unrealscript-mode-hook nil)

(defvar unrealscript-mode-map
(let ((unrealscript-mode-map (make-sparse-keymap)))
(define-key unrealscript-mode-map "\C-j" 'newline-and-indent)
unrealscript-mode-map)
"Keymap for UNREALSCRIPT major mode")


(defconst unrealscript-font-lock-keywords-1
(list
'("\\<\\(?:break\\|c\\(?:\\(?:as\\|ontinu\\)e\\)\\|do\\|e\\(?:lse\\|xtends\\)\\|for\\(?:each\\)?\\|i\\(?:f\\|nterface\\)\\|new\\|return\\|switch\\|var\\|while\\|class\\)\\>" . font-lock-keyword-face))
"Minimal highlighting expressions for UNREALSCRIPT mode.")

(defconst unrealscript-font-lock-keywords-2
(append unrealscript-font-lock-keywords-1
(list
'("\\<\\(?:array\\|b\\(?:ool\\|yte\\)\\|c\\(?:lass\\|o\\(?:erce\\|lor\\|ords\\)\\)\\|de\\(?:faultproperties\\|legate\\)\\|e\\(?:num\\|vent\\)\\|f\\(?:alse\\|loat\\|unction\\)\\|int\\|local\\|name\\|o\\(?:ptional\\|ut\\)\\|plane\\|r\\(?:egion\\|otator\\)\\|st\\(?:ate\\|r\\(?:ing\\|uct\\)\\)\\|true\\|v\\(?:\\(?:a\\|ecto\\)r\\)\\)\\>" . font-lock-keyword-face)))
"Additional Keywords to highlight in UNREALSCRIPT mode.")


(defconst unrealscript-font-lock-keywords-3
(append unrealscript-font-lock-keywords-2
(list
'("\\<\\(?:A\\(?:bstract\\|llowAbstract\\|uto\\(?:Comment\\|ExpandCategories\\)\\)\\|Co\\(?:llapseCategories\\|nfig\\)\\|D\\(?:ep\\(?:endsOn\\|recated\\)\\|isplayName\\|ontCollapseCategories\\)\\|Edit\\(?:Condition\\|InlineNew\\)\\|FriendlyName\\|Hide\\(?:Categories\\|DropDown\\)\\|I\\(?:\\(?:mplemen\\|nheri\\)ts\\)\\|N\\(?:ative\\(?:Replication\\)?\\|o\\(?:Export\\|nTransient\\|t\\(?:EditInlineNew\\|Placeable\\)\\)\\)\\|P\\(?:erObject\\(?:Config\\|Localized\\)\\|laceable\\)\\|ShowCategories\\|T\\(?:oolTip\\|ransient\\)\\|Within\\)\\>" . font-lock-type-face)
'("\\<\\(?:auto\\|c\\(?:lient\\|on\\(?:fig\\|st\\)\\)\\|d\\(?:atabinding\\|eprecated\\|uplicatetransient\\)\\|e\\(?:dit\\(?:const\\|fixedsize\\|inline\\(?:use\\)?\\|oronly\\)\\|x\\(?:ec\\|port\\)\\)\\|globalconfig\\|i\\(?:gnores\\|n\\(?:it\\|put\\|stanced\\|terp\\)\\|terator\\)\\|l\\(?:atent\\|ocalized\\)\\|n\\(?:ative\\(?:replication\\)?\\|o\\(?:clear\\|export\\|import\\|ntransactional\\|tforconsole\\)\\)\\|operator\\|p\\(?:o\\(?:\\(?:inte\\|stoperato\\)r\\)\\|r\\(?:eoperator\\|ivate\\|otected\\)\\|ublic\\)\\|re\\(?:liable\\|pnotify\\)\\|s\\(?:erver\\|i\\(?:mulated\\|ngular\\)\\|kip\\|tatic\\)\\|tra\\(?:nsient\\|vel\\)\\|unreliable\\)\\>" . font-lock-keyword-face)
'("\\<\\(?:A\\(?:bs\\|cos\\|dd\\(?:Item\\)?\\|llActors\\|s\\(?:c\\|in\\)\\|tan\\)\\|B\\(?:asedActors\\|egin\\(?:Play\\|State\\)\\)\\|C\\(?:aps\\|eil\\|h\\(?:ildActors\\|r\\)\\|l\\(?:amp\\|earTimer\\)\\|o\\(?:\\(?:llidingActor\\)?s\\)\\)\\|D\\(?:estroyed\\|i\\(?:\\(?:sabl\\|vid\\)e\\)\\|ynamicActors\\)\\|E\\(?:mpty\\|n\\(?:\\(?:abl\\|dStat\\)e\\)\\|val\\|xp\\)\\|F\\(?:Clamp\\|M\\(?:ax\\|in\\)\\|Rand\\|astTrace\\|in\\(?:d\\|ish\\(?:Anim\\|Interpolation\\)\\)\\)\\|G\\(?:etTimer\\(?:Count\\|Rate\\)\\|oTo\\(?:State\\)?\\)\\|I\\(?:n\\(?:Str\\|itGame\\|sert\\(?:Item\\)?\\|vert\\)\\|s\\(?:\\(?:InStat\\|TimerActiv\\)e\\)\\)\\|L\\(?:e\\(?:ft\\|n\\|rp\\)\\|o\\(?:cs\\|ge\\)\\)\\|M\\(?:ax\\|i\\(?:rrorVectorByNormal\\|[dn]\\)\\)\\|Normal\\|OverlappingActors\\|P\\(?:o\\(?:pState\\|stBeginPlay\\)\\|reBeginPlay\\|ushState\\)\\|R\\(?:and\\|e\\(?:move\\(?:I\\(?:ndex\\|tem\\)\\)?\\|pl\\(?:ace\\)?\\)\\|ight\\|ound\\)\\|S\\(?:et\\(?:State\\|Timer\\)\\|in\\|leep\\|merp\\|p\\(?:awn\\|lit\\)\\|q\\(?:rt\\|uare\\)\\)\\|T\\(?:an\\|ick\\|ouchingActors\\|race\\(?:Actors\\)?\\)\\|V\\(?:Rand\\|Size\\|isible\\(?:\\(?:Colliding\\)?Actors\\)\\)\\|\\(?:ro\\|vec\\)t\\)\\>" . font-lock-function-name-face)))
"Balls-out highlighting in UNREALSCRIPT mode.")

(defvar unrealscript-font-lock-keywords unrealscript-font-lock-keywords-3
"Default highlighting expressions for UNREALSCRIPT mode.")


(defvar unrealscript-indent-width 4)

(defun looking-at-unrealscript-indent-keyword ()
(or (looking-at "^[\t ]*while") (looking-at "^[\t ]*if") (looking-at "^[\t ]*else") (looking-at "^[\t ]*for")))

(defun looking-at-unrealscript-block-end ()
(or (looking-at "^[\t ]*end") (and (looking-at "^.*}[ \t]*$") (not (looking-at "^.*{")))))

(defun looking-at-unrealscript-block-start ()
(or (looking-at "^[\t ]*begin") (and (looking-at "^.*{") (not (looking-at "^.*}[ \t]*$")))))

;; Function to control indenting.
(defun unrealscript-indent-line ()
"Indent current line as Unrealscript code"
(interactive)
;; Set the point to beginning of line.
(beginning-of-line)
(if (bobp)
(indent-line-to 0)
(let ((not-indented t) (lines-back 0) cur-indent)
(if (looking-at-unrealscript-block-end) ; Check for closing brace
;; if we are at the end of a block
(progn
(save-excursion
(forward-line -1)
(setq lines-back (+ lines-back 1))
(setq cur-indent (- (current-indentation) unrealscript-indent-width)))
;; Safety check to make sure we don't indent negative.
(if (< cur-indent 0)
(setq cur-indent 0)))
;; else scan backward
(save-excursion
(if (looking-at-unrealscript-block-start) ; Opening block
(progn
(forward-line -1)
(setq lines-back (+ lines-back 1))
(setq cur-indent (current-indentation))
(setq not-indented nil))
(while not-indented
(forward-line -1)
(setq lines-back (+ lines-back 1))
(if (looking-at-unrealscript-block-end) ;; Closing Block
(progn
(setq cur-indent (current-indentation))
(setq not-indented nil))
(if (looking-at-unrealscript-block-start)
(progn
(setq cur-indent (+ (current-indentation) unrealscript-indent-width))
(setq not-indented nil))
(if (looking-at-unrealscript-indent-keyword)
(progn
(setq cur-indent (current-indentation))
(forward-line 1)
(setq lines-back (- lines-back 1))
(if (looking-at-unrealscript-block-start)
(setq not-indented nil) ;; has block
(if (zerop lines-back) ;; no block
(progn
(setq cur-indent (+ cur-indent unrealscript-indent-width))
(setq not-indented nil))
(setq not-indented nil))))
(if (bobp)
(setq not-indented nil)))))))))
(if cur-indent
(indent-line-to cur-indent)
(indent-line-to 0)))))

(defun unrealscript-populate-syntax-table (table)
"Populate the given syntax table as necessary for a C-like language.
This includes setting ' and \" as string delimiters, and setting up
the comment syntax to handle both line style \"//\" and block style
\"/*\" \"*/\" comments."

(modify-syntax-entry ?_ "_" table)
(modify-syntax-entry ?\\ "\\" table)
(modify-syntax-entry ?+ "." table)
(modify-syntax-entry ?- "." table)
(modify-syntax-entry ?= "." table)
(modify-syntax-entry ?% "." table)
(modify-syntax-entry ?< "." table)
(modify-syntax-entry ?> "." table)
(modify-syntax-entry ?& "." table)
(modify-syntax-entry ?| "." table)
(modify-syntax-entry ?\' "\"" table)
(modify-syntax-entry ?\240 "." table)

;; Set up block and line oriented comments. The new C
;; standard mandates both comment styles even in C, so since
;; all languages now require dual comments, we make this the
;; default.
(modify-syntax-entry ?/ ". 124b" table)
(modify-syntax-entry ?* ". 23" table)

(modify-syntax-entry ?\n "> b" table)
;; Give CR the same syntax as newline, for selective-display
(modify-syntax-entry ?\^m "> b" table)
table)

(defvar unrealscript-mode-syntax-table
(let ((unrealscript-mode-syntax-table (unrealscript-populate-syntax-table (make-syntax-table))))
unrealscript-mode-syntax-table)
"Syntax table for unrealscript-mode")

(defun unrealscript-mode ()
"Major mode for editing Unrealscript files"
(interactive)
(kill-all-local-variables)
(set-syntax-table unrealscript-mode-syntax-table)
(use-local-map unrealscript-mode-map)
(setq indent-line-function 'unrealscript-indent-line)
(setq font-lock-defaults '(unrealscript-font-lock-keywords nil t))
(setq major-mode 'unrealscript-mode)
(setq mode-name "UNREALSCRIPT")
(run-hooks 'unrealscript-mode-hook)
(setq case-fold-search t)
(setq font-lock-keywords-case-fold-search t))


(provide 'unrealscript-mode)

Wednesday, September 15

A micro manual for Lisp in C++

It was with some interest that I read A Micro Manual for Lisp in C. One thing I have noticed about such efforts is that they are nearly always implemented in idiomatic C. I thought (out of idle curiosity, mainly) I'd see what a similar thing implemented in idomatic C++ would look like.


The main practical advantage that it offered in the end was the fact that boost::shared_ptr gave me rudimentary garbage collection for free. I used boost::variant to give me polymorphic lisp objects and thus did not have to use naked pointers anywhere, which seems to me to be an improvement in code safety. The price of this is a nosier syntax. You pay your money and you takes your choice..

As a concrete example here is what the implementation of CONS looked like.



boost::shared_ptr<lisp::object> fn_cons(boost::shared_ptr<lisp::object> args,
boost::shared_ptr<lisp::object> env) {

boost::shared_ptr<lisp::object> list = make_cons(car(args), boost::shared_ptr<lisp::object>());
args = car(cdr(args));

while ((args != NULL) && (args->which() == lisp::e_CONS)) {
append(list, car(args));
args = cdr(args);
}
return list;
}

Its probably also worth noting that avoiding naked pointers meant using boost::function for function objects, thusly..



enum kind {
e_ATOM = 0,
e_CONS,
e_FUNC,
e_LAMBDA
};

struct atom {
std::string name;

atom(const std::string& n) : name(n) {
}
};

struct cons {
boost::shared_ptr<object> car;
boost::shared_ptr<object> cdr;

cons(boost::shared_ptr<object> first, boost::shared_ptr<object> second) : car(first), cdr(second) {

}
};

typedef boost::function< boost::shared_ptr<object> (boost::shared_ptr<object>,boost::shared_ptr<object>) > lisp_func;

struct func {
lisp_func fn;

func(const lisp_func& f) : fn(f) {

}
};

struct lambda {
boost::shared_ptr<object> args;
boost::shared_ptr<object> sexp;

lambda(boost::shared_ptr<object> a, boost::shared_ptr<object> s) : args(a), sexp(s) {

}
};

typedef boost::variant< atom, cons, func, lambda > base_object;

And creating the environment like so...



boost::shared_ptr<lisp::object> init_env() {

boost::shared_ptr<lisp::object> nul;

boost::shared_ptr<lisp::object> a_quote(new lisp::object(std::string("QUOTE")));
boost::shared_ptr<lisp::object> f_quote(new lisp::object(fn_quote));

boost::shared_ptr<lisp::object> a_car(new lisp::object(std::string("CAR")));
boost::shared_ptr<lisp::object> f_car(new lisp::object(fn_car));

boost::shared_ptr<lisp::object> a_cdr(new lisp::object(std::string("CDR")));
boost::shared_ptr<lisp::object> f_cdr(new lisp::object(fn_cdr));

boost::shared_ptr<lisp::object> a_cons(new lisp::object(std::string("CONS")));
boost::shared_ptr<lisp::object> f_cons(new lisp::object(fn_cons));

boost::shared_ptr<lisp::object> a_equal(new lisp::object(std::string("EQUAL")));
boost::shared_ptr<lisp::object> f_equal(new lisp::object(fn_equal));

boost::shared_ptr<lisp::object> a_atom(new lisp::object(std::string("ATOM")));
boost::shared_ptr<lisp::object> f_atom(new lisp::object(fn_atom));

boost::shared_ptr<lisp::object> a_cond(new lisp::object(std::string("COND")));
boost::shared_ptr<lisp::object> f_cond(new lisp::object(fn_cond));

boost::shared_ptr<lisp::object> a_lambda(new lisp::object(std::string("LAMBDA")));
boost::shared_ptr<lisp::object> f_lambda(new lisp::object(fn_lambda));

boost::shared_ptr<lisp::object> a_label(new lisp::object(std::string("LABEL")));
boost::shared_ptr<lisp::object> f_label(new lisp::object(fn_label));


boost::shared_ptr<lisp::object> env = make_cons(make_cons(a_quote,make_cons(f_quote,nul)),nul);


append(env,make_cons(a_car, make_cons(f_car,nul)));
append(env,make_cons(a_cdr, make_cons(f_cdr,nul)));
append(env,make_cons(a_cons, make_cons(f_cons,nul)));
append(env,make_cons(a_equal, make_cons(f_equal,nul)));
append(env,make_cons(a_atom, make_cons(f_atom,nul)));
append(env,make_cons(a_cond, make_cons(f_cond,nul)));
append(env,make_cons(a_lambda, make_cons(f_lambda,nul)));
append(env,make_cons(a_label, make_cons(f_label,nul)));

boost::shared_ptr<lisp::object> a_tee(new lisp::object(std::string("#T")));
tee = a_tee;
nil = make_cons(nul,nul);

return env;
}

Wednesday, August 11

OpenGL Development on Windows with Slime


I've been playing with OpenGL, Common Lisp (SBCL, CCL, Lispworks Personal) and SLIME again. Mostly I've been establishing a S-Expression exporter for Blender so I can export entire scenes to the engine I'm working on.




I did discover some wrinkles of working on OpenGL development with SLIME in Windows. CCL uses the :spawn communication style in SLIME which uses it's multi-threaded nature to run each repl request in a seperate thread from the executing lisp code. So if you have your OpenGL app running in the background as you develop it, if you write an experimental form at the repl that includes OpenGL calls, its going to fail, since it will not be executing in the main OpenGL thread.




The solution is to fall back on the nil communication style, which is not threaded and is blocking. This has the less than desirable effect of blocking your repl until the executing app finishes. This can be circumvented by calling swank-handle-requests - like so




(iterate
(initially (progn ,init-forms))
(with-simple-restart
(skip-photons-loop "Skip photons loop body")
,@body)
#+photons-debug
(with-simple-restart
(skip-swank-request "Skip swank evaluation")
(let ((connection
(or swank::*emacs-connection* (swank::default-connection))))
(swank::handle-requests connection t)))
(glfwSwapBuffers)
(until ,exit-test)
(finally (progn ,exit-forms))))



There are actually some subtelties here. The skip-photons-loop restart allows you to recover from an error in the loop body, by recompiling the offending form, when the handle-requests function is called, while the skip-swank-request restart allows you to recover from entering an invalid form at the repl without losing the application.

Monday, September 14

Rooms at last

You might have noticed a hiatus on this project, but finally I returned to it to finish off the floorplan: here it is. The rooms were generated mainly by using polar coordinates around the space defined by the corridoor end points, and turned out to be relatively straightforward.



At this point, the project really needs to switch away from pyglet to something more useful, that can do the needed 3d extrusion more easily...

Sunday, July 5

Dungeon Generation Part 4: Clearing space for the rooms

Here we are, we now have spaces where the rooms are going to go: my initial attempts at creating rooms did not go so well, so I had to backtrack and refactor the code, in order to get this to work.



Its actually a fairly simple computation: work out the length of the corridor, scale it, and re-adjust the co-ordinates of each end. This requires some book keeping in terms of data structures: knowing which end terminates at which room, so that you don't accidentally srhink the worong end.

Wednesday, June 17

Dungeon Generation Part 3 : Doors

Thinking about key points in positioning a room, I decided doors were a good idea, so I tried to code up a routine that would reasonably place doors. I interpolated about 30% up along a line and placed a small black perpendicular line prependicular to the line along the centre of the corridor. I went with the simple rule of thumb that a point that connected more than two corridors was the centre of the room. I got this:




Not bad, but not quite right. Probably the best way forward is to decide on an overall radius that will establish the size of the room and fit the doors to that.

Monday, June 15

Dungeon Generation Part Two: Corridors

And once the network is complete, we do corridors. This is straigtforward geometry: extrude two lines at either end of our connecting line, perpendicular to said line, then join them up. When you do that you get this:




A floorplan which should extrude nicely into the third dimension. However there's one more thing to do before we tackle this, and that is the actual rooms. Which is where things get a tad harder..

Thursday, June 11

Dungeon generation for fun and..well, fun...

Recently I was inspired by the PixelCity series of articles. I was impressed what could be achieved by judicious application of basic OpenGL and some clever aesthetic decisions. I'm inspired enough to try the same thing, only with dungeons. My weapons of choice are pyglet and Python.


The first and most obvious thing to try was to generate a network of corridors. Here's what I ended up with.



I generated this mainly by picking a point and then connecting it to one of it's closest neighbours. The points are initially distributed on a grid and peturbed by a random amount to make the layout less "manhattan" like. The order the points are picked in is related to the grid order, which is why you see a "spine" at the side of the dungeon. I thought this would make for interersting gameplay: monster - infested branches connected by a relatively safe trunk. So this is what I went with. In the next part, we will get some corridoor walls. Once I have a decent looking dungeon, I will release the source.

Saturday, February 7

Give it a kick, it might still be going..


Just to prove I haven't been completely inactive on the project front since my flirtation with Lisp (which isn't over yet, either, just maturing slowly), here's a screenshot of yet another thing. Basically this is a mesh importer for the excellent OGRE engine, powered by wxWidgets and The Open Asset Import Library. It doesn't handle textures or bones yet, but it does more than enough for now as the Open Asset Import Library imports an ungodly number of formats, it should be possible to magpie bits of free art from the web in whatever format and assemble them into a passable scene using this thing..

Tuesday, December 16

More Emacs Help

As well as keying the help provided by Google in Emacs to the major-mode, it's possible to use it with multiple libraries as well. For instance, the following snippet lets me look up documentation for one library (OGRE) by pressing 'o' and for another by pressing 'a' , rather like the Slime selector, discussed previously. It's a huseful way of pulling together doxygen API documentation when working on clonk-loads of libraries, as is all to typical of C++ or Java development.


(defun search-site-url (keyword &optional site inurl lucky)
"Do a Google search for KEYWORD. Restrict to SITE and INURL, if specified.
Jump to best match (I Feel Lucky) if LUCKY set.
"
;; (message (list keyword site inurl lucky))
(concat "http://www.google.com/"
(format "search?q=%s" (url-hexify-string keyword))
(if site (format "+site:%s" (url-hexify-string site)))
(if inurl (format "+inurl:%s" (url-hexify-string inurl)))
(if lucky "&btnI")))


(defun help-selector ()
(interactive)
(message "Select [%s]: "
(apply #'string (mapcar #'car help-selector-methods)))
(let* ((ch (save-window-excursion
(select-window (minibuffer-window))
(read-char)))
(params (find ch help-selector-methods :key #'car)))
(browse-url (apply #'search-site-url (thing-at-point 'symbol) (list (third params) (fourth params) (fifth params))))))

(defvar help-selector-methods '(( ?a "Assimp"
"assimp.sourceforge.net" "/lib_html/" t)
( ?o "Ogre"
"www.ogre3d.org" "/docs/api/html/" t)
( ?w "WxWidgets"
"docs.wxwidgets.org" "/stable/" t)))

(global-set-key [(shift f1)] 'help-selector)

Thursday, August 21

Lambda of the Daleks


As promised, the Daleks appear. They have been loaded dynamically from an md2 whilst the framework was running. Sometimes I find myself fighting my C++ reflexes. There's no need to bring the framework down in order to change it. It still seems like magic when I edit a function definition, compile it and *bam* the change appears in the game window...






...one day, all software will be developed this way. Onwards..

Sunday, August 17

And now for a bit of swank..


Now my testbed talks happily to emacs via slime. That's the "exploratory" part of "exploratory game developement.". Next up: Daleks...

And now for something completley different.


You guessed it - another testbed. This time, in 100% Common Lisp (SBCL), just for comparison.

Wednesday, August 13

More testbed


I've done some more playing around with that testbed, integrating ECL and G3Ds Shape API. It works ok, apart from a few wrinkles. It might even be the beginnings of a nice educational app for learning 3d transforms and the like. The main improvement is that the REPL gives you error feedback rather than crashing the app when you give it a bad form. A distinct must for interactivity...

Tuesday, July 22

What next?


Embedding ECLS in a 3D Engine - G3D - proved remarkably easy. The question is - now what. What should live on the Lisp side and what should live on the C++ side? Geometry for C++? AI for Lisp..what about collisions, then? Hmm..

Wednesday, July 16

Using Google for Context Sensitive Help in Emacs


Here's a hack that combines three of the most useful things in the known Universe: Google, Emacs and Firefox. Emacs 22.x has a standard method for invoking a browser: the browse URL function. To customise it use:


M-x customize-group browse-url
We are interested in the settings: Browse Url Browser Function which should be set to
browse-url-firefox
Then there is Browse Url Firefox Program which should be set to the full path of wherever firefox lives on your machine, and Browse Url Firefox New Window Is Tab which should be on to prevent multiple firefoxen cluttering up your windows when you try this.


Now that you have customized everything, you drop a crafted function into your .emacs and bind it to a key:



;; -- INTEGRATED HELP
(require 'url)
(defun search-site-url (site url keyword)
(concat "http://www.google.com/"
(format "search?q=%s+site:%s+inurl:%s&btnI"
(url-hexify-string keyword)
(url-hexify-string site)
(url-hexify-string url))))

(defun wxhelp ()
"Open a window showing the wxWidgets documentation for the word under the point"
(interactive)
(browse-url (search-site-url "docs.wxwidgets.org" "2\\\\.8\\\\.6"
(thing-at-point 'symbol))))

(global-set-key "\C-h\C-w" 'wxhelp)



Now, if you type Control H Control W while the cursor is over a symbol, Emacs will look it up via Google. For instance, if your cursor is over wxApp, by the magic of "I'm feeling Lucky", Google will find http://docs.wxwidgets.org/2.8.6/wx_wxapp.html which is the manual page for wxApp



Obviously the interesting function here is

site-search-url
which munges up an URL to feed to google to make it search a specific site for a keyword, filtering URLs that do not contain a certian string. The highly escaped \\\\ is to ensure that 2\.8\.6 appears in the final URL so that Google treats . as a literal.



The great utility of this hack is its universiality. It will work for any language or library with a sanely organised reference web site. Language specific lookup functions based on sites like cppreference.com are left as an exercise for the reader...