1
0
forked from 0ad/0ad

Atlas: Partial porting of the Terrain section to JS.

Slightly nicer rendering of circular brush previews.

This was SVN commit r5168.
This commit is contained in:
Ykkrosh 2007-06-12 22:01:25 +00:00
parent c113743348
commit 52aa29681c
9 changed files with 319 additions and 79 deletions

View File

@ -1,28 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<lists>
<attachpoints>
<item> head </item>
<item> helmet </item>
<item> head_extra </item>
<item> l_hand </item>
<item> shield </item>
<item> l_forearm </item>
<item> l_shoulder </item>
<item> r_hand </item>
<item> r_forearm </item>
<item> r_shoulder </item>
<item> chest </item>
<item> back </item>
<item> shoulders </item>
<item> l_leg </item>
<item> r_leg </item>
<item> l_hip </item>
<item> r_hip </item>
<item> tree </item>
<item> props_main </item>
<item> props_fancy </item>
<item>head </item>
<item>helmet </item>
<item>head_extra </item>
<item>l_hand </item>
<item>shield </item>
<item>l_forearm </item>
<item>l_shoulder </item>
<item>r_hand </item>
<item>r_forearm </item>
<item>r_shoulder </item>
<item>chest </item>
<item>back </item>
<item>shoulders </item>
<item>l_leg </item>
<item>r_leg </item>
<item>l_hip </item>
<item>r_hip </item>
<item>tree </item>
<item>props_main </item>
<item>props_fancy</item>
</attachpoints>
<animations>
@ -54,5 +52,5 @@
<colour name="Player 7" rgb="207 159 111" buttontext="255 255 255"/>
<colour name="Player 8" rgb="159 159 159" buttontext="255 255 255"/>
</playercolours>
</lists>
</lists>

View File

@ -0,0 +1,142 @@
var brushShapes = {
'circle': {
width: function (size) { return size },
height: function (size) { return size },
data: function (size) {
var data = [];
// All calculations are done in units of half-tiles, since that
// is the required precision
var mid_x = size-1;
var mid_y = size-1;
var scale = 1 / (Math.sqrt(2) - 1);
for (var y = 0; y < size; ++y)
{
for (var x = 0; x < size; ++x)
{
var dist_sq = // scaled to 0 in centre, 1 on edge
((2*x - mid_x)*(2*x - mid_x) +
(2*y - mid_y)*(2*y - mid_y)) / (size*size);
if (dist_sq <= 1)
data.push((Math.sqrt(2 - dist_sq) - 1) * scale);
else
data.push(0);
}
}
return data;
}
},
'square': {
width: function (size) { return size },
height: function (size) { return size },
data: function (size) {
var data = [];
for (var i = 0; i < size*size; ++i)
data.push(1);
return data;
}
}
};
var brush = {
shape: brushShapes['circle'],
size: 4,
strength: 1.0,
active: false,
send: function () {
Atlas.Message.Brush(
this.shape.width(this.size),
this.shape.height(this.size),
this.shape.data(this.size)
);
// TODO: rather than this hack to make things interact correctly with C++ tools,
// implement the tools in JS and do something better
Atlas.SetBrushStrength(this.strength);
}
};
function init(window)
{
window.sizer = new wxBoxSizer(wxOrientation.VERTICAL);
var tools = [
/* text label; internal tool name; button */
[ 'Modify', 'AlterElevation', undefined ],
[ 'Flatten', 'FlattenElevation', undefined ],
[ 'Paint', 'PaintTerrain', undefined ]
];
var selectedTool = null; // null if none selected, else an element of 'tools'
var toolSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Elevation tools'), wxOrientation.HORIZONTAL);
window.sizer.add(toolSizer, 0, wxStretch.EXPAND);
for each (var tool in tools)
{
var button = new wxButton(window, -1, tool[0]);
toolSizer.add(button, 1);
tool[2] = button;
// Explicitly set the background to the default colour, so that the button
// is always owner-drawn (by the wxButton code), rather than initially using the
// native (standard colour) button appearance then changing inconsistently later.
button.backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
(function(tool) { // (local scope)
button.onClicked = function () {
if (selectedTool == tool)
{
// Clicking on one tool twice should disable it
selectedTool = null;
this.backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
Atlas.SetCurrentTool('');
}
else
{
if (selectedTool)
selectedTool[2].backgroundColour = wxSystemSettings.getColour(wxSystemSettings.COLOUR_BTNFACE);
selectedTool = tool;
this.backgroundColour = new wxColour(0xEE, 0xCC, 0x55);
Atlas.SetCurrentTool(tool[1]);
brush.send();
}
};
})(tool);
}
var brushSizer = new wxStaticBoxSizer(new wxStaticBox(window, -1, 'Brush'), wxOrientation.VERTICAL);
window.sizer.add(brushSizer);
var shapes = [
[ 'Circle', brushShapes['circle'] ],
[ 'Square', brushShapes['square'] ]
];
var shapeNames = [];
for each (var s in shapes) shapeNames.push(s[0]);
var shapeBox = new wxRadioBox(window, -1, 'Shape', wxDefaultPosition, wxDefaultSize, shapeNames);
brushSizer.add(shapeBox);
shapeBox.onRadioBox = function(evt)
{
brush.shape = shapes[evt.integer][1];
brush.send();
};
var brushSettingsSizer = new wxFlexGridSizer(2);
brushSizer.add(brushSettingsSizer);
brushSettingsSizer.add(new wxStaticText(window, -1, 'Size'), 0, wxAlignment.RIGHT);
var sizeSpinner = new wxSpinCtrl(window, -1, 4, wxDefaultPosition, wxDefaultSize, wxSpinCtrl.ARROW_KEYS, 1, 100);
brushSettingsSizer.add(sizeSpinner);
sizeSpinner.onSpinCtrl = function(evt)
{
brush.size = evt.position;
brush.send();
};
brushSettingsSizer.add(new wxStaticText(window, -1, 'Strength'), wxAlignment.RIGHT);
var strengthSpinner = new wxSpinCtrl(window, -1, 10, wxDefaultPosition, wxDefaultSize, wxSpinCtrl.ARROW_KEYS, 1, 100);
brushSettingsSizer.add(strengthSpinner);
strengthSpinner.onSpinCtrl = function(evt)
{
brush.strength = evt.position / 10;
brush.send();
};
}

