Beispiel #1
0
    void operator()( const blocked_range2d<size_t>& r ) const
    {
        ContextPtr context = new Context( *m_parentContext );
        const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) );
        V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min );
        V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max );
        size_t imageStride = m_dataWindow.size().x + 1;

        for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize )
        {
            for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize )
            {
                for( vector<string>::const_iterator it = m_channelNames.begin(), eIt = m_channelNames.end(); it != eIt; it++ )
                {
                    context->set( ImagePlug::channelNameContextName, *it );
                    context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) );
                    Context::Scope scope( context.get() );
                    Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) );
                    Box2i b = boxIntersection( tileBound, operationWindow );

                    ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue();

                    for( int y = b.min.y; y<=b.max.y; y++ )
                    {
                        const float *tilePtr = &(tileData->readable()[0]) + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX);
                        float *channelPtr = m_imageChannelData[it-m_channelNames.begin()] + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x);
                        for( int x = b.min.x; x <= b.max.x; x++ )
                        {
                            *channelPtr++ = *tilePtr++;
                        }
                    }
                }
            }
        }
    }
Beispiel #2
0
Box2i scale( const Box2i& b, float sx, float sy, int xcenter, int ycenter)
{
    Box2i box = offsetBy( b, V2i( -xcenter, -ycenter));
    box = scale( box, sx, sy);
    box = offsetBy( box, V2i( xcenter, ycenter));
    return box;
}
Beispiel #3
0
V2i Application::normaliseTouch(SDL_TouchID device_id, V2i touch)
{
  // There's only 1 touch device: memorise itss resolution upon initial call
  static V2i device_resolution = V2i(SDL_GetTouch(device_id)->xres,
                                     SDL_GetTouch(device_id)->yres);

 static V2i default_window_size = V2i(WINDOW_DEFAULT_W, WINDOW_DEFAULT_H);

  // Normalise the touch position
  return touch * default_window_size / device_resolution;
}
Beispiel #4
0
Game::Game() ://mapGraph(NULL), mapPath(NULL), selected(NULL), selectedNode(NULL), delauny(NULL), 
drawDebug(true), objectMap(AABBf(V2f(-50,-50),V2f(50,50)), V2i(7,7)) {
	gameLoopRunning = true;
	paused = false;

	V2f min(-10, -10), max(10, 10);
}
Beispiel #5
0
BlackDogState::BlackDogState(bool _tutorial) :
GameState(),
parallax(),
obstacle(BASE_DIFFICULTY),
player_progress(STARTING_PROGRESS),
difficulty(BASE_DIFFICULTY),
victory(false),
tutorial(_tutorial ? TUT_FLAPPING : 0)
{
  // add the player character
  addThing(new AngelThing(V2i(player_progress, WINDOW_DEFAULT_H/2)));
  // add the dog
  addThing(new DogThing(V2i(0, WINDOW_DEFAULT_H/2), BASE_DIFFICULTY));
  // add the progress-mesure pixie
  addThing(new PixieThing(V2i(WINDOW_DEFAULT_W/2, WINDOW_DEFAULT_H/2)));
}
Beispiel #6
0
void Game::init() {
	objectMap.addLayer(Obstacle::STATIC, AABBf(V2f(-60, -60), V2f(60, 60)), V2i(16, 16));
	addObstacle(new CircleObject(Circf(V2f(-4, -4), 2), Obstacle::STATIC));
	addObstacle(new AABBObject(AABBf(V2f(-4, 1), V2f(-2, 5)), Obstacle::STATIC));
	addObstacle(new BoxObject(Boxf(V2f(4, 0), V2f(2, 5), V2f(1)), Obstacle::STATIC));
	addObstacle(new ConeObject(Conef(V2f(0, 1), 1.5f, (float)V_2PI * 1 / 8, (float)V_2PI * 3 / 8), Obstacle::STATIC)); // make the radius of the cone correct based on the calculated center
	V2f trapezoid[] = { V2f(-.5f, .5f), V2f(.5f, 1.2f), V2f(.5f, -1.5f), V2f(-.5f, -.8f) };
	int trapezoidCount = sizeof(trapezoid) / sizeof(trapezoid[0]);
	addObstacle(new PolygonObject(Polygon2f(V2f(5, -7), V2f((float)V_2PI*0.0f/6), trapezoid, trapezoidCount), Obstacle::STATIC)); // TODO raycast does not always hit the shape correctly from all sides
	V2f a(-2, -2), b(5, -4);
	addObstacle(new LineObject(Linef(&a, &b), Obstacle::STATIC));
	addObstacle(new PointObject(V2f(7, 7), Obstacle::STATIC));

	delaunyBoundary = new CircleObject(Circf(V2f::ZERO(), 100), Obstacle::EVERYTHING);
	delauny = new DelaunySet(delaunyBoundary);
	//delauny->addNode(V2f(3, 4));
	//delauny->addNode(V2f(-3, 4));
	//delauny->addNode(V2f(-3, -4));
	//delauny->addNode(V2f(0, -1));
//	for (int i = 0; i < 10; ++i) {
//		delauny->addNode(V2f::randomUnitVector() * Random::PRNGf() * 100);
//	}
	delauny->makeRandom(6);
	delauny->calculateAllTriangles();
}
Beispiel #7
0
V2i Pixel_Data::proxy_scale(const V2i & in, Pixel_Data_Info::PROXY proxy)
{
    const int scale = proxy_scale(proxy);

    return V2i(
        Math::ceil(in.x / static_cast<double>(scale)),
        Math::ceil(in.y / static_cast<double>(scale)));
}
Beispiel #8
0
//----------------------------------------
CState::CState(CWorld const* _pWorld)
 : m_pWorld(_pWorld),m_vGrid(_pWorld->GetGridSize().x() * _pWorld->GetGridSize().y())
{
  for (unsigned uGridPos = 0; uGridPos < m_vGrid.size(); ++uGridPos)
  {
	  m_vGrid[uGridPos] = uGridPos;
  }

  m_v2EmptySpacePos = V2i(0, 0);
}
Beispiel #9
0
void Widget::draw()
{
    Gl_Widget::draw();

    //DJV_DEBUG("Widget::draw");

    const Box2i & geom = this->geom();

    //DJV_DEBUG_PRINT("geom = " << geom);

    Gl_Util::ortho(V2i(geom.w, geom.h));
    DJV_DEBUG_GL(glViewport(0, 0, geom.w, geom.h));

    //DJV_DEBUG_GL(glClearColor(0.0, 0.0, 1.0, 0.0));
    DJV_DEBUG_GL(glClear(GL_COLOR_BUFFER_BIT));

    if (_image)
    {
        const Pixel_Data_Info & info = _image->info();

        //DJV_DEBUG_PRINT("info = " << info);

        try
        {
            _image->bind();
        }
        catch (Error error)
        {
            DJV_APP->error(error);
        }

        DJV_DEBUG_GL(glPushMatrix());

        double u [] = { 0, 0 }, v [] = { 0, 0 };

        u[! info.mirror.x] = info.size.x / static_cast<double>(info.size.x);
        v[! info.mirror.y] = info.size.y / static_cast<double>(info.size.y);

        const V2f uv [] =
        {
            V2f(u[0], v[0]),
            V2f(u[0], v[1]),
            V2f(u[1], v[1]),
            V2f(u[1], v[0])
        };

        DJV_DEBUG_GL(glBegin(GL_QUADS));

        Gl_Util::draw_box(info.size, uv);

        DJV_DEBUG_GL(glEnd());

        DJV_DEBUG_GL(glPopMatrix());
    }
}
Beispiel #10
0
Box2i
dataWindowForTile (const TileDescription &tileDesc,
		   int minX, int maxX,
		   int minY, int maxY,
		   int dx, int dy,
		   int lx, int ly)
{
    V2i tileMin = V2i (minX + dx * tileDesc.xSize,
		       minY + dy * tileDesc.ySize);

    V2i tileMax = tileMin + V2i (tileDesc.xSize - 1, tileDesc.ySize - 1);

    V2i levelMax = dataWindowForLevel
		       (tileDesc, minX, maxX, minY, maxY, lx, ly).max;

    tileMax = V2i (std::min (tileMax[0], levelMax[0]),
		   std::min (tileMax[1], levelMax[1]));

    return Box2i (tileMin, tileMax);
}
Beispiel #11
0
		void operator()( const blocked_range3d<size_t>& r ) const
		{
			ContextPtr context = new Context( *m_parentContext, Context::Borrowed );
			Context::Scope scope( context.get() );
			
			const Box2i operationWindow( V2i( r.rows().begin()+m_dataWindow.min.x, r.cols().begin()+m_dataWindow.min.y ), V2i( r.rows().end()+m_dataWindow.min.x-1, r.cols().end()+m_dataWindow.min.y-1 ) );
			V2i minTileOrigin = ImagePlug::tileOrigin( operationWindow.min );
			V2i maxTileOrigin = ImagePlug::tileOrigin( operationWindow.max );
			size_t imageStride = m_dataWindow.size().x + 1;
			
			for( size_t channelIndex = r.pages().begin(); channelIndex < r.pages().end(); ++channelIndex )
			{
				context->set( ImagePlug::channelNameContextName, m_channelNames[channelIndex] );
				float *channelBegin = m_imageChannelData[channelIndex];
				
				for( int tileOriginY = minTileOrigin.y; tileOriginY <= maxTileOrigin.y; tileOriginY += m_tileSize )
				{
					for( int tileOriginX = minTileOrigin.x; tileOriginX <= maxTileOrigin.x; tileOriginX += m_tileSize )
					{
						context->set( ImagePlug::tileOriginContextName, V2i( tileOriginX, tileOriginY ) );
						
						Box2i tileBound( V2i( tileOriginX, tileOriginY ), V2i( tileOriginX + m_tileSize - 1, tileOriginY + m_tileSize - 1 ) );
						Box2i b = boxIntersection( tileBound, operationWindow );
						size_t tileStrideSize = sizeof(float) * ( b.size().x + 1 );
						
						ConstFloatVectorDataPtr tileData = m_channelDataPlug->getValue();
						const float *tileDataBegin = &(tileData->readable()[0]);

						for( int y = b.min.y; y<=b.max.y; y++ )
						{
							const float *tilePtr = tileDataBegin + (y - tileOriginY) * m_tileSize + (b.min.x - tileOriginX);
							float *channelPtr = channelBegin + ( m_dataWindow.size().y - ( y - m_dataWindow.min.y ) ) * imageStride + (b.min.x - m_dataWindow.min.x);
							std::memcpy( channelPtr, tilePtr, tileStrideSize );
						}
					}
				}
			}
		}
