Browse Source

Merge pull request #79 from blitz-research/develop

Develop up
abakobo 8 năm trước cách đây
mục cha
commit
b121c211a0
77 tập tin đã thay đổi với 2003 bổ sung819 xóa
  1. 2 0
      .gitignore
  2. 2 2
      ABOUT.HTML
  3. 9 1
      VERSIONS.TXT
  4. 7 3
      bin/env_windows.txt
  5. BIN
      bin/mx2cc_linux
  6. BIN
      bin/mx2cc_macos
  7. BIN
      bin/mx2cc_windows.exe
  8. 3 1
      modules/assimp/assimp/MARKS_FIXES.TXT
  9. 4 0
      modules/assimp/assimp/contrib/zlib/gzguts.h
  10. 25 7
      modules/jni/jni.monkey2
  11. 91 25
      modules/jni/native/jni_glue.cpp
  12. 11 4
      modules/jni/native/jni_glue.h
  13. 1 0
      modules/mojo/app/style.monkey2
  14. 7 5
      modules/mojo3d-loaders/loaders/assimp.monkey2
  15. 4 4
      modules/mojo3d-loaders/tests/assets/castle/CASTLE1.X
  16. BIN
      modules/mojo3d-loaders/tests/assets/castle/stone2.jpg
  17. BIN
      modules/mojo3d-loaders/tests/assets/miramar-skybox.jpg
  18. 4 1
      modules/mojo3d-loaders/tests/castle.monkey2
  19. 6 1
      modules/mojo3d-physics/physics/collider.monkey2
  20. 187 96
      modules/mojo3d-physics/physics/rigidbody.monkey2
  21. 14 14
      modules/mojo3d-physics/physics/world.monkey2
  22. 0 0
      modules/mojo3d-physics/tests/assets/dummy.txt
  23. 210 0
      modules/mojo3d-physics/tests/fpsthing.monkey2
  24. 128 0
      modules/mojo3d-physics/tests/qcollide.monkey2
  25. 5 58
      modules/mojo3d-physics/tests/shapes.monkey2
  26. 14 12
      modules/mojo3d-physics/tests/util.monkey2
  27. 3 3
      modules/mojo3d/graphics/animator.monkey2
  28. 1 1
      modules/mojo3d/graphics/deferredrenderer.monkey2
  29. 63 63
      modules/mojo3d/graphics/entity.monkey2
  30. 167 170
      modules/mojo3d/graphics/entityexts.monkey2
  31. 11 5
      modules/mojo3d/graphics/gltf2loader.monkey2
  32. 6 3
      modules/mojo3d/graphics/mesh.monkey2
  33. 1 1
      modules/mojo3d/graphics/model.monkey2
  34. 7 7
      modules/mojo3d/graphics/renderer.monkey2
  35. 3 3
      modules/mojo3d/graphics/spritebuffer.monkey2
  36. 1 0
      modules/mojo3d/tests/cubetest.monkey2
  37. 3 2
      modules/mojo3d/tests/ducks.monkey2
  38. 10 5
      modules/monkey/monkey.monkey2
  39. 1 3
      modules/monkey/native/bbstring.h
  40. 9 0
      modules/monkey/native/bbstring.mm
  41. 3 0
      modules/sdl2/makefile_windows.monkey2
  42. 10 5
      modules/std/audio/load_wav.monkey2
  43. 9 11
      modules/std/filesystem/filesystem.monkey2
  44. 5 1
      modules/std/requesters/native/requesters_linux.cpp
  45. 34 7
      products/android/Monkey2Game/app/src/main/java/com/monkey2/lib/Monkey2Activity.java
  46. 5 5
      scripts/common.bat
  47. 12 12
      scripts/common.sh
  48. 2 0
      scripts/updatemods.bat
  49. 1 1
      scripts/updatemx2cc.sh
  50. 3 2
      src/createrelease/createrelease.monkey2
  51. 1 1
      src/mx2cc/mx2.monkey2
  52. 2 2
      src/mx2cc/mx2cc.monkey2
  53. 7 4
      src/mx2cc/test.monkey2
  54. 10 2
      src/mx2cc/value.monkey2
  55. 0 197
      src/ted2/bin/t.txt
  56. BIN
      src/ted2/bin/tmp.zip
  57. 62 4
      src/ted2go/MainWindow.monkey2
  58. 5 1
      src/ted2go/Prefs.monkey2
  59. 9 12
      src/ted2go/Ted2.monkey2
  60. BIN
      src/ted2go/assets/themes/hollow_assets/checkbox_icons.png
  61. BIN
      src/ted2go/assets/themes/hollow_assets/dialog_skin.png
  62. BIN
      src/ted2go/assets/themes/hollow_assets/progressbar_icons.png
  63. BIN
      src/ted2go/assets/themes/hollow_assets/tabclose_icons.png
  64. BIN
      src/ted2go/assets/themes/hollow_assets/treeview_icons.png
  65. BIN
      src/ted2go/assets/themes/irc/blink.png
  66. BIN
      src/ted2go/assets/themes/irc/important.png
  67. BIN
      src/ted2go/assets/themes/irc/notice.png
  68. BIN
      src/ted2go/assets/themes/prime_assets/button_skin.png
  69. BIN
      src/ted2go/assets/themes/prime_assets/square.png
  70. BIN
      src/ted2go/assets/themes/prime_assets/tabbutton_skin.png
  71. 474 0
      src/ted2go/assets/themes/theme-hollow.json
  72. 28 19
      src/ted2go/assets/themes/theme-prime-base.json
  73. 2 1
      src/ted2go/assets/themes/theme-smooth.json
  74. 1 0
      src/ted2go/assets/themes/themes.json
  75. 6 1
      src/ted2go/dialog/PrefsDialog.monkey2
  76. 67 1
      src/ted2go/dialog/UpdateModulesDialog.monkey2
  77. 225 30
      src/ted2go/view/IRCView.monkey2

+ 2 - 0
.gitignore

@@ -52,3 +52,5 @@ Monkey2 (Linux)
 
 /src/c2mx2/LLVM
 /src/mx23d
+
+/modules/mojo3d-physics/tests/assets/E1M1_clean.obj

+ 2 - 2
ABOUT.HTML