View File

@ -26,61 +26,130 @@
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/repetition/repeat.hpp>
#define FAIL(msg) do { JS_ReportError(cx, msg); return false; } while (false)
const int RUNTIME_SIZE = 1024*1024; // TODO: how much memory is needed?
const int STACK_CHUNK_SIZE = 8192;
////////////////////////////////////////////////////////////////
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
namespace
{
JS_ReportError(cx, "Unrecognised argument type");
// TODO: SetPendingException turns the error into a JS-catchable exception,
// but the error report doesn't say anything useful like the line number,
// so I'm just using ReportError instead for now (and failures are uncatchable
// and will terminate the whole script)
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unrecognised argument type")));
return false;
// Use templated structs instead of functions, so that we can use partial specialisation:
template<typename T> struct FromJSVal
{
static bool Convert(JSContext* cx, jsval WXUNUSED(v), T& WXUNUSED(out))
{
JS_ReportError(cx, "Unrecognised argument type");
// TODO: SetPendingException turns the error into a JS-catchable exception,
// but the error report doesn't say anything useful like the line number,
// so I'm just using ReportError instead for now (and failures are uncatchable
// and will terminate the whole script)
//JS_SetPendingException(cx, STRING_TO_JSVAL(JS_NewStringCopyZ(cx, "Unrecognised argument type")));
return false;
}
};
template<> struct FromJSVal<bool>
{
static bool Convert(JSContext* cx, jsval v, bool& out)
{
JSBool ret;
if (! JS_ValueToBoolean(cx, v, &ret)) return false;
out = (ret ? true : false);
return true;
}
};
template<> struct FromJSVal<float>
{
static bool Convert(JSContext* cx, jsval v, float& out)
{
jsdouble ret;
if (! JS_ValueToNumber(cx, v, &ret)) return false;
out = ret;
return true;
}
};
template<> struct FromJSVal<int>
{
static bool Convert(JSContext* cx, jsval v, int& out)
{
int32 ret;
if (! JS_ValueToInt32(cx, v, &ret)) return false;
out = ret;
return true;
}
};
template<> struct FromJSVal<std::wstring>
{
static bool Convert(JSContext* cx, jsval v, std::wstring& out)
{
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
jschar* ch = JS_GetStringChars(ret);
out = std::wstring(ch, ch+JS_GetStringLength(ret));
return true;
}
};
template<> struct FromJSVal<std::string>
{
static bool Convert(JSContext* cx, jsval v, std::string& out)
{
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
char* ch = JS_GetStringBytes(ret);
out = std::string(ch);
return true;
}
};
template<> struct FromJSVal<wxString>
{
static bool Convert(JSContext* cx, jsval v, wxString& out)
{
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
jschar* ch = JS_GetStringChars(ret);
out = wxString((const char*)ch, wxMBConvUTF16(), JS_GetStringLength(ret)*2);
return true;
}
};
template<typename T> struct FromJSVal<std::vector<T> >
{
static bool Convert(JSContext* cx, jsval v, std::vector<T>& out)
{
JSObject* obj;
if (! JS_ValueToObject(cx, v, &obj) || obj == NULL || !JS_IsArrayObject(cx, obj))
FAIL("Argument must be an array");
jsuint length;
if (! JS_GetArrayLength(cx, obj, &length))
FAIL("Failed to get array length");
for (jsint i = 0; i < length; ++i)
{
jsval el;
if (! JS_GetElement(cx, obj, i, &el))
FAIL("Failed to read array element");
T el2;
if (! FromJSVal<T>::Convert(cx, el, el2))
return false;
out.push_back(el2);
}
return true;
}
};
}
template<> bool ScriptInterface::FromJSVal<bool>(JSContext* cx, jsval v, bool& out)
template<typename T> bool ScriptInterface::FromJSVal(JSContext* cx, jsval v, T& out)
{
JSBool ret;
if (! JS_ValueToBoolean(cx, v, &ret)) return false;
out = (ret ? true : false);
return true;
return ::FromJSVal<T>::Convert(cx, v, out);
}
template<> bool ScriptInterface::FromJSVal<float>(JSContext* cx, jsval v, float& out)
{
jsdouble ret;
if (! JS_ValueToNumber(cx, v, &ret)) return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<int>(JSContext* cx, jsval v, int& out)
{
int32 ret;
if (! JS_ValueToInt32(cx, v, &ret)) return false;
out = ret;
return true;
}
template<> bool ScriptInterface::FromJSVal<std::wstring>(JSContext* cx, jsval v, std::wstring& out)
{
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
jschar* ch = JS_GetStringChars(ret);
out = std::wstring(ch, ch+JS_GetStringLength(ret));
return true;
}
template<> bool ScriptInterface::FromJSVal<std::string>(JSContext* cx, jsval v, std::string& out)
{
JSString* ret = JS_ValueToString(cx, v); // never returns NULL
char* ch = JS_GetStringBytes(ret);
out = std::string(ch);
return true;
}
// Explicit instantiation of functions that would otherwise be unused in this file
// but are required for linking with other files
template bool ScriptInterface::FromJSVal<wxString>(JSContext*, jsval, wxString&);
template<> jsval ScriptInterface::ToJSVal<float>(JSContext* cx, const float& val)
@ -148,6 +217,14 @@ namespace
wxPrintf(_T("wxJS %s: %s\n--------\n"), isWarning ? _T("warning") : _T("error"), logMessage.c_str());
}
// Functions in the Atlas.* namespace:
JSBool ForceGC(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* WXUNUSED(argv), jsval* WXUNUSED(rval))
{
JS_GC(cx);
return JS_TRUE;
}
JSBool LoadScript(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* argv, jsval* rval)
{
if (! ( JSVAL_IS_STRING(argv[0]) && JSVAL_IS_STRING(argv[1]) ))
@ -160,12 +237,8 @@ namespace
JS_GetStringChars(code), (uintN)JS_GetStringLength(code),
JS_GetStringBytes(name), rval);
}
JSBool ForceGC(JSContext* cx, JSObject* WXUNUSED(obj), uintN WXUNUSED(argc), jsval* WXUNUSED(argv), jsval* WXUNUSED(rval))
{
JS_GC(cx);
return JS_TRUE;
}
// Functions in the global namespace:
JSBool print(JSContext* cx, JSObject* WXUNUSED(obj), uintN argc, jsval* argv, jsval* WXUNUSED(rval))
{
@ -212,8 +285,8 @@ ScriptInterface_impl::ScriptInterface_impl()
JS_DefineFunction(m_cx, m_glob, "print", ::print, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
m_atlas = JS_DefineObject(m_cx, m_glob, "Atlas", NULL, NULL, JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_atlas, "ForceGC", ::ForceGC, 0, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
JS_DefineFunction(m_cx, m_atlas, "LoadScript", ::LoadScript, 2, JSPROP_ENUMERATE|JSPROP_READONLY|JSPROP_PERMANENT);
RegisterMessages(m_atlas);
}

View File

@ -29,7 +29,7 @@ public:
// Defined elsewhere:
// template <TR, T0..., TR (*fptr) (T0...)>
// void RegisterFunction(const wxString& functionName);
// void RegisterFunction(const char* functionName);
// (NOTE: The return type must be defined as a ToJSVal<TR> specialisation
// in ScriptInterface.cpp, else you'll end up with linker errors.)

View File

@ -23,6 +23,7 @@
#include "AtlasScript/ScriptInterface.h"
#include "Tools/Common/Tools.h"
#include "Tools/Common/Brushes.h"
static HighResTimer g_Timer;
@ -262,6 +263,21 @@ END_EVENT_TABLE()
static AtlasWindowCommandProc g_CommandProc;
AtlasWindowCommandProc& ScenarioEditor::GetCommandProc() { return g_CommandProc; }
namespace
{
// Wrapper function because SetCurrentTool takes an optional argument, which JS doesn't like
void SetCurrentTool_script(wxString name)
{
SetCurrentTool(name);
}
// TODO: see comment in terrain.js, and remove this when/if it's no longer necessary
void SetBrushStrength(float strength)
{
g_Brush_Elevation.SetStrength(strength);
}
}
ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterface)
: wxFrame(parent, wxID_ANY, _T(""), wxDefaultPosition, wxSize(1024, 768))
, m_FileHistory(_T("Scenario Editor")), m_ScriptInterface(scriptInterface)
@ -281,6 +297,8 @@ ScenarioEditor::ScenarioEditor(wxWindow* parent, ScriptInterface& scriptInterfac
//////////////////////////////////////////////////////////////////////////
// Script interface functions
GetScriptInterface().RegisterFunction<wxString, Datafile::GetDataDirectory>("GetDataDirectory");
GetScriptInterface().RegisterFunction<void, wxString, SetCurrentTool_script>("SetCurrentTool");
GetScriptInterface().RegisterFunction<void, float, SetBrushStrength>("SetBrushStrength");
{
const wxString relativePath (_T("tools/atlas/scripts/main.js"));

View File

@ -287,7 +287,7 @@ void SectionLayout::Build(ScenarioEditor& scenarioEditor)
m_PageMappings.insert(std::make_pair(name, (int)m_SidebarBook->GetPageCount()-1));
ADD_SIDEBAR_SCRIPT(_T("map"), _T("map.png"), _("Map"));
//ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
ADD_SIDEBAR_SCRIPT(_T("terrain"), _T("terrain.png"), _("Terrain"));
ADD_SIDEBAR(TerrainSidebar, _T("terrain.png"), _("Terrain"));
ADD_SIDEBAR(ObjectSidebar, _T("object.png"), _("Object"));
ADD_SIDEBAR(EnvironmentSidebar, _T("environment.png"), _("Environment"));

View File

@ -92,6 +92,11 @@ float Brush::GetStrength() const
return m_Strength;
}
void Brush::SetStrength(float strength)
{
m_Strength = strength;
}
//////////////////////////////////////////////////////////////////////////
class BrushShapeCtrl : public wxRadioBox

View File

@ -21,6 +21,7 @@ public:
std::vector<float> GetData() const;
float GetStrength() const;
void SetStrength(float strength);
void CreateUI(wxWindow* parent, wxSizer* sizer);

View File

@ -40,9 +40,10 @@ public:
float avg = (
m_Brush->Get(i-i0, j-j0) + m_Brush->Get(i-i0+1, j-j0) +
m_Brush->Get(i-i0, j-j0+1) + m_Brush->Get(i-i0+1, j-j0+1)
) / 4.f;
) / 4.f;
RenderTile(CColor(0, 1, 0, avg*0.8f), false);
RenderTileOutline(CColor(1, 1, 1, 0.4f), 1, true);
if (avg > 0.1f)
RenderTileOutline(CColor(1, 1, 1, std::min(0.4f, avg-0.1f)), 1, true);
}
const AtlasMessage::Brush* m_Brush;
@ -64,6 +65,8 @@ void Brush::SetData(int w, int h, const std::vector<float>& data)
m_H = h;
m_Data = data;
debug_assert(data.size() == w*h);
}
void Brush::GetCentre(int& x, int& y) const