Beispiel #12
0
Window::Window(const String & in) :
    _play(false),
    _frame(0),
    _idle_init(false),
    _idle_frame(0)
{
    DJV_DEBUG(String_Format("Window::Window(%%)").arg(int64_t(this)));

    // Initialize.

    try
    {
        _movie.init(in);
    }
    catch (Error in)
    {
        DJV_AUDIO_APP->error(in);
    }

    _widget = new Image_View;
    _play_widget = new Tool_Button("playback_forward");
    _play_widget->type(Tool_Button::TOGGLE);
    _slider = new Int_Slider;

    Layout_V * layout = new Layout_V(this);
    layout->margin(0);
    layout->spacing(0);
    layout->add(_widget);
    layout->stretch(_widget);
    Layout_H * layout_h = new Layout_H(layout);
    layout_h->margin(0);
    layout_h->add(_play_widget);
    layout_h->add(_slider);
    layout_h->stretch(_slider);

    _slider->range(0, static_cast<int>(_movie.info_time().list.size()) - 1);

    dirty();
    size(_movie.info().size + V2i(0, size_min().y));

    frame_update();
    play_update();

    _play_widget->signal.set(this, play_callback);
    _slider->signal.set(this, frame_callback);
    close_signal.set(this, close_callback);

    show();
}
Beispiel #13
0
Magnify_Dialog::Magnify_Dialog() :
    Dialog("Magnify"),
    _view        (0),
    _zoom        (ZOOM_200),
    _widget      (0),
    _zoom_widget (0),
    _close_widget(0)
{
    // Create widgets.

    _widget = new Magnify_Widget;

    _zoom_widget = new Choice_Widget(label_zoom());

    _close_widget = new Push_Button(label_close);

    // Layout.

    Vertical_Layout * layout = new Vertical_Layout(this);

    layout->add(_widget);
    layout->stretch(_widget);

    Horizontal_Layout * layout_h = new Horizontal_Layout(layout);
    layout_h->margin(0);
    layout_h->add(_zoom_widget);
    layout_h->add_spacer(-1, true);
    layout_h->add(_close_widget);
    layout_h->add_spacer(Layout::window_handle_size());

    // Preferences.

    Prefs prefs(Prefs::prefs(), "magnify_dialog");
    Prefs::get_(&prefs, "zoom", &_zoom);

    // Initialize.

    widget_update();

    size(Vector_Util::max(size_hint(), V2i(300, 300)));

    // Callbacks.

    _zoom_widget->signal.set(this, zoom_callback);

    _close_widget->signal.set(this, close_callback);
}
Beispiel #14
0
void Text_Display::dirty()
{
    Abstract_Text_Widget::dirty();

    _widget->textfont(text_font());
    _widget->textsize(Style::global()->font_size());
    _widget->textcolor(text_color());

    size_hint(
        V2i(
            //Math::max(
            //  Style::global()->size_button(),
            //  base::string_size(_size_string)
            //),
            Font::string_width(_size_string),
            fl_height()
        ) +
        Style::global()->margin_text() * 2 +
        frame_size() * 2);
}
Beispiel #15
0
Box2i scale( const Box2i& b, float sx, float sy)
{
    return Box2i( V2i( Math<float>::floor( b.min.x * sx), Math<float>::floor( b.min.y * sy)),
			 V2i( Math<float>::ceil(  b.max.x * sx), Math<float>::ceil(  b.max.y * sy)));
}
Beispiel #16
0
void Scale_Op::render(const Image & in) throw (Error)
{
    //DJV_DEBUG("Scale_Op::render");
    //DJV_DEBUG_PRINT("in = " << in);
    //DJV_DEBUG_PRINT("size = " << _value.size);
    //DJV_DEBUG_PRINT("type = " << _value.type);

    // Initialize.

    Pixel_Data_Info info = in.info();

    begin();

    if (DEFAULT == _value.type)
    {
        _texture.init(
            info,
            filter_to_gl(_value.default_min),
            filter_to_gl(_value.default_mag));

        const State_Default state(_value);

        if (_state_default != state)
        {
            //DJV_DEBUG_PRINT("init");

            _render.shader.init(src_default);

            _state_default = state;
        }
    }
    else
    {
        _texture.init(info);

        _render.texture_tmp.init(
            Pixel_Data_Info(V2i(_value.size.x, in.h()), info.pixel));

        const State_Custom state(_value);

        if (_state_custom != state)
        {
            //DJV_DEBUG_PRINT("init");

            _render.offscreen.init();

            Pixel_Data contrib;
            contrib_fnc(
                in.w(),
                _value.size.x,
                _value.custom_min,
                _value.custom_mag,
                &contrib);
            _render.contrib_x.init(contrib);
            _render.shader_x.init(String_Format(src_custom).
                                  arg(contrib.h()).
                                  arg(src_x));

            contrib_fnc(
                in.h(),
                _value.size.y,
                _value.custom_min,
                _value.custom_mag,
                &contrib);
            _render.contrib_y.init(contrib);
            _render.shader_y.init(String_Format(src_custom).
                                  arg(contrib.h()).
                                  arg(src_y));

            _state_custom = state;
        }
    }

    // Render.

    if (DEFAULT == _value.type)
    {
        _render.shader.bind();
        const GLuint program = _render.shader.program();

        glUniform2f(glGetUniformLocation(program, "scale_input"),
            static_cast<GLfloat>(in.w()), static_cast<GLfloat>(in.h()));
        glUniform2f(glGetUniformLocation(program, "scale_output"),
            static_cast<GLfloat>(_value.size.x),
            static_cast<GLfloat>(_value.size.y));

        glActiveTexture(GL_TEXTURE0);
        glUniform1i(glGetUniformLocation(program, "texture"), 0);
        _texture.bind();
        _texture.copy(in);

        info = in.info();
        info.size = _value.size;
        Gl_Util::ortho(_value.size);
        glViewport(0, 0, _value.size.x, _value.size.y);
        glClear(GL_COLOR_BUFFER_BIT);
        Util::quad(info);
    }
    else
    {
        // Horizontal.

        _render.offscreen.bind();
        _render.offscreen.set(_render.texture_tmp);

        _render.shader_x.bind();

        glActiveTexture(GL_TEXTURE0);
        glUniform1i(
            glGetUniformLocation(_render.shader_x.program(), "texture"), 0);
        _texture.bind();
        _texture.copy(in);

        glActiveTexture(GL_TEXTURE1);
        glUniform1i(
            glGetUniformLocation(_render.shader_x.program(), "contrib"), 1);
        _render.contrib_x.bind();

        info = in.info();
        info.size.x = _value.size.x;
        Gl_Util::ortho(info.size);
        glViewport(0, 0, info.size.x, info.size.y);
        glClear(GL_COLOR_BUFFER_BIT);
        Util::quad(info);

        _render.offscreen.unbind();

        // Vertical.

        _render.shader_y.bind();

        glActiveTexture(GL_TEXTURE0);
        glUniform1i(
            glGetUniformLocation(_render.shader_y.program(), "texture"), 0);
        _render.texture_tmp.bind();

        glActiveTexture(GL_TEXTURE1);
        glUniform1i(
            glGetUniformLocation(_render.shader_y.program(), "contrib"), 1);
        _render.contrib_y.bind();

        Gl_Util::ortho(_value.size);
        glViewport(0, 0, _value.size.x, _value.size.y);
        glClear(GL_COLOR_BUFFER_BIT);
        Util::quad(Pixel_Data_Info(_value.size, in.pixel()));
    }

    end();
}
Beispiel #17
0
// deal with input messages, return BACK, EXIT or CONTINUE
int Application::treatEvents()
{
  // local variables
  static V2i cursor(0,0);
  static bool clicking(false);

  // static to avoid reallocating it ever time we run the function
  static SDL_Event event;


  // write each event to our static variable
  while (SDL_PollEvent(&event))
  {
    switch (event.type)
    {
      // exit if the window is closed (ex: pressing the cross)
      case SDL_QUIT:
        return Application::EXIT;
        break;

      // check for keypresses
      case SDL_KEYDOWN:
        switch (event.key.keysym.sym)
        {
          case KEY_BACK:
            return Application::BACK;
          case KEY_EXIT:
            return Application::EXIT;
          case KEY_VOLUME_UP:
            AudioManager::getInstance()->volume_up();
            break;
          case KEY_VOLUME_DOWN:
            AudioManager::getInstance()->volume_down();
            break;
          default:
            // here we DO want a default break, as not all keys are needed
            break;
        }
        break;

      #if USE_TOUCH
      // touch events
      case SDL_FINGERDOWN:
        cursor = normaliseTouch(event.tfinger.touchId,
                                   V2i(event.tfinger.x, event.tfinger.y));
        clicking = true;
        break;

      case SDL_FINGERUP:
        clicking = false;
        break;

      case SDL_FINGERMOTION:
        cursor = normaliseTouch(event.tfinger.touchId,
                                V2i(event.tfinger.x, event.tfinger.y));
        break;
      #endif  // USE_TOUCH

      #if USE_MOUSE
      // mouse events
      case SDL_MOUSEBUTTONDOWN:
        if(event.button.button == SDL_BUTTON_RIGHT)
          return Application::BACK;
        clicking = true;
        break;

      case SDL_MOUSEBUTTONUP:
        if(event.button.button == SDL_BUTTON_LEFT)
          clicking = false;
        break;

      case SDL_MOUSEMOTION:
        cursor = V2f(event.motion.x, event.motion.y) / global::scale;
        break;
      #endif  // USE_MOUSE

      default:
        // not all possible inputs are needed, so we DO want a default break
        break;
    }
  }

  // Update the scene with the new input
  scene->getState()->setCursor(cursor, clicking);

  // No exit events encountered, we can continue
  return Application::CONTINUE;
}
Beispiel #18
0
void Shuttle::dirty()
{
    Frame::dirty();

    size_hint(V2i(_icon[0]->w(), _icon[0]->h()) + 3 * 2);
}
Beispiel #19
0
void Load::open(const File & in, Image_Io_Info & info) throw (Error)
{
    //DJV_DEBUG("Load::open");
    //DJV_DEBUG_PRINT("in = " << in);

    _close();

    // Open the file.

    CFStringRef cf_string_ref(File_Util::path_absolute(in));

    qt::QTNewMoviePropertyElement prop_list [10];
    qt::ItemCount prop_size = 0;

    prop_list[prop_size].propClass = qt::kQTPropertyClass_DataLocation;
    prop_list[prop_size].propID =
        qt::kQTDataLocationPropertyID_CFStringNativePath;
    //qt::kQTDataLocationPropertyID_CFStringPosixPath;
    prop_list[prop_size].propValueSize = sizeof(cf_string_ref.p);
    prop_list[prop_size].propValueAddress = (void *)&cf_string_ref.p;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::Boolean unresolved = true;
    prop_list[prop_size].propClass = qt::kQTPropertyClass_MovieInstantiation;
    prop_list[prop_size].propID =
        qt::kQTMovieInstantiationPropertyID_DontAskUnresolvedDataRefs;
    prop_list[prop_size].propValueSize = sizeof(unresolved);
    prop_list[prop_size].propValueAddress = &unresolved;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::Boolean active = true;
    prop_list[prop_size].propClass = qt::kQTPropertyClass_NewMovieProperty;
    prop_list[prop_size].propID = qt::kQTNewMoviePropertyID_Active;
    prop_list[prop_size].propValueSize = sizeof(active);
    prop_list[prop_size].propValueAddress = &active;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::Boolean no_interact = true;
    prop_list[prop_size].propClass = qt::kQTPropertyClass_NewMovieProperty;
    prop_list[prop_size].propID =
        qt::kQTNewMoviePropertyID_DontInteractWithUser;
    prop_list[prop_size].propValueSize = sizeof(no_interact);
    prop_list[prop_size].propValueAddress = &no_interact;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::QTVisualContextRef visual = 0;
    prop_list[prop_size].propClass = qt::kQTPropertyClass_Context;
    prop_list[prop_size].propID = qt::kQTContextPropertyID_VisualContext;
    prop_list[prop_size].propValueSize = sizeof(qt::QTVisualContextRef);
    prop_list[prop_size].propValueAddress = &visual;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::QTAudioContextRef audio = 0;
    prop_list[prop_size].propClass = qt::kQTPropertyClass_Context;
    prop_list[prop_size].propID = qt::kQTContextPropertyID_AudioContext;
    prop_list[prop_size].propValueSize = sizeof(qt::QTAudioContextRef);
    prop_list[prop_size].propValueAddress = &audio;
    prop_list[prop_size].propStatus = 0;
    ++prop_size;

    qt::OSErr err = qt::NewMovieFromProperties(
        prop_size,
        prop_list,
        0,
        0,
        &_movie);

    if (err != qt::noErr)
    {
        throw_error(name(), String_Format(error_open).arg(in).arg(err));
    }

    // Get the file information.

    _info.file_name = in;

    qt::Rect rect;
    qt::GetMovieBox(_movie, &rect);
    _info.size = V2i(rect.right - rect.left, rect.bottom - rect.top);
    _info.mirror.y = true;
    _info.pixel = Pixel::RGBA_U8;
    //_info.bgr = true;

    const qt::TimeScale time_scale = qt::GetMovieTimeScale(_movie);
    const qt::TimeValue time_duration = qt::GetMovieDuration(_movie);
    //const long frame_count = _frame_count(_movie);
    const qt::TimeValue frame_duration = _frame_duration(_movie);
    //const TimeValue frame_duration = time_duration / frame_count;
    _start_frame = _options.start_frame;

    //DJV_DEBUG_PRINT("time scale = " << static_cast<int>(time_scale));
    //DJV_DEBUG_PRINT("time duration = " << static_cast<int>(time_duration));
    //DJV_DEBUG_PRINT("frame count = " << static_cast<int>(frame_count));
    //DJV_DEBUG_PRINT("frame duration = " << static_cast<int>(frame_duration));
    //DJV_DEBUG_PRINT("start frame = " << _start_frame);

    _info.seq = Seq(
        _start_frame,
        frame_duration ?
        (_start_frame + (time_duration / frame_duration - 1)) :
        _start_frame,
        0,
        Speed(time_scale, frame_duration));

    //DJV_DEBUG_PRINT("time = " << _info.seq);

    info = _info;
    
    //! Allocate the temporary buffer.
    
    _tmp.set(_info);

    //! \todo Is this still necessary?

    unsigned long qt_format =
#   if defined(DJV_OSX)
        qt::k32ARGBPixelFormat;
#   else
        qt::k32RGBAPixelFormat;
#   endif

    err = qt::NewGWorldFromPtr(
        &_gworld,
        qt_format,
        &rect,
        0,
        0,
        0,
        (char *)_tmp.data(0, 0),
        _info.size.x * 4);

    if (err != qt::noErr)
    {
        throw_error(name(), String_Format(error_gworld).arg(in).arg(err));
    }

    qt::SetMovieGWorld(_movie, _gworld, 0);
}
Beispiel #20
0
INTERNAL void gameUpdate(GameState *state, Memory *memory, f32 dt)
{
	GameWorldState *world =
	    GET_STATE_DATA(state, &state->persistentArena, GameWorldState);

	if (!common_isSet(world->flags, gameworldstateflags_init))
	{

#ifdef DENGINE_DEBUG
		{
			u8 *data = (u8 *)world;
			for (i32 i = 0; i < sizeof(GameWorldState); i++)
				ASSERT(data[i] == 0);
		}
#endif
		world->pixelsPerMeter = 70.0f;

		MemoryIndex entityArenaSize =
		    (MemoryIndex)((f32)memory->transientSize * 0.5f);

		u8 *arenaBase = state->transientArena.base + state->transientArena.size;
		memory_arenaInit(&world->entityArena, arenaBase, entityArenaSize);

		world->camera.min = V2(0, 0);
		world->camera.max = state->renderer.size;
		world->size       = state->renderer.size;

		world->entityListSize = 1024;
		world->entityList     = MEMORY_PUSH_ARRAY(&world->entityArena,
		                                      world->entityListSize, Entity);

		{ // Init null entity
			Entity *nullEntity = &world->entityList[world->entityIndex++];
			nullEntity->id     = world->entityIdCounter++;
		}

		{ // Init asteroid entities
			world->numAsteroids = 15;
		}

		{ // Init audio renderer
			world->numAudioRenderers = 8;
			world->audioRenderer     = MEMORY_PUSH_ARRAY(
			    &world->entityArena, world->numAudioRenderers, AudioRenderer);
		}

		{ // Global Collision Rules
			setCollisionRule(world, entitytype_ship, entitytype_asteroid_small,
			                 TRUE);
			setCollisionRule(world, entitytype_ship, entitytype_asteroid_medium,
			                 TRUE);
			setCollisionRule(world, entitytype_ship, entitytype_asteroid_large,
			                 TRUE);
			setCollisionRule(world, entitytype_bullet,
			                 entitytype_asteroid_small, TRUE);
			setCollisionRule(world, entitytype_bullet,
			                 entitytype_asteroid_medium, TRUE);
			setCollisionRule(world, entitytype_bullet,
			                 entitytype_asteroid_large, TRUE);
		}

		world->numStarP = 100;
		world->starPList =
		    MEMORY_PUSH_ARRAY(&world->entityArena, world->numStarP, v2);
		world->starMinOpacity = 0.25f;

		for (i32 i = 0; i < world->numStarP; i++)
		{
			i32 randX = rand() % (i32)world->size.x;
			i32 randY = rand() % (i32)world->size.y;

			world->starPList[i] = V2i(randX, randY);
		}

		world->flags |= gameworldstateflags_init;

		world->scoreMultiplier                = 5;
		world->scoreMultiplierBarTimer        = 0.0f;
		world->scoreMultiplierBarThresholdInS = 2.0f;
	}

	if (common_isSet(world->flags, gameworldstateflags_level_started))
	{
		Font *arial40 = asset_fontGet(&state->assetManager, "Arial", 40);

		Renderer *renderer = &state->renderer;

		/* Render scores onto screen */
		v2 stringP =
		    V2((renderer->size.w * 0.5f), renderer->size.h - arial40->size);
		char gamePointsString[COMMON_ITOA_MAX_BUFFER_32BIT] = {0};
		common_itoa(world->score, gamePointsString,
		            ARRAY_COUNT(gamePointsString));

		renderer_stringFixedCentered(renderer, &state->transientArena, arial40,
		                             gamePointsString, stringP, V2(0, 0), 0,
		                             V4(1.0f, 1.0f, 1.0f, 1.0f), 1, 0);

		/* Render multiplier accumulator bar onto screen */
		v2 stringDim = asset_fontStringDimInPixels(arial40, gamePointsString);
		v2 multiplierOutlineSize =
		    V2(renderer->size.w * 0.5f, stringDim.h * 0.25f);

		v2 multiplierOutlineP = V2(renderer->size.w * 0.5f, stringP.h);
		multiplierOutlineP.x -= (multiplierOutlineSize.w * 0.5f);
		multiplierOutlineP.y -= stringDim.h * 1.5f;

		renderer_rectFixedOutline(
		    renderer, multiplierOutlineP, multiplierOutlineSize, V2(0, 0), 2, 0,
		    NULL, V4(0.2f, 0.3f, 0.8f, 1.0f), 2, renderflag_no_texture);

		f32 progressNormalised = world->scoreMultiplierBarTimer /
		                         world->scoreMultiplierBarThresholdInS;
		renderer_rectFixed(renderer, multiplierOutlineP,
		                   V2(multiplierOutlineSize.w * progressNormalised,
		                      multiplierOutlineSize.h),
		                   V2(0, 0), 0, NULL, V4(0.2f, 0.3f, 0.8f, 1.0f), 1,
		                   renderflag_no_texture);

		/* Render multiplier counter hud onto screen */
		v2 multiplierHudP    = V2(0, 0.05f * renderer->size.h);
		v2 multiplierHudSize =
		    V2((arial40->maxSize.w * 3.5f), arial40->fontHeight * 1.2f);

		renderer_rectFixed(renderer, multiplierHudP, multiplierHudSize,
		                   V2(0, 0), 0, NULL, V4(1, 1, 1, 0.1f), 2,
		                   renderflag_no_texture);

		/* Render multiplier counter string to hud */
		char multiplierToString[COMMON_ITOA_MAX_BUFFER_32BIT + 1] = {0};
		common_itoa(world->scoreMultiplier, multiplierToString + 1,
		            ARRAY_COUNT(multiplierToString) - 1);
		multiplierToString[0] = 'x';

		v2 multiplierToStringP = multiplierHudP;
		multiplierToStringP =
		    v2_add(multiplierToStringP, v2_scale(multiplierHudSize, 0.5f));

		renderer_stringFixedCentered(
		    renderer, &state->transientArena, arial40, multiplierToString,
		    multiplierToStringP, V2(0, 0), 0, V4(1.0f, 1.0f, 1.0f, 1.0f), 3, 0);

		/* Process multiplier bar updates */
		if (!common_isSet(world->flags, gameworldstateflags_player_lost))
		{
			f32 barTimerPenalty = 1.0f;
			if (world->timeSinceLastShot < 1.5f)
			{
				barTimerPenalty = 0.1f;
			}

			world->scoreMultiplierBarTimer += (barTimerPenalty * dt);
			world->timeSinceLastShot += dt;

			if (world->scoreMultiplierBarTimer >
			    world->scoreMultiplierBarThresholdInS)
			{
				world->scoreMultiplierBarTimer = 0;
				world->scoreMultiplier++;

				if (world->scoreMultiplier > 9999)
					world->scoreMultiplier = 9999;
			}
		}
	}

	if (common_isSet(world->flags, gameworldstateflags_player_lost))
	{
		Font *arial40 = asset_fontGet(&state->assetManager, "Arial", 40);

		char *gameOver = "Game Over";
		v2 gameOverP = v2_scale(state->renderer.size, 0.5f);
		renderer_stringFixedCentered(
		    &state->renderer, &state->transientArena, arial40, "Game Over",
		    gameOverP, V2(0, 0), 0, V4(1, 1, 1, 1), 0, 0);

		v2 gameOverSize = asset_fontStringDimInPixels(arial40, gameOver);
		v2 replayP = V2(gameOverP.x, gameOverP.y - (gameOverSize.h * 1.2f));

		renderer_stringFixedCentered(
		    &state->renderer, &state->transientArena, arial40,
		    "Press enter to play again or backspace to return to menu", replayP,
		    V2(0, 0), 0, V4(1, 1, 1, 1), 0, 0);

		if (platform_queryKey(&state->input.keys[keycode_enter],
		                      readkeytype_one_shot, 0.0f))
		{
			// TODO(doyle): Extract score init default values to some game
			// definitions file
			world->score                          = 0;
			world->scoreMultiplier                = 5;
			world->scoreMultiplierBarTimer        = 0.0f;
			world->scoreMultiplierBarThresholdInS = 2.0f;

			addPlayer(world);

			world->flags ^= gameworldstateflags_player_lost;
		}
		else if (platform_queryKey(&state->input.keys[keycode_backspace],
		                           readkeytype_one_shot, 0.0f))
		{
			common_memset((u8 *)world, 0, sizeof(*world));
			state->currState = appstate_StartMenuState;
			return;
		}
	}

	for (u32 i = world->asteroidCounter; i < world->numAsteroids; i++)
		addAsteroid(world, (rand() % asteroidsize_count));

	Radians starRotation   = DEGREES_TO_RADIANS(45.0f);
	v2 starSize            = V2(2, 2);

	ASSERT(world->starMinOpacity >= 0.0f && world->starMinOpacity <= 1.0f);
	f32 opacityFadeRateInS = 0.5f;
	if (world->starFadeAway)
	{
		opacityFadeRateInS *= -1.0f;
	}

	if (world->starOpacity > 1.0f)
	{
		world->starOpacity = 1.0f;
		world->starFadeAway = TRUE;
	}
	else if (world->starOpacity < world->starMinOpacity)
	{
		world->starOpacity = world->starMinOpacity;
		world->starFadeAway = FALSE;
	}

	world->starOpacity += (opacityFadeRateInS * dt);
	DEBUG_PUSH_VAR("Star Opacity: %5.2f", world->starOpacity, "f32");

	for (i32 i = 0; i < world->numStarP; i++)
	{
		world->starPList[i] = v2_add(world->starPList[i], V2(4.0f * dt, 0));
		world->starPList[i] = wrapPAroundBounds(
		    world->starPList[i], math_rectCreate(V2(0, 0), world->size));

		renderer_rect(&state->renderer, world->camera, world->starPList[i],
		              starSize, V2(0, 0), starRotation, NULL,
		              V4(0.8f, 0.8f, 0.8f, world->starOpacity), 0,
		              renderflag_no_texture | renderflag_wireframe);
	}

#ifdef DENGINE_DEBUG
	if (platform_queryKey(&state->input.keys[keycode_left_square_bracket],
	                      readkeytype_repeat, 0.2f))
	{
		addAsteroid(world, (rand() % asteroidsize_count));
	}
#endif

	ASSERT(world->entityList[0].id == NULL_ENTITY_ID);
	for (i32 i = 1; i < world->entityIndex; i++)
	{
		Entity *entity = &world->entityList[i];
		ASSERT(entity->type != entitytype_invalid);

		v2 pivotPoint    = {0};
		f32 ddPSpeedInMs = 0;
		v2 ddP           = {0};
		if (entity->type == entitytype_ship)
		{
			if (platform_queryKey(&state->input.keys[keycode_up],
			                      readkeytype_repeat, 0.0f))
			{
				// TODO(doyle): Renderer creates upfacing triangles by default,
				// but we need to offset rotation so that our base "0 degrees"
				// is right facing for trig to work
				Radians rotation =
				    DEGREES_TO_RADIANS((entity->rotation + 90.0f));
				v2 direction = V2(math_cosf(rotation), math_sinf(rotation));
				ddP          = direction;

				AudioVorbis *thrust =
				    asset_vorbisGet(&state->assetManager, "thrust");
				AudioRenderer *audioRenderer =
				    getFreeAudioRenderer(world, thrust, 3);
				if (audioRenderer)
				{
					audio_vorbisPlay(&state->transientArena,
					                 &state->audioManager, audioRenderer,
					                 thrust, 1);
				}
			}

			if (platform_queryKey(&state->input.keys[keycode_space],
			                      readkeytype_one_shot, KEY_DELAY_NONE))
			{
				addBullet(world, entity);

				if (world->timeSinceLastShot >= 0)
				{
					world->timeSinceLastShot = 0;

					f32 multiplierPenalty    = -2.0f;
					world->timeSinceLastShot += multiplierPenalty;
				}

				AudioVorbis *fire =
				    asset_vorbisGet(&state->assetManager, "fire");
				AudioRenderer *audioRenderer =
				    getFreeAudioRenderer(world, fire, 2);
				if (audioRenderer)
				{
					// TODO(doyle): Atm transient arena is not used, this is
					// just to fill out the arguments
					audio_vorbisPlay(&state->transientArena,
					                 &state->audioManager, audioRenderer, fire,
					                 1);
				}
			}

			Degrees rotationsPerSecond = 180.0f;
			if (platform_queryKey(&state->input.keys[keycode_left],
			                      readkeytype_repeat, 0.0f))
			{
				entity->rotation += (rotationsPerSecond)*dt;
			}

			if (platform_queryKey(&state->input.keys[keycode_right],
			                      readkeytype_repeat, 0.0f))
			{
				entity->rotation -= (rotationsPerSecond)*dt;
			}
			entity->rotation = (f32)((i32)entity->rotation);

			ddPSpeedInMs = 25;
			DEBUG_PUSH_VAR("Pos: %5.2f, %5.2f", entity->pos, "v2");
			DEBUG_PUSH_VAR("Velocity: %5.2f, %5.2f", entity->dP, "v2");
			DEBUG_PUSH_VAR("Rotation: %5.2f", entity->rotation, "f32");

			DEBUG_PUSH_VAR("TimeSinceLastShot: %5.2f", world->timeSinceLastShot,
			               "f32");

			renderer_rect(&state->renderer, world->camera, entity->pos,
			              V2(5, 5), V2(0, 0),
			              DEGREES_TO_RADIANS(entity->rotation), NULL,
			              V4(1.0f, 1.0f, 1.0f, 1.0f), 0, renderflag_no_texture);
		}
		else if (entity->type >= entitytype_asteroid_small &&
		         entity->type <= entitytype_asteroid_large)
		{

			i32 randValue = rand();

			// NOTE(doyle): If it is a new asteroid with no dp set, we need to
			// set a initial dp for it to move from.
			v2 localDp = {0};
			if ((i32)entity->dP.x == 0 && (i32)entity->dP.y == 0)
			{
				enum Direction direction = randValue % direction_count;
				switch (direction)
				{
				case direction_north:
				case direction_northwest:
				{
					localDp.x = 1.0f;
					localDp.y = 1.0f;
				}
				break;

				case direction_west:
				case direction_southwest:
				{
					localDp.x = -1.0f;
					localDp.y = -1.0f;
				}
				break;

				case direction_south:
				case direction_southeast:
				{
					localDp.x = 1.0f;
					localDp.y = -1.0f;
				}
				break;

				case direction_east:
				case direction_northeast:
				{
					localDp.x = 1.0f;
					localDp.y = 1.0f;
				}
				break;

				default:
				{
					ASSERT(INVALID_CODE_PATH);
				}
				break;
				}
			}
			// NOTE(doyle): Otherwise, if it has pre-existing dp, maintain our
			// direction by extrapolating from it's current dp
			else
			{
				if (entity->dP.x >= 0)
					localDp.x = 1.0f;
				else
					localDp.x = -1.0f;

				if (entity->dP.y >= 0)
					localDp.y = 1.0f;
				else
					localDp.y = -1.0f;
			}

			/*
			   NOTE(doyle): We compare current dP with the calculated dP. In the
			   event we want to artificially boost the asteroid, we set a higher
			   dP on creation, which will have a higher dP than the default dP
			   we calculate. So here we choose to keep it until it decays enough
			   that the default dP of the asteroid is accepted.
			 */
			v2 newDp     = v2_scale(localDp, world->pixelsPerMeter * 1.5f);
			f32 newDpSum = ABS(newDp.x) + ABS(newDp.y);
			f32 oldDpSum = ABS(entity->dP.x) + ABS(entity->dP.y);

			if (newDpSum > oldDpSum)
			{
				entity->dP = newDp;
			}
		}
		else if (entity->type == entitytype_bullet)
		{
			if (!math_rectContainsP(world->camera, entity->pos))
			{
				deleteEntity(world, i--);
				continue;
			}

			Radians rotation = DEGREES_TO_RADIANS((entity->rotation + 90.0f));
			v2 localDp       = V2(math_cosf(rotation), math_sinf(rotation));
			entity->dP       = v2_scale(localDp, world->pixelsPerMeter * 5);
		}
		else if (entity->type == entitytype_particle)
		{
			f32 diff = entity->color.a - 0.1f;
			if (diff < 0.01f)
			{
				deleteEntity(world, i--);
				continue;
			}

			f32 divisor =
			    MAX(entity->particleInitDp.x, entity->particleInitDp.y);
			f32 maxDp = MAX(entity->dP.x, entity->dP.y);

			entity->color.a = maxDp / divisor;
		}

		entity->pos = wrapPAroundBounds(entity->pos,
		                                math_rectCreate(V2(0, 0), world->size));

		/* Loop entity around world */
		i32 collisionIndex = moveEntity(world, &state->transientArena, entity,
		                                i, ddP, dt, ddPSpeedInMs);

		v4 collideColor = {0};
		if (collisionIndex != -1)
		{
			ASSERT(collisionIndex < world->entityIndex);

			Entity *collideEntity = &world->entityList[collisionIndex];

			Entity *colliderA;
			Entity *colliderB;

			if (collideEntity->type < entity->type)
			{
				colliderA = collideEntity;
				colliderB = entity;
			}
			else
			{
				colliderA = entity;
				colliderB = collideEntity;
			}

			// Assumptions made that the collision detect system relies on
			ASSERT(entitytype_ship            < entitytype_asteroid_small);
			ASSERT(entitytype_asteroid_small  < entitytype_asteroid_medium);
			ASSERT(entitytype_asteroid_medium < entitytype_asteroid_large);
			ASSERT(entitytype_asteroid_large  < entitytype_bullet);
			ASSERT(entitytype_asteroid_small + 1 == entitytype_asteroid_medium);
			ASSERT(entitytype_asteroid_medium + 1 == entitytype_asteroid_large);

			if (colliderA->type >= entitytype_asteroid_small &&
			    colliderA->type <= entitytype_asteroid_large)
			{
				f32 numParticles = 4;
				if (colliderA->type == entitytype_asteroid_medium)
				{
					AsteroidSpec spec = {0};
					spec.pos          = colliderA->pos;
					spec.dP           = v2_scale(colliderA->dP, -2.0f);
					addAsteroidWithSpec(world, asteroidsize_small, &spec);

					numParticles = 8;
					world->score += (10 * world->scoreMultiplier);
				}
				else if (colliderA->type == entitytype_asteroid_large)
				{
					AsteroidSpec spec = {0};
					spec.pos          = colliderA->pos;
					spec.dP           = v2_scale(colliderA->dP, -4.0f);
					addAsteroidWithSpec(world, asteroidsize_medium, &spec);

					spec.dP = v2_perpendicular(spec.dP);
					addAsteroidWithSpec(world, asteroidsize_small, &spec);

					spec.dP = v2_perpendicular(colliderA->dP);
					addAsteroidWithSpec(world, asteroidsize_small, &spec);

					numParticles = 16;
					world->score += (20 * world->scoreMultiplier);
				}
				else
				{
					world->score += (5 * world->scoreMultiplier);
				}

				for (i32 i = 0; i < numParticles; i++)
				{
					{ // Add particles
						Entity *particle =
						    &world->entityList[world->entityIndex++];
						particle->id = world->entityIdCounter++;

						particle->pos  = colliderA->pos;
						particle->size = V2(4.0f, 4.0f);

						i32 randValue = rand();
						Radians rotation =
						    DEGREES_TO_RADIANS((randValue % 360));
						v2 randDirectionVec =
						    V2(math_cosf(rotation), math_sinf(rotation));

						i32 particleDpLimit = 8;
						f32 randDpMultiplier =
						    (f32)(randValue % particleDpLimit) + 1;

						v2 newDp = v2_scale(colliderA->dP, randDpMultiplier);
						newDp    = v2_hadamard(newDp, randDirectionVec);

						particle->dP             = newDp;
						particle->particleInitDp = newDp;

						particle->offset     = v2_scale(particle->size, -0.5f);
						particle->hitbox     = particle->size;
						particle->rotation   = 0;
						particle->renderMode = rendermode_polygon;

						if (!world->particleVertexCache)
						{
							world->particleVertexCache =
							    MEMORY_PUSH_ARRAY(&world->entityArena, 4, v2);
							world->particleVertexCache[0] =
							    V2(0, particle->size.h);
							world->particleVertexCache[1] = V2(0, 0);
							world->particleVertexCache[2] =
							    V2(particle->size.w, 0);
							world->particleVertexCache[3] = particle->size;
						}

						particle->vertexPoints    = world->particleVertexCache;
						particle->numVertexPoints = 4;

						particle->type  = entitytype_particle;
						particle->color = V4(1.0f, 0.0f, 0, 1.0f);
					}
				}

				ASSERT(colliderB->type == entitytype_bullet);

				deleteEntity(world, collisionIndex);
				deleteEntity(world, i--);
				world->asteroidCounter--;

				ASSERT(world->asteroidCounter >= 0);

				char *sound;
				i32 choice = rand() % 3;
				if (choice == 0)
				{
					sound = "bang_small";
				}
				else if (choice == 1)
				{
					sound = "bang_medium";
				}
				else
				{
					sound = "bang_large";
				}

				AudioVorbis *explode =
				    asset_vorbisGet(&state->assetManager, sound);
				AudioRenderer *audioRenderer =
				    getFreeAudioRenderer(world, explode, 3);
				if (audioRenderer)
				{
					audio_vorbisPlay(&state->transientArena,
					                 &state->audioManager, audioRenderer,
					                 explode, 1);
				}

				continue;
			}
			else if (colliderA->type == entitytype_ship)
			{
				if (colliderB->type >= entitytype_asteroid_small &&
				    colliderB->type <= entitytype_asteroid_large)
				{
					world->flags |= gameworldstateflags_player_lost;

					if (collideEntity->type == entitytype_ship)
					{
						deleteEntity(world, collisionIndex);
					}
					else
					{
						deleteEntity(world, i--);
					}

					AudioVorbis *explode =
					    asset_vorbisGet(&state->assetManager, "bang_large");
					AudioRenderer *audioRenderer =
					    getFreeAudioRenderer(world, explode, 3);
					if (audioRenderer)
					{
						audio_vorbisPlay(&state->transientArena,
						                 &state->audioManager, audioRenderer,
						                 explode, 1);
					}

					continue;
				}
			}
		}

		RenderFlags flags = renderflag_wireframe | renderflag_no_texture;
		renderer_entity(&state->renderer, &state->transientArena, world->camera,
		                entity, V2(0, 0), 0, collideColor, 0, flags);
	}

	for (i32 i = 0; i < world->numAudioRenderers; i++)
	{
		AudioRenderer *audioRenderer = &world->audioRenderer[i];
		audio_updateAndPlay(&state->transientArena, &state->audioManager,
		                    audioRenderer);
	}
}
void Application_Message_Dialog::dirty()
{
    Dialog::dirty();

    _size_min = Vector_Util::max(Dialog::size_min(), V2i(400, 200));
}
Beispiel #22
0
INTERNAL void addAsteroidWithSpec(GameWorldState *world,
                                  enum AsteroidSize asteroidSize,
                                  AsteroidSpec *spec)
{
	world->asteroidCounter++;

	enum EntityType type;
	v2 size;
	v2 **vertexCache = NULL;

	if (asteroidSize == asteroidsize_small)
	{
		size        = V2i(25, 25);
		type        = entitytype_asteroid_small;
		vertexCache = world->asteroidSmallVertexCache;
	}
	else if (asteroidSize == asteroidsize_medium)
	{
		size        = V2i(50, 50);
		type        = entitytype_asteroid_medium;
		vertexCache = world->asteroidMediumVertexCache;
	}
	else if (asteroidSize == asteroidsize_large)
	{
		type        = entitytype_asteroid_large;
		size        = V2i(100, 100);
		vertexCache = world->asteroidLargeVertexCache;
	}
	else
	{
		ASSERT(INVALID_CODE_PATH);
	}

	Entity *asteroid = &world->entityList[world->entityIndex++];
	asteroid->id     = world->entityIdCounter++;

	i32 randValue = rand();
	if (!spec)
	{
		i32 randX = (randValue % (i32)world->size.w);
		i32 randY = (randValue % (i32)world->size.h);

		v2 midpoint = v2_scale(world->size, 0.5f);

		Rect topLeftQuadrant = {V2(0, midpoint.y),
		                        V2(midpoint.x, world->size.y)};
		Rect botLeftQuadrant  = {V2(0, 0), midpoint};
		Rect topRightQuadrant = {midpoint, world->size};
		Rect botRightQuadrant = {V2(midpoint.x, 0),
		                         V2(world->size.x, midpoint.y)};

		// NOTE(doyle): Off-screen so asteroids "float" into view. There's no
		// particular order, just pushing things offscreen when they get
		// generated
		// to float back into game space
		v2 newP = V2i(randX, randY);
		if (math_rectContainsP(topLeftQuadrant, newP))
		{
			newP.y += midpoint.y;
		}
		else if (math_rectContainsP(botLeftQuadrant, newP))
		{
			newP.x -= midpoint.x;
		}
		else if (math_rectContainsP(topRightQuadrant, newP))
		{
			newP.y -= midpoint.y;
		}
		else if (math_rectContainsP(botRightQuadrant, newP))
		{
			newP.x += midpoint.x;
		}
		else
		{
			ASSERT(INVALID_CODE_PATH);
		}
		asteroid->pos = newP;
	}
	else
	{
		asteroid->pos = spec->pos;
		asteroid->dP  = spec->dP;
	}

	asteroid->size            = size;
	asteroid->hitbox          = asteroid->size;
	asteroid->offset          = v2_scale(asteroid->size, -0.5f);
	asteroid->type            = type;
	asteroid->renderMode      = rendermode_polygon;
	asteroid->numVertexPoints = 10;

	i32 cacheIndex = randValue % ARRAY_COUNT(world->asteroidSmallVertexCache);
	ASSERT(ARRAY_COUNT(world->asteroidSmallVertexCache) ==
	       ARRAY_COUNT(world->asteroidMediumVertexCache));
	ASSERT(ARRAY_COUNT(world->asteroidSmallVertexCache) ==
	       ARRAY_COUNT(world->asteroidLargeVertexCache));

	if (!vertexCache[cacheIndex])
	{
		vertexCache[cacheIndex] = createAsteroidVertexList(
		    &world->entityArena, asteroid->numVertexPoints,
		    (i32)(asteroid->size.w * 0.5f));
	}

	asteroid->vertexPoints = vertexCache[cacheIndex];
	asteroid->color        = V4(1.0f, 1.0f, 1.0f, 1.0f);
}