diff --git a/rcbasic_build/rcbasic4_changes.ods b/rcbasic_build/rcbasic4_changes.ods index 071fbc4..9cf6179 100644 Binary files a/rcbasic_build/rcbasic4_changes.ods and b/rcbasic_build/rcbasic4_changes.ods differ diff --git a/rcbasic_runtime/rc_gfx.h b/rcbasic_runtime/rc_gfx.h index f03740d..8198b7a 100644 --- a/rcbasic_runtime/rc_gfx.h +++ b/rcbasic_runtime/rc_gfx.h @@ -26,6 +26,7 @@ #include "rc_utf8.h" #include #include "rc_sprite2D.h" +#include "rc_spritelib.h" #include using namespace irr; @@ -2137,18 +2138,6 @@ void rc_readInput_ToggleBackspace(bool flag) } -struct rc_image_obj -{ - irr::video::ITexture* image; - Uint8 alpha = 255; - irr::video::SColor color_mod = irr::video::SColor(255,255,255,255); -}; -irr::core::array rc_image; - -irr::video::E_BLEND_OPERATION rc_blend_mode = irr::video::EBO_ADD; -bool rc_bilinear_filter = false; - - int rc_loadImageEx(std::string img_file, Uint32 color_key = 0, bool use_color_key = true) { rc_image_obj img; @@ -2334,187 +2323,6 @@ int rc_getBlendMode() return (int)rc_blend_mode; } -void draw2DImage(irr::video::IVideoDriver *driver, irr::video::ITexture* texture, irr::core::rect sourceRect, irr::core::position2d position, irr::core::position2d rotationPoint, irr::f32 rotation, irr::core::vector2df scale, bool useAlphaChannel, irr::video::SColor color, irr::core::vector2d screenSize) -{ - if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) - return; - - // Store and clear the projection matrix - irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION); - driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4()); - - // Store and clear the view matrix - irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW); - driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4()); - - // Store and clear the world matrix - irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD); - driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4()); - - // Find horizontal and vertical axes after rotation - irr::f32 c = cos(-rotation*irr::core::DEGTORAD); - irr::f32 s = sin(-rotation*irr::core::DEGTORAD); - irr::core::vector2df horizontalAxis(c,s); - irr::core::vector2df verticalAxis(s,-c); - - // First, we'll find the offset of the center and then where the center would be after rotation - irr::core::vector2df centerOffset(position.X+sourceRect.getWidth()/2.0f*scale.X-rotationPoint.X,position.Y+sourceRect.getHeight()/2.0f*scale.Y-rotationPoint.Y); - irr::core::vector2df center = centerOffset.X*horizontalAxis - centerOffset.Y*verticalAxis; - center.X += rotationPoint.X; - center.Y += rotationPoint.Y; - - // Now find the corners based off the center - irr::core::vector2df cornerOffset(sourceRect.getWidth()*scale.X/2.0f,sourceRect.getHeight()*scale.Y/2.0f); - verticalAxis *= cornerOffset.Y; - horizontalAxis *= cornerOffset.X; - irr::core::vector2df corner[4]; - corner[0] = center + verticalAxis - horizontalAxis; - corner[1] = center + verticalAxis + horizontalAxis; - corner[2] = center - verticalAxis - horizontalAxis; - corner[3] = center - verticalAxis + horizontalAxis; - - // Find the uv coordinates of the sourceRect - irr::core::vector2df textureSize(texture->getSize().Width, texture->getSize().Height); - irr::core::vector2df uvCorner[4]; - uvCorner[0] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.UpperLeftCorner.Y); - uvCorner[1] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.UpperLeftCorner.Y); - uvCorner[2] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.LowerRightCorner.Y); - uvCorner[3] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.LowerRightCorner.Y); - for (irr::s32 i = 0; i < 4; i++) - uvCorner[i] /= textureSize; - - // Vertices for the image - irr::video::S3DVertex vertices[4]; - irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 }; - - // Convert pixels to world coordinates - //irr::core::vector2df screenSize(rc_canvas[rc_active_canvas].dimension.Width, rc_canvas[rc_active_canvas].dimension.Height); - - for (irr::s32 i = 0; i < 4; i++) { - vertices[i].Pos = irr::core::vector3df(((corner[i].X/screenSize.X)-0.5f)*2.0f,((corner[i].Y/screenSize.Y)-0.5f)*-2.0f,1); - vertices[i].TCoords = uvCorner[i]; - vertices[i].Color = color; - } - - // Create the material - // IMPORTANT: For irrlicht 1.8 and above you MUST ADD THIS LINE: - // material.BlendOperation = irr::video::EBO_ADD; - irr::video::SMaterial material; - material.Lighting = false; - material.ZWriteEnable = irr::video::EZW_OFF; - material.ZBuffer = false; - material.BackfaceCulling = false; - material.TextureLayer[0].Texture = texture; - material.TextureLayer[0].BilinearFilter = rc_bilinear_filter; - material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR); - material.BlendOperation = rc_blend_mode; - //material.BlendOperation = irr::video::EBO_ADD; - - if (useAlphaChannel) - material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND; - else - material.MaterialType = irr::video::EMT_SOLID; - - driver->setMaterial(material); - driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2); - - // Restore projection, world, and view matrices - driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat); - driver->setTransform(irr::video::ETS_VIEW,oldViewMat); - driver->setTransform(irr::video::ETS_WORLD,oldWorldMat); -} - -void draw2DImage2(irr::video::IVideoDriver *driver, irr::video::ITexture* texture, irr::core::rect sourceRect, irr::core::rect destRect, irr::core::position2d rotationPoint, irr::f32 rotation, bool useAlphaChannel, irr::video::SColor color, irr::core::vector2d screenSize ) -{ - if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) - return; - - // Store and clear the projection matrix - irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION); - driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4()); - - // Store and clear the view matrix - irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW); - driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4()); - - // Store and clear the world matrix - irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD); - driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4()); - - // Find horizontal and vertical axes after rotation - irr::f32 c = cos(-rotation*irr::core::DEGTORAD); - irr::f32 s = sin(-rotation*irr::core::DEGTORAD); - irr::core::vector2df horizontalAxis(c,s); - irr::core::vector2df verticalAxis(s,-c); - - // First, we'll find the offset of the center and then where the center would be after rotation - irr::core::vector2df centerOffset(destRect.UpperLeftCorner.X+destRect.getWidth()/2.0f-rotationPoint.X,destRect.UpperLeftCorner.Y+destRect.getHeight()/2.0f-rotationPoint.Y); - irr::core::vector2df center = centerOffset.X*horizontalAxis - centerOffset.Y*verticalAxis; - center.X += rotationPoint.X; - center.Y += rotationPoint.Y; - - // Now find the corners based off the center - irr::core::vector2df cornerOffset(destRect.getWidth()/2.0f,destRect.getHeight()/2.0f); - verticalAxis *= cornerOffset.Y; - horizontalAxis *= cornerOffset.X; - irr::core::vector2df corner[4]; - corner[0] = center + verticalAxis - horizontalAxis; - corner[1] = center + verticalAxis + horizontalAxis; - corner[2] = center - verticalAxis - horizontalAxis; - corner[3] = center - verticalAxis + horizontalAxis; - - // Find the uv coordinates of the sourceRect - irr::core::vector2df textureSize(texture->getSize().Width, texture->getSize().Height); - irr::core::vector2df uvCorner[4]; - uvCorner[0] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.UpperLeftCorner.Y); - uvCorner[1] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.UpperLeftCorner.Y); - uvCorner[2] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.LowerRightCorner.Y); - uvCorner[3] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.LowerRightCorner.Y); - for (irr::s32 i = 0; i < 4; i++) - uvCorner[i] /= textureSize; - - // Vertices for the image - irr::video::S3DVertex vertices[4]; - irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 }; - - // Convert pixels to world coordinates - //irr::core::vector2df screenSize(rc_canvas[rc_active_canvas].dimension.Width, rc_canvas[rc_active_canvas].dimension.Height); - - for (irr::s32 i = 0; i < 4; i++) { - vertices[i].Pos = irr::core::vector3df(((corner[i].X/screenSize.X)-0.5f)*2.0f,((corner[i].Y/screenSize.Y)-0.5f)*-2.0f,1); - vertices[i].TCoords = uvCorner[i]; - vertices[i].Color = color; - } - - // Create the material - // IMPORTANT: For irrlicht 1.8 and above you MUST ADD THIS LINE: - // material.BlendOperation = irr::video::EBO_ADD; - irr::video::SMaterial material; - material.Lighting = false; - material.ZWriteEnable = irr::video::EZW_OFF; - material.ZBuffer = false; - material.BackfaceCulling = false; - material.TextureLayer[0].Texture = texture; - material.TextureLayer[0].BilinearFilter = rc_bilinear_filter; //TODO: Add option to switch this on/off - material.BlendOperation = rc_blend_mode; - material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR); - //material.AntiAliasing = irr::video::EAAM_OFF; - - if (useAlphaChannel) - material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND; - else - material.MaterialType = irr::video::EMT_SOLID; - - driver->setMaterial(material); - driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2); - - // Restore projection, world, and view matrices - driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat); - driver->setTransform(irr::video::ETS_VIEW,oldViewMat); - driver->setTransform(irr::video::ETS_WORLD,oldWorldMat); -} - - void rc_drawImage(int img_id, int x, int y) { if(img_id < 0 || img_id >= rc_image.size()) @@ -3168,196 +2976,6 @@ int rc_canvasClip(int x, int y, int w, int h) -//------------------------------SPRITES------------------------------------------------------- -int rc_createSprite(int img_id) -{ - if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) - return -1; - - if(rc_canvas[rc_active_canvas].type != RC_CANVAS_TYPE_SPRITE) - return -1; - - //std::cout << "debug 1" << std::endl; - - int spr_id = -1; - for(int i = 0; i < rc_sprite.size(); i++) - { - if(!rc_sprite[i].active) - { - spr_id = i; - break; - } - } - - if(spr_id < 0) - { - spr_id = rc_sprite.size(); - rc_sprite2D_obj sprite; - rc_sprite.push_back(sprite); - } - - rc_sprite[spr_id].active = true; - rc_sprite[spr_id].image_id = img_id; - - b2BodyDef sprBodyDef; - sprBodyDef.type = b2_staticBody; - sprBodyDef.position.Set(0, 0); - sprBodyDef.angle = 0; - rc_sprite[spr_id].physics.body = rc_canvas[rc_active_canvas].physics2D.world->CreateBody(&sprBodyDef); - rc_sprite[spr_id].physics_enabled = false; - rc_sprite[spr_id].visible = true; - rc_sprite[spr_id].scale.set(1.0, 1.0); - rc_sprite[spr_id].position.set(0, 0); - rc_sprite[spr_id].alpha = 255; - rc_sprite[spr_id].rotation = 0; - rc_sprite[spr_id].z = 0; - rc_sprite[spr_id].color_mod.set(255,255,255,255); - rc_sprite[spr_id].parent_canvas = rc_active_canvas; - - rc_canvas[rc_active_canvas].sprite.push_back(&rc_sprite[spr_id]); - - return spr_id; -} - -void rc_deleteSprite(int spr_id) -{ - if(spr_id < 0 || spr_id >= rc_sprite.size()) - return; - - if(rc_sprite[spr_id].physics.body) - { - if(rc_sprite[spr_id].parent_canvas >= 0 && rc_sprite[spr_id].parent_canvas < rc_canvas.size()) - { - if(rc_canvas[rc_sprite[spr_id].parent_canvas].physics2D.world) - rc_canvas[rc_sprite[spr_id].parent_canvas].physics2D.world->DestroyBody(rc_sprite[spr_id].physics.body); - } - rc_sprite[spr_id].physics.body = NULL; - } - - rc_sprite[spr_id].active = false; - rc_sprite[spr_id].parent_canvas = -1; - - for(int i = 0; i < rc_canvas[rc_active_canvas].sprite.size(); i++) - { - rc_sprite2D_obj* canvas_sprite = rc_canvas[rc_active_canvas].sprite[i]; - rc_sprite2D_obj* global_sprite = &rc_sprite[spr_id]; - if(canvas_sprite == global_sprite) - { - rc_canvas[rc_active_canvas].sprite.erase(i); - break; - } - } -} - -void rc_setSpriteType(int spr_id, int body_type) -{ - if(spr_id < 0 || spr_id >= rc_sprite.size()) - return; - - if(!rc_sprite[spr_id].active) - return; - - rc_sprite[spr_id].physics.body->SetType((b2BodyType) body_type); -} - -void rc_setSpritePosition(int spr_id, double x, double y) -{ - if(spr_id < 0 || spr_id >= rc_sprite.size()) - return; - - if(!rc_sprite[spr_id].active) - return; - - float current_angle = rc_sprite[spr_id].physics.body->GetAngle(); - rc_sprite[spr_id].physics.body->SetTransform(b2Vec2(x, y), current_angle); -} - -//This function is called on each canvas on update -void drawSprites(int canvas_id) -{ - float step = rc_canvas[canvas_id].physics2D.timeStep; - int32 velocityIterations = rc_canvas[canvas_id].physics2D.velocityIterations; - int32 positionIterations = rc_canvas[canvas_id].physics2D.positionIterations; - - if(rc_canvas[canvas_id].physics2D.enabled) - rc_canvas[canvas_id].physics2D.world->Step(step, velocityIterations, positionIterations); - - //Setting the render target to the current canvas. NOTE: I might change this target to a separate sprite layer later. - VideoDriver->setRenderTarget(rc_canvas[canvas_id].texture, false, false); - - - irr::core::dimension2d src_size; - irr::core::rect sourceRect; - - irr::core::position2d position; - - irr::core::position2d rotationPoint; - - irr::f32 rotation = 0; - irr::core::vector2df scale(1.0, 1.0); - bool useAlphaChannel = true; - irr::video::SColor color; - - //irr::core::rect dest( irr::core::vector2d(x, y), irr::core::dimension2d(src_w, src_h));; - - irr::core::vector2df screenSize(rc_canvas[canvas_id].dimension.Width, rc_canvas[canvas_id].dimension.Height); - - int x = 0; - int y = 0; - - b2Vec2 physics_pos; - - irr::f32 RAD_TO_DEG = 180.0/3.141592653589793238463; - - for(int spr_index = 0; spr_index < rc_canvas[canvas_id].sprite.size(); spr_index++) - { - rc_sprite2D_obj* sprite = rc_canvas[canvas_id].sprite[spr_index]; - if(!sprite->visible) - continue; - - int img_id = sprite->image_id; - if(img_id < 0 || img_id >= rc_image.size()) - continue; - - src_size = rc_image[img_id].image->getSize(); - sourceRect = irr::core::rect( irr::core::vector2d(0, 0), src_size); - - physics_pos = sprite->physics.body->GetPosition(); - x = (int)physics_pos.x; - y = (int)physics_pos.y; - position.set(x, y); - - - rotationPoint.set(x + (src_size.Width/2), y + (src_size.Height/2)); - rotation = -1 * (sprite->physics.body->GetAngle() * RAD_TO_DEG); //convert Box2D radians to degrees - - scale.set(sprite->scale.X, sprite->scale.Y); - - color.set(sprite->alpha, - sprite->color_mod.getRed(), - sprite->color_mod.getGreen(), - sprite->color_mod.getBlue()); - - draw2DImage(VideoDriver, rc_image[img_id].image, sourceRect, position, rotationPoint, rotation, scale, useAlphaChannel, color, screenSize); - } - //Must set back to canvas 0 (the backbuffer) before returning - - VideoDriver->setRenderTarget(rc_canvas[0].texture, false, false); -} - -//NOTE TO TBIRD -// 1. Each sprite has a Box2D body. You can look in "rc_sprite2D.h" to see how a sprite is structured. -// 2. A box2D world is setup for each canvas. So a sprite will be attached to the canvas thats active when its created. When that canvas is destroyed, so is the sprite. -// 3. By default, I have the sprite.physics_enabled attribute set to false. I feel like it makes sense to have a user intentionally enable physics since a user may not want physics for every sprite. -// 4. The sprite.visible attribute only determines whether to draw the sprite. The physics simulation will still happen each frame unless physics are disabled. -// 5. Don't change the value of sprite.active. Its used to check whether a sprite exists or not. I have an array of sprites in rc_sprite2D.h and if the active attribute is set to false, I reuse that slot to create a new sprite. If there is no inactive sprites in the array then I add a new sprite index to the array. -// 6. The time step, velocity Iterations, and position iterations are part of the canvas.physics2D attribute. You will need to make functions to allow the user to change those. -// 7. If you want to modify how sprites are rendered then you can just change the drawSprites() function above these notes. - -//-----------------------------END OF SPRITE STUFF------------------------------------------------------------------------------ - - - bool rc_update() { diff --git a/rcbasic_runtime/rc_gfx_core.h b/rcbasic_runtime/rc_gfx_core.h index 2f4f715..236b93e 100644 --- a/rcbasic_runtime/rc_gfx_core.h +++ b/rcbasic_runtime/rc_gfx_core.h @@ -618,4 +618,199 @@ void printIrrMatrix(irr::core::matrix4 m) std::cout << "[ " << m[i*4] << ", " << m[i*4+1] << ", " << m[i*4+2] << ", " << m[i*4+3] << " ]" << std::endl; } + +struct rc_image_obj +{ + irr::video::ITexture* image; + Uint8 alpha = 255; + irr::video::SColor color_mod = irr::video::SColor(255,255,255,255); +}; +irr::core::array rc_image; + +irr::video::E_BLEND_OPERATION rc_blend_mode = irr::video::EBO_ADD; +bool rc_bilinear_filter = false; + + +void draw2DImage(irr::video::IVideoDriver *driver, irr::video::ITexture* texture, irr::core::rect sourceRect, irr::core::position2d position, irr::core::position2d rotationPoint, irr::f32 rotation, irr::core::vector2df scale, bool useAlphaChannel, irr::video::SColor color, irr::core::vector2d screenSize) +{ + if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) + return; + + // Store and clear the projection matrix + irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION); + driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4()); + + // Store and clear the view matrix + irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW); + driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4()); + + // Store and clear the world matrix + irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD); + driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4()); + + // Find horizontal and vertical axes after rotation + irr::f32 c = cos(-rotation*irr::core::DEGTORAD); + irr::f32 s = sin(-rotation*irr::core::DEGTORAD); + irr::core::vector2df horizontalAxis(c,s); + irr::core::vector2df verticalAxis(s,-c); + + // First, we'll find the offset of the center and then where the center would be after rotation + irr::core::vector2df centerOffset(position.X+sourceRect.getWidth()/2.0f*scale.X-rotationPoint.X,position.Y+sourceRect.getHeight()/2.0f*scale.Y-rotationPoint.Y); + irr::core::vector2df center = centerOffset.X*horizontalAxis - centerOffset.Y*verticalAxis; + center.X += rotationPoint.X; + center.Y += rotationPoint.Y; + + // Now find the corners based off the center + irr::core::vector2df cornerOffset(sourceRect.getWidth()*scale.X/2.0f,sourceRect.getHeight()*scale.Y/2.0f); + verticalAxis *= cornerOffset.Y; + horizontalAxis *= cornerOffset.X; + irr::core::vector2df corner[4]; + corner[0] = center + verticalAxis - horizontalAxis; + corner[1] = center + verticalAxis + horizontalAxis; + corner[2] = center - verticalAxis - horizontalAxis; + corner[3] = center - verticalAxis + horizontalAxis; + + // Find the uv coordinates of the sourceRect + irr::core::vector2df textureSize(texture->getSize().Width, texture->getSize().Height); + irr::core::vector2df uvCorner[4]; + uvCorner[0] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.UpperLeftCorner.Y); + uvCorner[1] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.UpperLeftCorner.Y); + uvCorner[2] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.LowerRightCorner.Y); + uvCorner[3] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.LowerRightCorner.Y); + for (irr::s32 i = 0; i < 4; i++) + uvCorner[i] /= textureSize; + + // Vertices for the image + irr::video::S3DVertex vertices[4]; + irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 }; + + // Convert pixels to world coordinates + //irr::core::vector2df screenSize(rc_canvas[rc_active_canvas].dimension.Width, rc_canvas[rc_active_canvas].dimension.Height); + + for (irr::s32 i = 0; i < 4; i++) { + vertices[i].Pos = irr::core::vector3df(((corner[i].X/screenSize.X)-0.5f)*2.0f,((corner[i].Y/screenSize.Y)-0.5f)*-2.0f,1); + vertices[i].TCoords = uvCorner[i]; + vertices[i].Color = color; + } + + // Create the material + // IMPORTANT: For irrlicht 1.8 and above you MUST ADD THIS LINE: + // material.BlendOperation = irr::video::EBO_ADD; + irr::video::SMaterial material; + material.Lighting = false; + material.ZWriteEnable = irr::video::EZW_OFF; + material.ZBuffer = false; + material.BackfaceCulling = false; + material.TextureLayer[0].Texture = texture; + material.TextureLayer[0].BilinearFilter = rc_bilinear_filter; + material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR); + material.BlendOperation = rc_blend_mode; + //material.BlendOperation = irr::video::EBO_ADD; + + if (useAlphaChannel) + material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND; + else + material.MaterialType = irr::video::EMT_SOLID; + + driver->setMaterial(material); + driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2); + + // Restore projection, world, and view matrices + driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat); + driver->setTransform(irr::video::ETS_VIEW,oldViewMat); + driver->setTransform(irr::video::ETS_WORLD,oldWorldMat); +} + +void draw2DImage2(irr::video::IVideoDriver *driver, irr::video::ITexture* texture, irr::core::rect sourceRect, irr::core::rect destRect, irr::core::position2d rotationPoint, irr::f32 rotation, bool useAlphaChannel, irr::video::SColor color, irr::core::vector2d screenSize ) +{ + if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) + return; + + // Store and clear the projection matrix + irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION); + driver->setTransform(irr::video::ETS_PROJECTION,irr::core::matrix4()); + + // Store and clear the view matrix + irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW); + driver->setTransform(irr::video::ETS_VIEW,irr::core::matrix4()); + + // Store and clear the world matrix + irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD); + driver->setTransform(irr::video::ETS_WORLD,irr::core::matrix4()); + + // Find horizontal and vertical axes after rotation + irr::f32 c = cos(-rotation*irr::core::DEGTORAD); + irr::f32 s = sin(-rotation*irr::core::DEGTORAD); + irr::core::vector2df horizontalAxis(c,s); + irr::core::vector2df verticalAxis(s,-c); + + // First, we'll find the offset of the center and then where the center would be after rotation + irr::core::vector2df centerOffset(destRect.UpperLeftCorner.X+destRect.getWidth()/2.0f-rotationPoint.X,destRect.UpperLeftCorner.Y+destRect.getHeight()/2.0f-rotationPoint.Y); + irr::core::vector2df center = centerOffset.X*horizontalAxis - centerOffset.Y*verticalAxis; + center.X += rotationPoint.X; + center.Y += rotationPoint.Y; + + // Now find the corners based off the center + irr::core::vector2df cornerOffset(destRect.getWidth()/2.0f,destRect.getHeight()/2.0f); + verticalAxis *= cornerOffset.Y; + horizontalAxis *= cornerOffset.X; + irr::core::vector2df corner[4]; + corner[0] = center + verticalAxis - horizontalAxis; + corner[1] = center + verticalAxis + horizontalAxis; + corner[2] = center - verticalAxis - horizontalAxis; + corner[3] = center - verticalAxis + horizontalAxis; + + // Find the uv coordinates of the sourceRect + irr::core::vector2df textureSize(texture->getSize().Width, texture->getSize().Height); + irr::core::vector2df uvCorner[4]; + uvCorner[0] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.UpperLeftCorner.Y); + uvCorner[1] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.UpperLeftCorner.Y); + uvCorner[2] = irr::core::vector2df(sourceRect.UpperLeftCorner.X,sourceRect.LowerRightCorner.Y); + uvCorner[3] = irr::core::vector2df(sourceRect.LowerRightCorner.X,sourceRect.LowerRightCorner.Y); + for (irr::s32 i = 0; i < 4; i++) + uvCorner[i] /= textureSize; + + // Vertices for the image + irr::video::S3DVertex vertices[4]; + irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 }; + + // Convert pixels to world coordinates + //irr::core::vector2df screenSize(rc_canvas[rc_active_canvas].dimension.Width, rc_canvas[rc_active_canvas].dimension.Height); + + for (irr::s32 i = 0; i < 4; i++) { + vertices[i].Pos = irr::core::vector3df(((corner[i].X/screenSize.X)-0.5f)*2.0f,((corner[i].Y/screenSize.Y)-0.5f)*-2.0f,1); + vertices[i].TCoords = uvCorner[i]; + vertices[i].Color = color; + } + + // Create the material + // IMPORTANT: For irrlicht 1.8 and above you MUST ADD THIS LINE: + // material.BlendOperation = irr::video::EBO_ADD; + irr::video::SMaterial material; + material.Lighting = false; + material.ZWriteEnable = irr::video::EZW_OFF; + material.ZBuffer = false; + material.BackfaceCulling = false; + material.TextureLayer[0].Texture = texture; + material.TextureLayer[0].BilinearFilter = rc_bilinear_filter; //TODO: Add option to switch this on/off + material.BlendOperation = rc_blend_mode; + material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR); + //material.AntiAliasing = irr::video::EAAM_OFF; + + if (useAlphaChannel) + material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND; + else + material.MaterialType = irr::video::EMT_SOLID; + + driver->setMaterial(material); + driver->drawIndexedTriangleList(&vertices[0],4,&indices[0],2); + + // Restore projection, world, and view matrices + driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat); + driver->setTransform(irr::video::ETS_VIEW,oldViewMat); + driver->setTransform(irr::video::ETS_WORLD,oldWorldMat); +} + + + #endif // RC_GFX_CORE_H_INCLUDED diff --git a/rcbasic_runtime/rc_spritelib.h b/rcbasic_runtime/rc_spritelib.h new file mode 100644 index 0000000..f8c3b22 --- /dev/null +++ b/rcbasic_runtime/rc_spritelib.h @@ -0,0 +1,195 @@ +#ifndef RC_SPRITELIB_H_INCLUDED +#define RC_SPRITELIB_H_INCLUDED + +#include "rc_sprite2D.h" +#include "rc_gfx_core.h" + +//------------------------------SPRITES------------------------------------------------------- +int rc_createSprite(int img_id) +{ + if(rc_active_canvas < 0 || rc_active_canvas >= rc_canvas.size()) + return -1; + + if(rc_canvas[rc_active_canvas].type != RC_CANVAS_TYPE_SPRITE) + return -1; + + //std::cout << "debug 1" << std::endl; + + int spr_id = -1; + for(int i = 0; i < rc_sprite.size(); i++) + { + if(!rc_sprite[i].active) + { + spr_id = i; + break; + } + } + + if(spr_id < 0) + { + spr_id = rc_sprite.size(); + rc_sprite2D_obj sprite; + rc_sprite.push_back(sprite); + } + + rc_sprite[spr_id].active = true; + rc_sprite[spr_id].image_id = img_id; + + b2BodyDef sprBodyDef; + sprBodyDef.type = b2_staticBody; + sprBodyDef.position.Set(0, 0); + sprBodyDef.angle = 0; + rc_sprite[spr_id].physics.body = rc_canvas[rc_active_canvas].physics2D.world->CreateBody(&sprBodyDef); + rc_sprite[spr_id].physics_enabled = false; + rc_sprite[spr_id].visible = true; + rc_sprite[spr_id].scale.set(1.0, 1.0); + rc_sprite[spr_id].position.set(0, 0); + rc_sprite[spr_id].alpha = 255; + rc_sprite[spr_id].rotation = 0; + rc_sprite[spr_id].z = 0; + rc_sprite[spr_id].color_mod.set(255,255,255,255); + rc_sprite[spr_id].parent_canvas = rc_active_canvas; + + rc_canvas[rc_active_canvas].sprite.push_back(&rc_sprite[spr_id]); + + return spr_id; +} + +void rc_deleteSprite(int spr_id) +{ + if(spr_id < 0 || spr_id >= rc_sprite.size()) + return; + + if(rc_sprite[spr_id].physics.body) + { + if(rc_sprite[spr_id].parent_canvas >= 0 && rc_sprite[spr_id].parent_canvas < rc_canvas.size()) + { + if(rc_canvas[rc_sprite[spr_id].parent_canvas].physics2D.world) + rc_canvas[rc_sprite[spr_id].parent_canvas].physics2D.world->DestroyBody(rc_sprite[spr_id].physics.body); + } + rc_sprite[spr_id].physics.body = NULL; + } + + rc_sprite[spr_id].active = false; + rc_sprite[spr_id].parent_canvas = -1; + + for(int i = 0; i < rc_canvas[rc_active_canvas].sprite.size(); i++) + { + rc_sprite2D_obj* canvas_sprite = rc_canvas[rc_active_canvas].sprite[i]; + rc_sprite2D_obj* global_sprite = &rc_sprite[spr_id]; + if(canvas_sprite == global_sprite) + { + rc_canvas[rc_active_canvas].sprite.erase(i); + break; + } + } +} + +void rc_setSpriteType(int spr_id, int body_type) +{ + if(spr_id < 0 || spr_id >= rc_sprite.size()) + return; + + if(!rc_sprite[spr_id].active) + return; + + rc_sprite[spr_id].physics.body->SetType((b2BodyType) body_type); +} + +void rc_setSpritePosition(int spr_id, double x, double y) +{ + if(spr_id < 0 || spr_id >= rc_sprite.size()) + return; + + if(!rc_sprite[spr_id].active) + return; + + float current_angle = rc_sprite[spr_id].physics.body->GetAngle(); + rc_sprite[spr_id].physics.body->SetTransform(b2Vec2(x, y), current_angle); +} + +//This function is called on each canvas on update +void drawSprites(int canvas_id) +{ + float step = rc_canvas[canvas_id].physics2D.timeStep; + int32 velocityIterations = rc_canvas[canvas_id].physics2D.velocityIterations; + int32 positionIterations = rc_canvas[canvas_id].physics2D.positionIterations; + + if(rc_canvas[canvas_id].physics2D.enabled) + rc_canvas[canvas_id].physics2D.world->Step(step, velocityIterations, positionIterations); + + //Setting the render target to the current canvas. NOTE: I might change this target to a separate sprite layer later. + VideoDriver->setRenderTarget(rc_canvas[canvas_id].texture, false, false); + + + irr::core::dimension2d src_size; + irr::core::rect sourceRect; + + irr::core::position2d position; + + irr::core::position2d rotationPoint; + + irr::f32 rotation = 0; + irr::core::vector2df scale(1.0, 1.0); + bool useAlphaChannel = true; + irr::video::SColor color; + + //irr::core::rect dest( irr::core::vector2d(x, y), irr::core::dimension2d(src_w, src_h));; + + irr::core::vector2df screenSize(rc_canvas[canvas_id].dimension.Width, rc_canvas[canvas_id].dimension.Height); + + int x = 0; + int y = 0; + + b2Vec2 physics_pos; + + irr::f32 RAD_TO_DEG = 180.0/3.141592653589793238463; + + for(int spr_index = 0; spr_index < rc_canvas[canvas_id].sprite.size(); spr_index++) + { + rc_sprite2D_obj* sprite = rc_canvas[canvas_id].sprite[spr_index]; + if(!sprite->visible) + continue; + + int img_id = sprite->image_id; + if(img_id < 0 || img_id >= rc_image.size()) + continue; + + src_size = rc_image[img_id].image->getSize(); + sourceRect = irr::core::rect( irr::core::vector2d(0, 0), src_size); + + physics_pos = sprite->physics.body->GetPosition(); + x = (int)physics_pos.x; + y = (int)physics_pos.y; + position.set(x, y); + + + rotationPoint.set(x + (src_size.Width/2), y + (src_size.Height/2)); + rotation = -1 * (sprite->physics.body->GetAngle() * RAD_TO_DEG); //convert Box2D radians to degrees + + scale.set(sprite->scale.X, sprite->scale.Y); + + color.set(sprite->alpha, + sprite->color_mod.getRed(), + sprite->color_mod.getGreen(), + sprite->color_mod.getBlue()); + + draw2DImage(VideoDriver, rc_image[img_id].image, sourceRect, position, rotationPoint, rotation, scale, useAlphaChannel, color, screenSize); + } + //Must set back to canvas 0 (the backbuffer) before returning + + VideoDriver->setRenderTarget(rc_canvas[0].texture, false, false); +} + +//NOTE TO TBIRD +// 1. Each sprite has a Box2D body. You can look in "rc_sprite2D.h" to see how a sprite is structured. +// 2. A box2D world is setup for each canvas. So a sprite will be attached to the canvas thats active when its created. When that canvas is destroyed, so is the sprite. +// 3. By default, I have the sprite.physics_enabled attribute set to false. I feel like it makes sense to have a user intentionally enable physics since a user may not want physics for every sprite. +// 4. The sprite.visible attribute only determines whether to draw the sprite. The physics simulation will still happen each frame unless physics are disabled. +// 5. Don't change the value of sprite.active. Its used to check whether a sprite exists or not. I have an array of sprites in rc_sprite2D.h and if the active attribute is set to false, I reuse that slot to create a new sprite. If there is no inactive sprites in the array then I add a new sprite index to the array. +// 6. The time step, velocity Iterations, and position iterations are part of the canvas.physics2D attribute. You will need to make functions to allow the user to change those. +// 7. If you want to modify how sprites are rendered then you can just change the drawSprites() function above these notes. + +//-----------------------------END OF SPRITE STUFF------------------------------------------------------------------------------ + +#endif // RC_SPRITELIB_H_INCLUDED