@@ -45,7 +45,7 @@ a:hover{
 
 <body>
 
-<h1>Monkey2 v1.1.05b</h1>
+<h1>Monkey2 v1.1.06</h1>
 
 <p>Welcome to monkey2, an open source, user friendly, cross platform programming language by Mark Sibly.
 
@@ -171,7 +171,7 @@ Christian Leth Jeppesen!,
 Rene Damm,
 Richard McLoughlin,
 Kennie,
- ussell King,
+Russell King,
 gcmartijn,
 Darryl,
 Michael Hauck,

+ 9 - 1
VERSIONS.TXT

@@ -1,10 +1,18 @@
 
-***** Monkey2 v1.1.05c *****
+***** Monkey2 v1.1.06 *****
 
 * App no longer renders when minimized/inactive.
 
 * Added 'Internal' decl visibility to mx2cc, which means a decl is only visible to other decls within the same module.
 
+* Namespace mojo3d now uses degrees. Namespaces monkey.math and std.geom still use radians.
+
+* Mx2cc now tries to make docs for type extensions. No way to link to 'em yet. 
+
+* Entity Position, Basis, Scale, Matrix etc properties are now in *world* space. Use LocalPosition, LocalBasis etc for local space versions. Ditto SetPosition, Setbasis, SetScale etc methods.
+
+* Added subclasses for RigidBody: StaticBody, DynamicBody and KinematicBody, use these with New instead of RigidBody.
+
 
 ***** Monkey2 v1.1.05b *****
 

+ 7 - 3
bin/env_windows.txt

@@ -37,11 +37,15 @@ MX2_CPP_OPTS_WINDOWS_RELEASE=-O3 -DNDEBUG
 
 MX2_USE_MSVC=0
 
+'https://en.wikipedia.org/wiki/Microsoft_Windows_SDK
+'
+MX2_WINDOWS_KIT=10.0.15063.0
+
 PATH=${ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\bin;${PATH}
 
-INCLUDE=${ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\INCLUDE;${ProgramFiles(x86)}\Windows Kits\10\include\10.0.10586.0\ucrt;${ProgramFiles(x86)}\Windows Kits\10\include\10.0.10586.0\um;${ProgramFiles(x86)}\Windows Kits\10\include\10.0.10586.0\shared;
+INCLUDE=${ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\INCLUDE;${ProgramFiles(x86)}\Windows Kits\10\include\${MX2_WINDOWS_KIT}\ucrt;${ProgramFiles(x86)}\Windows Kits\10\include\${MX2_WINDOWS_KIT}\um;${ProgramFiles(x86)}\Windows Kits\10\include\${MX2_WINDOWS_KIT}\shared;
 
-LIB=${ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\LIB;${ProgramFiles(x86)}\Windows Kits\10\lib\10.0.10586.0\ucrt\x86;${ProgramFiles(x86)}\Windows Kits\10\lib\10.0.10586.0\um\x86;
+LIB=${ProgramFiles(x86)}\Microsoft Visual Studio 14.0\VC\LIB;${ProgramFiles(x86)}\Windows Kits\10\lib\${MX2_WINDOWS_KIT}\ucrt\x86;${ProgramFiles(x86)}\Windows Kits\10\lib\${MX2_WINDOWS_KIT}\um\x86;
 
 'Linker options
 MX2_LD_OPTS_MSVC=
@@ -99,7 +103,7 @@ MX2_ANDROID_APP_ABI=armeabi-v7a x86
 
 'Controls APP_PLATFORM in Application.mk for NDK
 '
-MX2_ANDROID_APP_PLATFORM=10
+MX2_ANDROID_APP_PLATFORM=android-14
 
 
 '***** RASPBIAN *****

BIN
bin/mx2cc_linux


BIN
bin/mx2cc_macos


BIN
bin/mx2cc_windows.exe


+ 3 - 1
modules/assimp/assimp/MARKS_FIXES.TXT

@@ -1,4 +1,6 @@
 
 Tweaked include/assimp.config.h to #define some stuff like BUILD_DLL etc.
 
-Changed pushpack1.h to use __MINGW32__ instead of HOST_MINGW
+Changed pushpack1.h to use __MINGW32__ instead of HOST_MINGW
+
+Added #include <unistd.h> to gzguts.h in zlib for LSEEK fix.

+ 4 - 0
modules/assimp/assimp/contrib/zlib/gzguts.h

@@ -27,6 +27,10 @@
 #endif
 #include <fcntl.h>
 
+#ifndef _MSC_VER
+#include <unistd.h>
+#endif
+
 #ifdef _WIN32
 #  include <stddef.h>
 #endif

+ 25 - 7
modules/jni/jni.monkey2

@@ -10,27 +10,30 @@ Extern
 
 Struct _jclass
 End
-
 Alias jclass:_jclass Ptr
 
 Struct _jobject
 End
-
 Alias jobject:_jobject Ptr
 
 Struct _jstring
 End
-
 Alias jstring:_jstring Ptr
 
-Struct _jfieldID
+Struct _jarray
+End
+Alias jarray:_jarray Ptr
+
+Struct _jobjectArray
 End
+Alias jobjectArray:_jobjectArray Ptr
 
+Struct _jfieldID
+End
 Alias jfieldID:_jfieldID Ptr
 
 Struct _jmethodID
 End
-
 Alias jmethodID:_jmethodID Ptr
 
 Class JNIEnv Extends Void
@@ -48,8 +51,15 @@ Class JNIEnv Extends Void
 	'fields...
 	'
 	Method GetFieldID:jfieldID( clazz:jclass,name:CString,sig:CString )
-	
+		
+	Method GetBooleanField:Bool( obj:jobject,fieldID:jfieldID )
+		
+	Method GetIntField:Int( obj:jobject,fieldID:jfieldID )
+		
 	Method GetObjectField:jobject( obj:jobject,fieldID:jfieldID )
+
+	Method GetStringField:String( obj:jobject,fieldID:jfieldID ) Extension="bbJNI::GetStringField"
+		
 	
 	'static fields...
 	
@@ -65,10 +75,12 @@ Class JNIEnv Extends Void
 	
 	Method CallBooleanMethod:Bool( obj:jobject,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallBooleanMethod"
 
-	Method CallStringMethod:String( obj:jobject,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStringMethod"
+	Method CallIntMethod:Int( obj:jobject,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallIntMethod"
 
 	Method CallObjectMethod:jobject( obj:jobject,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallObjectMethod"
 	
+	Method CallStringMethod:String( obj:jobject,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStringMethod"
+
 	'static methods...
 	'
 	Method GetStaticMethodID:jmethodID( clazz:jclass,name:CString,sig:CString )
@@ -77,6 +89,8 @@ Class JNIEnv Extends Void
 
 	Method CallStaticBooleanMethod:Bool( clazz:jclass,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStaticBooleanMethod"
 
+	Method CallStaticIntMethod:int( clazz:jclass,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStaticIntMethod"
+
 	Method CallStaticStringMethod:String( clazz:jclass,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStaticStringMethod"
 	
 	Method CallStaticObjectMethod:jobject( clazz:jclass,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::CallStaticObjectMethod"
@@ -87,6 +101,10 @@ Class JNIEnv Extends Void
 		
 	Method NewObject:jobject( clazz:jclass,methodID:jmethodID,args:Variant[] ) Extension="bbJNI::NewObject"
 		
+	Method NewObjectArray:jobjectArray( length:Int,clazz:jclass,init:jobject )
+		
+	Method SetObjectArrayElement( jarray:jobjectArray,index:Int,value:jobject )
+		
 	'refs...
 	'
 	Method NewGlobalRef:jobject( obj:jobject )

+ 91 - 25
modules/jni/native/jni_glue.cpp

@@ -3,30 +3,6 @@
 
 namespace bbJNI{
 
-	bbString JStringToString( JNIEnv *env,jstring jstr ){
-	
-		const char *cstr=env->GetStringUTFChars( jstr,0 );
-		
-		bbString str=bbString::fromCString( cstr );
-		
-		env->ReleaseStringUTFChars( jstr,cstr );
-		
-		return str;
-	}
-	
-	jstring StringToJString( JNIEnv *env,bbString str ){
-	
-		int n=str.utf8Length()+1;
-		
-		char *buf=new char[n];
-		
-		str.toCString( buf,n );
-		
-		jstring jstr=env->NewStringUTF( buf );
-		
-		return jstr;
-	}
-
 	jvalue *makeArgs( JNIEnv *env,bbArray<bbVariant> args ){
 	
 		jvalue *jargs=new jvalue[args.length()];
@@ -53,6 +29,12 @@ namespace bbJNI{
 				
 				jarg->i=val;
 			
+			}else if( type==bbGetType<jobject>() ){
+			
+				jobject jobj=arg.get<jobject>();
+			
+				jarg->l=jobj;
+			
 			}else if( type==bbGetType<bbString>() ){
 			
 				bbString str=arg.get<bbString>();
@@ -66,7 +48,39 @@ namespace bbJNI{
 				jstring jstr=env->NewStringUTF( buf );
 				
 				jarg->l=jstr;
+/*				
+			}else if( type==bbGetType<bbArray<bbBool>>() ){
+			
+				bbArray<bbBool> arr=arg.get<bbArray<bbBool>>();
+				
+				jbooleanArray jarr=env->NewBooleanArray( arr.length() );
+				
+				jboolean *jdata=env->GetBooleanArrayElements( jarr,0 );
+				
+				memcpy( jdata,arr.data(),arr.length()*sizeof(bbBool) );
+				
+				env->ReleaseBooleanArrayElements( jarr,jdata,0 );
+				
+				jarg->l=(jobject)jarr;
+				
+			}else if( type==bbGetType<bbArray<bbInt>>() ){
+
+				bbArray<bbInt> arr=arg.get<bbArray<bbInt>>();
+				
+				jintArray jarr=env->NewIntArray( arr.length() );
+				
+				jint *jdata=env->GetIntArrayElements( jarr,0 );
+				
+				memcpy( jdata,arr.data(),arr.length()*sizeof(bbInt) );
+				
+				env->ReleaseIntArrayElements( jarr,jdata,0 );
+				
+				jarg->l=(jobject)jarr;
+			
+			}else if( type==bbGetType<bbArray<jobject>>() ){
 			
+			}else if( type==bbGetType<bbArray<bbString>>() ){
+*/			
 			}else{
 			
 				bbRuntimeError( "Can't evaluate JNI method param of typ:"+type->toString() );
@@ -78,6 +92,37 @@ namespace bbJNI{
 		return jargs;
 	}
 	
+	bbString JStringToString( JNIEnv *env,jstring jstr ){
+	
+		const char *cstr=env->GetStringUTFChars( jstr,0 );
+		
+		bbString str=bbString::fromCString( cstr );
+		
+		env->ReleaseStringUTFChars( jstr,cstr );
+		
+		return str;
+	}
+	
+	jstring StringToJString( JNIEnv *env,bbString str ){
+	
+		int n=str.utf8Length()+1;
+		
+		char *buf=new char[n];
+		
+		str.toCString( buf,n );
+		
+		jstring jstr=env->NewStringUTF( buf );
+		
+		return jstr;
+	}
+	
+	bbString GetStringField( JNIEnv *env,jobject obj,jfieldID fieldID ){
+	
+		jstring jstr=(jstring)env->GetObjectField( obj,fieldID );
+		
+		return JStringToString( env,jstr );
+	}
+
 	void CallVoidMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args ){
 		
 		jvalue *jargs=makeArgs( env,args );
@@ -98,6 +143,17 @@ namespace bbJNI{
 		return r;
 	}
 
+	bbInt CallIntMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args ){
+		
+		jvalue *jargs=makeArgs( env,args );
+		
+		bbInt r=env->CallIntMethodA( obj,methodID,jargs );
+		
+		delete[] jargs;
+		
+		return r;
+	}
+
 	bbString CallStringMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args ){
 		
 		jvalue *jargs=makeArgs( env,args );
@@ -140,6 +196,17 @@ namespace bbJNI{
 		return r;
 	}
 	
+	bbInt CallStaticIntMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args ){
+		
+		jvalue *jargs=makeArgs( env,args );
+		
+		bbInt r=env->CallStaticIntMethodA( clazz,methodID,jargs );
+		
+		delete[] jargs;
+		
+		return r;
+	}
+	
 	bbString CallStaticStringMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args ){
 		
 		jvalue *jargs=makeArgs( env,args );
@@ -174,4 +241,3 @@ namespace bbJNI{
 	}
 	
 }
-

+ 11 - 4
modules/jni/native/jni_glue.h

@@ -5,11 +5,20 @@
 
 namespace bbJNI{
 
+	bbString JStringToString( JNIEnv *env,jstring jstr );
+	
+	jstring StringToJString( JNIEnv *env,bbString str );
+	
+	
+	bbString GetStringField( JNIEnv *env,jobject obj,jfieldID fieldID );
+
 
 	void CallVoidMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args );
 	
 	bbBool CallBooleanMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args );
 
+	bbInt CallIntMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args );
+
 	bbString CallStringMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args );
 
 	jobject CallObjectMethod( JNIEnv *env,jobject obj,jmethodID methodID,bbArray<bbVariant> args );
@@ -19,6 +28,8 @@ namespace bbJNI{
 	
 	bbBool CallStaticBooleanMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args );
 
+	bbInt CallStaticIntMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args );
+
 	bbString CallStaticStringMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args );
 
 	jobject CallStaticObjectMethod( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args );
@@ -27,8 +38,4 @@ namespace bbJNI{
 	jobject NewObject( JNIEnv *env,jclass clazz,jmethodID methodID,bbArray<bbVariant> args );
 	
 
-	bbString JStringToString( JNIEnv *env,jstring jstr );
-	
-	jstring StringToJString( JNIEnv *env,bbString str );
-
 }

+ 1 - 0
modules/mojo/app/style.monkey2

@@ -6,6 +6,7 @@ Namespace mojo.app
 Class Style
 
 	Method New()
+		_font=App.DefaultFont		
 	End
 	
 	Method New( style:Style )

+ 7 - 5
modules/mojo3d-loaders/loaders/assimp.monkey2

@@ -55,6 +55,8 @@ Class AssimpLoader
 			
 			Local aimesh:=_scene.mMeshes[i]
 			
+			If i=0 mesh.AddMaterials( 1 )
+			
 			LoadMesh( aimesh,mesh,Null,False )
 		Next
 		
@@ -73,7 +75,7 @@ Class AssimpLoader
 			
 			Local aimesh:=_scene.mMeshes[i]
 			
-			If i mesh.AddMaterials( 1 )
+			mesh.AddMaterials( 1 )
 				
 			LoadMesh( aimesh,mesh,Null,False )
 			
@@ -234,9 +236,9 @@ Class AssimpLoader
 		Local rot:=matrix.m.Scale( 1/scl.x,1/scl.y,1/scl.z )
 		Local pos:=matrix.t
 
-		model.Position=pos
-		model.Basis=rot
-		model.Scale=scl
+		model.LocalPosition=pos
+		model.LocalBasis=rot
+		model.LocalScale=scl
 		
 		_nodes[ node.mName.data ]=model
 		_entityIds[ node.mName.data ]=_entities.Length
@@ -255,7 +257,7 @@ Class AssimpLoader
 			
 			Local aimesh:=_scene.mMeshes[ node.mMeshes[i] ]
 			
-			If i mesh.AddMaterials( 1 )
+			mesh.AddMaterials( 1 )
 			
 			LoadMesh( aimesh,mesh,model,boned )
 			

+ 4 - 4
modules/mojo3d-loaders/tests/assets/castle/CASTLE1.X

@@ -11,7 +11,7 @@ Material x3ds_mat_castlestone {
 0.898039, 0.898039, 0.898039;;
 0.000000, 0.000000, 0.000000;;
 TextureFilename {
-"CASTLEST.JPG";
+"castlest.jpg";
 }
 }
 
@@ -21,7 +21,7 @@ Material x3ds_mat_Material__3 {
 0.898039, 0.898039, 0.898039;;
 0.000000, 0.000000, 0.000000;;
 TextureFilename {
-"OLDBRIC.JPG";
+"oldbric.jpg";
 }
 }
 
@@ -31,7 +31,7 @@ Material x3ds_mat_shingle {
 0.898039, 0.898039, 0.898039;;
 0.000000, 0.000000, 0.000000;;
 TextureFilename {
-"SHINGLE.JPG";
+"shingle.jpg";
 }
 }
 
@@ -41,7 +41,7 @@ Material x3ds_mat_Material__8 {
 0.898039, 0.898039, 0.898039;;
 0.000000, 0.000000, 0.000000;;
 TextureFilename {
-"GOTHIC3.JPG";
+"gothic3.jpg";
 }
 }
 

BIN
modules/mojo3d-loaders/tests/assets/castle/stone2.jpg


BIN
modules/mojo3d-loaders/tests/assets/miramar-skybox.jpg


+ 4 - 1
modules/mojo3d-loaders/tests/castle.monkey2

@@ -9,6 +9,8 @@ Namespace myapp
 
 #Import "assets/"
 
+#Import "../../mojo3d/tests/assets/miramar-skybox.jpg"
+
 #Import "util"
 
 Using std..
@@ -55,7 +57,6 @@ Class MyWindow Extends Window
 		'create model
 		'		
 		_model=Model.Load( "asset::castle/CASTLE1.X" )
-'		_model=Model.Load( "asset::HOUSE.3DS" )
 '		_model=Model.Load( "desktop::Temple.3DS" )
 '		_model=Model.Load( "desktop::FairyHouse/FairyHouse.3DS" )
 		
@@ -92,6 +93,8 @@ Class MyWindow Extends Window
 End
 
 Function Main()
+	
+	Print RealPath( "desktop::hello.png" )
 
 	New AppInstance
 	

+ 6 - 1
modules/mojo3d-physics/physics/collider.monkey2

@@ -21,6 +21,11 @@ Class Collider
 		_btshape.setMargin( margin )
 	End
 
+	Method CalculateLocalInertia:Vec3f( mass:Float )
+		
+		Return _btshape.calculateLocalInertia( mass )
+	End
+
 	Property btShape:btCollisionShape()
 	
 		Return _btshape
@@ -155,7 +160,7 @@ Class MeshCollider Extends ConcaveCollider
 		
 		Local shape:=New btBvhTriangleMeshShape( _btmesh,True,True )
 		
-		CreateInternalEdgeInfo( shape )
+		'CreateInternalEdgeInfo( shape )
 		
 		_btshape=shape
 	End

+ 187 - 96
modules/mojo3d-physics/physics/rigidbody.monkey2

@@ -22,10 +22,10 @@ Class KinematicMotionState Extends bbKinematicMotionState
 	
 	Method GetWorldTransform:btTransform() Override
 		
-		Return _entity.WorldMatrix
+		Return _entity.Matrix
 	End
 	
-	Private
+Private
 	
 	Field _entity:Entity
 End
@@ -34,35 +34,27 @@ End
 #end
 Class RigidBody
 	
-	#rem monkeydoc Creates a new rigid body.
-	#end
-	Method New( mass:Float,collider:Collider,entity:Entity,kinematic:Bool=False,collGroup:Int=1,collMask:Int=1 )
+	Method Init( collider:Collider,entity:Entity,mass:Float,collGroup:Int,collMask:Int,btmotion:btMotionState )
 		
-		_mass=mass
+		Local inertia:btVector3=collider ? collider.CalculateLocalInertia( mass ) Else Null
+
+		_world=World.GetDefault()
 		_collider=collider
 		_entity=entity
-		_kinematic=kinematic
+		_mass=mass
 		_collGroup=collGroup
 		_collMask=collMask
-		
-		_world=World.GetDefault()
-		
-		If _kinematic
-			_btmotion=New KinematicMotionState( _entity )
-		Else
-			_btmotion=New btDefaultMotionState( _entity.WorldMatrix )
-		Endif
+		_btmotion=btmotion
 
-		Local inertia:btVector3=_collider ? _collider.btShape.calculateLocalInertia( _mass ) Else New btVector3( 0,0,0 )
-		
-		_btbody=New btRigidBody( _mass,_btmotion,_collider.btShape,inertia )
+		_btbody=New btRigidBody( mass,btmotion,collider.btShape,inertia )
 		
-		If _kinematic 
-			_btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_KINEMATIC_OBJECT )
-			_btbody.setActivationState( DISABLE_DEACTIVATION )
-		Endif
-		
-		If Cast<MeshCollider>( _collider ) _btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_CUSTOM_MATERIAL_CALLBACK )
+		_btbody.setFriction( 1 )
+		_btbody.setRollingFriction( 1 )
+		_btbody.setRestitution( 0 )
+
+		'If Cast<MeshCollider>( _collider ) _btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_CUSTOM_MATERIAL_CALLBACK )
+			
+		If Not _entity Return
 
 		_entity.Shown+=Lambda()
 		
@@ -74,33 +66,19 @@ Class RigidBody
 			_world.Remove( Self )
 		End
 		
-		_entity.Copied+=Lambda( copy:Entity )
-		
-			Local body:=New RigidBody( Mass,Collider,copy )
-			
-			body.LinearVelocity=LinearVelocity
-			body.Restitution=Restitution
-			body.Friction=Friction
-			body.RollingFriction=RollingFriction
-		End
-		
 		If _entity.Visible _world.Add( Self )
-			
-		Restitution=0
-		Friction=1
-		RollingFriction=0
 	End
 
-	Property Mass:Float()
-		
-		Return _mass
-	End
-	
 	Property Collider:Collider()
 		
 		Return _collider
 	End
 	
+	Property Entity:Entity()
+	
+		Return _entity
+	End
+	
 	Property CollisionGroup:Short()
 		
 		Return _collGroup
@@ -111,18 +89,22 @@ Class RigidBody
 		Return _collMask
 	End
 	
-	Property Entity:Entity()
+	Property Friction:Float()
 	
-		Return _entity
+		Return _btbody.getFriction()
+	
+	Setter( friction:Float )
+	
+		_btbody.setFriction( friction )
 	End
 	
-	Property LinearVelocity:Vec3f()
+	Property RollingFriction:Float()
 	
-		Return _btbody.getLinearVelocity()
+		Return _btbody.getRollingFriction()
 	
-	Setter( velocity:Vec3f )
+	Setter( friction:Float )
 	
-		_btbody.setLinearVelocity( velocity )
+		_btbody.setRollingFriction( friction )
 	End
 	
 	Property Restitution:Float()
@@ -134,22 +116,154 @@ Class RigidBody
 		_btbody.setRestitution( restitution )
 	End
 	
-	Property Friction:Float()
+	Property btBody:btRigidBody()
 	
-		Return _btbody.getFriction()
+		Return _btbody
+	End
+
+Protected
+
+	Field _world:World
+	Field _mass:Float	
+	Field _collider:Collider
+	Field _entity:Entity
+	Field _collGroup:Int
+	Field _collMask:Int
 	
-	Setter( friction:Float )
+	Field _btmotion:btMotionState
+	Field _btbody:btRigidBody
+	Field _seq:Int
 	
-		_btbody.setFriction( friction )
+	Method OnValidate() Virtual
 	End
 	
-	Property RollingFriction:Float()
+	Method OnUpdate() Virtual
+	End
 	
-		Return _btbody.getRollingFriction()
+Internal
+		
+	Method Validate()
+		
+		If _entity OnValidate()
+	End
 	
-	Setter( friction:Float )
+	Method Update()
+		
+		If _entity OnUpdate()
+	End
 	
-		_btbody.setRollingFriction( friction )
+End
+
+Class StaticBody Extends RigidBody
+	
+	Method New( collider:Collider,entity:Entity,collGroup:Int=1,collMask:Int=1 )
+		
+		Init( collider,entity,0,collGroup,collMask,Null )
+		
+		If entity entity.Copied+=Lambda( copy:Entity )
+		
+			Local body:=New StaticBody( collider,copy,collGroup,collMask )
+			
+			body.Friction=Friction
+			body.RollingFriction=RollingFriction
+			body.Restitution=Restitution
+		End
+		
+	End
+	
+Protected
+	
+	Method OnValidate() Override
+		
+		If _seq=_entity.Seq Return
+		
+		_btbody.setWorldTransform( _entity.Matrix )
+	End
+	
+	Method OnUpdate() Override
+		
+		_seq=_entity.Seq
+	End
+
+End
+
+Class KinematicBody Extends RigidBody
+	
+	Method New( collider:Collider,entity:Entity,collGroup:Int=1,collMask:Int=1 )
+
+		Init( collider,entity,0,collGroup,collMask,entity ? New KinematicMotionState( entity ) Else Null )
+
+		_btbody.setCollisionFlags( _btbody.getCollisionFlags() | btCollisionObject.CF_KINEMATIC_OBJECT )
+		_btbody.setActivationState( DISABLE_DEACTIVATION )
+		
+		If entity entity.Copied+=Lambda( copy:Entity )
+		
+			Local body:=New KinematicBody( collider,copy,collGroup,collMask )
+
+			body.Friction=Friction
+			body.RollingFriction=RollingFriction
+			body.Restitution=Restitution
+		End
+
+	End
+	
+Protected
+	
+	Method OnValidate() Override
+	End
+	
+	Method OnUpdate() Override
+	End
+	
+End
+
+Class DynamicBody Extends RigidBody
+
+	Method New( collider:Collider,entity:Entity,mass:Float=1,collGroup:Int=1,collMask:Int=1 )
+		
+		Init( collider,entity,mass,collGroup,collMask,entity ? New btDefaultMotionState( entity.Matrix ) Else null )
+			
+		If entity entity.Copied+=Lambda( copy:Entity )
+		
+			Local body:=New DynamicBody( collider,copy,mass,collGroup,collMask )
+
+			body.Gravity=Gravity
+			body.Friction=Friction
+			body.RollingFriction=RollingFriction
+			body.Restitution=Restitution
+		End
+	End
+	
+	Property Mass:Float()
+		
+		Return _mass
+	End
+	
+	Property Gravity:Vec3f()
+		
+		Return _btbody.getGravity()
+	
+	Setter( gravity:Vec3f )
+		
+		_btbody.setGravity( gravity )
+	End
+	
+	Property LinearVelocity:Vec3f()
+	
+		Return _btbody.getLinearVelocity()
+	
+	Setter( velocity:Vec3f )
+	
+		_btbody.setLinearVelocity( velocity )
+	End
+	
+	Property AngularVelocity:Vec3f()
+		
+		Return _btbody.getAngularVelocity()
+	
+	Setter( velocity:Vec3f )
+		
+		_btbody.setAngularVelocity( velocity )
 	End
 	
 	Method ApplyForce( force:Vec3f,relativePos:Vec3f )
@@ -172,65 +286,42 @@ Class RigidBody
 		_btbody.applyImpulse( impulse,relativePos )
 	End
 	
-	Method ApplyTorqueImpulse( torque:Vec3f )
-		
-		_btbody.applyTorqueImpulse( torque )
-	End
-	
 	Method ApplyCentralImpulse( impulse:Vec3f )
 		
 		_btbody.applyCentralImpulse( impulse )
 	End
 	
-	Function Get:RigidBody( btobject:btCollisionObject )
+	Method ApplyTorqueImpulse( torque:Vec3f )
 		
-		Return Cast<RigidBody>( handle_to_object( btobject.getUserPointer() ) )
+		_btbody.applyTorqueImpulse( torque )
 	End
-
-	Internal
 	
-	Property btBody:btRigidBody()
+Protected
 	
-		Return _btbody
-	End
-	
-	Method Validate()
+	Method OnValidate() Override
+		
+		If _seq=_entity.Seq Return
+		
+		_btbody.clearForces()
+		
+		_btbody.setLinearVelocity( New Vec3f( 0 ) )
 		
-		If _kinematic Or _seq=_entity.Seq Return
+		_btbody.setAngularVelocity( New Vec3f( 0 ) )
 		
-		_btbody.setWorldTransform( _entity.WorldMatrix )
+		_btbody.setWorldTransform( _entity.Matrix )
 		
-		_btmotion.setWorldTransform( _entity.WorldMatrix )
+		_btmotion.setWorldTransform( _entity.Matrix )
 	End
 	
-	Method Update()
+	Method OnUpdate() Override
 		
-		If _kinematic Return
-	
 		Local tform:=_btmotion.getWorldTransform()
 		
-		_entity.WorldPosition=tform.getOrigin()
+		_entity.Position=tform.getOrigin()
 		
-		_entity.WorldBasis=tform.getBasis()
+		_entity.Basis=tform.getBasis()
 		
 		_seq=_entity.Seq
 	End
-	
-Private
-
-	Field _self:RigidBody
 
-	Field _mass:Float	
-	Field _collider:Collider
-	Field _entity:Entity
-	Field _kinematic:Bool
-	Field _collGroup:Int
-	Field _collMask:Int
-	Field _world:World
-	Field _seq:Int
-	
-	Field _btmotion:btMotionState
-	Field _btbody:btRigidBody
-	
 End
-

+ 14 - 14
modules/mojo3d-physics/physics/world.monkey2

@@ -21,22 +21,22 @@ Class RaycastResult
 	Method New()
 	End
 	
-	Method New( btresult:btCollisionWorld.ClosestRayResultCallback )
-		time=btresult.m_closestHitFraction
-		body=Cast<RigidBody>( handle_to_object( btresult.m_collisionObject.getUserPointer() ) )
-		point=btresult.m_hitPointWorld
-		normal=btresult.m_hitNormalWorld
+	Method New( btresult:btCollisionWorld.ClosestRayResultCallback Ptr )
+		time=btresult->m_closestHitFraction
+		body=Cast<RigidBody>( handle_to_object( btresult->m_collisionObject.getUserPointer() ) )
+		point=btresult->m_hitPointWorld
+		normal=btresult->m_hitNormalWorld
 	End
 	
-	Method New( btresult:btCollisionWorld.ClosestConvexResultCallback )
+	Method New( btresult:btCollisionWorld.ClosestConvexResultCallback Ptr )
 		
-		Local castFrom:=Cast<Vec3f>( btresult.m_convexFromWorld )
-		Local castTo:=Cast<Vec3f>( btresult.m_convexToWorld )
+		Local castFrom:=Cast<Vec3f>( btresult->m_convexFromWorld )
+		Local castTo:=Cast<Vec3f>( btresult->m_convexToWorld )
 		
-		time=btresult.m_closestHitFraction
-		body=Cast<RigidBody>( handle_to_object( btresult.m_hitCollisionObject.getUserPointer() ) )
-		point=(castTo-castFrom) * btresult.m_closestHitFraction + castFrom
-		normal=btresult.m_hitNormalWorld
+		time=btresult->m_closestHitFraction
+		body=Cast<RigidBody>( handle_to_object( btresult->m_hitCollisionObject.getUserPointer() ) )
+		point=(castTo-castFrom) * btresult->m_closestHitFraction + castFrom
+		normal=btresult->m_hitNormalWorld
 	End
 	
 End
@@ -92,7 +92,7 @@ Class World
 		
 		If Not btresult.hasHit() Return Null
 		
-		Return New RaycastResult( btresult )
+		Return New RaycastResult( Varptr btresult )
 	End
 	
 	Method ConvexSweep:RaycastResult( collider:ConvexCollider,castFrom:AffineMat4f,castTo:AffineMat4f )
@@ -103,7 +103,7 @@ Class World
 		
 		If Not btresult.hasHit() Return Null
 		
-		Return New RaycastResult( btresult )
+		Return New RaycastResult( Varptr btresult )
 		
 	End
 	

+ 0 - 0
modules/mojo3d-physics/tests/assets/dummy.txt


+ 210 - 0
modules/mojo3d-physics/tests/fpsthing.monkey2

@@ -0,0 +1,210 @@
+Namespace myapp
+
+#Import "<std>"
+#Import "<mojo>"
+#Import "<mojo3d>"
+#Import "<mojo3d-loaders>"
+#Import "<mojo3d-physics>"
+
+#Import "assets/"
+
+#Import "../../mojo3d/tests/assets/miramar-skybox.jpg"
+
+#Import "../../mojo3d-loaders/tests/assets/castle/@/castle"
+
+#Import "util"
+
+#Import "qcollide"
+
+Using std..
+Using mojo..
+Using mojo3d..
+
+Const GRAVITY:=30	'coz reality sux!
+
+Class Player
+	
+	Field _model:Model
+	
+	Field _collider:ConvexCollider
+	
+	Field _paused:Bool
+	
+	Field _onground:Bool
+	
+	Field _yvel:Float
+	
+	Method New( radius:Float=.5,height:Float=1 )
+		
+		_model=Model.CreateCapsule( radius,height,Axis.Y,12,New PbrMaterial( Color.Sky ) )
+
+'		_model.Move( 0,0,0 )
+		
+		_collider=New CapsuleCollider( radius,height,Axis.Y )
+		
+		_collider.Margin=.01
+	End
+	
+	Method Update()
+		
+		Local src:=_model.Position
+		
+		Move()
+		
+		If _paused Return
+		
+		Local qresult:=qcollide.QCollide( _collider,src,_model.Position )
+
+		_model.Position=qresult.position
+		
+		src=_model.Position
+		
+		If _onground _yvel=-_collider.Margin
+			
+		_yvel-=GRAVITY/60.0/60.0
+
+		_model.MoveY( _yvel )
+
+		qresult=qcollide.QCollide( _collider,src,_model.Position )
+
+		_model.Position=qresult.position
+		
+		_yvel=_model.Position.y-src.y
+		
+		_onground=qresult.onground
+	End
+	
+	Method Move()
+		
+		If Keyboard.KeyDown( Key.Left )
+			
+			_model.RotateY( 2.5 )
+			
+		Else If Keyboard.KeyDown( Key.Right )
+			
+			_model.RotateY( -2.5 )
+		Endif
+
+		If _paused
+			
+			If Keyboard.KeyDown( Key.Up )
+				
+				_model.MoveY( .25 )
+				
+			Else If Keyboard.KeyDown( Key.Down )
+				
+				_model.MoveY( -.25 )
+				
+			Endif
+		Endif
+		
+		If Keyboard.KeyDown( Key.A )
+			
+			_model.MoveZ( .15 )
+			
+		Else If Keyboard.KeyDown( Key.Z )
+			
+			_model.MoveZ( -.15 )
+		Endif
+		
+		If Keyboard.KeyHit( Key.Space )
+			
+			_onground=False
+			
+			_yvel=.25
+		Endif
+		
+	End
+	
+End
+
+Class MyWindow Extends Window
+	
+	Field _scene:Scene
+	
+	Field _camera:Camera
+	
+	Field _light:Light
+	
+	Field _castle:Model
+	
+	Field _player:Player
+	
+	Field _sphere:SphereCollider
+	
+	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
+
+		Super.New( title,width,height,flags )
+		
+		
+		Print New Vec3f( 0,1,0 ).Cross( New Vec3f( 0,1,0 ) )
+		
+		_scene=Scene.GetCurrent()
+		_scene.SkyTexture=Texture.Load( "asset::miramar-skybox.jpg",TextureFlags.FilterMipmap|TextureFlags.Cubemap )
+		
+		'create light
+		'
+		_light=New Light
+		_light.Rotate( 60,60,0 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.ShadowsEnabled=False
+		
+		'Load castle
+		'
+		Local sz:=50
+		
+'		_castle=Model.Load( "asset::E1M1_clean.obj" )	'On the off chance you've got this...have no idea of license issues though.
+		_castle=Model.Load( "asset::castle/CASTLE1.X" )
+
+		_castle.Mesh.FitVertices( New Boxf( -sz,sz ),True )
+		
+		Local collider:=New MeshCollider( _castle.Mesh )
+		
+		Local body:=New StaticBody( collider,_castle,1,1 )
+		
+		'create player
+		'
+		_player=New Player( .75,.5 )
+		
+		'create camera
+		'
+		_camera=New Camera
+		_camera.Near=.1
+		_camera.Far=200
+		
+		_sphere=New SphereCollider( .2 )
+
+	End
+	
+	Method OnRender( canvas:Canvas ) Override
+		
+		RequestRender()
+		
+		If Keyboard.KeyHit( Key.Enter ) _player._paused=Not _player._paused
+		
+		_player.Update()
+
+		World.GetDefault().Update()
+		
+		Local src:=_player._model.Matrix * New Vec3f( 0,1,0 )
+		
+		Local dst:=_player._model.Matrix * New Vec3f( 0,1.5,-2.5 )
+		
+		_camera.Position=dst
+		
+		_camera.PointAt( _player._model.Position+New Vec3f( 0,1.5,0 ) )
+		
+		_scene.Render( canvas,_camera )
+		
+'		canvas.DrawText( "position="+_player._model.Position+", Rx="+_player._model.Rx+", Width="+Width+", Height="+Height+", FPS="+App.FPS,0,0 )
+	End
+	
+End
+
+Function Main()
+	
+	New AppInstance
+	
+	New MyWindow
+	
+	App.Run()
+End

+ 128 - 0
modules/mojo3d-physics/tests/qcollide.monkey2

@@ -0,0 +1,128 @@
+
+Namespace qcollide
+
+Private
+
+Function F:String( f:Float )
+	If f>=0 
+		Local i:Int=Floor( f*100+.5 )
+		Return String( i / 100 ) + "." + ("00"+String( i Mod 100 )).Slice( -2 )
+	Else
+		Local i:Int=Floor( -f*100+.5 )
+		Return "-"+String( i / 100 ) + "." + ("00"+String( i Mod 100 )).Slice( -2 )
+	Endif
+End
+
+Function V:String( V:Vec3f )
+	'Return String(V)
+	Return "("+F(V.x)+","+F(V.y)+","+F(V.z)+")"
+End
+
+Function P:String( P:Planef )
+	Return "("+F(P.n.x)+","+F(P.n.y)+","+F(P.n.z)+","+F(P.d)+")"
+End
+
+Public
+
+Struct QResult
+	Field position:Vec3f
+	Field onground:Bool
+End
+
+Function QCollide:QResult( collider:ConvexCollider,src:Vec3f,dst:Vec3f )
+	
+	Local start:=src
+	
+	Local plane0:Planef,plane1:Planef,state:=0,casts:=0
+	
+	Local qresult:QResult
+	
+	Local debug:=""
+	
+	Repeat
+
+		If dst.Distance( src )<.001 Exit
+		
+		casts+=1
+		
+		Local cresult:=World.GetDefault().ConvexSweep( collider,src,dst )
+		If Not cresult Exit
+
+'		debug+=", "
+		
+		If cresult.normal.y>.7071 qresult.onground=True
+			
+		Local plane:=New Planef( cresult.point,cresult.normal )
+
+		plane.d-=collider.Margin
+		
+		Local tline:=New Linef( src,dst-src )
+		
+		Local t:=plane.TIntersect( tline )
+		
+		If t>=1 dst=tline * t ; Exit
+		
+		If t>0 src=tline * t
+		
+		Select state
+
+		Case 0
+			
+			dst=plane.Nearest( dst )
+			
+'			debug+="A "+P( plane )
+				
+			plane0=plane
+				
+			state=1
+		
+		Case 1
+
+			Local v:=plane0.n.Cross( plane.n )
+			
+			If v.Length>.0001
+				
+				Local groove:=New Linef( src,v )
+				
+'				Local d0:=plane0.Distance( dst )
+				
+				dst=groove.Nearest( dst )
+				
+'				debug+="B "+P( plane )+" d0="+F(d0)+" sd0="+F(plane0.Distance(src))+" dd0="+F(plane0.Distance(dst))
+				
+				plane1=plane
+				
+				state=2
+			
+			Else
+				
+'				debug+="C "+P( plane )
+
+				dst=plane.Nearest( dst )
+
+				plane0=plane
+				
+				state=1
+			
+			Endif
+				
+		Case 2
+
+'			Local d0:=plane0.Distance( dst )
+'			Local d1:=plane1.Distance( dst )
+'			debug+="D "+P( plane )+" d0="+F(d0)+" d1="+F(d1)
+				
+			dst=src
+			
+			Exit
+		End
+
+	Forever
+	
+'	If casts>2 Print debug.Slice( 2 )+" casts="+casts
+
+	qresult.position=dst
+	
+	Return qresult
+
+End

+ 5 - 58
modules/mojo3d-physics/tests/shapes.monkey2

@@ -14,37 +14,6 @@ Using std..
 Using mojo..
 Using mojo3d..
 
-Function Randomize( model:Model )
-	
-	Local vertices:=model.Mesh.GetVertices()
-	
-	Local indices:=model.Mesh.GetAllIndices()
-	
-	Local mesh:=New Mesh
-	
-	mesh.AddVertices( vertices )
-	
-	mesh.AddMaterials( 9 )
-	
-	Local materials:=New Material[10]
-	
-	For Local i:=0 Until 10
-
-		materials[i]=New PbrMaterial( New Color( Rnd(),Rnd(),Rnd() ) )
-	
-	Next
-	
-	For Local i:=0 Until indices.Length Step 3
-		
-		mesh.AddTriangles( New UInt[]( indices[i],indices[i+1],indices[i+2] ),Rnd(10) )
-	
-	Next
-	
-	model.Mesh=mesh
-	
-	model.Materials=materials
-End
-
 Class MyWindow Extends Window
 	
 	Field _scene:Scene
@@ -55,8 +24,6 @@ Class MyWindow Extends Window
 	
 	Field _ground:Model
 	
-	Field _sphere:Model
-	
 	Field _collider:SphereCollider
 	
 	Method New( title:String="Simple mojo app",width:Int=640,height:Int=480,flags:WindowFlags=WindowFlags.Resizable )
@@ -74,8 +41,8 @@ Class MyWindow Extends Window
 		_camera.Far=60
 		_camera.Move( 0,10,-10 )
 		
-		New RigidBody( 0,New SphereCollider( 1 ),_camera,True )
-
+		New KinematicBody( New SphereCollider( 1 ),_camera )
+		
 		'create fog
 		'		
 		Local fog:=New FogEffect
@@ -86,7 +53,7 @@ Class MyWindow Extends Window
 		'create light
 		'
 		_light=New Light
-		_light.RotateX( Pi/2 )	'aim directional light 'down' - Pi/2=90 degrees.
+		_light.RotateX( 90 )	'aim directional light 'down'.
 		
 		'create ground
 		'
@@ -94,19 +61,10 @@ Class MyWindow Extends Window
 		
 		_ground=Model.CreateBox( groundBox,16,16,16,New PbrMaterial( Color.Green ) )
 		
-'		Randomize( _ground )
-		
-'		Local collider:Collider=New MeshCollider( _ground.Mesh )	'UGH - FIXME!
 		Local collider:Collider=New BoxCollider( groundBox )
 		
-		Local body:=New RigidBody( 0,collider,_ground )
+		Local body:=New StaticBody( collider,_ground )
 
-		_sphere=Model.CreateSphere( .5,24,12,New PbrMaterial( Color.White ) )
-		
-		_collider=New SphereCollider( .5 )
-		
-'		Return
-		
 		'create some meshes/colliders
 		
 		Local meshes:=New Mesh[5]
@@ -139,7 +97,7 @@ Class MyWindow Extends Window
 				Local model:=New Model( mesh,material )
 				model.Move( x,10,z )
 				
-				Local body:=New RigidBody( 1,colliders[i],model )
+				Local body:=New DynamicBody( colliders[i],model )
 				
 			Next
 		
@@ -152,17 +110,6 @@ Class MyWindow Extends Window
 		RequestRender()
 		
 		util.Fly( _camera,Self )
-		
-'		Local cresult:=World.GetDefault().ConvexSweep( _collider,_camera.Position,_camera.Position+_camera.Basis.k * 100 )
-
-		Local cresult:=World.GetDefault().ConvexSweep( _collider,_camera.Position+_camera.Basis.k * 2,_camera.Position+_camera.Basis.k * 100 )
-		
-		'Local cresult:=World.GetDefault().RayCast( _camera.Position,_camera.Position+_camera.Basis.k * 100 )
-		
-		If cresult 
-			Print "pos="+cresult.point
-			_sphere.Position=cresult.point'+cresult.normal
-		Endif
 			
 		World.GetDefault().Update()
 		

+ 14 - 12
modules/mojo3d-physics/tests/util.monkey2

@@ -2,30 +2,32 @@
 Namespace util
 
 Function Fly( entity:Entity,view:View )
+	
+	Const rspeed:=2.0
 
 	If Keyboard.KeyDown( Key.Up )
-		entity.RotateX( .05 )
+		entity.RotateX( rspeed )
 	Else If Keyboard.KeyDown( Key.Down )
-		entity.RotateX( -.05 )
+		entity.RotateX( -rspeed )
 	Endif
 	
-	If Keyboard.KeyDown( Key.Left )
-		entity.RotateY( .025,True )
-	Else If Keyboard.KeyDown( Key.Right )
-		entity.RotateY( -.025,True )
-	Endif
-
 	If Keyboard.KeyDown( Key.Q )
-		entity.RotateZ( .01 )
+		entity.RotateZ( rspeed )
 	Else If Keyboard.KeyDown( Key.W )
-		entity.RotateZ( -.01 )
+		entity.RotateZ( -rspeed )
 	Endif
 	
+	If Keyboard.KeyDown( Key.Left )
+		entity.RotateY( rspeed,True )
+	Else If Keyboard.KeyDown( Key.Right )
+		entity.RotateY( -rspeed,True )
+	Endif
+
 	If Mouse.ButtonDown( MouseButton.Left )
 		If Mouse.X<view.Width/3
-			entity.RotateY( .1,True )
+			entity.RotateY( rspeed,True )
 		Else If Mouse.X>view.Width/3*2
-			entity.RotateY( -.1,True )
+			entity.RotateY( -rspeed,True )
 		Else
 			entity.Move( New Vec3f( 0,0,.1 ) )
 		Endif

+ 3 - 3
modules/mojo3d/graphics/animator.monkey2

@@ -39,9 +39,9 @@ Class Animator
 			Local channel:=animation.Channels[i]
 			If Not channel continue
 			
-			_entities[i].Position=channel.GetPosition( time )
-			_entities[i].Basis=New Mat3f( channel.GetRotation( time ) )
-			_entities[i].Scale=channel.GetScale( time )
+			_entities[i].LocalPosition=channel.GetPosition( time )
+			_entities[i].LocalBasis=New Mat3f( channel.GetRotation( time ) )
+			_entities[i].LocalScale=channel.GetScale( time )
 		End
 		
 	End

+ 1 - 1
modules/mojo3d/graphics/deferredrenderer.monkey2

@@ -43,7 +43,7 @@ Class DeferredRenderer Extends Renderer
 	
 		_uniforms.SetVec4f( "LightColor",light.Color )
 		_uniforms.SetFloat( "LightRange",light.Range )
-		_uniforms.SetMat4f( "LightViewMatrix",_camera.InverseWorldMatrix * light.WorldMatrix )
+		_uniforms.SetMat4f( "LightViewMatrix",_camera.InverseMatrix * light.Matrix )
 		
 		_uniforms.SetMat4f( "InverseProjectionMatrix",-_camera.ProjectionMatrix )
 		

+ 63 - 63
modules/mojo3d/graphics/entity.monkey2

@@ -137,7 +137,7 @@ Class Entity
 	
 	Setter( visible:Bool )
 		
-		If _visible Show() Else Hide()
+		If visible Show() Else Hide()
 	End
 
 	#rem monkeydoc Entity animator.
@@ -151,136 +151,136 @@ Class Entity
 		_animator=animator
 	End
 	
-	'***** Local space properties *****
-
-	#rem monkeydoc Local transformation matrix.
+	'***** World space properties *****
 	
-	The local matrix combines the local position, orientation and scale of the entity into a single affine 4x4 matrix.
+	#rem monkeydoc World space transformation matrix.
+	
+	The world matrix combines the world position, basis matrix and scale of the entity into a single affine 4x4 matrix.
 	
 	#end
 	Property Matrix:AffineMat4f()
 		
-		If _dirty & Dirty.M
-			_M=New AffineMat4f( _r.Scale( _s ),_t )
-			_dirty&=~Dirty.M
+		If _dirty & Dirty.W
+			_W=_parent ? _parent.Matrix * LocalMatrix Else LocalMatrix
+			_dirty&=~Dirty.W
 		Endif
 		
-		Return _M
+		Return _W
 	End
-
-	#rem monkeydoc Local position.
+	
+	#rem monkeydoc Inverse world space transformation matrix.
+	#end
+	Property InverseMatrix:AffineMat4f()
+		
+		If _dirty & Dirty.IW
+			_IW=-Matrix
+			_dirty&=~Dirty.IW
+		Endif
+		
+		Return _IW
+	End
+	
+	#rem monkeydoc World space position.
 	#end
 	Property Position:Vec3f()
-
-		Return _t
+		
+		Return Matrix.t
 		
 	Setter( position:Vec3f )
 		
-		_t=position
+		_t=_parent ? _parent.InverseMatrix * position Else position
 		
 		Invalidate()
 	End
 	
-	#rem monkeydoc Local basis matrix.
-	
-	A basis matrix is a 3x3 matrix representation of an orientation.
+	#rem monkeydoc World space basis matrix.
 
+	A basis matrix is a 3x3 matrix representation of an orientation.
+	
 	A basis matrix is orthogonal (ie: the i,j,k members are perpendicular to each other) and normalized (ie: the i,j,k members all have unit length).
 	
 	#end
 	Property Basis:Mat3f()
 		
-		Return _r
+		Return _parent ? _parent.Basis * _r Else _r
 	
 	Setter( basis:Mat3f )
 		
-		_r=basis
+		_r=_parent ? ~_parent.Basis * basis Else basis
 		
 		Invalidate()
 	End
-
-	#rem monkeydoc Local scale.
+	
+	#rem monkeydoc World space scale.
 	#end	
 	Property Scale:Vec3f()
 		
-		Return _s
+		Return _parent ? _s * _parent.Scale Else _s
 	
 	Setter( scale:Vec3f )
 		
-		_s=scale
+		_s=_parent ? scale / _parent.Scale Else scale
 		
 		Invalidate()
 	End
 	
-	'***** World space properties *****
-	
-	#rem monkeydoc World transformation matrix.
-	
-	The world matrix combines the world position, basis matrix and scale of the entity into a single affine 4x4 matrix.
+	'***** Local space properties *****
+
+	#rem monkeydoc Local space transformation matrix.
 	
-	#end
-	Property WorldMatrix:AffineMat4f()
-		
-		If _dirty & Dirty.W
-			_W=_parent ? _parent.WorldMatrix * Matrix Else Matrix
-			_dirty&=~Dirty.W
-		Endif
-		
-		Return _W
-	End
+	The local matrix combines the local position, orientation and scale of the entity into a single affine 4x4 matrix.
 	
-	#rem monkeydoc Inverse world matrix.
 	#end
-	Property InverseWorldMatrix:AffineMat4f()
+	Property LocalMatrix:AffineMat4f()
 		
-		If _dirty & Dirty.IW
-			_IW=-WorldMatrix
-			_dirty&=~Dirty.IW
+		If _dirty & Dirty.M
+			_M=New AffineMat4f( _r.Scale( _s ),_t )
+			_dirty&=~Dirty.M
 		Endif
 		
-		Return _IW
+		Return _M
 	End
-	
-	#rem monkeydoc World position.
+
+	#rem monkeydoc Local space position.
 	#end
-	Property WorldPosition:Vec3f()
-		
-		Return WorldMatrix.t
+	Property LocalPosition:Vec3f()
+
+		Return _t
 		
 	Setter( position:Vec3f )
 		
-		_t=_parent ? _parent.InverseWorldMatrix * position Else position
+		_t=position
 		
 		Invalidate()
 	End
 	
-	#rem monkeydoc World basis matrix.
-
-	A basis matrix is a 3x3 matrix representation of an orientation.
+	#rem monkeydoc Local space basis matrix.
 	
+	A basis matrix is a 3x3 matrix representation of an orientation.
+
 	A basis matrix is orthogonal (ie: the i,j,k members are perpendicular to each other) and normalized (ie: the i,j,k members all have unit length).
 	
 	#end
-	Property WorldBasis:Mat3f()
+	Property LocalBasis:Mat3f()
 		
-		Return _parent ? _parent.WorldBasis * _r Else _r
+		Return _r
 	
 	Setter( basis:Mat3f )
 		
-		_r=_parent ? ~_parent.WorldBasis * basis Else basis
+		_r=basis
 		
 		Invalidate()
 	End
-	
-	#rem monkeydoc World scale.
+
+	#rem monkeydoc Local space scale.
 	#end	
-	Property WorldScale:Vec3f()
+	Property LocalScale:Vec3f()
 		
-		Return _parent ? Scale * _parent.WorldScale Else _s
+		Return _s
 	
 	Setter( scale:Vec3f )
 		
-		_s=_parent ? scale / _parent.WorldScale Else scale
+		_s=scale
 		
 		Invalidate()
 	End
@@ -301,7 +301,7 @@ Class Entity
 		Next
 	End
 	
-	#rem monkeydoc Hides the entity and all of its children
+	#rem monkeydoc Shows the entity and all of its children
 	#end
 	Method Show()
 		
@@ -324,8 +324,8 @@ Class Entity
 		Wend
 		
 		If _visible 
-			OnHide()
 			_visible=False
+			OnHide()
 		Endif
 
 		If _parent

+ 167 - 170
modules/mojo3d/graphics/entityexts.monkey2

@@ -13,7 +13,7 @@ Public
 #end
 Class Entity Extension
 
-	#rem monkeydoc Local rotation in degrees.
+	#rem monkeydoc Local space rotation in degrees.
 	#end
 	Property Rotation:Vec3f()
 		
@@ -24,18 +24,90 @@ Class Entity Extension
 		Basis=Mat3f.Rotation( rotation * DegreesToRadians )
 	End
 	
-	#rem monkeydoc World rotation in degrees.
+	#rem monkeydoc Local space rotation in degrees.
 	#end
-	Property WorldRotation:Vec3f()
+	Property LocalRotation:Vec3f()
 		
-		Return WorldBasis.GetRotation() * RadiansToDegrees
+		Return LocalBasis.GetRotation() * RadiansToDegrees
 	
 	Setter( rotation:Vec3f )
 		
-		WorldBasis=Mat3f.Rotation( rotation * DegreesToRadians )
+		Basis=Mat3f.Rotation( rotation * DegreesToRadians )
 	End
 	
-	#rem monkeydoc X coordinate of local position.
+	#Rem monkeydoc World space rotation around the X axis in degrees.
+	#End
+	Property Rx:Float()
+		
+		Return Rotation.x
+		
+	Setter( rx:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( rx,r.y,r.z )
+	End
+
+	#Rem monkeydoc World space rotation around the Y axis in degrees.
+	#End
+	Property Ry:Float()
+		
+		Return Rotation.y
+		
+	Setter( ry:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( r.x,ry,r.z )
+	End
+
+	#Rem monkeydoc World space rotation around the Z axis in degrees.
+	#End
+	Property Rz:Float()
+		
+		Return Rotation.z
+		
+	Setter( rz:Float )
+		
+		Local r:=Rotation
+		Rotation=New Vec3f( r.x,r.y,rz )
+	End
+	
+	#Rem monkeydoc Local space rotation around the X axis in degrees.
+	#End
+	Property LocalRx:Float()
+		
+		Return LocalRotation.x
+		
+	Setter( rx:Float )
+		
+		Local r:=LocalRotation
+		LocalRotation=New Vec3f( rx,r.y,r.z )
+	End
+
+	#Rem monkeydoc Local space rotation around the Y axis in degrees.
+	#End
+	Property LocalRy:Float()
+		
+		Return LocalRotation.y
+		
+	Setter( ry:Float )
+		
+		Local r:=LocalRotation
+		LocalRotation=New Vec3f( r.x,ry,r.z )
+	End
+
+	#Rem monkeydoc Local space rotation around the Z axis in degrees.
+	#End
+	Property LocalRz:Float()
+		
+		Return LocalRotation.z
+		
+	Setter( rz:Float )
+		
+		Local r:=LocalRotation
+		LocalRotation=New Vec3f( r.x,r.y,rz )
+	End
+
+	#rem monkeydoc World space X coordinate.
 	#end
 	Property X:Float()
 		
@@ -47,7 +119,7 @@ Class Entity Extension
 		Position=New Vec3f( x,v.y,v.z )
 	End
 	
-	#rem monkeydoc Y coordinate of local position.
+	#rem monkeydoc World space Y coordinate.
 	#end
 	Property Y:Float()
 	
@@ -59,7 +131,7 @@ Class Entity Extension
 		Position=New Vec3f( v.x,y,v.z )
 	End
 
-	#rem monkeydoc Z coordinate of local position.
+	#rem monkeydoc World space Z coordinate.
 	#end
 	Property Z:Float()
 	
@@ -71,115 +143,43 @@ Class Entity Extension
 		Position=New Vec3f( v.x,v.y,z )
 	End
 	
-	#rem monkeydoc X coordinate of world position.
+	#rem monkeydoc Local space X coordinate.
 	#end
-	Property WorldX:Float()
+	Property LocalX:Float()
 		
-		Return WorldPosition.x
+		Return LocalPosition.x
 		
 	Setter( x:Float )
 
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( x,v.y,v.z )
+		Local v:=LocalPosition		
+		LocalPosition=New Vec3f( x,v.y,v.z )
 	End
 	
-	#rem monkeydoc Y coordinate of world position.
+	#rem monkeydoc Local space Y coordinate.
 	#end
-	Property WorldY:Float()
+	Property LocalY:Float()
 	
-		Return WorldPosition.y
+		Return LocalPosition.y
 	
 	Setter( y:Float )
 		
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( v.x,y,v.z )
+		Local v:=LocalPosition		
+		LocalPosition=New Vec3f( v.x,y,v.z )
 	End
 
-	#rem monkeydoc Z coordinate of world position.
+	#rem monkeydoc Local space Z coordinate.
 	#end
-	Property WorldZ:Float()
+	Property LocalZ:Float()
 	
-		Return WorldPosition.z
+		Return LocalPosition.z
 	
 	Setter( z:Float )
 		
-		Local v:=WorldPosition		
-		WorldPosition=New Vec3f( v.x,v.y,z )
-	End
-	
-	#Rem monkeydoc Rotation around the X axis in degrees.
-	#End
-	Property Rx:Float()
-		
-		Return Rotation.x
-		
-	Setter( rx:Float )
-		
-		Local r:=Rotation
-		Rotation=New Vec3f( rx,r.y,r.z )
-	End
-
-	#Rem monkeydoc Rotation around the Y axis in degrees.
-	#End
-	Property Ry:Float()
-		
-		Return Rotation.y
-		
-	Setter( ry:Float )
-		
-		Local r:=Rotation
-		Rotation=New Vec3f( r.x,ry,r.z )
-	End
-
-	#Rem monkeydoc Rotation around the X axis in degrees.
-	#End
-	Property Rz:Float()
-		
-		Return Rotation.z
-		
-	Setter( rz:Float )
-		
-		Local r:=Rotation
-		Rotation=New Vec3f( r.x,r.y,rz )
-	End
-
-	#Rem monkeydoc Rotation around the X axis in degrees.
-	#End
-	Property WorldRx:Float()
-		
-		Return WorldRotation.x
-		
-	Setter( rx:Float )
-		
-		Local r:=WorldRotation
-		WorldRotation=New Vec3f( rx,r.y,r.z )
-	End
-
-	#Rem monkeydoc Rotation around the Y axis in degrees.
-	#End
-	Property WorldRy:Float()
-		
-		Return WorldRotation.y
-		
-	Setter( ry:Float )
-		
-		Local r:=WorldRotation
-		WorldRotation=New Vec3f( r.x,ry,r.z )
-	End
-
-	#Rem monkeydoc Rotation around the X axis in degrees.
-	#End
-	Property WorldRz:Float()
-		
-		Return WorldRotation.z
-		
-	Setter( rz:Float )
-		
-		Local r:=WorldRotation
-		WorldRotation=New Vec3f( r.x,r.y,rz )
+		Local v:=LocalPosition		
+		LocalPosition=New Vec3f( v.x,v.y,z )
 	End
 	
-	#rem monkeydoc Scale on the x axis.
+	#rem monkeydoc World space scale on the X axis.
 	#end
 	Property Sx:Float()
 		
@@ -191,7 +191,7 @@ Class Entity Extension
 		Scale=New Vec3f( sx,s.y,s.z )
 	End
 	
-	#rem monkeydoc Scale on the y axis.
+	#rem monkeydoc World space scale on the Y axis.
 	#end
 	Property Sy:Float()
 		
@@ -203,7 +203,7 @@ Class Entity Extension
 		Scale=New Vec3f( s.x,sy,s.z )
 	End
 	
-	#rem monkeydoc Scale on the z axis.
+	#rem monkeydoc World space scale on the Z axis.
 	#end
 	Property Sz:Float()
 		
@@ -212,123 +212,120 @@ Class Entity Extension
 	Setter( sz:Float )
 		
 		Local s:=Scale
-		
 		Scale=New Vec3f( s.x,s.y,sz )
 	End
 	
-	#rem monkeydoc Scale on the x axis.
+	#rem monkeydoc Local space scale on the X axis.
 	#end
-	Property WorldSx:Float()
+	Property LocalSx:Float()
 		
-		Return WorldScale.x
+		Return LocalScale.x
 	
 	Setter( sx:Float )
 		
-		Local s:=WorldScale
+		Local s:=LocalScale
 		
-		WorldScale=New Vec3f( sx,s.y,s.z )
+		LocalScale=New Vec3f( sx,s.y,s.z )
 	End
 	
-	#rem monkeydoc Scale on the y axis.
+	#rem monkeydoc Local space scale on the Y axis.
 	#end
-	Property WorldSy:Float()
+	Property LocalSy:Float()
 		
-		Return WorldScale.y
+		Return LocalScale.y
 	
 	Setter( sy:Float )
 		
-		Local s:=WorldScale
-		
-		WorldScale=New Vec3f( s.x,sy,s.z )
+		Local s:=LocalScale
+		LocalScale=New Vec3f( s.x,sy,s.z )
 	End
 	
-	#rem monkeydoc Scale on the z axis.
+	#rem monkeydoc Local space scale on the Z axis.
 	#end
-	Property WorldSz:Float()
+	Property LocalSz:Float()
 		
-		Return WorldScale.z
+		Return LocalScale.z
 	
 	Setter( sz:Float )
 		
-		Local s:=WorldScale
-		
-		WorldScale=New Vec3f( s.x,s.y,sz )
+		Local s:=LocalScale
+		LocalScale=New Vec3f( s.x,s.y,sz )
 	End
 	
-	#rem monkeydoc Sets entity basis matrix in local or world space.
+	#rem monkeydoc Sets entity position in local or world space.
 	#end
-	Method SetBasis( basis:Mat3f,worldSpace:Bool=False )
+	Method SetPosition( position:Vec3f,localSpace:Bool=False )
 		
-		If worldSpace WorldBasis=basis Else Basis=basis
+		If localSpace LocalPosition=position Else Position=position
 	End
 	
-	#rem monkeydoc Gets entity basis matrix in local or world space.
-	#end
-	method GetBasis:Mat3f( worldSpace:Bool=False )
+	Method SetPosition( x:Float,y:Float,z:Float,localSpace:Bool=False )
 		
-		Return worldSpace ? WorldBasis Else Basis
-	
+		SetPosition( New Vec3f( x,y,z ),localSpace )
 	End
-
-	#rem monkeydoc Sets entity position in local or world space.
+	
+	#rem monkeydoc Gets entity position in local or world space.
 	#end
-	Method SetPosition( position:Vec3f,worldSpace:Bool=False )
+	Method GetPostition:Vec3f( localSpace:Bool=False )
 		
-		If worldSpace WorldPosition=position Else Position=position
+		Return localSpace ? LocalPosition Else Position
 	End
 	
-	Method SetPosition( x:Float,y:Float,z:Float,worldSpace:Bool=False )
+	#rem monkeydoc Sets entity basis matrix in local or world space.
+	#end
+	Method SetBasis( basis:Mat3f,localSpace:Bool=False )
 		
-		SetPosition( New Vec3f( x,y,z ),worldSpace )
+		If localSpace LocalBasis=basis Else Basis=basis
 	End
 	
-	#rem monkeydoc Gets entity position in local or world space.
+	#rem monkeydoc Gets entity basis matrix in local or world space.
 	#end
-	Method GetPostition:Vec3f( worldSpace:Bool=False )
+	method GetBasis:Mat3f( localSpace:Bool=False )
 		
-		Return worldSpace ? WorldPosition Else Position
-	End
+		Return localSpace ? LocalBasis Else Basis
 	
+	End
+
 	#rem monkeydoc Sets entity rotation in euler angles in local or world space.
 	#end
-	Method SetRotation( rotation:Vec3f,worldSpace:Bool=False )
+	Method SetRotation( rotation:Vec3f,localSpace:Bool=False )
 		
 		Local basis:=Mat3f.Rotation( rotation * DegreesToRadians )
 		
-		If worldSpace WorldBasis=basis Else Basis=basis
+		If localSpace LocalBasis=basis Else Basis=basis
 	End
 	
-	Method SetRotation( rx:Float,ry:Float,rz:Float,worldSpace:Bool=False )
+	Method SetRotation( rx:Float,ry:Float,rz:Float,localSpace:Bool=False )
 		
-		SetRotation( New Vec3f( rx,ry,rz ),worldSpace )
+		SetRotation( New Vec3f( rx,ry,rz ),localSpace )
 	End
 	
 	#rem monkeydoc Gets entity rotation in euler angles in local or world space.
 	#end
-	Method GetRotation:Vec3f( worldSpace:Bool=False )
+	Method GetRotation:Vec3f( localSpace:Bool=False )
 		
-		Local basis:=worldSpace ? WorldBasis Else Basis
+		Local basis:=localSpace ? LocalBasis Else Basis
 		
 		Return basis.GetRotation() * RadiansToDegrees
 	End
 	
 	#rem monkeydoc Sets entity scale in local or world space.
 	#end
-	Method SetScale( scale:Vec3f,worldSpace:Bool=False )
+	Method SetScale( scale:Vec3f,localSpace:Bool=False )
 		
-		If worldSpace WorldScale=scale Else Scale=scale
+		If localSpace LocalScale=scale Else Scale=scale
 	End
 	
-	Method SetScale( sx:Float,sy:Float,sz:Float,worldSpace:Bool=False )
+	Method SetScale( sx:Float,sy:Float,sz:Float,localSpace:Bool=False )
 		
-		SetScale( New Vec3f( sx,sy,sz ),worldSpace )
+		SetScale( New Vec3f( sx,sy,sz ),localSpace )
 	End
 
 	#rem monkeydoc Gets entity scale in local or world space.
 	#end
-	Method GetScale:Vec3f( worldSpace:Bool=False )
+	Method GetScale:Vec3f( localSpace:Bool=False )
 		
-		Return worldSpace ? WorldScale Else Scale
+		Return localSpace ? LocalScale Else Scale
 	End
 	
 	#rem monkeydoc Moves the entity.
@@ -336,9 +333,9 @@ Class Entity Extension
 	Moves the entity relative to its current orientation.
 	
 	#end	
-	Method Move( tv:Vec3f,worldSpace:Bool=False )
+	Method Move( tv:Vec3f,localSpace:Bool=False )
 		
-		If worldSpace WorldPosition+=tv Else Position+=Basis * tv
+		If localSpace LocalPosition+=tv Else Position+=Basis * tv
 	End
 	
 	Method Move( tx:Float,ty:Float,tz:Float )
@@ -351,9 +348,9 @@ Class Entity Extension
 	Moves the entity relative to its current orientation.
 	
 	#end	
-	Method MoveX( tx:Float,worldSpace:Bool=False )
+	Method MoveX( tx:Float,localSpace:Bool=False )
 		
-		If worldSpace WorldX+=tx Else Position+=Basis.i * tx
+		If localSpace LocalX+=tx Else Position+=Basis.i * tx
 	End
 	
 	#rem monkeydoc Moves the entity on the Y axis.
@@ -361,9 +358,9 @@ Class Entity Extension
 	Moves the entity relative to its current orientation.
 	
 	#end	
-	Method MoveY( ty:Float,worldSpace:Bool=False )
+	Method MoveY( ty:Float,localSpace:Bool=False )
 
-		If worldSpace WorldY+=ty Else Position+=Basis.j * ty
+		If localSpace LocalY+=ty Else Position+=Basis.j * ty
 	End
 	
 	#rem monkeydoc Moves the entity on the Z axis.
@@ -371,75 +368,75 @@ Class Entity Extension
 	Moves the entity relative to its current orientation.
 	
 	#end	
-	Method MoveZ( tz:Float,worldSpace:Bool=False )
+	Method MoveZ( tz:Float,localSpace:Bool=False )
 
-		If worldSpace WorldZ+=tz Else Position+=Basis.k * tz
+		If localSpace LocalZ+=tz Else Position+=Basis.k * tz
 	End
 	
 	#rem monkeydoc Rotates the entity.
 	
 	Rotates the entity.
 	
-	If `postRotate` is true, the rotation is applied after the entity's world rotation.
+	If `localSpace` is false, the rotation is applied after the entity's world rotation.
 		
-	If `postRotate` is false, the rotation is applied before the entity's local rotation.
+	If `localSpace` is true, the rotation is applied before the entity's local rotation.
 		
 	#end
-	Method Rotate( rv:Vec3f,postRotate:Bool=False )
+	Method Rotate( rv:Vec3f,localSpace:Bool=False )
 		
 		Local basis:=Mat3f.Rotation( rv * DegreesToRadians )
 		
-		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+		If localSpace LocalBasis*=basis Else Basis=basis*Basis
 	End
 	
-	Method Rotate( rx:Float,ry:Float,rz:Float,postRotate:Bool=False )
+	Method Rotate( rx:Float,ry:Float,rz:Float,localSpace:Bool=False )
 		
-		Rotate( New Vec3f( rx,ry,rz ),postRotate )
+		Rotate( New Vec3f( rx,ry,rz ),localSpace )
 	End
 	
 	#rem monkeydoc Rotates the entity around the X axis.
 	#end
-	Method RotateX( rx:Float,postRotate:Bool=False )
+	Method RotateX( rx:Float,localSpace:Bool=False )
 		
 		Local basis:=Mat3f.Pitch( rx * DegreesToRadians )
 		
-		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+		If localSpace LocalBasis=basis*LocalBasis Else Basis*=basis
 	End
 
 	#rem monkeydoc Rotates the entity around the Y axis.
 	#end
-	Method RotateY( ry:Float,postRotate:Bool=False )
+	Method RotateY( ry:Float,localSpace:Bool=False )
 
 		Local basis:=Mat3f.Yaw( ry * DegreesToRadians )
 		
-		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+		If localSpace LocalBasis=basis*LocalBasis Else Basis*=basis
 	End
 
 	#rem monkeydoc Rotates the entity around the Z axis.
 	#end
-	Method RotateZ( rz:Float,postRotate:Bool=False )
+	Method RotateZ( rz:Float,localSpace:Bool=False )
 
 		Local basis:=Mat3f.Roll( rz * DegreesToRadians )
 		
-		If postRotate WorldBasis=basis*WorldBasis Else Basis*=basis
+		If localSpace LocalBasis=basis*LocalBasis Else Basis*=basis
 	End
 
 	#rem monkeydoc Points the entity at a target.
 	#end
 	Method PointAt( target:Vec3f,up:Vec3f=New Vec3f( 0,1,0 ) )
 		
-		Local k:=(target-WorldPosition).Normalize()
+		Local k:=(target-LocalPosition).Normalize()
 		
 		Local i:=up.Cross( k ).Normalize()
 		
 		Local j:=k.Cross( i )
 		
-		WorldBasis=New Mat3f( i,j,k )
+		LocalBasis=New Mat3f( i,j,k )
 	End
 	
 	Method PointAt( target:Entity,up:Vec3f=New Vec3f( 0,1,0 ) )
 		
-		PointAt( target.WorldPosition )
+		PointAt( target.LocalPosition )
 	End
-
+	
 End

+ 11 - 5
modules/mojo3d/graphics/gltf2loader.monkey2

@@ -225,13 +225,19 @@ Class Gltf2Loader
 					Next
 				Endif
 				
-				If materials And Not mesh.NumVertices mesh.AddMaterials( 1 )				
-				
 				mesh.AddVertices( vertices )
 				
-				mesh.AddTriangles( indices,mesh.NumMaterials-1 )
-				
-				If materials materials.Push( GetMaterial( prim.material ) )
+				If materials
+					
+					mesh.AddTriangles( indices,mesh.NumMaterials )
+
+					materials.Push( GetMaterial( prim.material ) )
+					
+				Else
+					
+					mesh.AddTriangles( indices,0 )
+					
+				Endif
 					
 				Print "Added "+vcount+" vertices, "+icount+" indices."
 				

+ 6 - 3
modules/mojo3d/graphics/mesh.monkey2

@@ -7,7 +7,7 @@ Class Mesh Extends Resource
 	
 	#rem monkeydoc Creates a new mesh.
 	
-	Creates a new empty mesh with 1 logical material.
+	Creates a new empty mesh.
 	
 	Meshes don't actual contain instances of materials. Instead, mesh triangles are added to 'logical' materials which are effectively just integer indices.
 	
@@ -20,7 +20,6 @@ Class Mesh Extends Resource
 		_bounds=Boxf.EmptyBounds
 		_vbuffer=New VertexBuffer( Vertex3fFormat.Instance,0 )
 		_ibuffers=New Stack<IndexBuffer>
-		AddMaterials( 1 )
 	End
 	
 	Method New( mesh:Mesh )
@@ -135,11 +134,15 @@ Class Mesh Extends Resource
 	
 	#rem monkeydoc Adds triangles to the mesh.
 	
-	The `materialid` parameter must be greater than or equal to 0 and less than NumMaterials.
+	The `materialid` parameter must be greater than or equal to 0 and less or equal to [[NumMaterials]].
+	
+	If `materialid` is equal to NumMaterials, a new material is automatically added first.
 	
 	#end
 	Method AddTriangles( indices:UInt Ptr,count:Int,materialid:Int=0 )
 		
+		If materialid=_ibuffers.Length AddMaterials( 1 )
+		
 		Local p:=_ibuffers[materialid].AddIndices( count )
 		
 		libc.memcpy( p,indices,count*4 )

+ 1 - 1
modules/mojo3d/graphics/model.monkey2

@@ -156,7 +156,7 @@ Class Model Extends Entity
 			
 			For Local i:=0 Until _bones.Length
 				Local bone:=_bones[i]
-				_boneMatrices[i]=New Mat4f( bone.entity.WorldMatrix * bone.offset )
+				_boneMatrices[i]=New Mat4f( bone.entity.Matrix * bone.offset )
 			Next
 		End
 		

+ 7 - 7
modules/mojo3d/graphics/renderer.monkey2

@@ -257,8 +257,8 @@ Class Renderer
 	
 		_camera=camera
 		
-		Local envMat:=_camera.WorldMatrix.m
-		Local viewMat:=_camera.InverseWorldMatrix
+		Local envMat:=_camera.Matrix.m
+		Local viewMat:=_camera.InverseMatrix
 		Local projMat:=_camera.ProjectionMatrix
 		Local invProjMat:=-projMat
 			
@@ -314,7 +314,7 @@ Class Renderer
 		_device.DepthFunc=DepthFunc.LessEqual
 		_device.RenderPass=1
 		
-		RenderRenderOps( _renderQueue.OpaqueOps,_camera.InverseWorldMatrix,_camera.ProjectionMatrix )
+		RenderRenderOps( _renderQueue.OpaqueOps,_camera.InverseMatrix,_camera.ProjectionMatrix )
 	End
 	
 	'MX2_RENDERPASS 0
@@ -326,7 +326,7 @@ Class Renderer
 		_device.DepthFunc=DepthFunc.Always
 		_device.RenderPass=0
 
-		RenderRenderOps( _spriteQueue.OpaqueOps,_camera.InverseWorldMatrix,_camera.ProjectionMatrix )
+		RenderRenderOps( _spriteQueue.OpaqueOps,_camera.InverseMatrix,_camera.ProjectionMatrix )
 	End
 	
 	'MX2_RENDERPASS 2
@@ -351,8 +351,8 @@ Class Renderer
 		_device.CullMode=CullMode.Back
 		_device.RenderPass=2
 
-		Local invLightMatrix:=light.InverseWorldMatrix
-		Local viewLight:=invLightMatrix * _camera.WorldMatrix
+		Local invLightMatrix:=light.InverseMatrix
+		Local viewLight:=invLightMatrix * _camera.Matrix
 		
 		For Local i:=0 Until _csmSplits.Length-1
 			
@@ -428,7 +428,7 @@ Class Renderer
 			
 			Local model:=op.instance
 			
-			Local modelMat:= model ? model.WorldMatrix Else New AffineMat4f
+			Local modelMat:= model ? model.Matrix Else New AffineMat4f
 			Local modelViewMat:=viewMatrix * modelMat
 			Local modelViewProjMat:=projMatrix * modelViewMat
 			Local modelViewNormMat:=~-modelViewMat.m

+ 3 - 3
modules/mojo3d/graphics/spritebuffer.monkey2

@@ -38,7 +38,7 @@ Class SpriteBuffer
 		Endif
 		
 		sprites.Sort( Lambda:Int( x:Sprite,y:Sprite )
-			Return camera.WorldPosition.Distance( y.WorldPosition ) <=> camera.WorldPosition.Distance( x.WorldPosition )
+			Return camera.Position.Distance( y.Position ) <=> camera.Position.Distance( x.Position )
 		End )
 		
 		Local cmaterial:=sprites[0].Material
@@ -53,7 +53,7 @@ Class SpriteBuffer
 				i0=i
 			Endif
 			
-			Local r:=camera.WorldBasis
+			Local r:=camera.Basis
 			
 			Select sprite.Mode
 			Case SpriteMode.Upright
@@ -61,7 +61,7 @@ Class SpriteBuffer
 				r.j=New Vec3f( 0,1,0 ) ; r.i=r.j.Cross( r.k ).Normalize()
 			End
 			
-			Local matrix:=New AffineMat4f( r.Scale( sprite.WorldScale ),sprite.WorldPosition )
+			Local matrix:=New AffineMat4f( r.Scale( sprite.Scale ),sprite.Position )
 			
 			Local handle:=sprite.Handle
 			

+ 1 - 0
modules/mojo3d/tests/cubetest.monkey2

@@ -1,3 +1,4 @@
+
 Namespace myapp
 
 #Import "<std>"

+ 3 - 2
modules/mojo3d/tests/ducks.monkey2

@@ -73,7 +73,6 @@ Class MyWindow Extends Window
 		'		
 		Local duck:=Model.Load( "asset::duck.gltf/Duck.gltf" )
 		duck.Mesh.FitVertices( New Boxf( -1,1 ) )
-		duck.Mesh.TransformVertices( Mat4f.Rotation( 0,Pi/2,0 ) )
 		
 		Local root:=duck.Copy()
 		root.Move( 0,10,0 )
@@ -91,6 +90,8 @@ Class MyWindow Extends Window
 				
 				copy.Move( 0,0,6+m*16 )
 				
+				copy.Scale=New Vec3f( 1 )
+				
 				For Local j:=0 Until copy.Materials.Length
 				
 					Local material:=Cast<PbrMaterial>( copy.Materials[j].Copy() )
@@ -121,7 +122,7 @@ Class MyWindow Extends Window
 		
 		For Local duck:=Eachin _ducks
 			
-			duck.Rotate( 0,1,0 )
+			duck.RotateY( 1 )
 		Next
 		
 		util.Fly( _camera,Self )

+ 10 - 5
modules/monkey/monkey.monkey2

@@ -5,11 +5,6 @@ Namespace monkey
 #import "<liblog.a>"
 #endif
 
-#Import "types.monkey2"
-#Import "math.monkey2"
-#Import "debug.monkey2"
-#Import "gc.monkey2"
-
 #Import "native/bbtypes.cpp"
 #Import "native/bbassert.cpp"
 #Import "native/bbmemory.cpp"
@@ -23,3 +18,13 @@ Namespace monkey
 #Import "native/bbvariant.cpp"
 #Import "native/bbtypeinfo.cpp"
 #Import "native/bbdeclinfo.cpp"
+
+#If __TARGET__="macos" Or __TARGET__="ios"
+#Import "native/bbstring.mm"
+#Endif
+
+#Import "types.monkey2"
+#Import "math.monkey2"
+#Import "debug.monkey2"
+#Import "gc.monkey2"
+

+ 1 - 3
modules/monkey/native/bbstring.h

@@ -252,9 +252,7 @@ class bbString{
 	void toWString( void *buf,int size )const;
 	
 #if __OBJC__	
-	NSString *ToNSString()const{
-		return [NSString stringWithUTF8String:c_str()];
-	}
+	NSString *ToNSString()const;
 #endif
 	
 	static bbString fromChar( int chr );

+ 9 - 0
modules/monkey/native/bbstring.mm

@@ -0,0 +1,9 @@
+
+#include "bbstring.h"
+
+bbString::bbString( NSString *str ):_rep( Rep::create( str.UTF8String ) ){
+}
+
+NSString *bbString::ToNSString()const{
+	return [NSString stringWithUTF8String:c_str()];
+}

+ 3 - 0
modules/sdl2/makefile_windows.monkey2

@@ -1,10 +1,13 @@
 
 Namespace sdl2
 
+
+
 #import "<libdsound.a>"
 #import "<libxinput.a>"
 #import "<libdinput8.a>"
 
+#import "<libadvapi32.a>"
 #import "<libole32.a>"
 #Import "<libshell32.a>"
 #import "<liboleaut32.a>"

+ 10 - 5
modules/std/audio/load_wav.monkey2

@@ -44,16 +44,20 @@ Function ReadWAV:AudioData( stream:std.stream.Stream )
 		Local tag:=stream.ReadInt()
 		Local size:=stream.ReadInt()
 		
+		Local aligned_size:=size+(size&1)	'chunk size *including* 2 byte alignment of next chunk.
+		
 		Select tag
 		Case $20746d66		'FMT
 			
 			Local fmt:=New FMT_Chunk
 			Local fmt_sz:=sizeof( fmt )
 			
-			If stream.Read( Varptr fmt,fmt_sz )<>fmt_sz Return Null
+			'read FMT chunk data
+			If fmt_sz>size Or stream.Read( Varptr fmt,fmt_sz )<>fmt_sz Return Null
 			
-			Local n:=size-fmt_sz
-			If n>0 And stream.Skip( n )<>n Return Null
+			'skip to next chunk
+			Local n:=aligned_size-fmt_sz
+			If n And stream.Skip( n )<>n Return Null
 			
 			If fmt.compType<>1 Return Null
 			
@@ -85,11 +89,12 @@ Function ReadWAV:AudioData( stream:std.stream.Stream )
 		
 		End
 		
-		stream.Skip( size )
+		'skip to next chunk
+		If stream.Skip( aligned_size )<>aligned_size Return Null
 		
 	Wend
 	
-	Return null
+	Return Null
 
 End
 

+ 9 - 11
modules/std/filesystem/filesystem.monkey2

@@ -44,10 +44,9 @@ Function CopyFile:Bool( srcPath:String,dstPath:String )="bbFileSystem::copyFile"
 
 Private
 
-Function FixRoot:String( path:String )
+Function FixPath:String( path:String )
 	
 	Local root:=ExtractRootDir( path )
-	
 	If Not root.EndsWith( "::" ) Return path
 	
 	path=path.Slice( root.Length )
@@ -63,7 +62,7 @@ End
 
 Function FixFilePath:String( path:String )
 	
-	Return FixRoot( StripSlashes( path ) )
+	Return FixPath( StripSlashes( path ) )
 End
 
 Public
@@ -137,6 +136,7 @@ Note that only the desktop and web targets have an assets directory. Other targe
 Function AssetsDir:String()
 #If __TARGET__="macos"
 	Return AppDir()+"../Resources/"
+	'Return ExtractDir( AppDir() )+"/Resources/"	'enable me!
 #Else If __DESKTOP_TARGET__ Or __WEB_TARGET__
 	Return AppDir()+"assets/"
 #Else
@@ -153,7 +153,7 @@ Note that only the desktop targets have a desktop directory. Other targets will
 #end
 Function DesktopDir:String()
 #If __TARGET__="windows"
-	Return GetEnv( "USERPROFILE" )+"/Desktop/"
+	Return GetEnv( "USERPROFILE" ).Replace( "\","/" )+"/Desktop/"
 #Else If __DESKTOP_TARGET__
  	Return GetEnv( "HOME" )+"/Desktop/"
  #Else
@@ -169,10 +169,10 @@ Note that only the desktop targets have a home directory. Other targets will ret
 
 #end
 Function HomeDir:String()
-#If __DESKTOP_TARGET__
-	Return GetEnv( "USERPROFILE" )+"/"
+#If __TARGET__="windows"
+	Return GetEnv( "USERPROFILE" ).Replace( "\","/" )+"/"
 #Else if __DESKTOP_TARGET__
-	Return GetEnv( "HOME" )+"/"+
+	Return GetEnv( "HOME" )+"/"
 #Else
 	Return ""
 #Endif
@@ -276,7 +276,7 @@ Then, any internal './' or '../' references in the path are collapsed.
 #end
 Function RealPath:String( path:String )
 	
-	path=FixRoot( path )
+	path=FixPath( path )
 	
 	Local rpath:=ExtractRootDir( path )
 	If rpath 
@@ -305,7 +305,7 @@ Function RealPath:String( path:String )
 	
 	#rem Not working on macos!
 	
-	path=FixRoot( path )
+	path=FixPath( path )
 	
 	Local buf:=New char_t[PATH_MAX]
 	
@@ -332,8 +332,6 @@ This function will not strip slashes from a root directory path.
 #end
 Function StripSlashes:String( path:String )
 	
-	path=path.Replace( "\","/" )
-	
 	If Not path.EndsWith( "/" ) Return path
 	
 	Local root:=ExtractRootDir( path )

+ 5 - 1
modules/std/requesters/native/requesters_linux.cpp

@@ -38,7 +38,11 @@ bbString bbRequesters::RequestFile( bbString title,bbString exts,bbBool save,bbS
 
 	if( path=="" ) path=".";
 		
-	return tinyfd_openFileDialog( bbCString( title ),bbCString( path ),0,0,0,0 );
+	if( save ){
+		return tinyfd_saveFileDialog( bbCString( title ),bbCString( path ),0,0,0 );
+	}else{
+		return tinyfd_openFileDialog( bbCString( title ),bbCString( path ),0,0,0,0 );
+	}
 }
 
 bbString bbRequesters::RequestDir( bbString title,bbString dir ){

+ 34 - 7
products/android/Monkey2Game/app/src/main/java/com/monkey2/lib/Monkey2Activity.java

@@ -3,15 +3,45 @@ package com.monkey2.lib;
 import android.os.Bundle;
 import android.os.Looper;
 import android.util.Log;
+import android.content.Intent;
 import android.view.ViewGroup;
 
 import org.libsdl.app.SDLActivity;
 
+import java.util.List;
+import java.util.LinkedList;
+
 public class Monkey2Activity extends SDLActivity {
     private static final String TAG = "Monkey2Activity";
 
     public static Monkey2Activity mSingleton;
 
+    public static List<Delegate> mDelegates=new LinkedList<Delegate>();
+
+    public static class Delegate{
+
+        void onActivityResult( int requestCode,int resultCode,Intent data ){}
+    }
+
+    public static Monkey2Activity instance(){
+
+        return mSingleton;
+    }
+
+    public static ViewGroup layout(){
+
+        return mSingleton.mLayout;
+    }
+
+    public static void addDelegate( Delegate delegate ){
+
+        if( mDelegates.contains( delegate ) ) return;
+
+        mDelegates.add( delegate );
+    }
+
+    // ***** overrides *****
+
     protected void onCreate(Bundle savedInstanceState) {
 
         super.onCreate(savedInstanceState);
@@ -24,14 +54,11 @@ public class Monkey2Activity extends SDLActivity {
         super.onDestroy();
     }
 
-    static public Monkey2Activity instance(){
+    protected void onActivityResult( int requestCode,int resultCode,Intent data ){
 
-        return mSingleton;
-    }
-
-    static public ViewGroup layout() {
+        for( Delegate delegate : mDelegates ){
 
-        return mSingleton.mLayout;
+            delegate.onActivityResult( requestCode,resultCode,data );
+        }
     }
-
 }

+ 5 - 5
scripts/common.bat

@@ -1,11 +1,11 @@
 
 set mx2cc=..\bin\mx2cc_windows.exe
-set mx2cc_new=..\src\mx2cc\mx2cc.buildv1.1.05\windows_release\mx2cc.exe
-set mx2cc_raspbian_new=..\src\mx2cc\mx2cc.buildv1.1.05\raspbian_release\mx2cc
+set mx2cc_new=..\src\mx2cc\mx2cc.buildv1.1.06\windows_release\mx2cc.exe
+set mx2cc_raspbian_new=..\src\mx2cc\mx2cc.buildv1.1.06\raspbian_release\mx2cc
 
 set ted2=..\bin\ted2_windows
-set ted2_new=..\src\ted2\ted2.buildv1.1.05\windows_release
-set ted2go_new=..\src\ted2go\Ted2.buildv1.1.05\windows_release
+set ted2_new=..\src\ted2\ted2.buildv1.1.06\windows_release
+set ted2go_new=..\src\ted2go\Ted2.buildv1.1.06\windows_release
 
 set launcher="..\Monkey2 (Windows).exe"
-set launcher_new=..\src\launcher\launcher.buildv1.1.05\windows_release\launcher.exe
+set launcher_new=..\src\launcher\launcher.buildv1.1.06\windows_release\launcher.exe

+ 12 - 12
scripts/common.sh

@@ -9,34 +9,34 @@ launcher_new=""
 if [ "$OSTYPE" = "linux-gnu" ]
 then
 	mx2cc="../bin/mx2cc_linux"
-	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.05/linux_release/mx2cc"
+	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.06/linux_release/mx2cc"
 	
 	ted2="../bin/ted2_linux"
-	ted2_new="../src/ted2/ted2.buildv1.1.05/linux_release"
-	ted2go_new="../src/ted2go/Ted2.buildv1.1.05/linux_release"
+	ted2_new="../src/ted2/ted2.buildv1.1.06/linux_release"
+	ted2go_new="../src/ted2go/Ted2.buildv1.1.06/linux_release"
 	
 	launcher="../Monkey2 (Linux)"
-	launcher_new="../src/launcher/launcher.buildv1.1.05/linux_release/launcher"
+	launcher_new="../src/launcher/launcher.buildv1.1.06/linux_release/launcher"
 	
 elif [ "$OSTYPE" = "linux-gnueabihf" ]
 then
 	mx2cc="../bin/mx2cc_raspbian"
-	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.02/raspbian_release/mx2cc"
+	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.06/raspbian_release/mx2cc"
 	
 	ted2="../bin/ted2_raspbian"
-	ted2_new="../src/ted2/ted2.buildv1.1.02/raspbian_release"
-	ted2go_new="../src/ted2go/Ted2.buildv1.1.02/raspbian_release"
+	ted2_new="../src/ted2/ted2.buildv1.1.06/raspbian_release"
+	ted2go_new="../src/ted2go/Ted2.buildv1.1.06/raspbian_release"
 	
 	launcher="../Monkey2 (Raspbian)"
-	launcher_new="../src/launcher/launcher.buildv1.1.02/raspbian_release/launcher"
+	launcher_new="../src/launcher/launcher.buildv1.1.06/raspbian_release/launcher"
 else
 	mx2cc="../bin/mx2cc_macos"
-	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.05/macos_release/mx2cc"
+	mx2cc_new="../src/mx2cc/mx2cc.buildv1.1.06/macos_release/mx2cc"
 	
 	ted2="../bin/ted2_macos.app"
-	ted2_new="../src/ted2/ted2.buildv1.1.05/macos_release/ted2.app"
-	ted2go_new="../src/ted2go/Ted2.buildv1.1.05/macos_release/ted2.app"
+	ted2_new="../src/ted2/ted2.buildv1.1.06/macos_release/ted2.app"
+	ted2go_new="../src/ted2go/Ted2.buildv1.1.06/macos_release/ted2.app"
 	
 	launcher="../Monkey2 (Macos).app"
-	launcher_new="../src/launcher/launcher.buildv1.1.05/macos_release/launcher.app"
+	launcher_new="../src/launcher/launcher.buildv1.1.06/macos_release/launcher.app"
 fi

+ 2 - 0
scripts/updatemods.bat

@@ -1,6 +1,8 @@
 
 echo off
 
+call common.bat
+
 echo.
 echo ***** Updating modules *****
 echo.

+ 1 - 1
scripts/updatemx2cc.sh

@@ -5,7 +5,7 @@ echo ""
 echo "***** Updating mx2cc *****"
 echo ""
 
-$mx2cc makemods -config=release
+$mx2cc makemods -config=release monkey libc miniz stb-image stb-image-write stb-vorbis std
 
 $mx2cc makeapp -apptype=console -config=release ../src/mx2cc/mx2cc.monkey2
 

+ 3 - 2
src/createrelease/createrelease.monkey2

@@ -5,9 +5,9 @@
 Using libc..
 Using std..
 
-Const MX2CC_VERSION:="1.1.05"
+Const MX2CC_VERSION:="1.1.06"
 
-Const RELEASE_SUFFIX:="b"
+Const RELEASE_SUFFIX:=""
 
 Const OUTPUT:="Monkey2-v"+MX2CC_VERSION+RELEASE_SUFFIX
 
@@ -16,6 +16,7 @@ Const IGNORE:="
 src/c2mx2
 src/createrelease
 modules/admob
+modules/iap
 modules/linq
 modules/gles30
 bin/ted2.state.json

+ 1 - 1
src/mx2cc/mx2.monkey2

@@ -50,4 +50,4 @@ Using libc
 ' 3) edit .sh and .bat files to use new version (common.sh, common.bat)
 ' 4) ./rebuildall
 '
-Const MX2CC_VERSION:="1.1.05"
+Const MX2CC_VERSION:="1.1.06"

+ 2 - 2
src/mx2cc/mx2cc.monkey2

@@ -25,7 +25,7 @@ Const FORCE_MSVC:=False
 
 Global StartDir:String
 
-Const TestArgs:="mx2cc makemods"
+'Const TestArgs:="mx2cc makemods"
 
 'Const TestArgs:="mx2cc makedocs mojo3d"
 
@@ -33,7 +33,7 @@ Const TestArgs:="mx2cc makemods"
 
 'Const TestArgs:="mx2cc makemods -config=debug monkey libc miniz stb-image stb-image-write stb-vorbis std"
 
-'Const TestArgs:="mx2cc makeapp -target=desktop -apptype=console -run src/mx2cc/test.monkey2"
+Const TestArgs:="mx2cc makeapp -target=desktop -apptype=console -run src/mx2cc/test.monkey2"
 
 'To build with old mx2cc...
 '

+ 7 - 4
src/mx2cc/test.monkey2

@@ -1,8 +1,11 @@
 
-#Import "test2
-
 Function Main()
-	
-	Test()
 
+	Local i:=010
+	
+	Print i	
+	Print 010.10
+	
+	Print 1.0e+20
+	
 End

+ 10 - 2
src/mx2cc/value.monkey2

@@ -233,6 +233,16 @@ Class LiteralValue Extends Value
 	
 	Method New( type:Type,value:String )
 		Self.type=type
+		
+		Local ptype:=TCast<PrimType>( type )
+		If ptype And ptype.IsNumeric
+			If ptype.IsIntegral
+				value=String( ULong( value ) )
+			Else If ptype.IsReal
+				value=String( Double( value ) )
+			Endif
+		Endif
+
 		Self.value=value
 	End
 	
@@ -250,11 +260,9 @@ Class LiteralValue Extends Value
 		'upcast to...
 		Local ptype:=TCast<PrimType>( type )
 		If Not ptype Return New UpCastValue( type,Self )
-'		If Not ptype SemantError( "LiteralValue.UpCast()" )
 		
 		Local ptype2:=TCast<PrimType>( Self.type )
 		If Not ptype2 Return New UpCastValue( type,Self )
-'		If Not ptype2 SemantError( "LiteralValue.UpCast()" )
 		
 		Local result:=""
 		

+ 0 - 197
src/ted2/bin/t.txt

@@ -1,197 +0,0 @@
-GNU Wget 1.18, a non-interactive network retriever.
-Usage: wget [OPTION]... [URL]...
-
-Mandatory arguments to long options are mandatory for short options too.
-
-Startup:
-  -V,  --version                   display the version of Wget and exit
-  -h,  --help                      print this help
-  -b,  --background                go to background after startup
-  -e,  --execute=COMMAND           execute a `.wgetrc'-style command
-
-Logging and input file:
-  -o,  --output-file=FILE          log messages to FILE
-  -a,  --append-output=FILE        append messages to FILE
-  -d,  --debug                     print lots of debugging information
-  -q,  --quiet                     quiet (no output)
-  -v,  --verbose                   be verbose (this is the default)
-  -nv, --no-verbose                turn off verboseness, without being quiet
-       --report-speed=TYPE         output bandwidth as TYPE.  TYPE can be bits
-  -i,  --input-file=FILE           download URLs found in local or external FILE
-       --input-metalink=FILE       download files covered in local Metalink FILE
-  -F,  --force-html                treat input file as HTML
-  -B,  --base=URL                  resolves HTML input-file links (-i -F)
-                                     relative to URL
-       --config=FILE               specify config file to use
-       --no-config                 do not read any config file
-       --rejected-log=FILE         log reasons for URL rejection to FILE
-
-Download:
-  -t,  --tries=NUMBER              set number of retries to NUMBER (0 unlimits)
-       --retry-connrefused         retry even if connection is refused
-  -O,  --output-document=FILE      write documents to FILE
-  -nc, --no-clobber                skip downloads that would download to
-                                     existing files (overwriting them)
-  -c,  --continue                  resume getting a partially-downloaded file
-       --start-pos=OFFSET          start downloading from zero-based position OFFSET
-       --progress=TYPE             select progress gauge type
-       --show-progress             display the progress bar in any verbosity mode
-  -N,  --timestamping              don't re-retrieve files unless newer than
-                                     local
-       --no-if-modified-since      don't use conditional if-modified-since get
-                                     requests in timestamping mode
-       --no-use-server-timestamps  don't set the local file's timestamp by
-                                     the one on the server
-  -S,  --server-response           print server response
-       --spider                    don't download anything
-  -T,  --timeout=SECONDS           set all timeout values to SECONDS
-       --dns-timeout=SECS          set the DNS lookup timeout to SECS
-       --connect-timeout=SECS      set the connect timeout to SECS
-       --read-timeout=SECS         set the read timeout to SECS
-  -w,  --wait=SECONDS              wait SECONDS between retrievals
-       --waitretry=SECONDS         wait 1..SECONDS between retries of a retrieval
-       --random-wait               wait from 0.5*WAIT...1.5*WAIT secs between retrievals
-       --no-proxy                  explicitly turn off proxy
-  -Q,  --quota=NUMBER              set retrieval quota to NUMBER
-       --bind-address=ADDRESS      bind to ADDRESS (hostname or IP) on local host
-       --limit-rate=RATE           limit download rate to RATE
-       --no-dns-cache              disable caching DNS lookups
-       --restrict-file-names=OS    restrict chars in file names to ones OS allows
-       --ignore-case               ignore case when matching files/directories
-  -4,  --inet4-only                connect only to IPv4 addresses
-  -6,  --inet6-only                connect only to IPv6 addresses
-       --prefer-family=FAMILY      connect first to addresses of specified family,
-                                     one of IPv6, IPv4, or none
-       --user=USER                 set both ftp and http user to USER
-       --password=PASS             set both ftp and http password to PASS
-       --ask-password              prompt for passwords
-       --no-iri                    turn off IRI support
-       --local-encoding=ENC        use ENC as the local encoding for IRIs
-       --remote-encoding=ENC       use ENC as the default remote encoding
-       --unlink                    remove file before clobber
-       --metalink-over-http        use Metalink metadata from HTTP response headers
-       --preferred-location        preferred location for Metalink resources
-
-Directories:
-  -nd, --no-directories            don't create directories
-  -x,  --force-directories         force creation of directories
-  -nH, --no-host-directories       don't create host directories
-       --protocol-directories      use protocol name in directories
-  -P,  --directory-prefix=PREFIX   save files to PREFIX/..
-       --cut-dirs=NUMBER           ignore NUMBER remote directory components
-
-HTTP options:
-       --http-user=USER            set http user to USER
-       --http-password=PASS        set http password to PASS
-       --no-cache                  disallow server-cached data
-       --default-page=NAME         change the default page name (normally
-                                     this is 'index.html'.)
-  -E,  --adjust-extension          save HTML/CSS documents with proper extensions
-       --ignore-length             ignore 'Content-Length' header field
-       --header=STRING             insert STRING among the headers
-       --max-redirect              maximum redirections allowed per page
-       --proxy-user=USER           set USER as proxy username
-       --proxy-password=PASS       set PASS as proxy password
-       --referer=URL               include 'Referer: URL' header in HTTP request
-       --save-headers              save the HTTP headers to file
-  -U,  --user-agent=AGENT          identify as AGENT instead of Wget/VERSION
-       --no-http-keep-alive        disable HTTP keep-alive (persistent connections)
-       --no-cookies                don't use cookies
-       --load-cookies=FILE         load cookies from FILE before session
-       --save-cookies=FILE         save cookies to FILE after session
-       --keep-session-cookies      load and save session (non-permanent) cookies
-       --post-data=STRING          use the POST method; send STRING as the data
-       --post-file=FILE            use the POST method; send contents of FILE
-       --method=HTTPMethod         use method "HTTPMethod" in the request
-       --body-data=STRING          send STRING as data. --method MUST be set
-       --body-file=FILE            send contents of FILE. --method MUST be set
-       --content-disposition       honor the Content-Disposition header when
-                                     choosing local file names (EXPERIMENTAL)
-       --content-on-error          output the received content on server errors
-       --auth-no-challenge         send Basic HTTP authentication information
-                                     without first waiting for the server's
-                                     challenge
-
-HTTPS (SSL/TLS) options:
-       --secure-protocol=PR        choose secure protocol, one of auto, SSLv2,
-                                     SSLv3, TLSv1 and PFS
-       --https-only                only follow secure HTTPS links
-       --no-check-certificate      don't validate the server's certificate
-       --certificate=FILE          client certificate file
-       --certificate-type=TYPE     client certificate type, PEM or DER
-       --private-key=FILE          private key file
-       --private-key-type=TYPE     private key type, PEM or DER
-       --ca-certificate=FILE       file with the bundle of CAs
-       --ca-directory=DIR          directory where hash list of CAs is stored
-       --crl-file=FILE             file with bundle of CRLs
-       --pinnedpubkey=FILE/HASHES  Public key (PEM/DER) file, or any number
-                                   of base64 encoded sha256 hashes preceded by
-                                   'sha256//' and seperated by ';', to verify
-                                   peer against
-       --random-file=FILE          file with random data for seeding the SSL PRNG
-       --egd-file=FILE             file naming the EGD socket with random data
-
-HSTS options:
-       --no-hsts                   disable HSTS
-       --hsts-file                 path of HSTS database (will override default)
-
-FTP options:
-       --ftp-user=USER             set ftp user to USER
-       --ftp-password=PASS         set ftp password to PASS
-       --no-remove-listing         don't remove '.listing' files
-       --no-glob                   turn off FTP file name globbing
-       --no-passive-ftp            disable the "passive" transfer mode
-       --preserve-permissions      preserve remote file permissions
-       --retr-symlinks             when recursing, get linked-to files (not dir)
-
-FTPS options:
-       --ftps-implicit                 use implicit FTPS (default port is 990)
-       --ftps-resume-ssl               resume the SSL/TLS session started in the control connection when
-                                         opening a data connection
-       --ftps-clear-data-connection    cipher the control channel only; all the data will be in plaintext
-       --ftps-fallback-to-ftp          fall back to FTP if FTPS is not supported in the target server
-WARC options:
-       --warc-file=FILENAME        save request/response data to a .warc.gz file
-       --warc-header=STRING        insert STRING into the warcinfo record
-       --warc-max-size=NUMBER      set maximum size of WARC files to NUMBER
-       --warc-cdx                  write CDX index files
-       --warc-dedup=FILENAME       do not store records listed in this CDX file
-       --no-warc-compression       do not compress WARC files with GZIP
-       --no-warc-digests           do not calculate SHA1 digests
-       --no-warc-keep-log          do not store the log file in a WARC record
-       --warc-tempdir=DIRECTORY    location for temporary files created by the
-                                     WARC writer
-
-Recursive download:
-  -r,  --recursive                 specify recursive download
-  -l,  --level=NUMBER              maximum recursion depth (inf or 0 for infinite)
-       --delete-after              delete files locally after downloading them
-  -k,  --convert-links             make links in downloaded HTML or CSS point to
-                                     local files
-       --convert-file-only         convert the file part of the URLs only (usually known as the basename)
-       --backups=N                 before writing file X, rotate up to N backup files
-  -K,  --backup-converted          before converting file X, back up as X.orig
-  -m,  --mirror                    shortcut for -N -r -l inf --no-remove-listing
-  -p,  --page-requisites           get all images, etc. needed to display HTML page
-       --strict-comments           turn on strict (SGML) handling of HTML comments
-
-Recursive accept/reject:
-  -A,  --accept=LIST               comma-separated list of accepted extensions
-  -R,  --reject=LIST               comma-separated list of rejected extensions
-       --accept-regex=REGEX        regex matching accepted URLs
-       --reject-regex=REGEX        regex matching rejected URLs
-       --regex-type=TYPE           regex type (posix)
-  -D,  --domains=LIST              comma-separated list of accepted domains
-       --exclude-domains=LIST      comma-separated list of rejected domains
-       --follow-ftp                follow FTP links from HTML documents
-       --follow-tags=LIST          comma-separated list of followed HTML tags
-       --ignore-tags=LIST          comma-separated list of ignored HTML tags
-  -H,  --span-hosts                go to foreign hosts when recursive
-  -L,  --relative                  follow relative links only
-  -I,  --include-directories=LIST  list of allowed directories
-       --trust-server-names        use the name specified by the redirection
-                                     URL's last component
-  -X,  --exclude-directories=LIST  list of excluded directories
-  -np, --no-parent                 don't ascend to the parent directory
-
-Mail bug reports and suggestions to <[email protected]>

BIN
src/ted2/bin/tmp.zip


+ 62 - 4
src/ted2go/MainWindow.monkey2

@@ -11,6 +11,7 @@ Namespace ted2go
 
 #Import "assets/fonts/@/fonts"
 
+#Import "assets/themes/irc/@/themes/irc"
 
 Global MainWindow:MainWindowInstance
 
@@ -57,13 +58,17 @@ Class MainWindowInstance Extends Window
 		End
 		
 		'IRC tab
+		
 		_ircView=New IRCView
-		_ircView.introScreen.Text="Get live help from other Monkey 2 users"
+		_ircView.introScreen.Text="Hang out with other Monkey 2 users"
 		_ircView.introScreen.OnNickChange+=Lambda( nick:String )
 			Prefs.IrcNickname=nick
 		End
+		
 		SetupChatTab()
 		
+		If Prefs.IrcConnect Then _ircView.introScreen.Connect()
+		
 		'Build tab
 		
 		_buildConsole=New ConsoleExt
@@ -367,6 +372,8 @@ Class MainWindowInstance Extends Window
 		_consolesTabView.AddTab( "Find",_findConsole,False )
 		_consolesTabView.AddTab( "Chat",_ircView,False )
 		
+		_consolesTabView.CurrentChanged+=OnChatClicked
+		
 		_statusBar=New StatusBarView
 		
 		_contentView=New DockingView
@@ -506,13 +513,14 @@ Class MainWindowInstance Extends Window
 	End
 	
 	Method Terminate()
-	
+		
 		_isTerminating=True
 		SaveState()
 		_enableSaving=False
 		OnForceStop() ' kill build process if started
 		ProcessReader.StopAll()
-
+		If _ircView Then _ircView.Quit("Closing Ted2Go")
+		
 		App.Terminate()
 	End
 
@@ -956,7 +964,7 @@ Class MainWindowInstance Extends Window
 	Field _helpIdent:String
 	
 	Method OnRender( canvas:Canvas ) Override
-	
+		
 		If Not _inited
 			_inited=True
 			OnInit()
@@ -969,6 +977,8 @@ Class MainWindowInstance Extends Window
 			SizeChanged()
 		Endif
 		
+		UpdateIrcIcon()
+		
 		Rendered()
 		Rendered=Null
 	End
@@ -995,6 +1005,8 @@ Class MainWindowInstance Extends Window
 		
 		If Not _ircView Return
 		
+		_ircView.ircHandler.OnMessage+=Self.OnChatMessage
+		
 		Local intro:=_ircView.introScreen
 		
 		If intro.IsConnected Return
@@ -1007,6 +1019,49 @@ Class MainWindowInstance Extends Window
 		
 	End
 	
+	Method OnChatClicked()
+		If _consolesTabView.CurrentView<>_ircView Then Return
+		
+		_consolesTabView.SetTabIcon( _ircView, Null )
+		_ircNotifyIcon=0
+	End
+	
+	Method OnChatMessage( message:IRCMessage, container:IRCMessageContainer, server:IRCServer )
+		If message.type<>"PRIVMSG" Or _consolesTabView.CurrentView=_ircView Then Return
+		
+		'Show notice icon
+		If message.text.Contains(server.nickname) Then
+			If _ircNotifyIcon<=1 Then _ircNotifyIcon=2
+	
+		Else
+			If _ircNotifyIcon<=0 Then _ircNotifyIcon=1
+		Endif
+		
+	End
+	
+	Method UpdateIrcIcon()
+		If _ircNotifyIcon<=0 Then Return
+		
+		Local time:Int=Int(Millisecs()*0.0025)
+		
+		If time=_ircIconBlink Then Return
+		_ircIconBlink=time
+		
+		If time Mod 2 Then
+			Select _ircNotifyIcon
+				
+				Case 1
+					_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/notice.png" ) )
+					
+				Case 2
+					_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/important.png" ) )
+			End
+		Else
+			_consolesTabView.SetTabIcon( _ircView, App.Theme.OpenImage( "irc/blink.png" ) )
+		Endif
+		
+	End
+	
 	Method LoadState( jobj:JsonObject )
 	
 		If jobj.Contains( "browserSize" )
@@ -1137,6 +1192,9 @@ Class MainWindowInstance Extends Window
 	Field _consolesTabView:TabView
 	Field _browsersTabView:TabView
 	
+	Field _ircNotifyIcon:Int
+	Field _ircIconBlink:Int
+	
 	Field _forceStop:Action
 
 	Field _tabMenu:Menu

+ 5 - 1
src/ted2go/Prefs.monkey2

@@ -21,7 +21,8 @@ Class Prefs
 	Global IrcNickname:String
 	Global IrcServer:="irc.freenode.net"
 	Global IrcPort:=6667
-	Global IrcRooms:="#monkey2" '#monkey2Ui#monkey23D
+	Global IrcRooms:="#monkey2" '#mojox#mojo2d
+	Global IrcConnect:Bool=False
 	'
 	Global EditorToolBarVisible:=False
 	Global EditorGutterVisible:=True
@@ -46,6 +47,8 @@ Class Prefs
 			IrcServer=Json_GetString( j2,"server",IrcServer )
 			IrcPort=Json_GetInt( j2,"port",IrcPort )
 			IrcRooms=Json_GetString( j2,"rooms",IrcRooms )
+			IrcConnect=Json_GetBool( j2,"connect",IrcConnect )
+			
 		Endif
 		
 		If json.Contains( "main" )
@@ -108,6 +111,7 @@ Class Prefs
 		j["server"]=New JsonString( IrcServer )
 		j["port"]=New JsonNumber( IrcPort )
 		j["rooms"]=New JsonString( IrcRooms )
+		j["connect"]=New JsonBool( IrcConnect )
 		
 		j=New JsonObject
 		json["completion"]=j

+ 9 - 12
src/ted2go/Ted2.monkey2

@@ -127,18 +127,16 @@ Function Main()
 	Local root:=Prefs.MonkeyRootPath
 	If Not root Then root=AppDir()
 	
-	' return real folder or empty string
-	Local path:=SetupMonkeyRootPath( root,True )
-	If Not path
-		libc.exit_( 1 )
-		Return
-	Endif
+	root=SetupMonkeyRootPath( root,True )
+	If Not root libc.exit_( 1 )
 	
-	If path<>root
-		Prefs.MonkeyRootPath=path
+	If root<>Prefs.MonkeyRootPath
+		Prefs.MonkeyRootPath=root
 		Prefs.SaveLocalState()
 	Endif
 	
+	ChangeDir( root )
+	
 	'load ted2 state
 	'
 	Local jobj:=JsonObject.Load( "bin/ted2.state.json" )
@@ -148,7 +146,8 @@ Function Main()
 	
 	'initial theme
 	'
-	If Not jobj.Contains( "theme" ) jobj["theme"]=New JsonString( "theme-classic-dark" )
+	If Not jobj.Contains( "theme" ) jobj["theme"]=New JsonString( "theme-prime-blue" )
+
 	If Not jobj.Contains( "themeScale" ) jobj["themeScale"]=New JsonNumber( 1 )
 	
 	Local config:=New StringMap<String>
@@ -194,9 +193,7 @@ End
 Function SetupMonkeyRootPath:String( rootPath:String,searchMode:Bool )
 	
 #If __DESKTOP_TARGET__
-	
-	ChangeDir( rootPath )
-	
+
 	If searchMode
 		' search for desired folder
 		Local found:=FindBinFolder( rootPath )

BIN
src/ted2go/assets/themes/hollow_assets/checkbox_icons.png


BIN
src/ted2go/assets/themes/hollow_assets/dialog_skin.png


BIN
src/ted2go/assets/themes/hollow_assets/progressbar_icons.png


BIN
src/ted2go/assets/themes/hollow_assets/tabclose_icons.png


BIN
src/ted2go/assets/themes/hollow_assets/treeview_icons.png


BIN
src/ted2go/assets/themes/irc/blink.png


BIN
src/ted2go/assets/themes/irc/important.png


BIN
src/ted2go/assets/themes/irc/notice.png


BIN
src/ted2go/assets/themes/prime_assets/button_skin.png


BIN
src/ted2go/assets/themes/prime_assets/square.png


BIN
src/ted2go/assets/themes/prime_assets/tabbutton_skin.png


+ 474 - 0
src/ted2go/assets/themes/theme-hollow.json

@@ -0,0 +1,474 @@
+//Theme by @Hezkore
+//Inspired by gruvbox
+{
+	"extends":"ted2-default",
+	
+	"fonts":{
+	
+		"normal":"Roboto-Medium.ttf,16",
+		"fixedWidth":"RobotoMono-Medium.ttf,19",
+		"small":"Roboto-Medium.ttf,14",
+		"editor":"MesloLGSDZ-Bold.ttf,19"
+	},
+
+	"colors":{
+
+		"border": "#121111",
+
+		"transparent": "#0000",
+		"darkest": "#222020",
+		"darker": "#32302F",
+		"dark": "#3C3836",
+		"bright": "#504945",
+		"brighter": "#665C54",
+		"brightest": "#7C6F64",
+
+		"text-default": "#CECECE",
+		"text-highlight": "#FFFFFF",
+		"text-disabled": "#6F6F6F",
+		"text-background": "#969696",
+		
+		"textview-cursor":"textview-color1",
+		"textview-selection":"brightest",
+		"textview-cursor-line":"dark",
+		"textview-whitespaces":"bright",
+
+		"textview-color0":"#FF00FF", 	//When is this used?
+		"textview-color1": "#EBDAB4",	//identifiers
+		"textview-color2": "#FB4934",	//keywords
+		"textview-color3": "#B8BB26",	//strings
+		"textview-color4": "#B087D2",	//numbers
+		"textview-color5": "#6F665E",	//comment
+		"textview-color6": "#FABD2F",	//preproc
+		"textview-color7": "#8EC07C",
+		
+		"windowClearColor":"darkest",
+		"menu-shortcut":"text-background",
+
+		"statusbar": "darkest",
+		"statusbar-active": "darkest"
+	},
+	
+	"styles":{
+
+		"default":{
+			"font":"normal",
+			"textColor":"text-default",
+			"iconColor":"#ffff",
+			"states":{
+				"disabled":{
+					"textColor":"text-disabled",
+					"iconColor":"#8fff"
+				}
+			}
+		},
+
+		"CodeMapView":{
+			"extends":"TextView",
+			"margin":[ 4,0,0,0 ]
+		},
+
+		"GutterView":{
+			"padding":[ -16,0,16,0 ],
+			"extends":"TextView",
+			"textColor":"text-disabled",
+			"backgroundColor":"darkest",
+			"font":"editor"
+		},
+		
+		"DebugToolBar":{
+			"extends":"ToolBar",
+			"border":[ 1 ],
+			"borderColor":"darker",
+			"icons":"debug_icons.png"
+		},
+		
+		"HelpTextField":{
+			"extends":"TextField",
+			"skinColor":"darker"
+		},
+
+		"Hint":{
+			"font":"small",
+			"textColor":"text-default",
+			"backgroundColor":"border"
+		},
+
+		"ToolBarExt":{
+			"extends":"ToolBar",
+			"padding":[ 0 ],
+			"margin":[ 0 ],
+			"backgroundColor":"dark",
+			"border":[ 0 ]
+		},
+
+		"MainToolBar":{
+			"extends":"ToolBarExt"
+		},
+
+		"EditorToolBar":{
+			"extends":"ToolBarExt"
+		},
+
+		"SourceToolBar":{
+			"extends":"ToolBarExt",
+			"backgroundColor":"darker"
+		},
+
+		"TabViewArrowPrev":{
+			"extends":"Button",
+			"textColor":"text-background",
+			"padding":[ 8,4 ],
+			"margin":[ 2,0,2,0 ]
+		},
+
+		"TabViewArrowNext":{
+			"extends":"Button",
+			"textColor":"text-background",
+			"padding":[ 8,4 ],
+			"margin":[ 2,0,12,0 ]
+		},
+
+		"ProgressBar":{
+			"icons":"hollow_assets/progressbar_icons.png"
+		},
+
+		"StatusBar":{
+			"extends":"DockingView",
+			"padding":[ 0,8,0,8 ],
+			"border":[0,1,0,0],
+			"borderColor":"border",
+			"backgroundColor":"statusbar"
+		},
+		"StatusBarText":{
+			"extends":"Label",
+			"font":"small"
+		},
+		"StatusBarLineInfo":{
+			"extends":"Label",
+			"margin":[ 40,0,0,0 ],
+			"font":"small"
+		},
+		"StatusBarIns":{
+			"extends":"Label",
+			"font":"small"
+		},
+		"StatusBarProgress":{
+			"extends":"ProgressBar",
+			"margin":[ 6,0 ]
+		},
+
+		"CompletionDialog":{
+			"extends":"Dialog"
+		},
+		"CompletionDialogContent":{
+			"padding":[ 1 ]
+		},
+		
+		"ProjectTabView":{
+			"extends":"TabView",
+			"backgroundColor":"dark"
+		},
+		
+		"StatusBarButton":{
+			"extends":"ToolButton",
+			"padding":[ 0 ],
+			"skinColor":"transparent"
+		},
+		
+		"Label":{
+			"padding":[8,4]
+		},
+		
+		"Button":{
+			"extends":"Label",
+			"padding":[4,2],
+			"backgroundColor":"dark",
+			"textColor":"text-default",
+			
+			"states":{
+				"hover":{
+					"textColor":"text-highlight",
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+
+		"ToolButton":{
+			"extends":"Button",
+			"backgroundColor":"dark",
+			"padding":[ 2,0 ],
+			"margin":[ 2,0 ],
+
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"backgroundColor":"brighter"
+				}
+			}
+		},
+		
+		"PushButton":{
+			"extends":"Button",
+			"margin":[4,4]
+		},
+		
+		"CheckButton":{
+			"extends":"Label"
+		},
+		
+		"CheckBox":{
+			"icons":"hollow_assets/checkbox_icons.png",
+			"iconColor":"textview-color7",
+			"margin":[0,0,0,0]
+		},
+		
+		"ScrollView":{
+		},
+		
+		"ScrollBar":{
+			"backgroundColor":"darker"
+		},
+		
+		"ScrollKnob":{
+			"padding":[ 6 ],
+			"border":[ 0 ],
+			"backgroundColor":"dark",
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				}
+			}
+		},
+		
+		"TextView":{
+			"font":"editor",
+			"textColor":"textview-color7",
+			"backgroundColor":"darker"
+		},
+		
+		"TextViewContent":{
+			"padding":[4]
+		},
+		
+		"TextField":{
+			"font":"normal",
+			"padding":[ 2 ],
+			"margin":[ 2 ],
+			"backgroundColor":"darker"
+		},
+		
+		"DockingView":{
+		},
+		
+		"DockedView":{
+		},
+		
+		"DockKnob":{
+			"padding":[ 3 ],
+			"backgroundColor":"darkest",
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+		
+		"ToolBar":{
+			"padding":[ 2 ],
+			"backgroundColor":"darker"
+		},
+		
+		"Menu":{
+			"extends":"DockingView",
+			"padding":[ 0 ],
+			"skin":"hollow_assets/dialog_skin.png",
+			"skinColor":"dark"
+		},
+		
+		"MenuButton":{
+			"extends":"Label",
+			"padding":[8,3],
+			"textColor":"text-default",
+			"states":{
+				"hover":{
+					"textColor":"text-highlight",
+					"backgroundColor":"brightest"
+				},
+				"active":{
+					"backgroundColor":"brightest"
+				},
+				"selected":{
+					"backgroundColor":"brightest"
+				}
+			}
+		},
+		
+		"MenuBar":{
+			"extends":"ToolBar",
+			"backgroundColor":"border",
+			"border":[ 0,0,0,1 ],
+			"backgroundColor":"dark",
+			"margin":[ 0 ]
+		},
+		
+		"MenuSeparator":{
+			"padding":[ 0,0,0,1 ],
+			"backgroundColor":"dark",
+			"border":[ 8,8,7,7 ]
+		},
+
+		"TabView":{
+		},
+		
+		"TabBar":{
+			"extends":"ToolBar",
+			"padding":[ 0,0,0,0 ],
+			"backgroundColor":"windowClearColor"
+		},
+		
+		"TabButton":{
+			"extends":"Button",
+			"font":"small",
+			"padding":[10],
+			"textColor":"text-disabled",
+			"backgroundColor":"darkest",
+			"borderColor":"darkest",
+			"border":[0,2,0,0],
+			"states":{
+				"hover":{
+					"textColor":"text-background"
+				},
+				"active":{
+					"textColor":"text-background"
+				},
+				"selected":{
+					"borderColor":"textview-color7",
+					"backgroundColor":"darker",
+					"textColor":"text-default"
+				}
+			}
+		},
+		
+		"TabClose":{
+			"margin":[32,0,0,0 ],
+			"icons":"hollow_assets/tabclose_icons.png",
+			"iconColor":"text-disabled",
+			"states":{
+				"hover":{
+					"iconColor":"text-background"
+				},
+				"active":{
+					"iconColor":"text-highlight"
+				}
+			}
+		},
+		
+		"TableView":{
+			"extends":"DockingView"
+		},
+		
+		"TableHeader":{
+			"extends":"Label",
+			"textColor":"text-highlight",
+			"borderColor":"darker"
+		},
+		
+		"TableColumn":{
+		},
+		
+		"TreeView":{
+			"backgroundColor":"darker",
+			"icons":"hollow_assets/treeview_icons.png",
+			"iconColor":"text-background"
+		},
+		
+		"TreeViewContent":{
+			"padding":[3]
+		},
+
+		"TreeViewNode":{
+			"font":"small",
+			"textColor":"text-background",
+			"padding":[0,2,0,2],
+			"states":{
+				"hover":{
+					"textColor":"text-default"
+				},
+				"selected":{
+					"textColor":"text-default"
+				}
+			}
+		},
+		
+		"ListView":{
+			"backgroundColor":"dark"
+		},
+		
+		"ListViewContent":{
+			"padding":[2]
+		},
+		
+		"ListViewItem":{
+			"padding":[1],
+			"states":{
+				"hover":{
+					"backgroundColor":"darker"
+				},
+				"selected":{
+					"backgroundColor":"darker",
+					"textColor":"text-highlight"
+				}
+			}
+		},
+		
+		"FileBrowser":{
+			"extends":"TreeView"
+		},
+		
+		"HtmlView":{
+		},
+		
+		"Console":{
+			"backgroundColor":"darker"
+		},
+		
+		"Dialog":{
+			"skin":"hollow_assets/dialog_skin.png",
+			"skinColor":"bright"
+		},
+		
+		"DialogTitle":{
+			"extends":"Label",
+			"backgroundColor":"brightest"
+		
+		},
+		
+		"DialogContent":{
+			"padding":[ 8,8,8,4 ]
+		},
+		
+		"DialogActions":{
+			"padding":[ 8,4,8,4 ]
+		}
+	}
+}

+ 28 - 19
src/ted2go/assets/themes/theme-prime-base.json

@@ -40,7 +40,7 @@
 		"textview-color4": "#6C71C4",	//numbers
 		"textview-color5": "#606060",	//comment
 		"textview-color6": "#E74C31",	//preproc
-		"textview-color7": "#FF00FF",	//When is this used?
+		"textview-color7": "#3298DB",
 		
 		"windowClearColor":"bright",
 		"menu-shortcut":"text-background",
@@ -111,8 +111,21 @@
 
 		"ToolButton":{
 			"extends":"Button",
+			"backgroundColor":"dark",
 			"padding":[ 2,0 ],
-			"margin":[ 2,0 ]
+			"margin":[ 2,0 ],
+
+			"states":{
+				"hover":{
+					"backgroundColor":"bright"
+				},
+				"active":{
+					"backgroundColor":"brighter"
+				},
+				"selected":{
+					"backgroundColor":"brighter"
+				}
+			}
 		},
 
 		"TabViewArrowPrev":{
@@ -180,21 +193,20 @@
 		"Button":{
 			"extends":"Label",
 			"padding":[4,2],
-			"skin":"prime_assets/square.png",
-			"skinColor":"darker",
+			"backgroundColor":"darker",
 			
 			"states":{
 				"hover":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				},
 				"active":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				},
 				"selected":{
 					"textColor":"accent",
-					"skinColor":"dark"
+					"backgroundColor":"dark"
 				}
 			}
 		},
@@ -222,22 +234,22 @@
 		},
 		
 		"ScrollKnob":{
-			"padding":[ 4 ],
+			"padding":[ 6 ],
 			"border":[ 0 ],
-			"skin":"prime_assets/square.png",
-			"skinColor":"brightest",
+			"backgroundColor":"brightest",
 			"states":{
 				"hover":{
-					"skinColor":"brighter"
+					"backgroundColor":"brighter"
 				},
 				"active":{
-					"skinColor":"brightest"
+					"backgroundColor":"brightest"
 				}
 			}
 		},
 		
 		"TextView":{
 			"font":"editor",
+			"textColor":"textview-color7",
 			"backgroundColor":"dark"
 		},
 		
@@ -247,10 +259,9 @@
 		
 		"TextField":{
 			"font":"normal",
-			"padding":[ 2 ],
+			"padding":[ 4 ],
 			"margin":[ 2 ],
-			"skin":"prime_assets/square.png",
-			"skinColor":"darker"
+			"backgroundColor":"darker"
 		},
 		
 		"DockingView":{
@@ -325,24 +336,22 @@
 		},
 		
 		"TabButton":{
-            "skinColor":"brighter",
+            "backgroundColor":"brighter",
 			"extends":"Button",
 			"font":"small",
 			"padding":[14,12,14,12],
 			"border":[0,0,0,4],
 			"borderColor":"accentDark",
-			"skin":"prime_assets/square.png",
 			"textColor":"text-background",
 			"states":{
 				"hover":{
-					"skinColor":"bright",
 					"textColor":"text-default"
 				},
 				"active":{
 					"textColor":"text-default"
 				},
 				"selected":{
-					"skinColor":"brightest",
+					"backgroundColor":"brightest",
 					"textColor":"text-highlight",
 					"borderColor":"accent"
 				}

+ 2 - 1
src/ted2go/assets/themes/theme-smooth.json

@@ -61,7 +61,8 @@
 		},
 
 		"GutterView":{
-			"border":[0],
+			"border":[0,0,1,0],
+			"borderColor":"textview-whitespaces",
 			"extends":"TextView",
 			"textColor":"text-disabled"
 		},

+ 1 - 0
src/ted2go/assets/themes/themes.json

@@ -4,6 +4,7 @@
 	"Monkey 1":"theme-monkey1",
 	"Blitzed":"theme-blitzed",
 	"Basic Blue":"theme-Basic-Blue",
+	"Hollow":"theme-hollow",
 	"Prime - Red":"theme-prime-red",
 	"Prime - Blue":"theme-prime-blue",
 	"Smooth":"theme-smooth",

+ 6 - 1
src/ted2go/dialog/PrefsDialog.monkey2

@@ -75,6 +75,7 @@ Class PrefsDialog Extends DialogExt
 	Field _chatServer:TextField
 	Field _chatPort:TextField
 	Field _chatRooms:TextField
+	Field _chatAutoConnect:CheckButton
 	
 	Method OnApply()
 	
@@ -109,6 +110,7 @@ Class PrefsDialog Extends DialogExt
 		Prefs.IrcServer=_chatServer.Text
 		Prefs.IrcPort=Int(_chatPort.Text)
 		Prefs.IrcRooms=_chatRooms.Text
+		Prefs.IrcConnect=_chatAutoConnect.Checked
 		
 		App.ThemeChanged()
 		
@@ -293,11 +295,13 @@ Class PrefsDialog Extends DialogExt
 	
 	Method GetChatDock:DockingView()
 		
-		Local chatTable:=New TableView( 2,4 )
+		Local chatTable:=New TableView( 2,5 )
 		_chatNick=New TextField( Prefs.IrcNickname )
 		_chatServer=New TextField( Prefs.IrcServer )
 		_chatPort=New TextField( ""+Prefs.IrcPort )
 		_chatRooms=New TextField( Prefs.IrcRooms )
+		_chatAutoConnect=New CheckButton( "Auto connect at start" )
+		_chatAutoConnect.Checked=Prefs.IrcConnect
 		chatTable[0,0]=New Label( "Nickname" )
 		chatTable[1,0]=_chatNick
 		chatTable[0,1]=New Label( "Server" )
@@ -306,6 +310,7 @@ Class PrefsDialog Extends DialogExt
 		chatTable[1,2]=_chatPort
 		chatTable[0,3]=New Label( "Rooms" )
 		chatTable[1,3]=_chatRooms
+		chatTable[0,4]=_chatAutoConnect
 		
 		Local docker:=New DockingView
 		docker.AddView( New Label( " " ),"top" )

+ 67 - 1
src/ted2go/dialog/UpdateModulesDialog.monkey2

@@ -1,4 +1,3 @@
-
 Namespace ted2go
 
 
@@ -163,7 +162,73 @@ Class UpdateModulesDialog Extends DialogExt
 	
 	Global _modsNames:=New StringStack
 	
+	#rem MARK WAS HERE!!!!!
+	
+	EnumModules code lifted from mx2cc.monkey2
+	
+	This sorts modules into dependancy order.
+	
+	#end
+	Function EnumModules( out:StringStack,cur:String,deps:StringMap<StringStack> )
+		If out.Contains( cur ) Return
+		
+		For Local dep:=Eachin deps[cur]
+			EnumModules( out,dep,deps )
+		Next
+		
+		out.Push( cur )
+	End
+	
+	Function EnumModules:String[]()
+	
+		Local mods:=New StringMap<StringStack>
+		
+		Local modsPath:=MainWindow.ModsPath
+	
+		For Local f:=Eachin LoadDir( modsPath )
+		
+			Local dir:=modsPath+f+"/"
+			If GetFileType( dir )<>FileType.Directory Continue
+			
+			Local str:=LoadString( dir+"module.json" )
+			If Not str Continue
+			
+			Local obj:=JsonObject.Parse( str )
+			If Not obj 
+				Print "Error parsing json:"+dir+"module.json"
+				Continue
+			Endif
+			
+			Local name:=obj["module"].ToString()
+			If name<>f Continue
+			
+			Local deps:=New StringStack
+			If name<>"monkey" deps.Push( "monkey" )
+			
+			Local jdeps:=obj["depends"]
+			If jdeps
+				For Local dep:=Eachin jdeps.ToArray()
+					deps.Push( dep.ToString() )
+				Next
+			Endif
+			
+			mods[name]=deps
+		Next
+		
+		Local out:=New StringStack
+		For Local cur:=Eachin mods.Keys
+			EnumModules( out,cur,mods )
+		Next
+		
+		Return out.ToArray()
+	End
 	
+	Function GetModulesNames( out:StringStack )
+		
+		out.AddAll( EnumModules() )
+	End
+
+#rem	
 	Function GetModulesNames( out:StringStack )
 	
 		Local modsPath:=MainWindow.ModsPath
@@ -179,6 +244,7 @@ Class UpdateModulesDialog Extends DialogExt
 			Endif
 		Next
 	End
+#end
 	
 	Function ShowMessage( title:String,msg:String,okButton:String="  OK  " )
 		

+ 225 - 30
src/ted2go/view/IRCView.monkey2

@@ -3,8 +3,6 @@
 
 Namespace ted2go
 
-#Import "assets/"
-
 '=MODULE=
 
 'This class is the main class
@@ -56,12 +54,12 @@ Class IRCServer Extends IRCMessageContainer
 	Field serverPort:Int
 	Field sendBuffer:DataBuffer
 	Field receiveBuffer:DataBuffer=New DataBuffer(512)
-	Field fiberSleep:Float=0.26 'Lower value gets internet messages faster but uses more CPU
+	Field fiberSleep:Float=0.35 'Lower value gets internet messages faster but uses more CPU
 	Field updateFiber:Fiber 'Fiber for updating internet mesages
 	Field socket:Socket
 	Field stream:SocketStream
 	Field nickname:String
-	Field realname:String="KoreIRC"
+	Field realname:String="KoreIRC 1.0"
 	Field messageContainers:=New List<IRCMessageContainer>
 	Field skipContainers:=New String[]("chanserv","nickserv","services","*") 'Make sure these are lower case!
 	
@@ -386,10 +384,10 @@ Class IRCServer Extends IRCMessageContainer
 					Endif
 					
 				Case "NICK" 'Changing names
-					'Update local name
+					'Was local name?
 					Local wasSelf:Bool
 					If GetNickname(fromUser)=nickname Then
-						wasSelf=true
+						wasSelf=True
 						nickname=msg
 					Endif
 					
@@ -399,8 +397,12 @@ Class IRCServer Extends IRCMessageContainer
 							If u.name=GetNickname(fromUser) Then
 								u.name=msg
 								container.SortUsers()
-								TriggerOnMessage(msg,fromUser,msg,type,container)
-								parent.OnUserUpdate(container,Self)
+								
+								If Not wasSelf Then 
+									TriggerOnMessage(msg,fromUser,msg,type,container)
+									parent.OnUserUpdate(container,Self)
+								Endif
+								
 								Exit
 							Endif
 						Next
@@ -464,11 +466,19 @@ Class IRCServer Extends IRCMessageContainer
 		nC.type=type
 		messageContainers.AddLast(nC)
 		parent.OnNewContainer(nC,Self)
+		
+		'Load history for chat rooms
+		If nC.name.StartsWith("#") Then nC.LoadHistory()
+		
 		Return nC
 	End
 	
 	'Remove a specific message container
 	Method RemoveMessageContainer(container:IRCMessageContainer)
+		
+		'Save history for chat rooms
+		If container.name.StartsWith("#") Then container.SaveHistory()
+		
 		messageContainers.Remove(container)
 		If parent Then parent.OnRemoveContainer(container,Self)
 	End
@@ -507,6 +517,50 @@ Class IRCMessageContainer
 	Field gotUsers:Bool 'Have we gotten users before?
 	Field messages:=New List<IRCMessage>
 	
+	Method LogPath:String()
+		
+		Return AppDir() + "/logs/" + parent.serverAddress + "/" + name + ".txt"
+		
+	End
+	
+	Method LoadHistory()
+		Local file:String=LoadString( Self.LogPath() )
+		If Not file Then Return
+		
+		Local lines:=file.Split( "~n" )
+		Local type:String
+		Local time:String
+		Local user:String
+		Local message:String
+		
+		For Local s:=Eachin lines
+			If Not s.Contains( ">" ) Or Not s.Contains( " " ) Or Not s.Contains( ":" ) Then Continue
+			
+			type=s.Split( ">" )[0]
+			time=s.Split( ">" )[1].Split( " " )[0]
+			user=s.Split( ">" )[1].Split( " " )[1].Split( ":" )[0]
+			message=s.Split( type + ">" + time + " " + user + ":" )[1]
+			
+			Self.AddMessage( message, user, Self.name, type.ToUpper() ).time=time
+		Next
+		
+	End
+	
+	Method SaveHistory()
+		Local log:String
+		
+		For Local m:=Eachin Self.messages
+			If m.type<>"PRIVMSG" And m.type<>"QUIT" And m.type<>"PART" And m.type<>"JOIN" And m.type<>"NICK" Then Continue
+			
+			log+=m.type+">"+m.time+" "+m.fromUser+":"+m.text+"~n"
+		Next
+		
+		If log.Length>2 Then
+			CreateFile( LogPath(), True )
+			SaveString( log, LogPath() )
+		Endif
+	End
+	
 	Method Remove() Virtual
 		parent.messageContainers.Remove(Self)
 	End
@@ -520,7 +574,7 @@ Class IRCMessageContainer
 	End
 	
 	'Add a message to this container
-	Method AddMessage(msg:String,fromUser:String="",toUser:String="",type:String="",hostname:string="")
+	Method AddMessage:IRCMessage(msg:String,fromUser:String="",toUser:String="",type:String="",hostname:string="")
 		Local nM:=New IRCMessage
 		nM.parent=Self
 		nM.text=msg
@@ -529,6 +583,7 @@ Class IRCMessageContainer
 		nM.type=type
 		nM.hostname=hostname
 		messages.AddLast(nM)
+		Return nM
 	End
 	
 	'Sort the userlist
@@ -580,6 +635,75 @@ End
 
 '=IRC VIEW=
 
+'Highlighter for IRC history text
+Function IrcTextHighlighter:Int( text:String,colors:Byte[],sol:Int,eol:Int,state:Int )
+	Local i0:=sol
+	Local msgStep:Int
+	Local userColor:Int
+	Local userStart:Int
+	Local userEnd:Int
+	Local userDone:Bool
+	
+	While i0<eol
+		Local chr:=text[i0]
+		
+		If userDone Then 
+			colors[i0]=0
+		Else
+			colors[i0]=1
+		Endif
+		
+		'Reset
+		If chr=110 And msgStep=6 Then 'n
+			msgStep=0
+			userDone=False
+		Elseif msgStep=6 And chr<>110
+			msgStep=5
+		Endif
+		If chr=126 And msgStep=5 Then '~
+			msgStep=6
+		Endif
+		
+		'Detect username
+		If chr=9 And msgStep=2 Then 'Tab
+			userStart=i0
+			msgStep=3
+			userColor=0
+		Elseif msgStep=2 And chr<>9 Then
+			msgStep=5
+		Endif
+		If chr=32 And msgStep=4 Then 'Space after :
+			userEnd=i0-1
+			msgStep=5
+			userDone=True
+			For Local i1:Int=userStart Until userEnd
+				colors[i1]=2 + (userColor Mod 5)
+			Next
+		Elseif msgStep=4 And chr<>32 Then
+			msgStep=5
+		Endif
+		If chr=58 And msgStep=3 Then ':
+			msgStep=4
+		Endif
+		
+		If msgStep=3 Then userColor+=chr
+		
+		'Detect time
+		If chr=91 And msgStep=0 Then '[
+			msgStep=1
+		Endif
+		If msgStep=1 Then colors[i0]=1
+		
+		If chr=93 And msgStep=1 Then ']
+			msgStep=2
+		Endif
+		
+		i0+=1
+	Wend
+	
+	Return state
+End
+
 'This is a pre-made IRC client, ready to be used in any MojoX application
 Class IRCView Extends DockingView
 	Field ircHandler:IRC
@@ -598,6 +722,8 @@ Class IRCView Extends DockingView
 	Field selectedServer:IRCServer
 	Field selectedMessageContainer:IRCMessageContainer
 	
+	Field maxHistory:Int=50
+	
 	Property Intro:IRCIntroView()
 		Return introScreen
 	End
@@ -615,6 +741,7 @@ Class IRCView Extends DockingView
 		
 		'Chat history field
 		historyField=New TextView
+		historyField.Document.TextHighlighter=IrcTextHighlighter
 		historyField.ReadOnly=True
 		historyField.WordWrap=True
 		chatScreen.ContentView=historyField
@@ -859,7 +986,7 @@ Class IRCView Extends DockingView
 			selectedMessageContainer.AddMessage(selectedServer.nickname+": "+text)
 		Else
 			'Yep, add PRIVMSG and send!
-			selectedMessageContainer.AddMessage(selectedServer.nickname+": "+text)
+			selectedMessageContainer.AddMessage( text, selectedServer.nickname, selectedMessageContainer.name, "PRIVMSG" )
 			text="PRIVMSG "+selectedMessageContainer.name+" :"+text
 		Endif
 		
@@ -867,6 +994,23 @@ Class IRCView Extends DockingView
 		AddChatMessage(selectedMessageContainer.messages.Last)
 	End
 	
+	Method SaveAllHistory()
+		For Local s:IRCServer=Eachin Self.ircHandler.servers
+		For Local c:IRCMessageContainer=Eachin s.messageContainers
+			c.SaveHistory()
+		Next
+		Next
+	End
+	
+	Method Quit(message:String=Null)
+		SaveAllHistory()
+		
+		For Local s:IRCServer=Eachin Self.ircHandler.servers
+			s.SendString("QUIT :"+message)
+			s.Disconnect()
+		Next
+	End
+	
 	Method OnMessageIRC(message:IRCMessage,container:IRCMessageContainer,server:IRCServer)
 		'For message information, visit: https://tools.ietf.org/html/rfc2812
 		Local doNotify:Bool 'Should we notify the user?
@@ -877,48 +1021,45 @@ Class IRCView Extends DockingView
 				
 			Case "JOIN"
 				If message.fromUser=server.nickname Then
+					UpdateHistory()
 					container.AddMessage("You are now talking in "+container.name)
 				Else
-					container.AddMessage(message.fromUser+" joined "+container.name)
+					container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				Endif
 				
 			Case "PART"
-				If message.text Then
-					container.AddMessage(message.fromUser+" left "+container.name+" (reason: "+message.text+")")
-				Else
-					container.AddMessage(message.fromUser+" left "+container.name)
-				Endif
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				
 			Case "QUIT"
-				If message.text Then
-					container.AddMessage(message.fromUser+" quit (reason: "+message.text+")")
-				Else
-					container.AddMessage(message.fromUser+" quit")
-				Endif
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				
 			Case "NICK"
-				If nicknameLabel.Text.Left(nicknameLabel.Text.Length-1) Then
-					container.AddMessage("You are now known as "+message.toUser)
-				Else
-					container.AddMessage(message.fromUser+" is now known as "+message.toUser)
+				Local wasSelf:Bool
+				If container=server And selectedMessageContainer Then
+					wasSelf=true
+					container=selectedMessageContainer
 				Endif
 				
+				container.AddMessage( message.text, message.fromUser, message.toUser, message.type)
+				
+				If wasSelf Then UpdateUsers()
+				
 			Case "PRIVMSG"
-				container.AddMessage(message.fromUser+": "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				doNotify=True
 			
 			Case "NOTICE"
-				container.AddMessage("NOTICE >"+message.fromUser+"<: "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 			
 			Default
-				container.AddMessage(message.fromUser+": "+message.text)
+				container.AddMessage( message.text, message.fromUser, container.name, message.type)
 				doNotify=True
 		End
 		
 		'Display message if we're in that container right now
 		If container=selectedMessageContainer Then
 			AddChatMessage(container.messages.Last)
-		Elseif doNotify Then 'Notify user perhaps?
+		Elseif doNotify Then
 			Local node:TreeView.Node=GetMessageContainerNode(container.name,server.name)
 			If node Then node.Icon=App.Theme.OpenImage("irc/notice.png")
 		Endif
@@ -1095,14 +1236,68 @@ Class IRCView Extends DockingView
 	
 	Method UpdateHistory()
 		historyField.Clear()
+		
 		If Not selectedMessageContainer Then Return
+		
+		'Limit message count
+		While selectedMessageContainer.messages.Count()>maxHistory
+			selectedMessageContainer.messages.Remove(selectedMessageContainer.messages.First)
+		Wend
+		
 		For Local m:=Eachin selectedMessageContainer.messages
 			AddChatMessage(m)
 		Next
 	End
 	
+	Function PadTime:String(timeStr:String)
+		If Not timeStr.Contains(":") Return timeStr
+		
+		Local t:=timeStr.Split(":")
+		If t[0].Length<=1 Then t[0]="0"+t[0]
+		If t[1].Length<=1 Then t[1]="0"+t[1]
+		
+		Return t[0]+":"+t[1]
+	End
+	
 	Method AddChatMessage(message:IRCMessage)
-		historyField.AppendText("["+message.time+"]~t"+message.text+"~n")
+		Local time:String="["+PadTime(message.time)+"]~t"
+		
+		Select message.type.ToUpper()
+			
+			Case "JOIN"
+				historyField.AppendText( time + message.fromUser + " joined " + message.toUser + "~n" )
+				
+			Case "PART"
+				If message.text Then
+					historyField.AppendText( time + message.fromUser + " left " + message.toUser + " (Reason " + message.text + ")~n" )
+				Else
+					historyField.AppendText( time + message.fromUser + " left " + message.toUser + "~n" )
+				Endif
+				
+			Case "QUIT"
+				If message.text Then
+					historyField.AppendText( time + message.fromUser + " quit (Reason '" + message.text + "')~n" )
+				Else
+					historyField.AppendText( time + message.fromUser + " quit~n" )
+				Endif
+				
+			Case "NICK"
+				historyField.AppendText( time + message.fromUser + " is now known as " + message.text + "~n" )
+				
+			Case "MODE"
+				'historyField.AppendText( time + message.fromUser + " sets MODE " + message.text + "~n" )
+				
+			Case "NOTICE"
+				historyField.AppendText( time + message.fromUser + ": <NOTICE> " + message.text + "~n" )
+				
+			Default
+				If message.fromUser Then
+					historyField.AppendText( time + message.fromUser + ": " + message.text + "~n" )
+				Else
+					historyField.AppendText( time + message.text + "~n" )
+				Endif
+		End
+		
 	End
 	
 	Method OnRender(canvas:Canvas) Override