class LavaLampPawn extends GamePawn;
var DynamicLightEnvironmentComponent LightEnvironment;
//override to make player mesh visible by default
simulated event BecomeViewTarget( PlayerController PC )
{
Super.BecomeViewTarget(PC);
}
simulated singular event Rotator GetBaseAimRotation()
{
local Rotator tempRot;
tempRot = Rotation;
SetRotation(tempRot);
return tempRot;
}
defaultproperties
{
bCanWalk = true
bCanSwim = false
bCanFly = false
bCanClimbLadders = false
bWorldGeometry = true
bCollideActors = false
bCollideWorld = false
bBlockActors = false
Begin Object Class=DynamicLightEnvironmentComponent Name=MyLightEnvironment
bSynthesizeSHLight=TRUE
bIsCharacterLightEnvironment=TRUE
bUseBooleanEnvironmentShadowing=FALSE
End Object
Components.Add(MyLightEnvironment)
LightEnvironment=MyLightEnvironment
}
General rambling about game development and design by a professional games developer. Pickings at Python, complaints about C, lashings of Lisp, and whatever else is the language du jour.
Friday, December 2
Unrealscript Overview Part Four : Player Pawn
Unrealscript Overview Part Three : NPC Controller
AI Controllers usually are implemented as State machines. Unrealscript has a state construct that makes this easy. Code enclosed in a named state block is executed only when the object enters that state. The controller has an explicit method for switching state called GotoState(). As this is a simple example I have not overridden any methods inside the state block, but have written short fragments of code that can be executed latentley. Latent functions are Unrealscript speak for functions that run in the background. There are a small group of these available to you (grepping through the Controller, Actor and Pawn classes should reaval them), mostly related to Pawn movmement. Here, the rising state sets the Pawn velocity when it is etered, and then periodically checks to see if the bubble has risen too far. When it has, it enters the Popping state, wherein it stands still for a second, and then is destroyed.
The Destroy() function brings us to another point. Objects derived from actor have a special property in that they are spawned rather than created with the new() operator. In our BubblePawn class we called a base method called SpawnDefaultController(), and set ControllerClass to class'LavaLamp.BubbleController'. SpawnDefaultController will then create an instance of that controller class, and ensure that the controller Poseses (ie controls/owns) the Pawn by calling Posses. It is perfectly theoretially possible to pass a pawn around different controllers to implement different behaviours - although the state mechanism makes this less useful than it might sound.
class BubbleController extends AIController;
var float RisingDistance;
var float ExtraVelocity;
simulated Event SetInitialState()
{
ExtraVelocity = FRand() * 1.25;
bScriptInitialized = true;
GotoState('Rising');
}
state Rising
{
Begin:
Pawn.Velocity = vect(0.0, 0.0, 0.85);
Pawn.Velocity.Z += ExtraVelocity;
RisingLoop:
if (Pawn.Location.Z > RisingDistance) {
StopLatentExecution();
GotoState('Popping');
}
Sleep(1);
goto 'RisingLoop';
}
state Popping
{
Begin:
Pawn.Velocity = vect(0.0, 0.0, 0.0);
PoppingLoop:
Sleep(1);
Pawn.Destroy();
Pawn = None;
Destroy();
goto 'PoppingLoop';
}
defaultproperties
{
RisingDistance = 2048.0
}
Unrealscript Overview Part Two : NPC Pawn
In BubblePawn, we are also initalising a material instance, an instance of a Material created with the Material Editor and given some named parameters within that editor. Creating a Material Instance lets us vary those parameters on a per-instance basis without creating a whole new material. This is what this looks like in the content browser.
..and in the Material Editor.
Tick() is the routine called every time the game state is updated. Overriding it enables you to completely alter the behavior of a given Pawn. In our case we are simply moving the bubble upward and varying it's scale to give a throbbing effect.
We are using dynamic lighting in our environment and thus we define properties for dynamic lighting of the object in our default properties. To aid speed we also turn off as many collision checks as possible. The static mesh component that we add is the mesh drawn by the renderer for this Pawn. The components are simply entries in a dynamic array of components and may be created or destroyed dynamically. The sprite removal is to simply remove a default placeholder sprite automatically added by the system, as this is a placeable and can actually be placed in the editor in the scene.
class BubblePawn extends Pawn placeable;
var float accumulatedTime;
var StaticMeshComponent BubbleMesh;
var MaterialInstanceConstant MatInst;
simulated event PostBeginPlay()
{
local LinearColor specularColor;
accumulatedTime = FRand() * 180.0;
super.PostBeginPlay();
`log("spawning bubble ... ");
SpawnDefaultController();
MatInst = new(None) Class'MaterialInstanceConstant';
MatInst.SetParent(BubbleMesh.GetMaterial(0));
BubbleMesh.SetMaterial(0, MatInst);
MatInst.SetScalarParameterValue('LavaEmissiveMultiplier', FRand());
MatInst.SetScalarParameterValue('LavaSpecularPower', FRand() * 16.0);
specularColor = makeLinearColor(FRand(), FRand(), FRand(), 1.0);
MatInst.SetVectorParameterValue('LavalSpecularColor', specularColor);
}
event Tick(float DeltaTime)
{
accumulatedTime += DeltaTime;
SetDrawScale( 1.0 + 0.75 * Sin(accumulatedTime) );
Move(self.Velocity);
super.Tick(DeltaTime);
}
defaultproperties
{
WalkingPhysics=Phys_NONE
bCollideActors=false
bCollideWorld=false
DrawScale3D=(X=32.0,Y=32.0,Z=32.0)
bNoEncroachCheck = true
bIgnoreEncroachers = true
ControllerClass=class'LavaLamp.BubbleController'
Begin Object Class=DynamicLightEnvironmentComponent Name=BubbleLightEnvironment
ModShadowFadeoutTime=0.25
MinTimeBetweenFullUpdates=0.2
AmbientGlow=(R=.25,G=.25,B=.25,A=1)
AmbientShadowColor=(R=0.15,G=0.15,B=0.15)
LightShadowMode=LightShadow_ModulateBetter
ShadowFilterQuality=SFQ_High
bSynthesizeSHLight=TRUE
End Object
Components.Add(BubbleLightEnvironment)
Begin Object Class=StaticMeshComponent Name=InitialMesh
StaticMesh=StaticMesh'Lava.Meshes.Bubble'
bOwnerNoSee=false
LightEnvironment=BubbleLightEnvironment;
BlockRigidBody=false
CollideActors=false
BlockZeroExtent=false
BlockNonZeroExtent=false
End Object
Components.Add(InitialMesh);
Components.Remove(Sprite);
BubbleMesh = InitialMesh
name='Bubble';
}
Dumping Blender Scenes as Lua
Tuesday, November 15
Implementing Lisp in C++
As usual, here's the code.
Thursday, October 20
Exporting Lisp From Blender
My Lisp-based export problems are over!
Wednesday, September 7
Things that freak me out about Clojure
* Maps and Sets are functions. You can apply a value to a set and it returns that value if it's a member, nil if it's not. Similarly with maps. Apply a value to a map and it either returns nil, or the value the value maps to in the map. Seems strange at first but it allows some neat filtering tricks
* Keywords are functions. Keywords evaluate to themselves, just like in CL. Unlike in CL they are also functions so (:keyword map) actually tests map for membership of :keyword and returns the value mapped to the keyword. Again, this is handy.
* -> : This macro flattens out nested function calls so (-> fn1 fn2 fn3) is (fn1 (fn2 (fn3))). I really dislike this: I can see how coming from Java it would seemingly make code easier to read, and reduce syntatic noise, but I find the parens useful as a guide for indentation. This might be down to taste.
* Iteration is totally different. There's nothing like the LOOP or ITERATE. There is a while and a loop/recur, but the sequence handling is sufficently powerful that this is a fallback. It forces me to think differently and this is definitely a good thing.
* Probably more to come, but meanwhile I'll keep in mind these rules for writing Clojure.
Saturday, August 13
Code Kata For Game Developers
- Mandelbrot set plotting
- Simple raytracer.
- Breakout clone.
- Simple MUD.
Wednesday, June 1
Unrealscript Overview Part One: The Game Info Class
Monday, May 16
Emas and the Unreal Development Kit Workflow
This is the support code I have: a set of interactive functions for invoking the udk, building code, running the editor. It creates a group of settings that can be customised with M-x customize-group udk. I watch the log in an emacs buffer via global-revert-tail mode. I have not yet added support for UDK log files to compilation mode, but thats an obvious tweak I'll try at some point. I've had enough fiddling with regular expressions for now.
PS: I thought I'd reproduce the unrealscript mode here, just in case. It lives where you put your other extra mode files.
Thursday, April 7
Bricolage - Now with font rendering.
The next step for the bricolage engine I'm writing is to add font rendering. Something one should add early on in development, so you can read all those panic messages from your machine and keep an eye on CPU usage and memory usage as you go along. For this I have been using Angel Code's BMFont tool, which is noticeably better than any other I've seen: even supports signed distance fields..
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.
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...
/*
========================================================================
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
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.



