Sunday, March 4

Unrealscript mode for Emacs

Edit: I've since abandoned this mode and moved to a simpler (but less buggy) version not based on cc-mode that lives here


;;; unrealscript-mode.el --- unrealscript mode derived from cc-mode

;; Author: 2007 John Connors
;; Maintainer: John Connors <johnc at yagc dot co dot uk>
;; Created: March 2007
;; Version: See cc-mode.el
;; Keywords: unrealscript cc-mode languages oop

;; This program is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation; either version 2 of the License, or
;; (at your option) any later version.
;;
;; This program is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;; GNU General Public License for more details.
;;
;; You should have received a copy of the GNU General Public License
;; along with this program; see the file COPYING. If not, write to
;; the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
;; Boston, MA 02111-1307, USA.

;;; Commentary:


;; Note: The interface used in this file requires CC Mode 5.30 or
;; later.

;;; Code:

(require 'cc-mode)

;; These are only required at compile time to get the sources for the
;; language constants. (The cc-fonts require and the font-lock
;; related constants could additionally be put inside an
;; (eval-after-load "font-lock" ...) but then some trickery is
;; necessary to get them compiled.)
(eval-when-compile
(require 'cc-langs)
(require 'cc-fonts))

(eval-and-compile
;; Make our mode known to the language constant system. Use Java
;; mode as the fallback for the constants we don't change here.
;; This needs to be done also at compile time since the language
;; constants are evaluated then.
(c-add-language 'unrealscript-mode 'java-mode))

;; unrealscript has no boolean but a string and a vector type.
(c-lang-defconst c-primitive-type-kwds
unrealscript
(append
'("bool" "name" "object" "actor" "string" "vector")
(delete "boolean"
;; Use append to not be destructive on the
;; return value below.
(append
;; Due to the fallback to Java, we need not give
;; a language to `c-lang-const'.
(c-lang-const c-primitive-type-kwds)
nil))))

(c-lang-defconst c-type-modifier-keywds
unrealscript
(append
'("config" "deprecated" "edfindable" "editconstarray"
"editinline" "export" "noexport" "globalconfig"
"localized" "const" "editconst" "input" "travel"
"skip" "export")
(c-lang-const c-type-modifier-keywds)
nil))


;; "Keywords introducing extra declaration specifiers in the region
;; between the header and the body \(i.e. the \"K&R-region\") in
;; declarations."

(c-lang-defconst c-postfix-decl-spec-kwds
unrealscript
(append
'("abstract" "native" "nativereplication" "nousercreate" "within"
"perobjectconfig" "transient" "noexport" "dependson" "exportstructs"
"cacheexempt" "hidedropdown" "parseconfig" "dontcollapsecategories"
"collapsecategories" "hidecategories" "showcategories" "placeable"
"notplaceable" "instanced")
(c-lang-const c-postfix-decl-spec-kwds)
nil))

;; "Keywords that may be followed by a comma separated list of type
;; identifiers, where each optionally can be prefixed by keywords. (Can
;; also be used for the special case when the list can contain only one
;; element.)"
(c-lang-defconst c-type-list-kwds
unrealscript
(cons "within"
(c-lang-const c-type-list-kwds)))

;; Function declarations begin with "function" in this language.
;; There's currently no special keyword list for that in CC Mode, but
;; treating it as a modifier works fairly well.
(c-lang-defconst c-modifier-kwds
unrealscript
(cons
"function"
(c-lang-const c-modifier-kwds)))


(c-lang-defconst c-block-decls-with-vars
unrealscript
(cons
"state"
(c-lang-const c-other-block-decl-kwds)))

;; "Keywords that may be followed by a parenthesis expression that doesn't
;; contain type identifiers."
(c-lang-defconst c-paren-nontype-kwds
unrealscript
(append
'("state" "var")
(c-lang-const c-paren-nontype-kwds)
nil))


;; "Keywords that may be followed by a parenthesis expression containing
;; type identifiers separated by arbitrary tokens."
(c-lang-defconst c-paren-type-kwds
unrealscript
(append
'("config" "dependson")
(c-lang-const c-paren-type-kwds)))

;; ;; No cpp in this language, but there's still a "#exec" directive to
;; ;; fontify. (The definitions for the extra keywords above are enough
;; ;; to incorporate them into the fontification regexps for types and
;; ;; keywords, so no additional font-lock patterns are required.)
;; (c-lang-defconst c-cpp-matchers
;; unrealscript
;; ;; Use the eval form for `font-lock-keywords' to be able to use
;; ;; the `c-preprocessor-face-name' variable that maps to a
;; ;; suitable face depending on the (X)Emacs version.
;; '(eval . (list "^\\s *\\(#exec\\)\\>\\(.*\\)"
;; (list 1 c-preprocessor-face-name)
;; '(2 font-lock-string-face))))

(defcustom unrealscript-font-lock-extra-types nil
"*List of extra types (aside from the type keywords) to recognize in Unrealscript mode.
Each list item should be a regexp matching a single identifier."
)

(defconst unrealscript-font-lock-keywords-1
(c-lang-const c-matchers-1 unrealscript)
"Minimal highlighting for UNREALSCRIPT mode.")

(defconst unrealscript-font-lock-keywords-2
(c-lang-const c-matchers-2 unrealscript)
"Fast normal highlighting for UNREALSCRIPT mode.")

(defconst unrealscript-font-lock-keywords-3
(c-lang-const c-matchers-3 unrealscript)
"Accurate normal highlighting for UNREALSCRIPT mode.")

(defvar unrealscript-font-lock-keywords unrealscript-font-lock-keywords-3
"Default expressions to highlight in UNREALSCRIPT mode.")

(defvar unrealscript-mode-syntax-table nil
"Syntax table used in unrealscript-mode buffers.")
(or unrealscript-mode-syntax-table
(setq unrealscript-mode-syntax-table
(funcall (c-lang-const c-make-mode-syntax-table unrealscript))))

(defvar unrealscript-mode-abbrev-table nil
"Abbreviation table used in unrealscript-mode buffers.")

(c-define-abbrev-table 'unrealscript-mode-abbrev-table
;; Keywords that if they occur first on a line might alter the
;; syntactic context, and which therefore should trig reindentation
;; when they are completed.
'(("else" "else" c-electric-continued-statement 0)
("while" "while" c-electric-continued-statement 0)))

(defvar unrealscript-mode-map (let ((map (c-make-inherited-keymap)))
;; Add bindings which are only useful for UNREALSCRIPT
map)
"Keymap used in unrealscript-mode buffers.")

(easy-menu-define unrealscript-menu unrealscript-mode-map "UNREALSCRIPT Mode Commands"
;; Can use `unrealscript' as the language for `c-mode-menu'
;; since its definition covers any language. In
;; this case the language is used to adapt to the
;; nonexistence of a cpp pass and thus removing some
;; irrelevant menu alternatives.
(cons "UNREALSCRIPT" (c-lang-const c-mode-menu unrealscript)))

;;;###Autoload
(add-to-list 'auto-mode-alist '("\\.uc\\'" . unrealscript-mode))

;;;###autoload
(defun unrealscript-mode ()
"Major mode for editing UNREALSCRIPT UnrealScript is a
Java-like object-orientated programming (OOP) language created by
Epic Games for programming in-game content for the UnrealEngine.

The hook `
c-mode-common-hook' is run with no args at mode
initialization, then `
unrealscript-mode-hook'.

Key bindings:
\\{unrealscript-mode-map}"

(interactive)
(kill-all-local-variables)
(c-initialize-cc-mode t)
(set-syntax-table unrealscript-mode-syntax-table)
(setq major-mode 'unrealscript-mode
mode-name "UnrealScript"
local-abbrev-table unrealscript-mode-abbrev-table
abbrev-mode t)
(use-local-map c-mode-map)
;; `c-init-language-vars' is a macro that is expanded at compile
;; time to a large `setq' with all the language variables and their
;; customized values for our language.
(c-init-language-vars unrealscript-mode)
;; `c-common-init' initializes most of the components of a CC Mode
;; buffer, including setup of the mode menu, font-lock, etc.
;; There's also a lower level routine `c-basic-common-init' that
;; only makes the necessary initialization to get the syntactic
;; analysis and similar things working.
(c-common-init 'unrealscript-mode)
(easy-menu-add unrealscript-menu)
(run-hooks 'c-mode-common-hook)
(run-hooks 'unrealscript-mode-hook)
(setq font-lock-keywords-case-fold-search t)
(c-update-modeline))


(provide 'unrealscript-mode)


Thursday, March 1

Isometric Projection Matrix

One thing that seems to get asked a lot is how to create an isometric style game using a 3d pipline and textured quads, instead of 2d sprite tiles. It is also a question that does not seem to get answered much, so I thought I would whip out a quick demonstration of how to do it with the Irrlicht Engine.


One thing to note is that this is not a textbook isometric projection which is a form of axonometric projection, but a fudge that makes the width and height of a cube in the viewport equal. This emulates tile based games nicely. Of course, with a "real" projection it might be possible to dynamically change the projection angle, which might be an interesting effect.




#include <irrlicht.h>

using namespace irr;

using namespace core;
using namespace scene;
using namespace video;
using namespace io;
using namespace gui;



int main()
{


IrrlichtDevice *device =
createDevice( video::EDT_SOFTWARE2, dimension2d<s32>(640, 480), 16,
false, false, false, 0);

device->setWindowCaption(L"Isometric Projection");

IVideoDriver* driver = device->getVideoDriver();


while(device->run())
{
/*
Anything can be drawn between a beginScene() and an endScene()
call. The beginScene clears the screen with a color and also
the depth buffer if wanted. Then we let the Scene Manager and
the GUI Environment draw their content. With the endScene()
call everything is presented on the screen.
*/

driver->beginScene(true, true, SColor(255,100,101,140));

/**
* we are not interested in this transform so let us set it to
* identity
*/

driver->setTransform(video::ETS_WORLD, core::matrix4());
driver->setTransform(video::ETS_VIEW, core::matrix4());

/**
* first portion of transform - in matrix form
* x' = (x - z)
* y' = y + 0.5 * ( x + z )
* this maps the z to the x and y axes in such a way that
* the result appears isometric.
*/

matrix4 projMatrix;
projMatrix.makeIdentity();
projMatrix.M[0] = 1.0f;
projMatrix.M[8] = -1.0f;
projMatrix.M[1] = 0.5f;
projMatrix.M[5] = 1.0f;
projMatrix.M[9] = 0.5f;
projMatrix.M[10] = 0.0;

/**
* second portion of transform -- scale to fit clipping
* volume. If the scale is 1.0f then the unit vectors fit the
* clipping volume. The volume is a cuboid, centered in the origin.
* The scaling will determine the size of this volume which will
* contain the portion of the world that we can see.
*/

f32 scale = 4.0f;
matrix4 clipMatrix;
clipMatrix.buildProjectionMatrixOrthoLH(2 * scale, 2 * scale ,-1 * scale, 2 * scale);


/**
* concatentate transform - we multiply transforms together in
* the opposite order to that which we would apply them to a
* vector because of the law of associativity applies to
* matrices, but not commutivity and NM != MN fun,
* eh..matrices..gotta luv em..
*/

projMatrix = clipMatrix * projMatrix;

/**
* ok we now have our projection matrix
*/

driver->setTransform(video::ETS_PROJECTION, projMatrix);


/**
* draw unit x, y, and z vectors in our new space
*/


// x axis is red
driver->draw3DLine(vector3df(-1.0, 0.0, 0.0),
vector3df(1.0, 0.0, 0.0),
SColor(255,255,0,0));

// y axis is green
driver->draw3DLine(vector3df(0.0, -1.0, 0.0),
vector3df(0.0, 1.0, 0.0),
SColor(255,0,255,0));

// z axis is blue
driver->draw3DLine(vector3df(0.0, 0.0, -1.0),
vector3df(0.0, 0.0, 1.0),
SColor(255,0,0,255));

/**
* done
*/

driver->endScene();
}

device->drop();

return 0;
}