diff options
author | Juan Linietsky <reduzio@gmail.com> | 2014-09-15 11:33:30 -0300 |
---|---|---|
committer | Juan Linietsky <reduzio@gmail.com> | 2014-09-15 11:33:30 -0300 |
commit | 8cab401d08f8e25aa9b2dc710204785858ff3dbb (patch) | |
tree | 1a4cec868f937fb24d340ee33fbeba2f1c6fa9f2 /modules/gdscript | |
parent | 1a2cb755e2d8b9d59178f36702f6dff7235b9088 (diff) |
3D Physics Rework, Other Stuff
-=-=-=-=-=-=-=-=-=-=-=-=-=-
3D Physics:
-Fixed "Bounce" parameter in 3D
-Fixed bug affecting Area (sometims it would not detect properly)
-Vehicle Body has seen heavy work
-Added Query API for doing space queries in 3D. Needs some docs though.
-Added JOINTS! Adapted Bullet Joints: and created easy gizmos for setting them up:
-PinJoint
-HingeJoint (with motor)
-SliderJoint
-ConeTwistJoint
-Generic6DOFJoint
-Added OBJECT PICKING! based on the new query API. Any physics object now (Area or Body) has the following signals and virtual functions:
-input_event (mouse or multitouch input over the body)
-mouse_enter (mouse entered the body area)
-mouse_exit (mouse exited body area)
For Area it needs to be activated manually, as it isn't by default (ray goes thru).
Other:
-Begun working on Windows 8 (RT) port. Compiles but does not work yet.
-Added TheoraPlayer library for improved to-texture and portable video support.
-Fixed a few bugs in the renderer, collada importer, collada exporter, etc.
Diffstat (limited to 'modules/gdscript')
-rw-r--r-- | modules/gdscript/gd_compiler.cpp | 35 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.cpp | 53 | ||||
-rw-r--r-- | modules/gdscript/gd_parser.h | 4 | ||||
-rw-r--r-- | modules/gdscript/gd_script.cpp | 288 | ||||
-rw-r--r-- | modules/gdscript/gd_script.h | 39 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.cpp | 8 | ||||
-rw-r--r-- | modules/gdscript/gd_tokenizer.h | 1 | ||||
-rw-r--r-- | modules/gdscript/register_types.cpp | 1 |
8 files changed, 374 insertions, 55 deletions
diff --git a/modules/gdscript/gd_compiler.cpp b/modules/gdscript/gd_compiler.cpp index fc9040da18..e6dd9d9ae1 100644 --- a/modules/gdscript/gd_compiler.cpp +++ b/modules/gdscript/gd_compiler.cpp @@ -60,7 +60,8 @@ bool GDCompiler::_create_unary_operator(CodeGen& codegen,const GDParser::Operato codegen.opcodes.push_back(GDFunction::OPCODE_OPERATOR); // perform operator codegen.opcodes.push_back(op); //which operator codegen.opcodes.push_back(src_address_a); // argument 1 - codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter) + codegen.opcodes.push_back(src_address_a); // argument 2 (repeated) + //codegen.opcodes.push_back(GDFunction::ADDR_TYPE_NIL); // argument 2 (unary only takes one parameter) return true; } @@ -507,6 +508,34 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre codegen.opcodes.push_back(arguments[i]); } } break; + case GDParser::OperatorNode::OP_YIELD: { + + + ERR_FAIL_COND_V(on->arguments.size() && on->arguments.size()!=2,-1); + + Vector<int> arguments; + int slevel = p_stack_level; + for(int i=0;i<on->arguments.size();i++) { + + int ret = _parse_expression(codegen,on->arguments[i],slevel); + if (ret<0) + return ret; + if (ret&GDFunction::ADDR_TYPE_STACK<<GDFunction::ADDR_BITS) { + slevel++; + codegen.alloc_stack(slevel); + } + arguments.push_back(ret); + } + + //push call bytecode + codegen.opcodes.push_back(arguments.size()==0?GDFunction::OPCODE_YIELD:GDFunction::OPCODE_YIELD_SIGNAL); // basic type constructor + for(int i=0;i<arguments.size();i++) + codegen.opcodes.push_back(arguments[i]); //arguments + codegen.opcodes.push_back(GDFunction::OPCODE_YIELD_RESUME); + //next will be where to place the result :) + + } break; + //indexing operator case GDParser::OperatorNode::OP_INDEX: case GDParser::OperatorNode::OP_INDEX_NAMED: { @@ -644,8 +673,8 @@ int GDCompiler::_parse_expression(CodeGen& codegen,const GDParser::Node *p_expre case GDParser::OperatorNode::OP_BIT_OR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_OR,p_stack_level)) return -1;} break; case GDParser::OperatorNode::OP_BIT_XOR: { if (!_create_binary_operator(codegen,on,Variant::OP_BIT_XOR,p_stack_level)) return -1;} break; //shift - case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break; - case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break; + case GDParser::OperatorNode::OP_SHIFT_LEFT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_LEFT,p_stack_level)) return -1;} break; + case GDParser::OperatorNode::OP_SHIFT_RIGHT: { if (!_create_binary_operator(codegen,on,Variant::OP_SHIFT_RIGHT,p_stack_level)) return -1;} break; //assignment operators case GDParser::OperatorNode::OP_ASSIGN_ADD: case GDParser::OperatorNode::OP_ASSIGN_SUB: diff --git a/modules/gdscript/gd_parser.cpp b/modules/gdscript/gd_parser.cpp index 40c262c503..ef9e85a8c2 100644 --- a/modules/gdscript/gd_parser.cpp +++ b/modules/gdscript/gd_parser.cpp @@ -256,6 +256,55 @@ GDParser::Node* GDParser::_parse_expression(Node *p_parent,bool p_static,bool p_ tokenizer->advance(); expr=constant; + } else if (tokenizer->get_token()==GDTokenizer::TK_PR_YIELD) { + + //constant defined by tokenizer + + tokenizer->advance(); + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after 'yield'"); + return NULL; + } + + tokenizer->advance(); + + OperatorNode *yield = alloc_node<OperatorNode>(); + yield->op=OperatorNode::OP_YIELD; + + if (tokenizer->get_token()==GDTokenizer::TK_PARENTHESIS_CLOSE) { + expr=yield; + tokenizer->advance(); + } else { + + Node *object = _parse_and_reduce_expression(p_parent,p_static); + if (!object) + return NULL; + yield->arguments.push_back(object); + + if (tokenizer->get_token()!=GDTokenizer::TK_COMMA) { + + _set_error("Expected ',' after first argument of 'yield'"); + return NULL; + } + + tokenizer->advance(); + + Node *signal = _parse_and_reduce_expression(p_parent,p_static); + if (!signal) + return NULL; + yield->arguments.push_back(signal); + + if (tokenizer->get_token()!=GDTokenizer::TK_PARENTHESIS_CLOSE) { + + _set_error("Expected ')' after second argument of 'yield'"); + return NULL; + } + + tokenizer->advance(); + + expr=yield; + } + } else if (tokenizer->get_token()==GDTokenizer::TK_SELF) { @@ -1067,6 +1116,10 @@ GDParser::Node* GDParser::_reduce_expression(Node *p_node,bool p_to_const) { } return op; //don't reduce yet + + } else if (op->op==OperatorNode::OP_YIELD) { + return op; + } else if (op->op==OperatorNode::OP_INDEX) { //can reduce indices into constant arrays or dictionaries diff --git a/modules/gdscript/gd_parser.h b/modules/gdscript/gd_parser.h index ae43a26f5a..50b84d389a 100644 --- a/modules/gdscript/gd_parser.h +++ b/modules/gdscript/gd_parser.h @@ -183,6 +183,7 @@ public: //call/constructor operator OP_CALL, OP_PARENT_CALL, + OP_YIELD, OP_EXTENDS, //indexing operator OP_INDEX, @@ -225,7 +226,7 @@ public: OP_ASSIGN_BIT_XOR, OP_BIT_AND, OP_BIT_OR, - OP_BIT_XOR + OP_BIT_XOR, }; Operator op; @@ -258,6 +259,7 @@ public: Node* condition; AssertNode() { type=TYPE_ASSERT; } }; + struct NewLineNode : public Node { int line; NewLineNode() { type=TYPE_NEWLINE; } diff --git a/modules/gdscript/gd_script.cpp b/modules/gdscript/gd_script.cpp index cc7aa70234..92962fa3f7 100644 --- a/modules/gdscript/gd_script.cpp +++ b/modules/gdscript/gd_script.cpp @@ -183,7 +183,7 @@ static String _get_var_type(const Variant* p_type) { } -Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err) { +Variant GDFunction::call(GDInstance *p_instance, const Variant **p_args, int p_argcount, Variant::CallError& r_err, CallState *p_state) { if (!_code_ptr) { @@ -205,77 +205,91 @@ Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_ar #endif - if (p_argcount!=_argument_count) { + uint32_t alloca_size=0; + GDScript *_class; + int ip=0; + int line=_initial_line; - if (p_argcount>_argument_count) { + if (p_state) { + //use existing (supplied) state (yielded) + stack=(Variant*)p_state->stack.ptr(); + call_args=(Variant**)&p_state->stack[sizeof(Variant)*p_state->stack_size]; + line=p_state->line; + ip=p_state->ip; + alloca_size=p_state->stack.size(); + _class=p_state->_class; + p_instance=p_state->instance; + defarg=p_state->defarg; + self=p_state->self; + //stack[p_state->result_pos]=p_state->result; //assign stack with result - r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; - r_err.argument=_argument_count; + } else { - return Variant(); - } else if (p_argcount < _argument_count - _default_arg_count) { + if (p_argcount!=_argument_count) { - r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; - r_err.argument=_argument_count - _default_arg_count; - return Variant(); - } else { + if (p_argcount>_argument_count) { - defarg=_argument_count-p_argcount; - } - } + r_err.error=Variant::CallError::CALL_ERROR_TOO_MANY_ARGUMENTS; + r_err.argument=_argument_count; - uint32_t alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size; + return Variant(); + } else if (p_argcount < _argument_count - _default_arg_count) { - if (alloca_size) { + r_err.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_err.argument=_argument_count - _default_arg_count; + return Variant(); + } else { - uint8_t *aptr = (uint8_t*)alloca(alloca_size); + defarg=_argument_count-p_argcount; + } + } - if (_stack_size) { + alloca_size = sizeof(Variant*)*_call_size + sizeof(Variant)*_stack_size; - stack=(Variant*)aptr; - for(int i=0;i<p_argcount;i++) - memnew_placement(&stack[i],Variant(*p_args[i])); - for(int i=p_argcount;i<_stack_size;i++) - memnew_placement(&stack[i],Variant); - } else { - stack=NULL; - } + if (alloca_size) { - if (_call_size) { + uint8_t *aptr = (uint8_t*)alloca(alloca_size); - call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size]; - } else { + if (_stack_size) { - call_args=NULL; - } + stack=(Variant*)aptr; + for(int i=0;i<p_argcount;i++) + memnew_placement(&stack[i],Variant(*p_args[i])); + for(int i=p_argcount;i<_stack_size;i++) + memnew_placement(&stack[i],Variant); + } else { + stack=NULL; + } + if (_call_size) { - } else { - stack=NULL; - call_args=NULL; - } + call_args = (Variant**)&aptr[sizeof(Variant)*_stack_size]; + } else { + + call_args=NULL; + } - GDScript *_class; + } else { + stack=NULL; + call_args=NULL; + } - if (p_instance) { - if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) { + if (p_instance) { + if (p_instance->base_ref && static_cast<Reference*>(p_instance->owner)->is_referenced()) { - self=REF(static_cast<Reference*>(p_instance->owner)); + self=REF(static_cast<Reference*>(p_instance->owner)); + } else { + self=p_instance->owner; + } + _class=p_instance->script.ptr(); } else { - self=p_instance->owner; + _class=_script; } - _class=p_instance->script.ptr(); - } else { - _class=_script; } - int ip=0; - int line=_initial_line; String err_text; - - #ifdef DEBUG_ENABLED if (ScriptDebugger::get_singleton()) @@ -784,6 +798,97 @@ Variant GDFunction::call(GDInstance *p_instance,const Variant **p_args, int p_ar ip+=4+argc; } continue; + case OPCODE_YIELD: + case OPCODE_YIELD_SIGNAL: { + + int ipofs=1; + if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { + CHECK_SPACE(4); + ipofs+=2; + } else { + CHECK_SPACE(2); + + } + + Ref<GDFunctionState> gdfs = memnew( GDFunctionState ); + gdfs->function=this; + + gdfs->state.stack.resize(alloca_size); + //copy variant stack + for(int i=0;i<_stack_size;i++) { + memnew_placement(&stack[sizeof(Variant)*i],Variant(stack[i])); + } + gdfs->state.stack_size=_stack_size; + gdfs->state.self=self; + gdfs->state.alloca_size=alloca_size; + gdfs->state._class=_class; + gdfs->state.ip=ip+ipofs; + gdfs->state.line=line; + //gdfs->state.result_pos=ip+ipofs-1; + gdfs->state.defarg=defarg; + gdfs->state.instance=p_instance; + gdfs->function=this; + + retvalue=gdfs; + + if (_code_ptr[ip]==OPCODE_YIELD_SIGNAL) { + GET_VARIANT_PTR(argobj,1); + GET_VARIANT_PTR(argname,2); + //do the oneshot connect + + if (argobj->get_type()!=Variant::OBJECT) { + err_text="First argument of yield() not of type object."; + break; + } + if (argname->get_type()!=Variant::STRING) { + err_text="Second argument of yield() not a string (for signal name)."; + break; + } + Object *obj=argobj->operator Object *(); + String signal = argname->operator String(); +#ifdef DEBUG_ENABLED + + if (!obj) { + err_text="First argument of yield() is null."; + break; + } + if (ScriptDebugger::get_singleton()) { + if (!ObjectDB::instance_validate(obj)) { + err_text="First argument of yield() is a previously freed instance."; + break; + } + } + if (signal.length()==0) { + + err_text="Second argument of yield() is an empty string (for signal name)."; + break; + } + +#endif + Error err = obj->connect(signal,gdfs.ptr(),"_signal_callback",varray(gdfs),Object::CONNECT_ONESHOT); + if (err!=OK) { + err_text="Error connecting to signal: "+signal+" during yield()."; + break; + } + + + } + + exit_ok=true; + + } break; + case OPCODE_YIELD_RESUME: { + + CHECK_SPACE(2); + if (!p_state) { + err_text=("Invalid Resume (bug?)"); + break; + } + GET_VARIANT_PTR(result,1); + *result=p_state->result; + ip+=2; + + } continue; case OPCODE_JUMP: { CHECK_SPACE(2); @@ -1168,6 +1273,93 @@ GDFunction::GDFunction() { } +///////////////////// + + +Variant GDFunctionState::_signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error) { + + Variant arg; + r_error.error=Variant::CallError::CALL_OK; + + ERR_FAIL_COND_V(!function,Variant()); + + if (p_argcount==0) { + r_error.error=Variant::CallError::CALL_ERROR_TOO_FEW_ARGUMENTS; + r_error.argument=1; + return Variant(); + } else if (p_argcount==1) { + //noooneee + } else if (p_argcount==2) { + arg=*p_args[0]; + } else { + Array extra_args; + for(int i=0;i<p_argcount-1;i++) { + extra_args.push_back(*p_args[i]); + } + arg=extra_args; + } + + Ref<GDFunctionState> self = *p_args[p_argcount-1]; + + if (self.is_null()) { + r_error.error=Variant::CallError::CALL_ERROR_INVALID_ARGUMENT; + r_error.argument=p_argcount-1; + r_error.expected=Variant::OBJECT; + return Variant(); + } + + state.result=arg; + Variant ret = function->call(NULL,NULL,0,r_error,&state); + function=NULL; //cleaned up; + state.result=Variant(); + return ret; +} + + +bool GDFunctionState::is_valid() const { + + return function!=NULL; +} + +Variant GDFunctionState::resume(const Variant& p_arg) { + + ERR_FAIL_COND_V(!function,Variant()); + + state.result=p_arg; + Variant::CallError err; + Variant ret = function->call(NULL,NULL,0,err,&state); + function=NULL; //cleaned up; + state.result=Variant(); + return ret; +} + + +void GDFunctionState::_bind_methods() { + + ObjectTypeDB::bind_method(_MD("resume:var","arg"),&GDFunctionState::resume,DEFVAL(Variant())); + ObjectTypeDB::bind_method(_MD("is_valid"),&GDFunctionState::is_valid); + ObjectTypeDB::bind_native_method(METHOD_FLAGS_DEFAULT,"_signal_callback",&GDFunctionState::_signal_callback,MethodInfo("_signal_callback")); + +} + +GDFunctionState::GDFunctionState() { + + function=NULL; +} + +GDFunctionState::~GDFunctionState() { + + if (function!=NULL) { + //never called, deinitialize stack + for(int i=0;i<state.stack_size;i++) { + Variant *v=(Variant*)&state.stack[sizeof(Variant)*i]; + v->~Variant(); + } + } +} + +/////////////////////////// + GDNativeClass::GDNativeClass(const StringName& p_name) { name=p_name; @@ -2183,6 +2375,8 @@ void GDScriptLanguage::get_reserved_words(List<String> *p_words) const { "or", "export", "assert", + "yield", + "static", 0}; diff --git a/modules/gdscript/gd_script.h b/modules/gdscript/gd_script.h index 3300ee77c8..62787cf6f8 100644 --- a/modules/gdscript/gd_script.h +++ b/modules/gdscript/gd_script.h @@ -37,6 +37,8 @@ class GDInstance; class GDScript; + + class GDFunction { public: @@ -58,6 +60,9 @@ public: OPCODE_CALL_BUILT_IN, OPCODE_CALL_SELF, OPCODE_CALL_SELF_BASE, + OPCODE_YIELD, + OPCODE_YIELD_SIGNAL, + OPCODE_YIELD_RESUME, OPCODE_JUMP, OPCODE_JUMP_IF, OPCODE_JUMP_IF_NOT, @@ -132,6 +137,20 @@ friend class GDCompiler; public: + struct CallState { + + GDInstance *instance; + Vector<uint8_t> stack; + int stack_size; + Variant self; + uint32_t alloca_size; + GDScript *_class; + int ip; + int line; + int defarg; + Variant result; + + }; _FORCE_INLINE_ bool is_static() const { return _static; } @@ -150,12 +169,30 @@ public: _FORCE_INLINE_ bool is_empty() const { return _code_size==0; } int get_argument_count() const { return _argument_count; } - Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err); + Variant call(GDInstance *p_instance,const Variant **p_args, int p_argcount,Variant::CallError& r_err,CallState *p_state=NULL); GDFunction(); }; +class GDFunctionState : public Reference { + + OBJ_TYPE(GDFunctionState,Reference); +friend class GDFunction; + GDFunction *function; + GDFunction::CallState state; + Variant _signal_callback(const Variant** p_args, int p_argcount, Variant::CallError& r_error); +protected: + static void _bind_methods(); +public: + + bool is_valid() const; + Variant resume(const Variant& p_arg=Variant()); + GDFunctionState(); + ~GDFunctionState(); +}; + + class GDNativeClass : public Reference { OBJ_TYPE(GDNativeClass,Reference); diff --git a/modules/gdscript/gd_tokenizer.cpp b/modules/gdscript/gd_tokenizer.cpp index a92e2f22ea..f73c895d74 100644 --- a/modules/gdscript/gd_tokenizer.cpp +++ b/modules/gdscript/gd_tokenizer.cpp @@ -95,6 +95,7 @@ const char* GDTokenizer::token_names[TK_MAX]={ "var", "preload", "assert", +"yield", "'['", "']'", "'{'", @@ -826,6 +827,7 @@ void GDTokenizerText::_advance() { {TK_PR_VAR,"var"}, {TK_PR_PRELOAD,"preload"}, {TK_PR_ASSERT,"assert"}, + {TK_PR_YIELD,"yield"}, {TK_PR_CONST,"const"}, //controlflow {TK_CF_IF,"if"}, @@ -1006,7 +1008,7 @@ void GDTokenizerText::advance(int p_amount) { ////////////////////////////////////////////////////////////////////////////////////////////////////// -#define BYTECODE_VERSION 1 +#define BYTECODE_VERSION 2 Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) { @@ -1016,8 +1018,8 @@ Error GDTokenizerBuffer::set_code_buffer(const Vector<uint8_t> & p_buffer) { ERR_FAIL_COND_V( p_buffer.size()<24 || p_buffer[0]!='G' || p_buffer[1]!='D' || p_buffer[2]!='S' || p_buffer[3]!='C',ERR_INVALID_DATA); int version = decode_uint32(&buf[4]); - if (version>1) { - ERR_EXPLAIN("Bytecode is too New!"); + if (version>BYTECODE_VERSION) { + ERR_EXPLAIN("Bytecode is too New! Please use a newer engine version."); ERR_FAIL_COND_V(version>BYTECODE_VERSION,ERR_INVALID_DATA); } int identifier_count = decode_uint32(&buf[8]); diff --git a/modules/gdscript/gd_tokenizer.h b/modules/gdscript/gd_tokenizer.h index c517e07b89..1dd538867e 100644 --- a/modules/gdscript/gd_tokenizer.h +++ b/modules/gdscript/gd_tokenizer.h @@ -102,6 +102,7 @@ public: TK_PR_VAR, TK_PR_PRELOAD, TK_PR_ASSERT, + TK_PR_YIELD, TK_BRACKET_OPEN, TK_BRACKET_CLOSE, TK_CURLY_BRACKET_OPEN, diff --git a/modules/gdscript/register_types.cpp b/modules/gdscript/register_types.cpp index abb3d5a946..8b46773502 100644 --- a/modules/gdscript/register_types.cpp +++ b/modules/gdscript/register_types.cpp @@ -130,6 +130,7 @@ void register_gdscript_types() { ResourceLoader::add_resource_format_loader(resource_loader_gd); resource_saver_gd=memnew( ResourceFormatSaverGDScript ); ResourceSaver::add_resource_format_saver(resource_saver_gd); + ObjectTypeDB::register_virtual_type<GDFunctionState>(); #ifdef TOOLS_ENABLED |