示例#1
0
// search for T-intersection problem
// BP : It can be mush more faster doing this at the same time of the splitpoly
// but we must use a different structure : polygone pointing on segs
// segs pointing on polygone and on vertex (too mush complicated, well not
// realy but i am soo lasy), the methode discibed is also better for segs presition
static INT32 SolveTProblem(void)
{
	poly_t *p;
	INT32 i;
	size_t l;

	if (cv_grsolvetjoin.value == 0)
		return 0;

	DEBPRINT("Solving T-joins. This may take a while. Please wait...\n");
	CON_Drawer(); //let the user know what we are doing
	I_FinishUpdate(); // page flip or blit buffer

	numsplitpoly = 0;

	for (l = 0; l < addsubsector; l++)
	{
		p = extrasubsectors[l].planepoly;
		if (p)
			for (i = 0; i < p->numpts; i++)
				SearchSegInBSP((INT32)numnodes-1, &p->pts[i], p);
	}
	//DEBPRINT("numsplitpoly %d\n", numsplitpoly);
	return numsplitpoly;
}
示例#2
0
static void D_Display(void)
{
    static boolean menuactivestate = false;
    static gamestate_t oldgamestate = -1;
    boolean redrawsbar = false;

    static boolean wipe = false;
    INT32 wipedefindex = 0;

    if (dedicated)
        return;

    if (nodrawers)
        return; // for comparative timing/profiling

    // check for change of screen size (video mode)
    if (setmodeneeded && !wipe)
        SCR_SetMode(); // change video mode

    if (vid.recalc)
        SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()

    // change the view size if needed
    if (setsizeneeded)
    {
        R_ExecuteSetViewSize();
        oldgamestate = -1; // force background redraw
        redrawsbar = true;
    }

    // save the current screen if about to wipe
    if (gamestate != wipegamestate)
    {
        wipe = true;
        F_WipeStartScreen();
    }
    else
        wipe = false;

    // draw buffered stuff to screen
    // Used only by linux GGI version
    I_UpdateNoBlit();

    if (wipe)
    {
        // set for all later
        wipedefindex = gamestate; // wipe_xxx_toblack
        if (gamestate == GS_INTERMISSION)
        {
            if (intertype == int_spec) // Special Stage
                wipedefindex = wipe_specinter_toblack;
            else if (intertype != int_coop) // Multiplayer
                wipedefindex = wipe_multinter_toblack;
        }

        if (rendermode != render_none)
        {
            // Fade to black first
            if (gamestate != GS_LEVEL // fades to black on its own timing, always
                    && wipedefs[wipedefindex] != UINT8_MAX)
            {
                V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
#ifdef HWRENDER
                if(rendermode != render_soft)
                    HWR_PrepFadeToBlack();
#endif
                F_WipeEndScreen();
                F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
            }

            F_WipeStartScreen();
        }
    }

    // do buffered drawing
    switch (gamestate)
    {
    case GS_LEVEL:
        if (!gametic)
            break;
        HU_Erase();
        if (automapactive)
            AM_Drawer();
        if (wipe || menuactivestate || (rendermode != render_soft && rendermode != render_none) || vid.recalc)
            redrawsbar = true;
        break;

    case GS_INTERMISSION:
        Y_IntermissionDrawer();
        HU_Erase();
        HU_Drawer();
        break;

    case GS_TIMEATTACK:
        break;

    case GS_INTRO:
        F_IntroDrawer();
        if (wipegamestate == (gamestate_t)-1)
            wipe = true;
        break;

    case GS_CUTSCENE:
        F_CutsceneDrawer();
        HU_Erase();
        HU_Drawer();
        break;

    case GS_GAMEEND:
        F_GameEndDrawer();
        break;

    case GS_EVALUATION:
        F_GameEvaluationDrawer();
        HU_Drawer();
        break;

    case GS_CONTINUING:
        F_ContinueDrawer();
        break;

    case GS_CREDITS:
        F_CreditDrawer();
        HU_Erase();
        HU_Drawer();
        break;

    case GS_TITLESCREEN:
        F_TitleScreenDrawer();
        break;

    case GS_WAITINGPLAYERS:
    // The clientconnect drawer is independent...
    case GS_DEDICATEDSERVER:
    case GS_NULL:
        break;
    }

    // clean up border stuff
    // see if the border needs to be initially drawn
    if (gamestate == GS_LEVEL)
    {
#if 0
        if (oldgamestate != GS_LEVEL)
            R_FillBackScreen(); // draw the pattern into the back screen
#endif

        // draw the view directly
        if (!automapactive && !dedicated && cv_renderview.value)
        {
            if (players[displayplayer].mo || players[displayplayer].playerstate == PST_DEAD)
            {
                topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
                objectsdrawn = 0;
#ifdef HWRENDER
                if (rendermode != render_soft)
                    HWR_RenderPlayerView(0, &players[displayplayer]);
                else
#endif
                    if (rendermode != render_none)
                        R_RenderPlayerView(&players[displayplayer]);
            }

            // render the second screen
            if (splitscreen && players[secondarydisplayplayer].mo)
            {
#ifdef HWRENDER
                if (rendermode != render_soft)
                    HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
                else
#endif
                    if (rendermode != render_none)
                    {
                        viewwindowy = vid.height / 2;
                        M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));

                        topleft = screens[0] + viewwindowy*vid.width + viewwindowx;

                        R_RenderPlayerView(&players[secondarydisplayplayer]);

                        viewwindowy = 0;
                        M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
                    }
            }

            // Image postprocessing effect
            if (postimgtype)
                V_DoPostProcessor(0, postimgtype, postimgparam);
            if (postimgtype2)
                V_DoPostProcessor(1, postimgtype2, postimgparam2);
        }

        if (lastdraw)
        {
            if (rendermode == render_soft)
                VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
            lastdraw = false;
        }

        ST_Drawer(redrawsbar);

        HU_Drawer();
    }

    // change gamma if needed
    if (gamestate != oldgamestate && gamestate != GS_LEVEL)
        V_SetPalette(0);

    menuactivestate = menuactive;
    oldgamestate = wipegamestate = gamestate;

    // draw pause pic
    if (paused && cv_showhud.value && (!menuactive || netgame))
    {
        INT32 py;
        patch_t *patch;
        if (automapactive)
            py = 4;
        else
            py = viewwindowy + 4;
        patch = W_CachePatchName("M_PAUSE", PU_CACHE);
        V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
    }

    // vid size change is now finished if it was on...
    vid.recalc = 0;

    // FIXME: draw either console or menu, not the two
    if (gamestate != GS_TIMEATTACK)
        CON_Drawer();

    M_Drawer(); // menu is drawn even on top of everything
    NetUpdate(); // send out any new accumulation

    // It's safe to end the game now.
    if (G_GetExitGameFlag())
    {
        Command_ExitGame_f();
        G_ClearExitGameFlag();
    }

    //
    // normal update
    //
    if (!wipe)
    {
        if (cv_netstat.value)
        {
            char s[50];
            Net_GetNetStat();

            s[sizeof s - 1] = '\0';

            snprintf(s, sizeof s - 1, "get %d b/s", getbps);
            V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-40, V_YELLOWMAP, s);
            snprintf(s, sizeof s - 1, "send %d b/s", sendbps);
            V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-30, V_YELLOWMAP, s);
            snprintf(s, sizeof s - 1, "GameMiss %.2f%%", gamelostpercent);
            V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-20, V_YELLOWMAP, s);
            snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
            V_DrawRightAlignedString(BASEVIDWIDTH, BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
        }

        I_FinishUpdate(); // page flip or blit buffer
        return;
    }

    //
    // wipe update
    //
    wipedefindex += WIPEFINALSHIFT;

    if (rendermode != render_none)
    {
        F_WipeEndScreen();
        F_RunWipe(wipedefs[wipedefindex], gamestate != GS_TIMEATTACK);
    }
}
示例#3
0
static void D_Display(void)
{
	static boolean menuactivestate = false;
	static gamestate_t oldgamestate = -1;
	boolean redrawsbar = false;
	static boolean wipe = false;

	if (dedicated)
		return;

	if (nodrawers)
		return; // for comparative timing/profiling

	// check for change of screen size (video mode)
	if (setmodeneeded && !wipe)
		SCR_SetMode(); // change video mode

	if (vid.recalc)
		SCR_Recalc(); // NOTE! setsizeneeded is set by SCR_Recalc()

	// change the view size if needed
	if (setsizeneeded)
	{
		R_ExecuteSetViewSize();
		oldgamestate = -1; // force background redraw
		redrawsbar = true;
	}

	// save the current screen if about to wipe
	if (gamestate != wipegamestate)
	{
		wipe = true;

#ifndef SHUFFLE
		if (rendermode == render_soft)
#endif
			F_WipeStartScreen();
	}
	else
		wipe = false;

	// Hardware mode does not fade wipe.
	// Thus, don't delay it unless needed for synchronisity.
#ifndef SHUFFLE
	if (rendermode != render_soft /*&& !netgame*/)
	{
		if (gamestate != GS_INTRO
		&& gamestate != GS_INTRO2
		&& gamestate != GS_CUTSCENE)
			wipe = false;
	}
#endif
	// draw buffered stuff to screen
	// Used only by linux GGI version
	I_UpdateNoBlit();

	// Fade to black first
	if (rendermode != render_none)
	{
		if (wipe)
		{
			if (!(mapheaderinfo[gamemap-1]->interscreen[0] == '#'
				&& gamestate == GS_INTERMISSION))
			{
				V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
#if defined (SHUFFLE) && defined (HWRENDER)
				if(rendermode != render_soft)
				{
					HWR_PrepFadeToBlack();
				}
#endif
			}
			F_WipeEndScreen(0, 0, vid.width, vid.height);

			F_RunWipe(2*TICRATE, gamestate != GS_TIMEATTACK);

			WipeInAction = false;
		}
		F_WipeStartScreen();
	}

	// do buffered drawing
	switch (gamestate)
	{
		case GS_LEVEL:
			if (!gametic)
				break;
			HU_Erase();
			if (automapactive)
				AM_Drawer();
			if (wipe || menuactivestate || (rendermode != render_soft && rendermode != render_none) || vid.recalc)
				redrawsbar = true;
			break;

		case GS_INTERMISSION:
			Y_IntermissionDrawer();
			HU_Erase();
			HU_Drawer();
			break;

		case GS_TIMEATTACK:
			break;

		case GS_INTRO:
		case GS_INTRO2:
			F_IntroDrawer();
			break;

		case GS_CUTSCENE:
			F_CutsceneDrawer();
			HU_Erase();
			HU_Drawer();
			break;

		case GS_GAMEEND:
			F_GameEndDrawer();
			break;

		case GS_EVALUATION:
			F_GameEvaluationDrawer();
			break;

		case GS_CREDITS:
			F_CreditDrawer();
			HU_Erase();
			HU_Drawer();
			break;

		case GS_TITLESCREEN:
			F_TitleScreenDrawer();
			break;

		case GS_DEMOSCREEN:
			D_PageDrawer(pagename);
		case GS_DEDICATEDSERVER:
		case GS_WAITINGPLAYERS:
		case GS_NULL:
			break;
	}

	// Transitions for Introduction
	if (gamestate == GS_INTRO && oldgamestate == GS_INTRO2)
		wipe = true;
	else if (gamestate == GS_INTRO2 && oldgamestate == GS_INTRO)
		wipe = true;

	// clean up border stuff
	// see if the border needs to be initially drawn
	if (gamestate == GS_LEVEL)
	{
		if (oldgamestate != GS_LEVEL)
		{
#if 0
			R_FillBackScreen(); // draw the pattern into the back screen
#endif
		}

		// draw the view directly
		if (!automapactive && !dedicated && cv_renderview.value)
		{
			if (players[displayplayer].mo)
			{
				topleft = screens[0] + viewwindowy*vid.width + viewwindowx;
#ifdef HWRENDER
				if (rendermode != render_soft)
					HWR_RenderPlayerView(0, &players[displayplayer]);
				else
#endif
				if (rendermode != render_none)
					R_RenderPlayerView(&players[displayplayer]);
			}

			// render the second screen
			if (secondarydisplayplayer != consoleplayer && players[secondarydisplayplayer].mo)
			{
#ifdef HWRENDER
				if (rendermode != render_soft)
					HWR_RenderPlayerView(1, &players[secondarydisplayplayer]);
				else
#endif
				if (rendermode != render_none)
				{
					viewwindowy = vid.height / 2;
					M_Memcpy(ylookup, ylookup2, viewheight*sizeof (ylookup[0]));

					topleft = screens[0] + viewwindowy*vid.width + viewwindowx;

					R_RenderPlayerView(&players[secondarydisplayplayer]);

					viewwindowy = 0;
					M_Memcpy(ylookup, ylookup1, viewheight*sizeof (ylookup[0]));
				}
			}

			// Image postprocessing effect
			if (postimgtype)
				V_DoPostProcessor(0, postimgtype, postimgparam);
			if (postimgtype2)
				V_DoPostProcessor(1, postimgtype2, postimgparam2);
		}

		if (lastdraw)
		{
			if (rendermode == render_soft)
				VID_BlitLinearScreen(screens[0], screens[1], vid.width*vid.bpp, vid.height, vid.width*vid.bpp, vid.rowbytes);
			lastdraw = false;
		}

		ST_Drawer(redrawsbar);

		HU_Drawer();
	}

	// change gamma if needed
	if (gamestate != oldgamestate && gamestate != GS_LEVEL)
		V_SetPalette(0);

	menuactivestate = menuactive;
	oldgamestate = wipegamestate = gamestate;

	// draw pause pic
	if (paused && (!menuactive || netgame))
	{
		INT32 py;
		patch_t *patch;
		if (automapactive)
			py = 4;
		else
			py = viewwindowy + 4;
		patch = W_CachePatchName("M_PAUSE", PU_CACHE);
		V_DrawScaledPatch(viewwindowx + (BASEVIDWIDTH - SHORT(patch->width))/2, py, 0, patch);
	}

	// vid size change is now finished if it was on...
	vid.recalc = 0;

	// FIXME: draw either console or menu, not the two
	if (gamestate != GS_TIMEATTACK)
		CON_Drawer();

	M_Drawer(); // menu is drawn even on top of everything
	NetUpdate(); // send out any new accumulation

	// It's safe to end the game now.
	if (G_GetExitGameFlag())
	{
		Command_ExitGame_f();
		G_ClearExitGameFlag();
	}

	//
	// normal update
	//
	if (!wipe)
	{
		if (cv_netstat.value)
		{
			char s[50];
			Net_GetNetStat();

			s[sizeof s - 1] = '\0';

			snprintf(s, sizeof s - 1, "get %d b/s", getbps);
			V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT-ST_HEIGHT-40, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "send %d b/s", sendbps);
			V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT-ST_HEIGHT-30, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "GameMiss %.2f%%", gamelostpercent);
			V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT-ST_HEIGHT-20, V_YELLOWMAP, s);
			snprintf(s, sizeof s - 1, "SysMiss %.2f%%", lostpercent);
			V_DrawString(BASEVIDWIDTH - V_StringWidth(s), BASEVIDHEIGHT-ST_HEIGHT-10, V_YELLOWMAP, s);
		}

		I_FinishUpdate(); // page flip or blit buffer

		if (takescreenshot) // Only take screenshots after drawing.
			M_DoScreenShot();

		return;
	}

	//
	// wipe update
	//

	if (rendermode != render_none)
	{
		F_WipeEndScreen(0, 0, vid.width, vid.height);

		F_RunWipe(2*TICRATE, gamestate != GS_TIMEATTACK);

		WipeInAction = false;
	}
}
示例#4
0
// call this routine after the BSP of a Doom wad file is loaded,
// and it will generate all the convex polys for the hardware renderer
void HWR_CreatePlanePolygons(INT32 bspnum)
{
	poly_t *rootp;
	polyvertex_t *rootpv;
	size_t i;
	fixed_t rootbbox[4];

	DEBPRINT("Creating polygons, please wait...\n");
	ls_count = ls_percent = 0; // reset the loading status
	CON_Drawer(); //let the user know what we are doing
	I_FinishUpdate(); // page flip or blit buffer

	HWR_ClearPolys();

	// find min/max boundaries of map
	//DEBPRINT("Looking for boundaries of map...\n");
	M_ClearBox(rootbbox);
	for (i = 0;i < numvertexes; i++)
		M_AddToBox(rootbbox, vertexes[i].x, vertexes[i].y);

	//DEBPRINT("Generating subsector polygons... %d subsectors\n", numsubsectors);

	HWR_FreeExtraSubsectors();
	// allocate extra data for each subsector present in map
	totsubsectors = numsubsectors + NEWSUBSECTORS;
	extrasubsectors = calloc(totsubsectors, sizeof (*extrasubsectors));
	if (extrasubsectors == NULL)
		I_Error("couldn't malloc extrasubsectors totsubsectors %s\n", sizeu1(totsubsectors));

	// allocate table for back to front drawing of subsectors
	/*gr_drawsubsectors = (INT16 *)malloc(sizeof (*gr_drawsubsectors) * totsubsectors);
	if (!gr_drawsubsectors)
		I_Error("couldn't malloc gr_drawsubsectors\n");*/

	// number of the first new subsector that might be added
	addsubsector = numsubsectors;

	// construct the initial convex poly that encloses the full map
	rootp = HWR_AllocPoly(4);
	rootpv = rootp->pts;

	rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT  ]);
	rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]);  //lr
	rootpv++;
	rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXLEFT  ]);
	rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP   ]);  //ur
	rootpv++;
	rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]);
	rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXTOP   ]);  //ul
	rootpv++;
	rootpv->x = FIXED_TO_FLOAT(rootbbox[BOXRIGHT ]);
	rootpv->y = FIXED_TO_FLOAT(rootbbox[BOXBOTTOM]);  //ll
	rootpv++;

	WalkBSPNode(bspnum, rootp, NULL,rootbbox);

	i = SolveTProblem();
	//DEBPRINT("%d point divides a polygon line\n",i);
	AdjustSegs();

	//debug debug..
	//if (nobackpoly)
	//    DEBPRINT("no back polygon %u times\n",nobackpoly);
	//"(should happen only with the deep water trick)"
	//if (skipcut)
	//    DEBPRINT("%u cuts were skipped because of only one point\n",skipcut);

	//DEBPRINT("done: %u total subsector convex polygons\n", totalsubsecpolys);
}
示例#5
0
// poly : the convex polygon that encloses all child subsectors
static void WalkBSPNode(INT32 bspnum, poly_t *poly, UINT16 *leafnode, fixed_t *bbox)
{
	node_t *bsp;
	poly_t *backpoly, *frontpoly;
	fdivline_t fdivline;
	polyvertex_t *pt;
	INT32 i;

	// Found a subsector?
	if (bspnum & NF_SUBSECTOR)
	{
		if (bspnum == -1)
		{
			// BP: i think this code is useless and wrong because
			// - bspnum==-1 happens only when numsubsectors == 0
			// - it can't happens in bsp recursive call since bspnum is a INT32 and children is UINT16
			// - the BSP is complet !! (there just can have subsector without segs) (i am not sure of this point)

			// do we have a valid polygon ?
			if (poly && poly->numpts > 2)
			{
				DEBPRINT("Adding a new subsector\n");
				if (addsubsector == numsubsectors + NEWSUBSECTORS)
					I_Error("WalkBSPNode: not enough addsubsectors\n");
				else if (addsubsector > 0x7fff)
					I_Error("WalkBSPNode: addsubsector > 0x7fff\n");
				*leafnode = (UINT16)((UINT16)addsubsector | NF_SUBSECTOR);
				extrasubsectors[addsubsector].planepoly = poly;
				addsubsector++;
			}

			//add subsectors without segs here?
			//HWR_SubsecPoly(0, NULL);
		}
		else
		{
			HWR_SubsecPoly(bspnum&(~NF_SUBSECTOR), poly);
			//Hurdler: implement a loading status

			if (ls_count-- <= 0)
			{
				char s[16];
				int x, y;

				I_OsPolling();
				ls_count = numsubsectors/50;
				CON_Drawer();
				sprintf(s, "%d%%", (++ls_percent)<<1);
				x = BASEVIDWIDTH/2;
				y = BASEVIDHEIGHT/2;
				V_DrawFill(0, 0, vid.width, vid.height, 31); // Black background to match fade in effect
				//V_DrawPatchFill(W_CachePatchName("SRB2BACK",PU_CACHE)); // SRB2 background, ehhh too bright.
				M_DrawTextBox(x-58, y-8, 13, 1);
				V_DrawString(x-50, y, V_YELLOWMAP, "Loading...");
				V_DrawString(x+50-V_StringWidth(s), y, V_YELLOWMAP, s);

				// Is this really necessary at this point..?
				V_DrawCenteredString(BASEVIDWIDTH/2, 40, V_YELLOWMAP, "OPENGL MODE IS INCOMPLETE AND MAY");
				V_DrawCenteredString(BASEVIDWIDTH/2, 50, V_YELLOWMAP, "NOT DISPLAY SOME SURFACES.");
				V_DrawCenteredString(BASEVIDWIDTH/2, 70, V_YELLOWMAP, "USE AT SONIC'S RISK.");

				I_UpdateNoVsync();
			}
		}
		M_ClearBox(bbox);
		poly = extrasubsectors[bspnum&~NF_SUBSECTOR].planepoly;

		for (i = 0, pt = poly->pts; i < poly->numpts; i++,pt++)
			M_AddToBox(bbox, FLOAT_TO_FIXED(pt->x), FLOAT_TO_FIXED(pt->y));

		return;
	}

	bsp = &nodes[bspnum];
	SearchDivline(bsp, &fdivline);
	SplitPoly(&fdivline, poly, &frontpoly, &backpoly);
	poly = NULL;

	//debug
	if (!backpoly)
		nobackpoly++;

	// Recursively divide front space.
	if (frontpoly)
	{
		WalkBSPNode(bsp->children[0], frontpoly, &bsp->children[0],bsp->bbox[0]);

		// copy child bbox
		M_Memcpy(bbox, bsp->bbox[0], 4*sizeof (fixed_t));
	}
	else
		I_Error("WalkBSPNode: no front poly?");

	// Recursively divide back space.
	if (backpoly)
	{
		// Correct back bbox to include floor/ceiling convex polygon
		WalkBSPNode(bsp->children[1], backpoly, &bsp->children[1],
			bsp->bbox[1]);

		// enlarge bbox with seconde child
		M_AddToBox(bbox, bsp->bbox[1][BOXLEFT  ],
			bsp->bbox[1][BOXTOP   ]);
		M_AddToBox(bbox, bsp->bbox[1][BOXRIGHT ],
			bsp->bbox[1][BOXBOTTOM]);
	}
}