the features that are needed to run the game. Compile tested with scons, make, but not cmake, that seems to have an issue not related to these changes. The changes include: * GLSL support in the viewport and game engine, enable in the game menu in textured draw mode. * Synced and merged part of the duplicated blender and gameengine/ gameplayer drawing code. * Further refactoring of game engine drawing code, especially mesh storage changed a lot. * Optimizations in game engine armatures to avoid recomputations. * A python function to get the framerate estimate in game. * An option take object color into account in materials. * An option to restrict shadow casters to a lamp's layers. * Increase from 10 to 18 texture slots for materials, lamps, word. An extra texture slot shows up once the last slot is used. * Memory limit for undo, not enabled by default yet because it needs the .B.blend to be changed. * Multiple undo for image painting. * An offset for dupligroups, so not all objects in a group have to be at the origin.
376 lines
9.2 KiB
C
376 lines
9.2 KiB
C
/**
|
|
* $Id:
|
|
*
|
|
* ***** BEGIN GPL LICENSE BLOCK *****
|
|
*
|
|
* This program is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation; either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
*
|
|
* The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
|
|
* All rights reserved.
|
|
*
|
|
* The Original Code is: all of this file.
|
|
*
|
|
* Contributor(s): none yet.
|
|
*
|
|
* ***** END GPL LICENSE BLOCK *****
|
|
*/
|
|
|
|
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "DNA_mesh_types.h"
|
|
#include "DNA_meshdata_types.h"
|
|
#include "DNA_object_types.h"
|
|
#include "DNA_screen_types.h"
|
|
#include "DNA_scene_types.h"
|
|
#include "DNA_userdef_types.h"
|
|
|
|
#include "BKE_depsgraph.h"
|
|
#include "BKE_global.h"
|
|
#include "BKE_object.h"
|
|
|
|
#include "BLI_blenlib.h"
|
|
#include "BLI_dynstr.h"
|
|
|
|
#include "BKE_utildefines.h"
|
|
|
|
#include "BIF_editmesh.h"
|
|
#include "BIF_interface.h"
|
|
#include "BIF_screen.h"
|
|
#include "BIF_resources.h"
|
|
#include "BIF_toolbox.h"
|
|
#include "BIF_space.h"
|
|
|
|
#include "BDR_editcurve.h"
|
|
|
|
#include "BSE_edit.h"
|
|
|
|
#include "mydevice.h"
|
|
|
|
/* ***************** generic editmode undo system ********************* */
|
|
/*
|
|
|
|
Add this in your local code:
|
|
|
|
void undo_editmode_push(char *name,
|
|
void (*freedata)(void *), // pointer to function freeing data
|
|
void (*to_editmode)(void *), // data to editmode conversion
|
|
void * (*from_editmode)(void)) // editmode to data conversion
|
|
int (*validate_undo)(void *)) // check if undo data is still valid
|
|
|
|
|
|
Further exported for UI is:
|
|
|
|
void undo_editmode_step(int step); // undo and redo
|
|
void undo_editmode_clear(void) // free & clear all data
|
|
void undo_editmode_menu(void) // history menu
|
|
|
|
|
|
*/
|
|
/* ********************************************************************* */
|
|
|
|
/* local prototypes --------------- */
|
|
void undo_editmode_step(int step); // undo and redo
|
|
void undo_editmode_clear(void); // free & clear all data
|
|
void undo_editmode_menu(void); // history menu
|
|
void undo_editmode_push(char *name, void (*freedata)(void *),
|
|
void (*to_editmode)(void *), void *(*from_editmode)(void),
|
|
int (*validate_undo)(void *));
|
|
struct uiBlock *editmode_undohistorymenu(void *arg_unused);
|
|
|
|
|
|
|
|
#define MAXUNDONAME 64
|
|
typedef struct UndoElem {
|
|
struct UndoElem *next, *prev;
|
|
ID id; // copy of editmode object ID
|
|
Object *ob; // pointer to edited object
|
|
int type; // type of edited object
|
|
void *undodata;
|
|
uintptr_t undosize;
|
|
char name[MAXUNDONAME];
|
|
void (*freedata)(void *);
|
|
void (*to_editmode)(void *);
|
|
void * (*from_editmode)(void);
|
|
int (*validate_undo)(void *);
|
|
} UndoElem;
|
|
|
|
static ListBase undobase={NULL, NULL};
|
|
static UndoElem *curundo= NULL;
|
|
|
|
|
|
/* ********************* xtern api calls ************* */
|
|
|
|
static void undo_restore(UndoElem *undo)
|
|
{
|
|
if(undo) {
|
|
waitcursor(1);
|
|
undo->to_editmode(undo->undodata);
|
|
waitcursor(0);
|
|
|
|
countall();
|
|
}
|
|
}
|
|
|
|
/* name can be a dynamic string */
|
|
void undo_editmode_push(char *name, void (*freedata)(void *),
|
|
void (*to_editmode)(void *), void *(*from_editmode)(void),
|
|
int (*validate_undo)(void *))
|
|
{
|
|
UndoElem *uel;
|
|
int nr;
|
|
uintptr_t memused, totmem, maxmem;
|
|
|
|
/* at first here was code to prevent an "original" key to be insterted twice
|
|
this was giving conflicts for example when mesh changed due to keys or apply */
|
|
|
|
/* remove all undos after (also when curundo==NULL) */
|
|
while(undobase.last != curundo) {
|
|
uel= undobase.last;
|
|
uel->freedata(uel->undodata);
|
|
BLI_freelinkN(&undobase, uel);
|
|
}
|
|
|
|
/* make new */
|
|
curundo= uel= MEM_callocN(sizeof(UndoElem), "undo file");
|
|
strncpy(uel->name, name, MAXUNDONAME-1);
|
|
BLI_addtail(&undobase, uel);
|
|
|
|
uel->freedata= freedata;
|
|
uel->to_editmode= to_editmode;
|
|
uel->from_editmode= from_editmode;
|
|
uel->validate_undo= validate_undo;
|
|
|
|
/* limit amount to the maximum amount*/
|
|
nr= 0;
|
|
uel= undobase.last;
|
|
while(uel) {
|
|
nr++;
|
|
if(nr==U.undosteps) break;
|
|
uel= uel->prev;
|
|
}
|
|
if(uel) {
|
|
while(undobase.first!=uel) {
|
|
UndoElem *first= undobase.first;
|
|
first->freedata(first->undodata);
|
|
BLI_freelinkN(&undobase, first);
|
|
}
|
|
}
|
|
|
|
/* copy */
|
|
memused= MEM_get_memory_in_use();
|
|
curundo->undodata= curundo->from_editmode();
|
|
curundo->undosize= MEM_get_memory_in_use() - memused;
|
|
curundo->ob= G.obedit;
|
|
curundo->id= G.obedit->id;
|
|
curundo->type= G.obedit->type;
|
|
|
|
if(U.undomemory != 0) {
|
|
/* limit to maximum memory (afterwards, we can't know in advance) */
|
|
totmem= 0;
|
|
maxmem= ((uintptr_t)U.undomemory)*1024*1024;
|
|
|
|
uel= undobase.last;
|
|
while(uel && uel->prev) {
|
|
totmem+= uel->undosize;
|
|
if(totmem>maxmem) break;
|
|
uel= uel->prev;
|
|
}
|
|
|
|
if(uel) {
|
|
if(uel->prev && uel->prev->prev)
|
|
uel= uel->prev;
|
|
|
|
while(undobase.first!=uel) {
|
|
UndoElem *first= undobase.first;
|
|
first->freedata(first->undodata);
|
|
BLI_freelinkN(&undobase, first);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* helper to remove clean other objects from undo stack */
|
|
static void undo_clean_stack(void)
|
|
{
|
|
UndoElem *uel, *next;
|
|
int mixed= 0;
|
|
|
|
/* global undo changes pointers, so we also allow identical names */
|
|
/* side effect: when deleting/renaming object and start editing new one with same name */
|
|
|
|
uel= undobase.first;
|
|
while(uel) {
|
|
next= uel->next;
|
|
|
|
/* for when objects are converted, renamed, or global undo changes pointers... */
|
|
if(uel->type==G.obedit->type && strcmp(uel->id.name, G.obedit->id.name)==0 &&
|
|
(!uel->validate_undo || uel->validate_undo(uel->undodata))) {
|
|
uel->ob= G.obedit;
|
|
}
|
|
else {
|
|
mixed= 1;
|
|
uel->freedata(uel->undodata);
|
|
BLI_freelinkN(&undobase, uel);
|
|
}
|
|
|
|
uel= next;
|
|
}
|
|
|
|
if(mixed) curundo= undobase.last;
|
|
}
|
|
|
|
/* 1= an undo, -1 is a redo. we have to make sure 'curundo' remains at current situation */
|
|
void undo_editmode_step(int step)
|
|
{
|
|
|
|
/* prevent undo to happen on wrong object, stack can be a mix */
|
|
undo_clean_stack();
|
|
|
|
if(step==0) {
|
|
undo_restore(curundo);
|
|
}
|
|
else if(step==1) {
|
|
|
|
if(curundo==NULL || curundo->prev==NULL) error("No more steps to undo");
|
|
else {
|
|
if(G.f & G_DEBUG) printf("undo %s\n", curundo->name);
|
|
curundo= curundo->prev;
|
|
undo_restore(curundo);
|
|
}
|
|
}
|
|
else {
|
|
/* curundo has to remain current situation! */
|
|
|
|
if(curundo==NULL || curundo->next==NULL) error("No more steps to redo");
|
|
else {
|
|
undo_restore(curundo->next);
|
|
curundo= curundo->next;
|
|
if(G.f & G_DEBUG) printf("redo %s\n", curundo->name);
|
|
}
|
|
}
|
|
|
|
DAG_object_flush_update(G.scene, G.obedit, OB_RECALC_DATA);
|
|
allqueue(REDRAWVIEW3D, 1);
|
|
allqueue(REDRAWBUTSEDIT, 0);
|
|
allqueue(REDRAWIMAGE, 0);
|
|
}
|
|
|
|
void undo_editmode_clear(void)
|
|
{
|
|
UndoElem *uel;
|
|
|
|
uel= undobase.first;
|
|
while(uel) {
|
|
uel->freedata(uel->undodata);
|
|
uel= uel->next;
|
|
}
|
|
BLI_freelistN(&undobase);
|
|
curundo= NULL;
|
|
}
|
|
|
|
/* based on index nr it does a restore */
|
|
static void undo_number(int nr)
|
|
{
|
|
UndoElem *uel;
|
|
int a=1;
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, a++) {
|
|
if(a==nr) break;
|
|
}
|
|
curundo= uel;
|
|
undo_editmode_step(0);
|
|
}
|
|
|
|
/* ************** for interaction with menu/pullown */
|
|
|
|
void undo_editmode_menu(void)
|
|
{
|
|
UndoElem *uel;
|
|
DynStr *ds= BLI_dynstr_new();
|
|
short event;
|
|
char *menu;
|
|
|
|
undo_clean_stack(); // removes other objects from it
|
|
|
|
BLI_dynstr_append(ds, "Editmode Undo History %t");
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next) {
|
|
BLI_dynstr_append(ds, "|");
|
|
BLI_dynstr_append(ds, uel->name);
|
|
}
|
|
|
|
menu= BLI_dynstr_get_cstring(ds);
|
|
BLI_dynstr_free(ds);
|
|
|
|
event= pupmenu_col(menu, 20);
|
|
MEM_freeN(menu);
|
|
|
|
if(event>0) undo_number(event);
|
|
}
|
|
|
|
static void do_editmode_undohistorymenu(void *arg, int event)
|
|
{
|
|
|
|
if(G.obedit==NULL || event<1) return;
|
|
|
|
if (event==1) {
|
|
if(G.obedit->type==OB_MESH) remake_editMesh();
|
|
}
|
|
else undo_number(event-1);
|
|
|
|
allqueue(REDRAWVIEW3D, 0);
|
|
}
|
|
|
|
uiBlock *editmode_undohistorymenu(void *arg_unused)
|
|
{
|
|
uiBlock *block;
|
|
UndoElem *uel;
|
|
short yco = 20, menuwidth = 120;
|
|
short item=2;
|
|
|
|
undo_clean_stack(); // removes other objects from it
|
|
|
|
block= uiNewBlock(&curarea->uiblocks, "view3d_edit_mesh_undohistorymenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin);
|
|
uiBlockSetButmFunc(block, do_editmode_undohistorymenu, NULL);
|
|
|
|
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Undo All Changes", 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, 1, "");
|
|
|
|
for(uel= undobase.first; uel; uel= uel->next, item++) {
|
|
if (uel==curundo) uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
|
uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, uel->name, 0, yco-=20, menuwidth, 19, NULL, 0.0, 0.0, 1, (float)item, "");
|
|
if (uel==curundo) uiDefBut(block, SEPR, 0, "", 0, yco-=6, menuwidth, 6, NULL, 0.0, 0.0, 0, 0, "");
|
|
}
|
|
|
|
uiBlockSetDirection(block, UI_RIGHT);
|
|
uiTextBoundsBlock(block, 60);
|
|
return block;
|
|
}
|
|
|
|
void *undo_editmode_get_prev(Object *ob)
|
|
{
|
|
UndoElem *ue= undobase.last;
|
|
if(ue && ue->prev && ue->prev->ob==ob) return ue->prev->undodata;
|
|
return NULL;
|
|
}
|