Browse Source

3D Import Import & UDP
-=-=-=-=-=-=-=-=-=-=-

-Animation Import filter support
-Animation Clip import support
-Animation Optimizer Fixes, Improvements and Visibile Options
-Extremely Experimental UDP support.

Juan Linietsky 11 years ago
parent
commit
6dd8768811
41 changed files with 1050 additions and 185 deletions
  1. 1 1
      core/io/packet_peer.cpp
  2. 48 0
      core/io/packet_peer_udp.cpp
  3. 35 0
      core/io/packet_peer_udp.h
  4. 2 0
      core/register_core_types.cpp
  5. 2 2
      demos/2d/shower_of_bullets/bullets.gd
  6. 5 0
      drivers/gles2/rasterizer_gles2.cpp
  7. 2 0
      drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h
  8. 2 2
      drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h
  9. 2 1
      drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm
  10. 2 0
      drivers/theoraplayer/src/TheoraVideoClip.cpp
  11. 10 4
      drivers/theoraplayer/src/TheoraVideoManager.cpp
  12. 37 14
      drivers/theoraplayer/video_stream_theoraplayer.cpp
  13. 3 0
      drivers/theoraplayer/video_stream_theoraplayer.h
  14. 2 0
      drivers/unix/os_unix.cpp
  15. 188 0
      drivers/unix/packet_peer_udp_posix.cpp
  16. 56 0
      drivers/unix/packet_peer_udp_posix.h
  17. 1 1
      platform/android/export/export.cpp
  18. 1 0
      platform/windows/SCsub
  19. 2 0
      platform/windows/os_windows.cpp
  20. 167 0
      platform/windows/packet_peer_udp_winsock.cpp
  21. 51 0
      platform/windows/packet_peer_udp_winsock.h
  22. 18 17
      scene/gui/base_button.cpp
  23. 2 2
      scene/gui/video_player.cpp
  24. 1 1
      scene/gui/video_player.h
  25. 158 125
      scene/resources/animation.cpp
  26. 1 0
      scene/resources/animation.h
  27. 1 0
      scene/resources/video_stream.cpp
  28. 2 0
      scene/resources/video_stream.h
  29. 8 0
      servers/audio/audio_server_sw.cpp
  30. 5 0
      servers/audio/audio_server_sw.h
  31. 1 0
      servers/audio_server.h
  32. 1 1
      servers/physics_2d_server.cpp
  33. 1 1
      servers/physics_server.cpp
  34. 48 5
      tools/editor/animation_editor.cpp
  35. 6 0
      tools/editor/animation_editor.h
  36. 29 0
      tools/editor/editor_file_system.cpp
  37. 3 0
      tools/editor/editor_file_system.h
  38. 7 6
      tools/editor/io_plugins/editor_import_collada.cpp
  39. 137 1
      tools/editor/io_plugins/editor_scene_import_plugin.cpp
  40. 1 0
      tools/editor/io_plugins/editor_scene_import_plugin.h
  41. 1 1
      tools/export/blender25/io_scene_dae/export_dae.py

+ 1 - 1
core/io/packet_peer.cpp

@@ -111,7 +111,7 @@ Variant PacketPeer::_bnd_get_var() const {
 void PacketPeer::_bind_methods() {
 void PacketPeer::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("get_var"),&PacketPeer::_bnd_get_var);
 	ObjectTypeDB::bind_method(_MD("get_var"),&PacketPeer::_bnd_get_var);
-	ObjectTypeDB::bind_method(_MD("put_var", "var:Variant"),&PacketPeer::put_var);
+	ObjectTypeDB::bind_method(_MD("put_var", "var:var"),&PacketPeer::put_var);
 	ObjectTypeDB::bind_method(_MD("get_available_packet_count"),&PacketPeer::get_available_packet_count);
 	ObjectTypeDB::bind_method(_MD("get_available_packet_count"),&PacketPeer::get_available_packet_count);
 };
 };
 
 

+ 48 - 0
core/io/packet_peer_udp.cpp

@@ -0,0 +1,48 @@
+#include "packet_peer_udp.h"
+
+
+
+PacketPeerUDP* (*PacketPeerUDP::_create)()=NULL;
+
+int PacketPeerUDP::_get_packet_address() const {
+
+	IP_Address ip = get_packet_address();
+	return ip.host;
+}
+
+String PacketPeerUDP::_get_packet_ip() const {
+
+	return get_packet_address();
+}
+
+
+void PacketPeerUDP::_bind_methods() {
+
+	ObjectTypeDB::bind_method(_MD("listen:Error","port","recv_buf_size"),&PacketPeerUDP::listen,DEFVAL(65536));
+	ObjectTypeDB::bind_method(_MD("close"),&PacketPeerUDP::close);
+	ObjectTypeDB::bind_method(_MD("poll:Error"),&PacketPeerUDP::poll);
+	ObjectTypeDB::bind_method(_MD("is_listening"),&PacketPeerUDP::is_listening);
+	ObjectTypeDB::bind_method(_MD("get_packet_ip"),&PacketPeerUDP::_get_packet_ip);
+	ObjectTypeDB::bind_method(_MD("get_packet_address"),&PacketPeerUDP::_get_packet_address);
+	ObjectTypeDB::bind_method(_MD("set_send_address","address","port"),&PacketPeerUDP::set_send_address);
+
+
+}
+
+Ref<PacketPeerUDP> PacketPeerUDP::create_ref() {
+
+	if (!_create)
+		return Ref<PacketPeerUDP>();
+	return Ref<PacketPeerUDP>(_create());
+}
+
+PacketPeerUDP* PacketPeerUDP::create() {
+
+	if (!_create)
+		return NULL;
+	return _create();
+}
+
+PacketPeerUDP::PacketPeerUDP()
+{
+}

+ 35 - 0
core/io/packet_peer_udp.h

@@ -0,0 +1,35 @@
+#ifndef PACKET_PEER_UDP_H
+#define PACKET_PEER_UDP_H
+
+
+#include "io/packet_peer.h"
+
+class PacketPeerUDP : public PacketPeer {
+	OBJ_TYPE(PacketPeerUDP,PacketPeer);
+
+protected:
+
+	static PacketPeerUDP* (*_create)();
+	static void _bind_methods();
+
+	int _get_packet_address() const;
+	String _get_packet_ip() const;
+
+public:
+
+	virtual Error listen(int p_port,int p_recv_buffer_size=65536)=0;
+	virtual void close()=0;
+	virtual Error poll()=0;
+	virtual bool is_listening() const=0;
+	virtual IP_Address get_packet_address() const=0;
+	virtual int get_packet_port() const=0;
+	virtual void set_send_address(const IP_Address& p_address,int p_port)=0;
+
+
+	static Ref<PacketPeerUDP> create_ref();
+	static PacketPeerUDP* create();
+
+	PacketPeerUDP();
+};
+
+#endif // PACKET_PEER_UDP_H

+ 2 - 0
core/register_core_types.cpp

@@ -29,6 +29,7 @@
 #include "register_core_types.h"
 #include "register_core_types.h"
 
 
 #include "io/tcp_server.h"
 #include "io/tcp_server.h"
+#include "io/packet_peer_udp.h"
 #include "io/config_file.h"
 #include "io/config_file.h"
 #include "os/main_loop.h"
 #include "os/main_loop.h"
 #include "io/packet_peer.h"
 #include "io/packet_peer.h"
@@ -115,6 +116,7 @@ void register_core_types() {
 	ObjectTypeDB::register_virtual_type<StreamPeer>();
 	ObjectTypeDB::register_virtual_type<StreamPeer>();
 	ObjectTypeDB::register_create_type<StreamPeerTCP>();
 	ObjectTypeDB::register_create_type<StreamPeerTCP>();
 	ObjectTypeDB::register_create_type<TCP_Server>();
 	ObjectTypeDB::register_create_type<TCP_Server>();
+	ObjectTypeDB::register_create_type<PacketPeerUDP>();
 	ObjectTypeDB::register_create_type<StreamPeerSSL>();
 	ObjectTypeDB::register_create_type<StreamPeerSSL>();
 	ObjectTypeDB::register_virtual_type<IP>();
 	ObjectTypeDB::register_virtual_type<IP>();
 	ObjectTypeDB::register_virtual_type<PacketPeer>();
 	ObjectTypeDB::register_virtual_type<PacketPeer>();

+ 2 - 2
demos/2d/shower_of_bullets/bullets.gd

@@ -65,9 +65,9 @@ func _ready():
 	
 	
 func _exit_tree():
 func _exit_tree():
 	for b in bullets:
 	for b in bullets:
-		Physics2DServer.free(b.body)
+		Physics2DServer.free_rid(b.body)
 	
 	
-	Physics2DServer.free(shape)
+	Physics2DServer.free_rid(shape)
 	# Initalization here
 	# Initalization here
 	bullets.clear()
 	bullets.clear()
 	
 	

+ 5 - 0
drivers/gles2/rasterizer_gles2.cpp

@@ -4173,6 +4173,9 @@ void RasterizerGLES2::capture_viewport(Image* r_capture) {
 	pixels.resize(viewport.width*viewport.height*4);
 	pixels.resize(viewport.width*viewport.height*4);
 	DVector<uint8_t>::Write w = pixels.write();
 	DVector<uint8_t>::Write w = pixels.write();
 	glPixelStorei(GL_PACK_ALIGNMENT, 4);
 	glPixelStorei(GL_PACK_ALIGNMENT, 4);
+
+	uint64_t time = OS::get_singleton()->get_ticks_usec();
+
 	if (current_rt) {
 	if (current_rt) {
 #ifdef GLEW_ENABLED
 #ifdef GLEW_ENABLED
 		glReadBuffer(GL_COLOR_ATTACHMENT0);
 		glReadBuffer(GL_COLOR_ATTACHMENT0);
@@ -4182,11 +4185,13 @@ void RasterizerGLES2::capture_viewport(Image* r_capture) {
 		// back?
 		// back?
 		glReadPixels( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height,GL_RGBA,GL_UNSIGNED_BYTE,w.ptr());
 		glReadPixels( viewport.x, window_size.height-(viewport.height+viewport.y), viewport.width,viewport.height,GL_RGBA,GL_UNSIGNED_BYTE,w.ptr());
 	}
 	}
+	printf("readpixels time %i\n", (int)(OS::get_singleton()->get_ticks_usec() - time));
 	w=DVector<uint8_t>::Write();
 	w=DVector<uint8_t>::Write();
 
 
 	r_capture->create(viewport.width,viewport.height,0,Image::FORMAT_RGBA,pixels);
 	r_capture->create(viewport.width,viewport.height,0,Image::FORMAT_RGBA,pixels);
 	r_capture->flip_y();
 	r_capture->flip_y();
 
 
+	printf("total time %i\n", (int)(OS::get_singleton()->get_ticks_usec() - time));
 
 
 #endif
 #endif
 
 

+ 2 - 0
drivers/theoraplayer/include/theoraplayer/TheoraVideoClip.h

@@ -87,6 +87,7 @@ protected:
     std::string mName;
     std::string mName;
 	int mWidth, mHeight, mStride;
 	int mWidth, mHeight, mStride;
 	int mNumFrames;
 	int mNumFrames;
+	int audio_track;
 
 
 	int mSubFrameWidth, mSubFrameHeight, mSubFrameOffsetX, mSubFrameOffsetY;
 	int mSubFrameWidth, mSubFrameHeight, mSubFrameOffsetX, mSubFrameOffsetY;
 	float mAudioGain; //! multiplier for audio samples. between 0 and 1
 	float mAudioGain; //! multiplier for audio samples. between 0 and 1
@@ -233,6 +234,7 @@ public:
 	bool getAutoRestart() { return mAutoRestart; }
 	bool getAutoRestart() { return mAutoRestart; }
 
 
 
 
+	void set_audio_track(int p_track) { audio_track=p_track; }
 
 
 	/**
 	/**
 	    TODO: user priority. Useful only when more than one video is being decoded
 	    TODO: user priority. Useful only when more than one video is being decoded

+ 2 - 2
drivers/theoraplayer/include/theoraplayer/TheoraVideoManager.h

@@ -67,8 +67,8 @@ public:
 	//! search registered clips by name
 	//! search registered clips by name
 	TheoraVideoClip* getVideoClipByName(std::string name);
 	TheoraVideoClip* getVideoClipByName(std::string name);
 
 
-	TheoraVideoClip* createVideoClip(std::string filename,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0);
-	TheoraVideoClip* createVideoClip(TheoraDataSource* data_source,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0);
+	TheoraVideoClip* createVideoClip(std::string filename,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0, int p_track=0);
+	TheoraVideoClip* createVideoClip(TheoraDataSource* data_source,TheoraOutputMode output_mode=TH_RGB,int numPrecachedOverride=0,bool usePower2Stride=0, int p_audio_track=0);
 
 
 	void update(float timeDelta);
 	void update(float timeDelta);
 
 

+ 2 - 1
drivers/theoraplayer/src/AVFoundation/TheoraVideoClip_AVFoundation.mm

@@ -271,7 +271,8 @@ void TheoraVideoClip_AVFoundation::load(TheoraDataSource* source)
 	AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
 	AVAssetTrack *videoTrack = [tracks objectAtIndex:0];
 
 
 	NSArray* audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
 	NSArray* audioTracks = [asset tracksWithMediaType:AVMediaTypeAudio];
-	AVAssetTrack *audioTrack = audioTracks.count > 0 ? [audioTracks objectAtIndex:0] : NULL;
+	AVAssetTrack *audioTrack = audioTracks.count > 0 ? [audioTracks objectAtIndex:audio_track] : NULL;
+	printf("*********** using audio track %i\n", audio_track);
 	
 	
 #ifdef _AVFOUNDATION_BGRX
 #ifdef _AVFOUNDATION_BGRX
 	bool yuv_output = (mOutputMode != TH_BGRX && mOutputMode != TH_RGBA);
 	bool yuv_output = (mOutputMode != TH_BGRX && mOutputMode != TH_RGBA);

+ 2 - 0
drivers/theoraplayer/src/TheoraVideoClip.cpp

@@ -51,6 +51,8 @@ TheoraVideoClip::TheoraVideoClip(TheoraDataSource* data_source,
 	mWaitingForCache(false),
 	mWaitingForCache(false),
 	mOutputMode(TH_UNDEFINED)
 	mOutputMode(TH_UNDEFINED)
 {
 {
+
+	audio_track=0;
 	mAudioMutex = NULL;
 	mAudioMutex = NULL;
 	mThreadAccessMutex = new TheoraMutex();
 	mThreadAccessMutex = new TheoraMutex();
 	mTimer = mDefaultTimer = new TheoraTimer();
 	mTimer = mDefaultTimer = new TheoraTimer();

+ 10 - 4
drivers/theoraplayer/src/TheoraVideoManager.cpp

@@ -35,6 +35,8 @@ extern "C"
 	void initYUVConversionModule();
 	void initYUVConversionModule();
 }
 }
 
 
+#include "core/os/memory.h"
+
 //#define _DECODING_BENCHMARK //uncomment to test average decoding time on a given device
 //#define _DECODING_BENCHMARK //uncomment to test average decoding time on a given device
 
 
 
 
@@ -184,16 +186,18 @@ TheoraAudioInterfaceFactory* TheoraVideoManager::getAudioInterfaceFactory()
 TheoraVideoClip* TheoraVideoManager::createVideoClip(std::string filename,
 TheoraVideoClip* TheoraVideoManager::createVideoClip(std::string filename,
 													 TheoraOutputMode output_mode,
 													 TheoraOutputMode output_mode,
 													 int numPrecachedOverride,
 													 int numPrecachedOverride,
-													 bool usePower2Stride)
+													 bool usePower2Stride,
+													 int p_track)
 {
 {
-	TheoraDataSource* src=new TheoraFileDataSource(filename);
-	return createVideoClip(src,output_mode,numPrecachedOverride,usePower2Stride);
+	TheoraDataSource* src=memnew(TheoraFileDataSource(filename));
+	return createVideoClip(src,output_mode,numPrecachedOverride,usePower2Stride, p_track);
 }
 }
 
 
 TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_source,
 TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_source,
 													 TheoraOutputMode output_mode,
 													 TheoraOutputMode output_mode,
 													 int numPrecachedOverride,
 													 int numPrecachedOverride,
-													 bool usePower2Stride)
+													 bool usePower2Stride,
+													 int p_audio_track)
 {
 {
 	mWorkMutex->lock();
 	mWorkMutex->lock();
 
 
@@ -226,6 +230,8 @@ TheoraVideoClip* TheoraVideoManager::createVideoClip(TheoraDataSource* data_sour
 #ifdef __FFMPEG
 #ifdef __FFMPEG
 		clip = new TheoraVideoClip_FFmpeg(data_source, output_mode, nPrecached, usePower2Stride);
 		clip = new TheoraVideoClip_FFmpeg(data_source, output_mode, nPrecached, usePower2Stride);
 #endif
 #endif
+
+	clip->set_audio_track(p_audio_track);
 	clip->load(data_source);
 	clip->load(data_source);
 	clip->decodeNextFrame(); // ensure the first frame is always preloaded and have the main thread do it to prevent potential thread starvatio
 	clip->decodeNextFrame(); // ensure the first frame is always preloaded and have the main thread do it to prevent potential thread starvatio
 
 

+ 37 - 14
drivers/theoraplayer/video_stream_theoraplayer.cpp

@@ -39,6 +39,8 @@
 #include "core/ring_buffer.h"
 #include "core/ring_buffer.h"
 #include "core/os/thread_safe.h"
 #include "core/os/thread_safe.h"
 
 
+#include "core/globals.h"
+
 static TheoraVideoManager* mgr = NULL;
 static TheoraVideoManager* mgr = NULL;
 
 
 class TPDataFA : public TheoraDataSource {
 class TPDataFA : public TheoraDataSource {
@@ -141,6 +143,7 @@ public:
 		playing=false;
 		playing=false;
 		_clear();
 		_clear();
 	};
 	};
+
 	virtual bool is_playing() const { return true; };
 	virtual bool is_playing() const { return true; };
 
 
 	virtual void set_paused(bool p_paused) {};
 	virtual void set_paused(bool p_paused) {};
@@ -164,12 +167,16 @@ public:
 
 
 	void input(float* p_data, int p_samples) {
 	void input(float* p_data, int p_samples) {
 
 
+
 		_THREAD_SAFE_METHOD_;
 		_THREAD_SAFE_METHOD_;
+		//printf("input %i samples from %p\n", p_samples, p_data);
 		if (rb.space_left() < p_samples) {
 		if (rb.space_left() < p_samples) {
 			rb_power += 1;
 			rb_power += 1;
 			rb.resize(rb_power);
 			rb.resize(rb_power);
 		}
 		}
 		rb.write(p_data, p_samples);
 		rb.write(p_data, p_samples);
+
+		update(); //update too here for less latency
 	};
 	};
 
 
 	void update() {
 	void update() {
@@ -177,15 +184,16 @@ public:
 		_THREAD_SAFE_METHOD_;
 		_THREAD_SAFE_METHOD_;
 		int todo = get_todo();
 		int todo = get_todo();
 		int16_t* buffer = get_write_buffer();
 		int16_t* buffer = get_write_buffer();
-		int samples = rb.data_left();
-		const int to_write = MIN(todo, samples);
+		int frames = rb.data_left()/channels;
+		const int to_write = MIN(todo, frames);
 
 
-		for (int i=0; i<to_write; i++) {
+		for (int i=0; i<to_write*channels; i++) {
 
 
-			uint16_t sample = uint16_t(rb.read() * 32767);
+			int v = rb.read() * 32767;
+			int16_t sample = CLAMP(v,-32768,32767);
 			buffer[i] = sample;
 			buffer[i] = sample;
 		};
 		};
-		write(to_write/channels);
+		write(to_write);
 		total_wrote += to_write;
 		total_wrote += to_write;
 	};
 	};
 
 
@@ -231,7 +239,7 @@ public:
 	TPAudioGodot(TheoraVideoClip* owner, int nChannels, int p_freq)
 	TPAudioGodot(TheoraVideoClip* owner, int nChannels, int p_freq)
 		: TheoraAudioInterface(owner, nChannels, p_freq), TheoraTimer() {
 		: TheoraAudioInterface(owner, nChannels, p_freq), TheoraTimer() {
 
 
-		printf("***************** audio interface constructor\n");
+		printf("***************** audio interface constructor freq %i\n", p_freq);
 		channels = nChannels;
 		channels = nChannels;
 		freq = p_freq;
 		freq = p_freq;
 		stream = Ref<AudioStreamInput>(memnew(AudioStreamInput(nChannels, p_freq)));
 		stream = Ref<AudioStreamInput>(memnew(AudioStreamInput(nChannels, p_freq)));
@@ -247,12 +255,13 @@ public:
 
 
 	void update(float time_increase)
 	void update(float time_increase)
 	{
 	{
-		mTime = (float)(stream->get_total_wrote() / channels) / freq;
+		//mTime = (float)(stream->get_total_wrote()) / freq;
+		//mTime = MAX(0,mTime-AudioServer::get_singleton()->get_output_delay());
 		//mTime = (float)sample_count / channels / freq;
 		//mTime = (float)sample_count / channels / freq;
-		//mTime += time_increase;
+		mTime += time_increase;
 		//float duration=mClip->getDuration();
 		//float duration=mClip->getDuration();
 		//if (mTime > duration) mTime=duration;
 		//if (mTime > duration) mTime=duration;
-		//printf("time at timer is %f, samples %i\n", mTime, sample_count);
+		//printf("time at timer is %f, %f, samples %i\n", mTime, time_increase, sample_count);
 	}
 	}
 };
 };
 
 
@@ -358,13 +367,15 @@ void VideoStreamTheoraplayer::pop_frame(Ref<ImageTexture> p_tex) {
 #endif
 #endif
 
 
 	float w=clip->getWidth(),h=clip->getHeight();
 	float w=clip->getWidth(),h=clip->getHeight();
-    int imgsize = w * h * f->mBpp;
+	int imgsize = w * h * f->mBpp;
 
 
 	int size = f->getStride() * f->getHeight() * f->mBpp;
 	int size = f->getStride() * f->getHeight() * f->mBpp;
 	data.resize(imgsize);
 	data.resize(imgsize);
-	DVector<uint8_t>::Write wr = data.write();
-    uint8_t* ptr = wr.ptr();
-    copymem(ptr, f->getBuffer(), imgsize);
+	{
+		DVector<uint8_t>::Write wr = data.write();
+		uint8_t* ptr = wr.ptr();
+		memcpy(ptr, f->getBuffer(), imgsize);
+	}
     /*
     /*
     for (int i=0; i<h; i++) {
     for (int i=0; i<h; i++) {
         int dstofs = i * w * f->mBpp;
         int dstofs = i * w * f->mBpp;
@@ -421,6 +432,13 @@ void VideoStreamTheoraplayer::update(float p_time) {
 	mgr->update(p_time);
 	mgr->update(p_time);
 };
 };
 
 
+
+void VideoStreamTheoraplayer::set_audio_track(int p_idx) {
+	audio_track=p_idx;
+	if (clip)
+		clip->set_audio_track(audio_track);
+}
+
 void VideoStreamTheoraplayer::set_file(const String& p_file) {
 void VideoStreamTheoraplayer::set_file(const String& p_file) {
 
 
 	FileAccess* f = FileAccess::open(p_file, FileAccess::READ);
 	FileAccess* f = FileAccess::open(p_file, FileAccess::READ);
@@ -436,10 +454,13 @@ void VideoStreamTheoraplayer::set_file(const String& p_file) {
 		mgr->setAudioInterfaceFactory(audio_factory);
 		mgr->setAudioInterfaceFactory(audio_factory);
 	};
 	};
 
 
+	int track = GLOBAL_DEF("theora/audio_track", 0); // hack
+
 	if (p_file.find(".mp4") != -1) {
 	if (p_file.find(".mp4") != -1) {
 		
 		
 		std::string file = p_file.replace("res://", "").utf8().get_data();
 		std::string file = p_file.replace("res://", "").utf8().get_data();
-		clip = mgr->createVideoClip(file, TH_BGRX, 16);
+		clip = mgr->createVideoClip(file, TH_RGBX, 2, false, track);
+		//clip->set_audio_track(audio_track);
 		memdelete(f);
 		memdelete(f);
 
 
 	} else {
 	} else {
@@ -448,6 +469,7 @@ void VideoStreamTheoraplayer::set_file(const String& p_file) {
 
 
 		try {
 		try {
 			clip = mgr->createVideoClip(ds);
 			clip = mgr->createVideoClip(ds);
+			clip->set_audio_track(audio_track);
 		} catch (_TheoraGenericException e) {
 		} catch (_TheoraGenericException e) {
 			printf("exception ocurred! %s\n", e.repr().c_str());
 			printf("exception ocurred! %s\n", e.repr().c_str());
 			clip = NULL;
 			clip = NULL;
@@ -478,6 +500,7 @@ VideoStreamTheoraplayer::VideoStreamTheoraplayer() {
 	started = false;
 	started = false;
 	playing = false;
 	playing = false;
 	loop = false;
 	loop = false;
+	audio_track=0;
 };
 };
 
 
 
 

+ 3 - 0
drivers/theoraplayer/video_stream_theoraplayer.h

@@ -18,6 +18,8 @@ class VideoStreamTheoraplayer : public VideoStream {
 	bool playing;
 	bool playing;
 	bool loop;
 	bool loop;
 
 
+	int audio_track;
+
 public:
 public:
 
 
 	virtual void stop();
 	virtual void stop();
@@ -43,6 +45,7 @@ public:
 	void update(float p_time);
 	void update(float p_time);
 
 
 	void set_file(const String& p_file);
 	void set_file(const String& p_file);
+	void set_audio_track(int p_idx);
 
 
 	~VideoStreamTheoraplayer();
 	~VideoStreamTheoraplayer();
 	VideoStreamTheoraplayer();
 	VideoStreamTheoraplayer();

+ 2 - 0
drivers/unix/os_unix.cpp

@@ -42,6 +42,7 @@
 #include "dir_access_unix.h"
 #include "dir_access_unix.h"
 #include "tcp_server_posix.h"
 #include "tcp_server_posix.h"
 #include "stream_peer_tcp_posix.h"
 #include "stream_peer_tcp_posix.h"
+#include "packet_peer_udp_posix.h"
 
 
 
 
 #include <stdarg.h>
 #include <stdarg.h>
@@ -115,6 +116,7 @@ void OS_Unix::initialize_core() {
 #ifndef NO_NETWORK
 #ifndef NO_NETWORK
 	TCPServerPosix::make_default();
 	TCPServerPosix::make_default();
 	StreamPeerTCPPosix::make_default();
 	StreamPeerTCPPosix::make_default();
+	PacketPeerUDPPosix::make_default();
 	IP_Unix::make_default();
 	IP_Unix::make_default();
 #endif
 #endif
 	mempool_static = new MemoryPoolStaticMalloc;
 	mempool_static = new MemoryPoolStaticMalloc;

+ 188 - 0
drivers/unix/packet_peer_udp_posix.cpp

@@ -0,0 +1,188 @@
+#include "packet_peer_udp_posix.h"
+
+#ifdef UNIX_ENABLED
+
+
+#include <errno.h>
+#include <unistd.h>
+#include <netdb.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+#include <stdio.h>
+
+#ifndef NO_FCNTL
+#include <sys/fcntl.h>
+#else
+#include <sys/ioctl.h>
+#endif
+
+#ifdef JAVASCRIPT_ENABLED
+#include <arpa/inet.h>
+#endif
+
+
+int PacketPeerUDPPosix::get_available_packet_count() const {
+
+	Error err = const_cast<PacketPeerUDPPosix*>(this)->poll();
+	if (err!=OK)
+		return 0;
+
+	return queue_count;
+}
+
+Error PacketPeerUDPPosix::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{
+
+	Error err = const_cast<PacketPeerUDPPosix*>(this)->poll();
+	if (err!=OK)
+		return err;
+	if (queue_count==0)
+		return ERR_UNAVAILABLE;
+
+	uint32_t size;
+	rb.read((uint8_t*)&size,4,true);
+	rb.read((uint8_t*)&packet_ip.host,4,true);
+	rb.read((uint8_t*)&packet_port,4,true);
+	rb.read(packet_buffer,size,true);
+	--queue_count;
+	*r_buffer=packet_buffer;
+	r_buffer_size=size;
+	return OK;
+
+}
+Error PacketPeerUDPPosix::put_packet(const uint8_t *p_buffer,int p_buffer_size){
+
+	int sock = _get_socket();
+	ERR_FAIL_COND_V( sock == -1, FAILED );
+	struct sockaddr_in addr;
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(peer_port);
+	addr.sin_addr = *((struct in_addr*)&peer_addr.host);
+
+	errno = 0;
+	int err;
+	while ( (err = sendto(sock, p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+
+		if (errno != EAGAIN) {
+			return FAILED;
+		}
+	}
+
+	return OK;
+}
+
+int PacketPeerUDPPosix::get_max_packet_size() const{
+
+	return 512; // uhm maybe not
+}
+
+Error PacketPeerUDPPosix::listen(int p_port, int p_recv_buffer_size){
+
+	close();
+	int sock = _get_socket();
+	if (sock == -1 )
+		return ERR_CANT_CREATE;
+	sockaddr_in addr = {0};
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(p_port);
+	addr.sin_addr.s_addr = INADDR_ANY;
+	if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+		close();
+		return ERR_UNAVAILABLE;
+	}
+	printf("UDP Connection listening on port %i\n", p_port);
+	rb.resize(nearest_power_of_2(p_recv_buffer_size));
+	return OK;
+}
+
+void PacketPeerUDPPosix::close(){
+
+	if (sockfd != -1)
+		::close(sockfd);
+	sockfd=-1;
+	rb.resize(8);
+	queue_count=0;
+}
+
+Error PacketPeerUDPPosix::poll() {
+
+	struct sockaddr_in from = {0};
+	socklen_t len = sizeof(struct sockaddr_in);
+	int ret;
+	while ( (ret = recvfrom(sockfd, recv_buffer, MIN(sizeof(recv_buffer),rb.data_left()-12), MSG_DONTWAIT, (struct sockaddr*)&from, &len)) > 0) {
+		rb.write((uint8_t*)&from.sin_addr, 4);
+		uint32_t port = ntohs(from.sin_port);
+		rb.write((uint8_t*)&port, 4);
+		rb.write((uint8_t*)&ret, 4);
+		rb.write(recv_buffer, ret);
+
+		len = sizeof(struct sockaddr_in);
+		++queue_count;
+	};
+
+	if (ret == 0 || (ret == -1 && errno != EAGAIN) ) {
+		close();
+		return FAILED;
+	};
+
+	return OK;
+}
+bool PacketPeerUDPPosix::is_listening() const{
+
+	return sockfd!=-1;
+}
+
+IP_Address PacketPeerUDPPosix::get_packet_address() const {
+
+	return packet_ip;
+}
+
+int PacketPeerUDPPosix::get_packet_port() const{
+
+	return packet_port;
+}
+
+int PacketPeerUDPPosix::_get_socket() {
+
+	if (sockfd != -1)
+		return sockfd;
+
+	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	ERR_FAIL_COND_V( sockfd == -1, -1 );
+	//fcntl(sockfd, F_SETFL, O_NONBLOCK);
+
+	return sockfd;
+}
+
+
+void PacketPeerUDPPosix::set_send_address(const IP_Address& p_address,int p_port) {
+
+	peer_addr=p_address;
+	peer_port=p_port;
+}
+
+PacketPeerUDP* PacketPeerUDPPosix::_create() {
+
+	return memnew(PacketPeerUDPPosix);
+};
+
+void PacketPeerUDPPosix::make_default() {
+
+	PacketPeerUDP::_create = PacketPeerUDPPosix::_create;
+};
+
+
+PacketPeerUDPPosix::PacketPeerUDPPosix() {
+
+	sockfd=-1;
+	packet_port=0;
+	queue_count=0;
+	peer_port=0;
+}
+
+PacketPeerUDPPosix::~PacketPeerUDPPosix() {
+
+	close();
+}
+#endif

+ 56 - 0
drivers/unix/packet_peer_udp_posix.h

@@ -0,0 +1,56 @@
+#ifndef PACKET_PEER_UDP_POSIX_H
+#define PACKET_PEER_UDP_POSIX_H
+
+#ifdef UNIX_ENABLED
+
+#include "io/packet_peer_udp.h"
+#include "ring_buffer.h"
+
+class PacketPeerUDPPosix : public PacketPeerUDP {
+
+
+	enum {
+		PACKET_BUFFER_SIZE=65536
+	};
+
+	mutable RingBuffer<uint8_t> rb;
+	uint8_t recv_buffer[PACKET_BUFFER_SIZE];
+	mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
+	IP_Address packet_ip;
+	int packet_port;
+	mutable int queue_count;
+	int sockfd;
+
+	IP_Address peer_addr;
+	int peer_port;
+
+	_FORCE_INLINE_ int _get_socket();
+
+	static PacketPeerUDP* _create();
+
+public:
+
+	virtual int get_available_packet_count() const;
+	virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const;
+	virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size);
+
+	virtual int get_max_packet_size() const;
+
+	virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+	virtual void close();
+	virtual Error poll();
+	virtual bool is_listening() const;
+
+	virtual IP_Address get_packet_address() const;
+	virtual int get_packet_port() const;
+
+	virtual void set_send_address(const IP_Address& p_address,int p_port);
+
+	static void make_default();
+
+	PacketPeerUDPPosix();
+	~PacketPeerUDPPosix();
+};
+
+#endif // PACKET_PEER_UDP_POSIX_H
+#endif

+ 1 - 1
platform/android/export/export.cpp

@@ -1533,7 +1533,7 @@ EditorExportPlatformAndroid::EditorExportPlatformAndroid() {
 	device_lock = Mutex::create();
 	device_lock = Mutex::create();
 	quit_request=false;
 	quit_request=false;
 	orientation=0;
 	orientation=0;
-	remove_prev=false;
+	remove_prev=true;
 
 
 	device_thread=Thread::create(_device_poll_thread,this);
 	device_thread=Thread::create(_device_poll_thread,this);
 	devices_changed=true;
 	devices_changed=true;

+ 1 - 0
platform/windows/SCsub

@@ -7,6 +7,7 @@ common_win=[
 	"ctxgl_procaddr.cpp",
 	"ctxgl_procaddr.cpp",
 	"key_mapping_win.cpp",
 	"key_mapping_win.cpp",
 	"tcp_server_winsock.cpp",
 	"tcp_server_winsock.cpp",
+	"packet_peer_udp_winsock.cpp",
 	"stream_peer_winsock.cpp",
 	"stream_peer_winsock.cpp",
 ]
 ]
 
 

+ 2 - 0
platform/windows/os_windows.cpp

@@ -45,6 +45,7 @@
 #include "servers/visual/visual_server_wrap_mt.h"
 #include "servers/visual/visual_server_wrap_mt.h"
 
 
 #include "tcp_server_winsock.h"
 #include "tcp_server_winsock.h"
+#include "packet_peer_udp_winsock.h"
 #include "stream_peer_winsock.h"
 #include "stream_peer_winsock.h"
 #include "os/pc_joystick_map.h"
 #include "os/pc_joystick_map.h"
 #include "lang_table.h"
 #include "lang_table.h"
@@ -173,6 +174,7 @@ void OS_Windows::initialize_core() {
 
 
 	TCPServerWinsock::make_default();
 	TCPServerWinsock::make_default();
 	StreamPeerWinsock::make_default();
 	StreamPeerWinsock::make_default();
+	PacketPeerUDPWinsock::make_default();
 	
 	
 	mempool_static = new MemoryPoolStaticMalloc;
 	mempool_static = new MemoryPoolStaticMalloc;
 #if 1
 #if 1

+ 167 - 0
platform/windows/packet_peer_udp_winsock.cpp

@@ -0,0 +1,167 @@
+#include "packet_peer_udp_winsock.h"
+
+#include <winsock2.h>
+
+int PacketPeerUDPWinsock::get_available_packet_count() const {
+
+	Error err = const_cast<PacketPeerUDPWinsock*>(this)->poll();
+	if (err!=OK)
+		return 0;
+
+	return queue_count;
+}
+
+Error PacketPeerUDPWinsock::get_packet(const uint8_t **r_buffer,int &r_buffer_size) const{
+
+	Error err = const_cast<PacketPeerUDPWinsock*>(this)->poll();
+	if (err!=OK)
+		return err;
+	if (queue_count==0)
+		return ERR_UNAVAILABLE;
+
+	uint32_t size;
+	rb.read((uint8_t*)&size,4,true);
+	rb.read((uint8_t*)&packet_ip.host,4,true);
+	rb.read((uint8_t*)&packet_port,4,true);
+	rb.read(packet_buffer,size,true);
+	--queue_count;
+	*r_buffer=packet_buffer;
+	r_buffer_size=size;
+	return OK;
+
+}
+Error PacketPeerUDPWinsock::put_packet(const uint8_t *p_buffer,int p_buffer_size){
+
+	int sock = _get_socket();
+	ERR_FAIL_COND_V( sock == -1, FAILED );
+	struct sockaddr_in addr;
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(peer_port);
+	addr.sin_addr = *((struct in_addr*)&peer_addr.host);
+
+	errno = 0;
+	int err;
+	while ( (err = sendto(sock, (const char*)p_buffer, p_buffer_size, 0, (struct sockaddr*)&addr, sizeof(addr))) != p_buffer_size) {
+
+		if (WSAGetLastError() != WSAEWOULDBLOCK) {
+			return FAILED;
+		};
+	}
+
+	return OK;
+}
+
+int PacketPeerUDPWinsock::get_max_packet_size() const{
+
+	return 512; // uhm maybe not
+}
+
+Error PacketPeerUDPWinsock::listen(int p_port, int p_recv_buffer_size){
+
+	close();
+	int sock = _get_socket();
+	if (sock == -1 )
+		return ERR_CANT_CREATE;
+	sockaddr_in addr = {0};
+	addr.sin_family = AF_INET;
+	addr.sin_port = htons(p_port);
+	addr.sin_addr.s_addr = INADDR_ANY;
+	if (bind(sock, (struct sockaddr*)&addr, sizeof(sockaddr_in)) == -1 ) {
+		close();
+		return ERR_UNAVAILABLE;
+	}
+	printf("UDP Connection listening on port %i\n", p_port);
+	rb.resize(nearest_power_of_2(p_recv_buffer_size));
+	return OK;
+}
+
+void PacketPeerUDPWinsock::close(){
+
+	if (sockfd != -1)
+		::closesocket(sockfd);
+	sockfd=-1;
+	rb.resize(8);
+	queue_count=0;
+}
+
+Error PacketPeerUDPWinsock::poll() {
+
+	struct sockaddr_in from = {0};
+	int len = sizeof(struct sockaddr_in);
+	int ret;
+	while ( (ret = recvfrom(sockfd, (char*)recv_buffer, MIN(sizeof(recv_buffer),rb.data_left()-12), 0, (struct sockaddr*)&from, &len)) > 0) {
+		rb.write((uint8_t*)&from.sin_addr, 4);
+		uint32_t port = ntohs(from.sin_port);
+		rb.write((uint8_t*)&port, 4);
+		rb.write((uint8_t*)&ret, 4);
+		rb.write(recv_buffer, ret);
+
+		len = sizeof(struct sockaddr_in);
+		++queue_count;
+	};
+
+	if (ret == 0 || (ret == SOCKET_ERROR && WSAGetLastError() != WSAEWOULDBLOCK) ) {
+		close();
+		return FAILED;
+	};
+
+	return OK;
+}
+bool PacketPeerUDPWinsock::is_listening() const{
+
+	return sockfd!=-1;
+}
+
+IP_Address PacketPeerUDPWinsock::get_packet_address() const {
+
+	return packet_ip;
+}
+
+int PacketPeerUDPWinsock::get_packet_port() const{
+
+	return packet_port;
+}
+
+int PacketPeerUDPWinsock::_get_socket() {
+
+	if (sockfd != -1)
+		return sockfd;
+
+	sockfd = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	ERR_FAIL_COND_V( sockfd == -1, -1 );
+	//fcntl(sockfd, F_SETFL, O_NONBLOCK);
+
+	return sockfd;
+}
+
+
+void PacketPeerUDPWinsock::set_send_address(const IP_Address& p_address,int p_port) {
+
+	peer_addr=p_address;
+	peer_port=p_port;
+}
+
+void PacketPeerUDPWinsock::make_default() {
+
+	PacketPeerUDP::_create = PacketPeerUDPWinsock::_create;
+};
+
+
+PacketPeerUDP* PacketPeerUDPWinsock::_create() {
+
+	return memnew(PacketPeerUDPWinsock);
+};
+
+
+PacketPeerUDPWinsock::PacketPeerUDPWinsock() {
+
+	sockfd=-1;
+	packet_port=0;
+	queue_count=0;
+	peer_port=0;
+}
+
+PacketPeerUDPWinsock::~PacketPeerUDPWinsock() {
+
+	close();
+}

+ 51 - 0
platform/windows/packet_peer_udp_winsock.h

@@ -0,0 +1,51 @@
+#ifndef PACKET_PEER_UDP_WINSOCK_H
+#define PACKET_PEER_UDP_WINSOCK_H
+
+#include "io/packet_peer_udp.h"
+#include "ring_buffer.h"
+
+class PacketPeerUDPWinsock : public PacketPeerUDP {
+
+
+	enum {
+		PACKET_BUFFER_SIZE=65536
+	};
+
+	mutable RingBuffer<uint8_t> rb;
+	uint8_t recv_buffer[PACKET_BUFFER_SIZE];
+	mutable uint8_t packet_buffer[PACKET_BUFFER_SIZE];
+	IP_Address packet_ip;
+	int packet_port;
+	mutable int queue_count;
+	int sockfd;
+
+	IP_Address peer_addr;
+	int peer_port;
+
+	_FORCE_INLINE_ int _get_socket();
+
+	static PacketPeerUDP* _create();
+
+public:
+
+	virtual int get_available_packet_count() const;
+	virtual Error get_packet(const uint8_t **r_buffer,int &r_buffer_size) const;
+	virtual Error put_packet(const uint8_t *p_buffer,int p_buffer_size);
+
+	virtual int get_max_packet_size() const;
+
+	virtual Error listen(int p_port,int p_recv_buffer_size=65536);
+	virtual void close();
+	virtual Error poll();
+	virtual bool is_listening() const;
+
+	virtual IP_Address get_packet_address() const;
+	virtual int get_packet_port() const;
+
+	virtual void set_send_address(const IP_Address& p_address,int p_port);
+
+	static void make_default();
+	PacketPeerUDPWinsock();
+	~PacketPeerUDPWinsock();
+};
+#endif // PACKET_PEER_UDP_WINSOCK_H

+ 18 - 17
scene/gui/base_button.cpp

@@ -55,9 +55,9 @@ void BaseButton::_input_event(InputEvent p_event) {
 				if (b.pressed) {
 				if (b.pressed) {
 
 
 					if (!toggle_mode) { //mouse press attempt
 					if (!toggle_mode) { //mouse press attempt
-					
-            status.press_attempt=true;
-            status.pressing_inside=true;
+
+						status.press_attempt=true;
+						status.pressing_inside=true;
 
 
 						pressed();
 						pressed();
 						emit_signal("pressed");
 						emit_signal("pressed");
@@ -74,13 +74,13 @@ void BaseButton::_input_event(InputEvent p_event) {
 					}
 					}
 
 
 
 
-        } else {
-          
-          if (status.press_attempt &&status.pressing_inside) {
-		        pressed();
-		        emit_signal("pressed");
-          }
-          status.press_attempt=false;
+				} else {
+
+					if (status.press_attempt && status.pressing_inside) {
+//						released();
+						emit_signal("released");
+					}
+					status.press_attempt=false;
 				}
 				}
 				update();
 				update();
 				break;
 				break;
@@ -95,14 +95,14 @@ void BaseButton::_input_event(InputEvent p_event) {
 				
 				
 				
 				
 				if (status.press_attempt &&status.pressing_inside) {
 				if (status.press_attempt &&status.pressing_inside) {
-						
+
 					if (!toggle_mode) { //mouse press attempt
 					if (!toggle_mode) { //mouse press attempt
-					
+
 						pressed();
 						pressed();
-						emit_signal("pressed");										
+						emit_signal("pressed");
 						
 						
 					} else {
 					} else {
-					
+
 						status.pressed=!status.pressed;
 						status.pressed=!status.pressed;
 						
 						
 						pressed();
 						pressed();
@@ -110,11 +110,11 @@ void BaseButton::_input_event(InputEvent p_event) {
 						
 						
 						toggled(status.pressed);
 						toggled(status.pressed);
 						emit_signal("toggled",status.pressed);
 						emit_signal("toggled",status.pressed);
-					
+
 					}
 					}
-		
+
 				}
 				}
-			
+
 				status.press_attempt=false;
 				status.press_attempt=false;
 				
 				
 			}
 			}
@@ -363,6 +363,7 @@ void BaseButton::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_draw_mode"),&BaseButton::get_draw_mode);
 	ObjectTypeDB::bind_method(_MD("get_draw_mode"),&BaseButton::get_draw_mode);
 
 
 	ADD_SIGNAL( MethodInfo("pressed" ) );
 	ADD_SIGNAL( MethodInfo("pressed" ) );
+	ADD_SIGNAL( MethodInfo("released" ) );
 	ADD_SIGNAL( MethodInfo("toggled", PropertyInfo( Variant::BOOL,"pressed") ) );
 	ADD_SIGNAL( MethodInfo("toggled", PropertyInfo( Variant::BOOL,"pressed") ) );
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "disabled"), _SCS("set_disabled"), _SCS("is_disabled"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode"));
 	ADD_PROPERTY( PropertyInfo( Variant::BOOL, "toggle_mode"), _SCS("set_toggle_mode"), _SCS("is_toggle_mode"));

+ 2 - 2
scene/gui/video_player.cpp

@@ -192,7 +192,7 @@ String VideoPlayer::get_stream_name() const {
 	return stream->get_name();
 	return stream->get_name();
 };
 };
 
 
-float VideoPlayer::get_pos() const {
+float VideoPlayer::get_stream_pos() const {
 
 
 	if (stream.is_null())
 	if (stream.is_null())
 		return 0;
 		return 0;
@@ -231,7 +231,7 @@ void VideoPlayer::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("get_stream_name"),&VideoPlayer::get_stream_name);
 	ObjectTypeDB::bind_method(_MD("get_stream_name"),&VideoPlayer::get_stream_name);
 
 
-	ObjectTypeDB::bind_method(_MD("get_pos"),&VideoPlayer::get_pos);
+	ObjectTypeDB::bind_method(_MD("get_stream_pos"),&VideoPlayer::get_stream_pos);
 
 
 	ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&VideoPlayer::set_autoplay);
 	ObjectTypeDB::bind_method(_MD("set_autoplay","enabled"),&VideoPlayer::set_autoplay);
 	ObjectTypeDB::bind_method(_MD("has_autoplay"),&VideoPlayer::has_autoplay);
 	ObjectTypeDB::bind_method(_MD("has_autoplay"),&VideoPlayer::has_autoplay);

+ 1 - 1
scene/gui/video_player.h

@@ -77,7 +77,7 @@ public:
 	float get_volume_db() const;
 	float get_volume_db() const;
 
 
 	String get_stream_name() const;
 	String get_stream_name() const;
-	float get_pos() const;
+	float get_stream_pos() const;
 
 
 	void set_autoplay(bool p_vol);
 	void set_autoplay(bool p_vol);
 	bool has_autoplay() const;
 	bool has_autoplay() const;

+ 158 - 125
scene/resources/animation.cpp

@@ -1716,189 +1716,222 @@ void Animation::clear() {
 
 
 }
 }
 
 
-void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) {
 
 
-	ERR_FAIL_INDEX(p_idx,tracks.size());
-	ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM);
-	TransformTrack *tt= static_cast<TransformTrack*>(tracks[p_idx]);
-	for(int i=1;i<tt->transforms.size()-1;i++) {
 
 
-		TKey<TransformKey> &t0 = tt->transforms[i-1];
-		TKey<TransformKey> &t1 = tt->transforms[i];
-		TKey<TransformKey> &t2 = tt->transforms[i+1];
+bool Animation::_transform_track_optimize_key(const TKey<TransformKey> &t0,const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) {
 
 
-		real_t c = (t1.time-t0.time)/(t2.time-t0.time);
-		real_t t[3]={-1,-1,-1};
 
 
-		{ //translation
+	real_t c = (t1.time-t0.time)/(t2.time-t0.time);
+	real_t t[3]={-1,-1,-1};
 
 
-			const Vector3 &v0=t0.value.loc;
-			const Vector3 &v1=t1.value.loc;
-			const Vector3 &v2=t2.value.loc;
+	{ //translation
 
 
-			if (v0.distance_to(v2)<CMP_EPSILON) {
-				//0 and 2 are close, let's see if 1 is close
-				if (v0.distance_to(v1)>CMP_EPSILON) {
-					//not close, not optimizable
-					continue;
-				}
+		const Vector3 &v0=t0.value.loc;
+		const Vector3 &v1=t1.value.loc;
+		const Vector3 &v2=t2.value.loc;
 
 
-			} else {
+		if (v0.distance_to(v2)<CMP_EPSILON) {
+			//0 and 2 are close, let's see if 1 is close
+			if (v0.distance_to(v1)>CMP_EPSILON) {
+				//not close, not optimizable
+				return false;
+			}
 
 
-				Vector3 pd = (v2-v0);
-				float d0 = pd.dot(v0);
-				float d1 = pd.dot(v1);
-				float d2 = pd.dot(v2);
-				if (d1<d0 || d1>d2) {
-					continue; //beyond segment range
-				}
+		} else {
 
 
-				Vector3 s[2]={ v0, v2 };
-				real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
+			Vector3 pd = (v2-v0);
+			float d0 = pd.dot(v0);
+			float d1 = pd.dot(v1);
+			float d2 = pd.dot(v2);
+			if (d1<d0 || d1>d2) {
+				return false;
+			}
 
 
-				if (d>pd.length()*p_alowed_linear_err) {
-					continue; //beyond allowed error for colinearity
-				}
+			Vector3 s[2]={ v0, v2 };
+			real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
 
 
-				t[0] = (d1-d0)/(d2-d0);
+			if (d>pd.length()*p_alowed_linear_err) {
+				return false; //beyond allowed error for colinearity
 			}
 			}
-		}
 
 
-		{ //rotation
+			t[0] = (d1-d0)/(d2-d0);
+		}
+	}
 
 
-			const Quat &q0=t0.value.rot;
-			const Quat &q1=t1.value.rot;
-			const Quat &q2=t2.value.rot;
+	{ //rotation
 
 
-			//localize both to rotation from q0
+		const Quat &q0=t0.value.rot;
+		const Quat &q1=t1.value.rot;
+		const Quat &q2=t2.value.rot;
 
 
-			if ((q0-q2).length() < CMP_EPSILON) {
+		//localize both to rotation from q0
 
 
-				if ((q0-q1).length() > CMP_EPSILON)
-					continue;
+		if ((q0-q2).length() < CMP_EPSILON) {
 
 
-			} else {
+			if ((q0-q1).length() > CMP_EPSILON)
+				return false;
 
 
+		} else {
 
 
-				Quat r02 = (q0.inverse() * q2).normalized();
-				Quat r01 = (q0.inverse() * q1).normalized();
 
 
-				Vector3 v02,v01;
-				real_t a02,a01;
+			Quat r02 = (q0.inverse() * q2).normalized();
+			Quat r01 = (q0.inverse() * q1).normalized();
 
 
-				r02.get_axis_and_angle(v02,a02);
-				r01.get_axis_and_angle(v01,a01);
+			Vector3 v02,v01;
+			real_t a02,a01;
 
 
-				if (Math::abs(a02)>p_max_optimizable_angle)
-					continue;
+			r02.get_axis_and_angle(v02,a02);
+			r01.get_axis_and_angle(v01,a01);
 
 
-				if (v01.dot(v02)<0) {
-					//make sure both rotations go the same way to compare
-					v02=-v02;
-					a02=-a02;
-				}
+			if (Math::abs(a02)>p_max_optimizable_angle)
+				return false;
 
 
-				real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized()))/Math_PI;
-				if (err_01>p_alowed_angular_err) {
-					//not rotating in the same axis
-					continue;
-				}
+			if (v01.dot(v02)<0) {
+				//make sure both rotations go the same way to compare
+				v02=-v02;
+				a02=-a02;
+			}
 
 
-				if (a01*a02 < 0 ) {
-					//not rotating in the same direction
-					continue;
-				}
+			real_t err_01 = Math::acos(v01.normalized().dot(v02.normalized()))/Math_PI;
+			if (err_01>p_alowed_angular_err) {
+				//not rotating in the same axis
+				return false;
+			}
 
 
-				real_t tr = a01/a02;
-				if (tr<0 || tr>1)
-					continue; //rotating too much or too less
+			if (a01*a02 < 0 ) {
+				//not rotating in the same direction
+				return false;
+			}
 
 
-				t[1]=tr;
+			real_t tr = a01/a02;
+			if (tr<0 || tr>1)
+				return false; //rotating too much or too less
 
 
-			}
+			t[1]=tr;
 
 
 		}
 		}
 
 
-		{ //scale
+	}
 
 
-			const Vector3 &v0=t0.value.scale;
-			const Vector3 &v1=t1.value.scale;
-			const Vector3 &v2=t2.value.scale;
+	{ //scale
 
 
-			if (v0.distance_to(v2)<CMP_EPSILON) {
-				//0 and 2 are close, let's see if 1 is close
-				if (v0.distance_to(v1)>CMP_EPSILON) {
-					//not close, not optimizable
-					continue;
-				}
+		const Vector3 &v0=t0.value.scale;
+		const Vector3 &v1=t1.value.scale;
+		const Vector3 &v2=t2.value.scale;
 
 
-			} else {
+		if (v0.distance_to(v2)<CMP_EPSILON) {
+			//0 and 2 are close, let's see if 1 is close
+			if (v0.distance_to(v1)>CMP_EPSILON) {
+				//not close, not optimizable
+				return false;
+			}
 
 
-				Vector3 pd = (v2-v0);
-				float d0 = pd.dot(v0);
-				float d1 = pd.dot(v1);
-				float d2 = pd.dot(v2);
-				if (d1<d0 || d1>d2) {
-					continue; //beyond segment range
-				}
+		} else {
 
 
-				Vector3 s[2]={ v0, v2 };
-				real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
+			Vector3 pd = (v2-v0);
+			float d0 = pd.dot(v0);
+			float d1 = pd.dot(v1);
+			float d2 = pd.dot(v2);
+			if (d1<d0 || d1>d2) {
+				return false; //beyond segment range
+			}
 
 
-				if (d>pd.length()*p_alowed_linear_err) {
-					continue; //beyond allowed error for colinearity
-				}
+			Vector3 s[2]={ v0, v2 };
+			real_t d =Geometry::get_closest_point_to_segment(v1,s).distance_to(v1);
 
 
-				t[2] = (d1-d0)/(d2-d0);
+			if (d>pd.length()*p_alowed_linear_err) {
+				return false; //beyond allowed error for colinearity
 			}
 			}
+
+			t[2] = (d1-d0)/(d2-d0);
 		}
 		}
+	}
 
 
-		bool erase=false;
-		if (t[0]==-1 && t[1]==-1 && t[2]==-1) {
+	bool erase=false;
+	if (t[0]==-1 && t[1]==-1 && t[2]==-1) {
 
 
-			erase=true;
-		} else {
+		erase=true;
+	} else {
 
 
-			erase=true;
-			real_t lt=-1;
-			for(int j=0;j<3;j++) {
-				//search for t on first, one must be it
-				if (t[j]!=-1) {
-					lt=t[j]; //official t
-					//validate rest
-					for(int k=j+1;k<3;k++) {
-						if (t[k]==-1)
-							continue;
-
-						if (Math::abs(lt-t[k])>p_alowed_linear_err) {
-							erase=false;
-							break;
-						}
+		erase=true;
+		real_t lt=-1;
+		for(int j=0;j<3;j++) {
+			//search for t on first, one must be it
+			if (t[j]!=-1) {
+				lt=t[j]; //official t
+				//validate rest
+				for(int k=j+1;k<3;k++) {
+					if (t[k]==-1)
+						continue;
+
+					if (Math::abs(lt-t[k])>p_alowed_linear_err) {
+						erase=false;
+						break;
 					}
 					}
-					break;
 				}
 				}
+				break;
 			}
 			}
+		}
 
 
-			ERR_CONTINUE( lt==-1 );
+		ERR_FAIL_COND_V( lt==-1,false );
 
 
-			if (erase) {
+		if (erase) {
 
 
-				if (Math::abs(lt-c)>p_alowed_linear_err) {
-					//todo, evaluate changing the transition if this fails?
-					//this could be done as a second pass and would be
-					//able to optimize more
-					erase=false;
-				} else {
+			if (Math::abs(lt-c)>p_alowed_linear_err) {
+				//todo, evaluate changing the transition if this fails?
+				//this could be done as a second pass and would be
+				//able to optimize more
+				erase=false;
+			} else {
 
 
-					//print_line(itos(i)+"because of interp");
-				}
+				//print_line(itos(i)+"because of interp");
 			}
 			}
+		}
+
+	}
+
+
+	return erase;
+
+
+}
+
 
 
+void Animation::_transform_track_optimize(int p_idx,float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle) {
+
+	ERR_FAIL_INDEX(p_idx,tracks.size());
+	ERR_FAIL_COND(tracks[p_idx]->type!=TYPE_TRANSFORM);
+	TransformTrack *tt= static_cast<TransformTrack*>(tracks[p_idx]);
+	bool prev_erased=false;
+	TKey<TransformKey> first_erased;
+
+	for(int i=1;i<tt->transforms.size()-1;i++) {
+
+		TKey<TransformKey> &t0 = tt->transforms[i-1];
+		TKey<TransformKey> &t1 = tt->transforms[i];
+		TKey<TransformKey> &t2 = tt->transforms[i+1];
+
+		bool erase = _transform_track_optimize_key(t0,t1,t2,p_alowed_linear_err,p_alowed_angular_err,p_max_optimizable_angle);
+
+
+		if (prev_erased && !_transform_track_optimize_key(t0,first_erased,t2,p_alowed_linear_err,p_alowed_angular_err,p_max_optimizable_angle)) {
+			 //avoid error to go beyond first erased key
+			erase=false;
 		}
 		}
 
 
+
 		if (erase) {
 		if (erase) {
+
+			if (!prev_erased) {
+				first_erased=t1;
+				prev_erased=true;
+			}
+
 			tt->transforms.remove(i);
 			tt->transforms.remove(i);
 			i--;
 			i--;
+
+		} else {
+			prev_erased=false;
 		}
 		}
 
 
 
 

+ 1 - 0
scene/resources/animation.h

@@ -204,6 +204,7 @@ private:
 		return idxr;
 		return idxr;
 	}
 	}
 
 
+	bool _transform_track_optimize_key(const TKey<TransformKey> &t0,const TKey<TransformKey> &t1, const TKey<TransformKey> &t2, float p_alowed_linear_err,float p_alowed_angular_err,float p_max_optimizable_angle);
 	void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01,float p_max_optimizable_angle=Math_PI*0.125);
 	void _transform_track_optimize(int p_idx, float p_allowed_err=0.05, float p_alowed_angular_err=0.01,float p_max_optimizable_angle=Math_PI*0.125);
 
 
 protected:	
 protected:	

+ 1 - 0
scene/resources/video_stream.cpp

@@ -33,6 +33,7 @@ void VideoStream::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("get_pending_frame_count"),&VideoStream::get_pending_frame_count);
 	ObjectTypeDB::bind_method(_MD("get_pending_frame_count"),&VideoStream::get_pending_frame_count);
 	ObjectTypeDB::bind_method(_MD("pop_frame"),&VideoStream::pop_frame);
 	ObjectTypeDB::bind_method(_MD("pop_frame"),&VideoStream::pop_frame);
 	ObjectTypeDB::bind_method(_MD("peek_frame"),&VideoStream::peek_frame);
 	ObjectTypeDB::bind_method(_MD("peek_frame"),&VideoStream::peek_frame);
+	ObjectTypeDB::bind_method(_MD("set_audio_track","idx"),&VideoStream::set_audio_track);
 };
 };
 
 
 
 

+ 2 - 0
scene/resources/video_stream.h

@@ -62,6 +62,8 @@ public:
 	virtual void pop_frame(Ref<ImageTexture> p_tex)=0;
 	virtual void pop_frame(Ref<ImageTexture> p_tex)=0;
 	virtual Image peek_frame() const=0;
 	virtual Image peek_frame() const=0;
 
 
+	virtual void set_audio_track(int p_idx) =0;
+
 	virtual void update(float p_time)=0;
 	virtual void update(float p_time)=0;
 
 
 	VideoStream();
 	VideoStream();

+ 8 - 0
servers/audio/audio_server_sw.cpp

@@ -332,6 +332,7 @@ void AudioServerSW::driver_process_chunk(int p_frames,int32_t *p_buffer) {
 void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) {
 void AudioServerSW::driver_process(int p_frames,int32_t *p_buffer) {
 
 
 
 
+	_output_delay=p_frames/double(AudioDriverSW::get_singleton()->get_mix_rate());
 	//process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE
 	//process in chunks to make sure to never process more than INTERNAL_BUFFER_SIZE
 	int todo=p_frames;
 	int todo=p_frames;
 	while(todo) {
 	while(todo) {
@@ -795,6 +796,8 @@ void AudioServerSW::init() {
 	mixer = memnew( AudioMixerSW( sample_manager, latency, AudioDriverSW::get_singleton()->get_mix_rate(),mix_chans,mixer_use_fx,mixer_interp,_mixer_callback,this ) );
 	mixer = memnew( AudioMixerSW( sample_manager, latency, AudioDriverSW::get_singleton()->get_mix_rate(),mix_chans,mixer_use_fx,mixer_interp,_mixer_callback,this ) );
 	mixer_step_usecs=mixer->get_step_usecs();
 	mixer_step_usecs=mixer->get_step_usecs();
 
 
+	_output_delay=0;
+
 	stream_volume=0.3;
 	stream_volume=0.3;
 	// start the audio driver
 	// start the audio driver
 	if (AudioDriverSW::get_singleton())
 	if (AudioDriverSW::get_singleton())
@@ -911,6 +914,11 @@ float AudioServerSW::get_event_voice_global_volume_scale() const {
 	return event_voice_volume_scale;
 	return event_voice_volume_scale;
 }
 }
 
 
+double AudioServerSW::get_output_delay() const {
+
+	return _output_delay;
+}
+
 double AudioServerSW::get_mix_time() const {
 double AudioServerSW::get_mix_time() const {
 
 
 	return AudioDriverSW::get_singleton()->get_mix_time();
 	return AudioDriverSW::get_singleton()->get_mix_time();

+ 5 - 0
servers/audio/audio_server_sw.h

@@ -92,6 +92,8 @@ class AudioServerSW : public AudioServer {
 	float peak_left,peak_right;
 	float peak_left,peak_right;
 	uint32_t max_peak;
 	uint32_t max_peak;
 
 
+	double _output_delay;
+
 	VoiceRBSW voice_rb;
 	VoiceRBSW voice_rb;
 
 
 	bool exit_update_thread;
 	bool exit_update_thread;
@@ -206,6 +208,9 @@ public:
 
 
 	virtual double get_mix_time() const; //useful for video -> audio sync
 	virtual double get_mix_time() const; //useful for video -> audio sync
 
 
+	virtual double get_output_delay() const;
+
+
 	AudioServerSW(SampleManagerSW *p_sample_manager);
 	AudioServerSW(SampleManagerSW *p_sample_manager);
 	~AudioServerSW();
 	~AudioServerSW();
 
 

+ 1 - 0
servers/audio_server.h

@@ -274,6 +274,7 @@ public:
 	static AudioServer *get_singleton();
 	static AudioServer *get_singleton();
 
 
 	virtual double get_mix_time() const=0; //useful for video -> audio sync
 	virtual double get_mix_time() const=0; //useful for video -> audio sync
+	virtual double get_output_delay() const=0;
 
 
 	AudioServer();
 	AudioServer();
 	virtual ~AudioServer();
 	virtual ~AudioServer();

+ 1 - 1
servers/physics_2d_server.cpp

@@ -514,7 +514,7 @@ void Physics2DServer::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&Physics2DServer::joint_get_type);
 	ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&Physics2DServer::joint_get_type);
 
 
-	ObjectTypeDB::bind_method(_MD("free","rid"),&Physics2DServer::free);
+	ObjectTypeDB::bind_method(_MD("free_rid","rid"),&Physics2DServer::free);
 
 
 	ObjectTypeDB::bind_method(_MD("set_active","active"),&Physics2DServer::set_active);
 	ObjectTypeDB::bind_method(_MD("set_active","active"),&Physics2DServer::set_active);
 
 

+ 1 - 1
servers/physics_server.cpp

@@ -655,7 +655,7 @@ void PhysicsServer::_bind_methods() {
 
 
 	ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&PhysicsServer::joint_get_type);
 	ObjectTypeDB::bind_method(_MD("joint_get_type","joint"),&PhysicsServer::joint_get_type);
 */
 */
-	ObjectTypeDB::bind_method(_MD("free","rid"),&PhysicsServer::free);
+	ObjectTypeDB::bind_method(_MD("free_rid","rid"),&PhysicsServer::free);
 
 
 	ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsServer::set_active);
 	ObjectTypeDB::bind_method(_MD("set_active","active"),&PhysicsServer::set_active);
 
 

+ 48 - 5
tools/editor/animation_editor.cpp

@@ -687,9 +687,7 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 
 		case TRACK_MENU_OPTIMIZE: {
 		case TRACK_MENU_OPTIMIZE: {
 
 
-			animation->optimize();
-
-			track_editor->update();
+			optimize_dialog->popup_centered(Size2(250,180));
 		} break;
 		} break;
 
 
 
 
@@ -698,6 +696,18 @@ void AnimationKeyEditor::_menu_track(int p_type) {
 
 
 }
 }
 
 
+
+void AnimationKeyEditor::_animation_optimize()  {
+
+
+	print_line("OPTIMIZE!");
+	animation->optimize(optimize_linear_error->get_val(),optimize_angular_error->get_val(),optimize_max_angle->get_val());
+	track_editor->update();
+	undo_redo->clear_history();
+
+}
+
+
 float AnimationKeyEditor::_get_zoom_scale() const {
 float AnimationKeyEditor::_get_zoom_scale() const {
 
 
 	float zv = zoom->get_val();
 	float zv = zoom->get_val();
@@ -2335,11 +2345,12 @@ void AnimationKeyEditor::_notification(int p_what) {
 				tpp->add_item("Out-In",TRACK_MENU_SET_ALL_TRANS_OUTIN);
 				tpp->add_item("Out-In",TRACK_MENU_SET_ALL_TRANS_OUTIN);
 				tpp->set_name("Transitions");
 				tpp->set_name("Transitions");
 				tpp->connect("item_pressed",this,"_menu_track");
 				tpp->connect("item_pressed",this,"_menu_track");
+				optimize_dialog->connect("confirmed",this,"_animation_optimize");
 
 
 				menu_track->get_popup()->add_child(tpp);
 				menu_track->get_popup()->add_child(tpp);
 				menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
 				menu_track->get_popup()->add_submenu_item("Set Transitions..","Transitions");
-				//menu_track->get_popup()->add_separator();
-				//menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
+				menu_track->get_popup()->add_separator();
+				menu_track->get_popup()->add_item("Optimize Animation",TRACK_MENU_OPTIMIZE);
 
 
 
 
 
 
@@ -3099,6 +3110,7 @@ void AnimationKeyEditor::_bind_methods() {
 	ObjectTypeDB::bind_method(_MD("_animation_len_update"),&AnimationKeyEditor::_animation_len_update);
 	ObjectTypeDB::bind_method(_MD("_animation_len_update"),&AnimationKeyEditor::_animation_len_update);
 
 
 	ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation);
 	ObjectTypeDB::bind_method(_MD("set_animation"),&AnimationKeyEditor::set_animation);
+	ObjectTypeDB::bind_method(_MD("_animation_optimize"),&AnimationKeyEditor::_animation_optimize);
 
 
 
 
 	ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );
 	ADD_SIGNAL( MethodInfo("resource_selected", PropertyInfo( Variant::OBJECT, "res"),PropertyInfo( Variant::STRING, "prop") ) );
@@ -3224,6 +3236,37 @@ AnimationKeyEditor::AnimationKeyEditor(UndoRedo *p_undo_redo, EditorHistory *p_h
 	remove_button->set_disabled(true);
 	remove_button->set_disabled(true);
 	remove_button->set_tooltip("Remove selected track.");
 	remove_button->set_tooltip("Remove selected track.");
 
 
+
+	optimize_dialog = memnew( ConfirmationDialog );
+	add_child(optimize_dialog);
+	optimize_dialog->set_title("Anim. Optimizer");
+	VBoxContainer *optimize_vb = memnew( VBoxContainer );
+	optimize_dialog->add_child(optimize_vb);
+	optimize_dialog->set_child_rect(optimize_vb);
+	optimize_linear_error = memnew( SpinBox );
+	optimize_linear_error->set_max(1.0);
+	optimize_linear_error->set_min(0.001);
+	optimize_linear_error->set_step(0.001);
+	optimize_linear_error->set_val(0.05);
+	optimize_vb->add_margin_child("Max. Linear Error:",optimize_linear_error);
+	optimize_angular_error = memnew( SpinBox );
+	optimize_angular_error->set_max(1.0);
+	optimize_angular_error->set_min(0.001);
+	optimize_angular_error->set_step(0.001);
+	optimize_angular_error->set_val(0.01);
+
+	optimize_vb->add_margin_child("Max. Angular Error:",optimize_angular_error);
+	optimize_max_angle = memnew( SpinBox );
+	optimize_vb->add_margin_child("Max Optimizable Angle:",optimize_max_angle);
+	optimize_max_angle->set_max(360.0);
+	optimize_max_angle->set_min(0.0);
+	optimize_max_angle->set_step(0.1);
+	optimize_max_angle->set_val(22);
+
+	optimize_dialog->get_ok()->set_text("Optimize");
+
+
+
 	/*keying = memnew( Button );
 	/*keying = memnew( Button );
 	keying->set_toggle_mode(true);
 	keying->set_toggle_mode(true);
 	//keying->set_text("Keys");
 	//keying->set_text("Keys");

+ 6 - 0
tools/editor/animation_editor.h

@@ -169,6 +169,11 @@ class AnimationKeyEditor : public VBoxContainer  {
 	ToolButton *move_down_button;
 	ToolButton *move_down_button;
 	ToolButton *remove_button;
 	ToolButton *remove_button;
 
 
+	ConfirmationDialog *optimize_dialog;
+	SpinBox *optimize_linear_error;
+	SpinBox *optimize_angular_error;
+	SpinBox *optimize_max_angle;
+
 	SpinBox *step;
 	SpinBox *step;
 
 
 	MenuButton *menu_track;
 	MenuButton *menu_track;
@@ -257,6 +262,7 @@ class AnimationKeyEditor : public VBoxContainer  {
 	StringName alc;
 	StringName alc;
 
 
 	void _animation_changed();
 	void _animation_changed();
+	void _animation_optimize();
 
 
 	void _scroll_changed(double);
 	void _scroll_changed(double);
 
 

+ 29 - 0
tools/editor/editor_file_system.cpp

@@ -992,6 +992,35 @@ void EditorFileSystem::_resource_saved(const String& p_path){
 	EditorFileSystem::get_singleton()->update_file(p_path);
 	EditorFileSystem::get_singleton()->update_file(p_path);
 }
 }
 
 
+String EditorFileSystem::_find_first_from_source(EditorFileSystemDirectory* p_dir,const String &p_src) const {
+
+	for(int i=0;i<p_dir->files.size();i++) {
+		for(int j=0;j<p_dir->files[i].meta.sources.size();j++) {
+
+			if (p_dir->files[i].meta.sources[j].path==p_src)
+				return p_dir->get_file_path(i);
+		}
+	}
+
+	for(int i=0;i<p_dir->subdirs.size();i++) {
+
+		String ret = _find_first_from_source(p_dir->subdirs[i],p_src);
+		if (ret.length()>0)
+			return ret;
+	}
+
+	return String();
+}
+
+
+String EditorFileSystem::find_resource_from_source(const String& p_path) const {
+
+
+	if (filesystem)
+		return _find_first_from_source(filesystem,p_path);
+	return String();
+}
+
 void EditorFileSystem::update_file(const String& p_file) {
 void EditorFileSystem::update_file(const String& p_file) {
 
 
     EditorFileSystemDirectory *fs=NULL;
     EditorFileSystemDirectory *fs=NULL;

+ 3 - 0
tools/editor/editor_file_system.h

@@ -75,6 +75,7 @@ class EditorFileSystemDirectory : public Object {
 
 
 	static void _bind_methods();
 	static void _bind_methods();
 
 
+
 friend class EditorFileSystem;
 friend class EditorFileSystem;
 public:
 public:
 
 
@@ -180,6 +181,7 @@ class EditorFileSystem : public Node {
 	List<String> sources_changed;
 	List<String> sources_changed;
 
 
 	static void _resource_saved(const String& p_path);
 	static void _resource_saved(const String& p_path);
+	String _find_first_from_source(EditorFileSystemDirectory* p_dir,const String &p_src) const;
 
 
 protected:
 protected:
 
 
@@ -197,6 +199,7 @@ public:
 	void scan_sources();
 	void scan_sources();
 	void get_changed_sources(List<String> *r_changed);
 	void get_changed_sources(List<String> *r_changed);
 	void update_file(const String& p_file);
 	void update_file(const String& p_file);
+	String find_resource_from_source(const String& p_path) const;
 	EditorFileSystemDirectory *get_path(const String& p_path);
 	EditorFileSystemDirectory *get_path(const String& p_path);
 	String get_file_type(const String& p_file) const;
 	String get_file_type(const String& p_file) const;
 	EditorFileSystem();
 	EditorFileSystem();

+ 7 - 6
tools/editor/io_plugins/editor_import_collada.cpp

@@ -1844,12 +1844,15 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 		anim_length=collada.state.animation_clips[p_clip].end;
 		anim_length=collada.state.animation_clips[p_clip].end;
 
 
 	while(f<anim_length) {
 	while(f<anim_length) {
-		if (f>=anim_length)
-			f=anim_length;
 
 
 		base_snapshots.push_back(f);
 		base_snapshots.push_back(f);
 		f+=snapshot_interval;
 		f+=snapshot_interval;
+
+		if (f>=anim_length) {
+			base_snapshots.push_back(anim_length);
+		}
 	}
 	}
+
 	//print_line("anim len: "+rtos(anim_length));
 	//print_line("anim len: "+rtos(anim_length));
 	animation->set_length(anim_length);
 	animation->set_length(anim_length);
 
 
@@ -1894,6 +1897,8 @@ void ColladaImport::create_animation(int p_clip, bool p_make_tracks_in_all_bones
 			for(int i=0;i<at.keys.size();i++)
 			for(int i=0;i<at.keys.size();i++)
 				snapshots.push_back(at.keys[i].time);
 				snapshots.push_back(at.keys[i].time);
 
 
+			print_line("using anim snapshots");
+
 		}
 		}
 
 
 
 
@@ -2185,8 +2190,6 @@ Node* EditorSceneImporterCollada::import_scene(const String& p_path, uint32_t p_
 			else
 			else
 				name=state.animations[i]->get_name();
 				name=state.animations[i]->get_name();
 
 
-			if (p_flags&IMPORT_ANIMATION_OPTIMIZE)
-				state.animations[i]->optimize();
 			if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) {
 			if (p_flags&IMPORT_ANIMATION_DETECT_LOOP) {
 
 
 				if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
 				if (name.begins_with("loop") || name.ends_with("loop") || name.begins_with("cycle") || name.ends_with("cycle")) {
@@ -2232,8 +2235,6 @@ Ref<Animation> EditorSceneImporterCollada::import_animation(const String& p_path
 		}
 		}
 	}
 	}
 
 
-	if (p_flags&IMPORT_ANIMATION_OPTIMIZE)
-		anim->optimize();
 
 
 	return anim;
 	return anim;
 }
 }

+ 137 - 1
tools/editor/io_plugins/editor_scene_import_plugin.cpp

@@ -80,17 +80,25 @@ class EditorImportAnimationOptions : public VBoxContainer {
 
 
 
 
 	TreeItem *fps;
 	TreeItem *fps;
+	TreeItem* optimize_linear_error;
+	TreeItem* optimize_angular_error;
+	TreeItem* optimize_max_angle;
+
 	TreeItem *clips_base;
 	TreeItem *clips_base;
+
 	TextEdit *filters;
 	TextEdit *filters;
 	Vector<TreeItem*> clips;
 	Vector<TreeItem*> clips;
 
 
 	Tree *flags;
 	Tree *flags;
 	Tree *clips_tree;
 	Tree *clips_tree;
+	Tree *optimization_tree;
 	Vector<TreeItem*> items;
 	Vector<TreeItem*> items;
 
 
 	bool updating;
 	bool updating;
 	bool validating;
 	bool validating;
 
 
+
+
 	void _changed();
 	void _changed();
 	void _item_edited();
 	void _item_edited();
 	void _button_action(Object *p_obj,int p_col,int p_id);
 	void _button_action(Object *p_obj,int p_col,int p_id);
@@ -107,6 +115,15 @@ public:
 	void set_fps(int p_fps);
 	void set_fps(int p_fps);
 	int get_fps() const;
 	int get_fps() const;
 
 
+	void set_optimize_linear_error(float p_error);
+	float get_optimize_linear_error() const;
+
+	void set_optimize_angular_error(float p_error);
+	float get_optimize_angular_error() const;
+
+	void set_optimize_max_angle(float p_error);
+	float get_optimize_max_angle() const;
+
 	void setup_clips(const Array& p_clips);
 	void setup_clips(const Array& p_clips);
 	Array get_clips() const;
 	Array get_clips() const;
 
 
@@ -453,9 +470,41 @@ EditorImportAnimationOptions::EditorImportAnimationOptions() {
 	fps = flags->create_item(fps_base);
 	fps = flags->create_item(fps_base);
 	fps->set_cell_mode(0,TreeItem::CELL_MODE_RANGE);
 	fps->set_cell_mode(0,TreeItem::CELL_MODE_RANGE);
 	fps->set_editable(0,true);
 	fps->set_editable(0,true);
-	fps->set_range(0,15);
 	fps->set_range_config(0,1,120,1);
 	fps->set_range_config(0,1,120,1);
+	fps->set_range(0,15);
 
 
+	optimization_tree = memnew( Tree );
+	optimization_tree->set_columns(2);
+	tab->add_child(optimization_tree);
+	optimization_tree->set_name("Optimizer");
+	optimization_tree->set_column_expand(0,true);
+	optimization_tree->set_column_expand(1,false);
+	optimization_tree->set_column_min_width(1,80);
+	optimization_tree->set_hide_root(true);
+
+
+	TreeItem *optimize_root = optimization_tree->create_item();
+
+	optimize_linear_error = optimization_tree->create_item(optimize_root);
+	optimize_linear_error->set_text(0,"Max Linear Error");
+	optimize_linear_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
+	optimize_linear_error->set_editable(1,true);
+	optimize_linear_error->set_range_config(1,0,1,0.001);
+	optimize_linear_error->set_range(1,0.05);
+
+	optimize_angular_error = optimization_tree->create_item(optimize_root);
+	optimize_angular_error->set_text(0,"Max Angular Error");
+	optimize_angular_error->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
+	optimize_angular_error->set_editable(1,true);
+	optimize_angular_error->set_range_config(1,0,1,0.001);
+	optimize_angular_error->set_range(1,0.01);
+
+	optimize_max_angle = optimization_tree->create_item(optimize_root);
+	optimize_max_angle->set_text(0,"Max Angle");
+	optimize_max_angle->set_cell_mode(1,TreeItem::CELL_MODE_RANGE);
+	optimize_max_angle->set_editable(1,true);
+	optimize_max_angle->set_range_config(1,0,360,0.001);
+	optimize_max_angle->set_range(1,int(180*0.125));
 
 
 	clips_tree = memnew( Tree );
 	clips_tree = memnew( Tree );
 	clips_tree->set_hide_root(true);
 	clips_tree->set_hide_root(true);
@@ -498,6 +547,38 @@ int EditorImportAnimationOptions::get_fps() const {
 	return fps->get_range(0);
 	return fps->get_range(0);
 }
 }
 
 
+
+void EditorImportAnimationOptions::set_optimize_linear_error(float p_optimize_linear_error) {
+
+	optimize_linear_error->set_range(1,p_optimize_linear_error);
+}
+
+float EditorImportAnimationOptions::get_optimize_linear_error() const {
+
+	return optimize_linear_error->get_range(1);
+}
+
+void EditorImportAnimationOptions::set_optimize_angular_error(float p_optimize_angular_error) {
+
+	optimize_angular_error->set_range(1,p_optimize_angular_error);
+}
+
+float EditorImportAnimationOptions::get_optimize_angular_error() const {
+
+	return optimize_angular_error->get_range(1);
+}
+
+void EditorImportAnimationOptions::set_optimize_max_angle(float p_optimize_max_angle) {
+
+	optimize_max_angle->set_range(1,p_optimize_max_angle);
+}
+
+float EditorImportAnimationOptions::get_optimize_max_angle() const {
+
+	return optimize_max_angle->get_range(1);
+}
+
+
 void EditorImportAnimationOptions::set_filter(const String& p_filter) {
 void EditorImportAnimationOptions::set_filter(const String& p_filter) {
 
 
 	filters->set_text(p_filter);
 	filters->set_text(p_filter);
@@ -544,6 +625,17 @@ void EditorSceneImportDialog::_choose_file(const String& p_path) {
 	}
 	}
 #endif
 #endif
 
 
+	if (p_path!=String()) {
+
+		String from_path = EditorFileSystem::get_singleton()->find_resource_from_source(EditorImportPlugin::validate_source_path(p_path));
+		print_line("from path.."+from_path);
+		if (from_path!=String()) {
+			popup_import(from_path);
+
+		}
+	}
+
+
 	import_path->set_text(p_path);
 	import_path->set_text(p_path);
 
 
 }
 }
@@ -650,6 +742,9 @@ void EditorSceneImportDialog::_import(bool p_and_open) {
 	rim->set_option("texture_quality",texture_options->get_quality());
 	rim->set_option("texture_quality",texture_options->get_quality());
 	rim->set_option("animation_flags",animation_options->get_flags());
 	rim->set_option("animation_flags",animation_options->get_flags());
 	rim->set_option("animation_bake_fps",animation_options->get_fps());
 	rim->set_option("animation_bake_fps",animation_options->get_fps());
+	rim->set_option("animation_optimizer_linear_error",animation_options->get_optimize_linear_error());
+	rim->set_option("animation_optimizer_angular_error",animation_options->get_optimize_angular_error());
+	rim->set_option("animation_optimizer_max_angle",animation_options->get_optimize_max_angle());
 	rim->set_option("animation_filters",animation_options->get_filter());
 	rim->set_option("animation_filters",animation_options->get_filter());
 	rim->set_option("animation_clips",animation_options->get_clips());
 	rim->set_option("animation_clips",animation_options->get_clips());
 	rim->set_option("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String());
 	rim->set_option("post_import_script",script_path->get_text()!=String()?EditorImportPlugin::validate_source_path(script_path->get_text()):String());
@@ -791,6 +886,13 @@ void EditorSceneImportDialog::popup_import(const String &p_from) {
 			animation_options->set_filter(rimd->get_option("animation_filters"));
 			animation_options->set_filter(rimd->get_option("animation_filters"));
 		if (rimd->has_option("animation_bake_fps"))
 		if (rimd->has_option("animation_bake_fps"))
 			animation_options->set_fps(rimd->get_option("animation_bake_fps"));
 			animation_options->set_fps(rimd->get_option("animation_bake_fps"));
+		if (rimd->has_option("animation_optimizer_linear_error"))
+			animation_options->set_optimize_linear_error(rimd->get_option("animation_optimizer_linear_error"));
+		if (rimd->has_option("animation_optimizer_angular_error"))
+			animation_options->set_optimize_angular_error(rimd->get_option("animation_optimizer_angular_error"));
+		if (rimd->has_option("animation_optimizer_max_angle"))
+			animation_options->set_optimize_max_angle(rimd->get_option("animation_optimizer_max_angle"));
+
 		script_path->set_text(rimd->get_option("post_import_script"));
 		script_path->set_text(rimd->get_option("post_import_script"));
 		if (rimd->has_option("import_this_time"))
 		if (rimd->has_option("import_this_time"))
 			this_import->select(rimd->get_option("import_this_time"));
 			this_import->select(rimd->get_option("import_this_time"));
@@ -2223,6 +2325,8 @@ Error EditorSceneImportPlugin::import1(const Ref<ResourceImportMetadata>& p_from
 	int fps = 24;
 	int fps = 24;
 	if (from->has_option("animation_bake_fps"))
 	if (from->has_option("animation_bake_fps"))
 		fps=from->get_option("animation_bake_fps");
 		fps=from->get_option("animation_bake_fps");
+
+
 	Array clips;
 	Array clips;
 	if (from->has_option("animation_clips"))
 	if (from->has_option("animation_clips"))
 		clips=from->get_option("animation_clips");
 		clips=from->get_option("animation_clips");
@@ -2503,6 +2607,26 @@ void EditorSceneImportPlugin::_filter_tracks(Node *scene, const String& p_text)
 
 
 }
 }
 
 
+void EditorSceneImportPlugin::_optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle) {
+
+	if (!scene->has_node(String("AnimationPlayer")))
+		return;
+		Node* n = scene->get_node(String("AnimationPlayer"));
+	ERR_FAIL_COND(!n);
+	AnimationPlayer *anim = n->cast_to<AnimationPlayer>();
+	ERR_FAIL_COND(!anim);
+
+
+	List<StringName> anim_names;
+	anim->get_animation_list(&anim_names);
+	for(List<StringName>::Element *E=anim_names.front();E;E=E->next()) {
+
+		Ref<Animation> a = anim->get_animation(E->get());
+		a->optimize(p_max_lin_error,p_max_ang_error,Math::deg2rad(p_max_angle));
+	}
+}
+
+
 Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from) {
 Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, const Ref<ResourceImportMetadata>& p_from) {
 
 
 	Error err=OK;
 	Error err=OK;
@@ -2512,6 +2636,16 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c
 	Array animation_clips = p_from->get_option("animation_clips");
 	Array animation_clips = p_from->get_option("animation_clips");
 	String animation_filter = p_from->get_option("animation_filters");
 	String animation_filter = p_from->get_option("animation_filters");
 	int scene_flags = from->get_option("flags");
 	int scene_flags = from->get_option("flags");
+	float anim_optimizer_linerr=0.05;
+	float anim_optimizer_angerr=0.01;
+	float anim_optimizer_maxang=22;
+
+	if (from->has_option("animation_optimizer_linear_error"))
+		anim_optimizer_linerr=from->get_option("animation_optimizer_linear_error");
+	if (from->has_option("animation_optimizer_angular_error"))
+		anim_optimizer_angerr=from->get_option("animation_optimizer_angular_error");
+	if (from->has_option("animation_optimizer_max_angle"))
+		anim_optimizer_maxang=from->get_option("animation_optimizer_max_angle");
 
 
 	EditorProgress progress("import","Import Scene",104);
 	EditorProgress progress("import","Import Scene",104);
 	progress.step("Importing Scene..",2);
 	progress.step("Importing Scene..",2);
@@ -2536,6 +2670,8 @@ Error EditorSceneImportPlugin::import2(Node *scene, const String& p_dest_path, c
 	Map< Ref<ImageTexture>,TextureRole > imagemap;
 	Map< Ref<ImageTexture>,TextureRole > imagemap;
 
 
 	scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap);
 	scene=_fix_node(scene,scene,collision_map,scene_flags,imagemap);
+	if (animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_OPTIMIZE)
+		_optimize_animations(scene,anim_optimizer_linerr,anim_optimizer_angerr,anim_optimizer_maxang);
 	if (animation_clips.size())
 	if (animation_clips.size())
 		_create_clips(scene,animation_clips,animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS);
 		_create_clips(scene,animation_clips,animation_flags&EditorSceneAnimationImportPlugin::ANIMATION_FORCE_ALL_TRACKS_IN_ALL_CLIPS);
 
 

+ 1 - 0
tools/editor/io_plugins/editor_scene_import_plugin.h

@@ -114,6 +114,7 @@ class EditorSceneImportPlugin : public EditorImportPlugin {
 	void _merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes);
 	void _merge_existing_node(Node *p_node,Node *p_imported_scene,Set<Ref<Resource> >& checked_resources,Set<Node*> &checked_nodes);
 
 
 	void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes);
 	void _add_new_nodes(Node *p_node,Node *p_imported,Node *p_imported_scene,Set<Node*> &checked_nodes);
+	void _optimize_animations(Node *scene, float p_max_lin_error,float p_max_ang_error,float p_max_angle);
 
 
 	void _merge_scenes(Node *p_node, Node *p_imported);
 	void _merge_scenes(Node *p_node, Node *p_imported);
 	void _scan_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials);
 	void _scan_materials(Node*p_base,Node *p_node,Map<String,Ref<Material> > &mesh_materials,Map<String,Ref<Material> >& override_materials);

+ 1 - 1
tools/export/blender25/io_scene_dae/export_dae.py

@@ -1457,7 +1457,7 @@ class DaeExporter:
 
 
 				print(str(x))
 				print(str(x))
 
 
-				tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]),allowed_skeletons)
+				tcn = self.export_animation(int(x.frame_range[0]),int(x.frame_range[1]+0.5),allowed_skeletons)
 				framelen=(1.0/self.scene.render.fps)
 				framelen=(1.0/self.scene.render.fps)
 				start = x.frame_range[0]*framelen
 				start = x.frame_range[0]*framelen
 				end = x.frame_range[1]*framelen
 				end = x.frame_range[1]*framelen